Cal.com Booking Intake
Transforma agendamentos do Cal.com em leads e reuniões no CRM, criando a pessoa no Core quando preciso.
- Plataforma
- n8n · Abrir no n8n
- Status
- Ativo
- ID do workflow
QtaJdIPuUy8l6AXn- Nós
- 32
- Versão no n8n
d1323e82-7bd7-4179-b1d9-2c00b8a9f260- Último salvamento no n8n
- 2026-11-06T21:25:19.572Z
O Cal.com Booking Intake transforma em registros do CRM um agendamento feito no Cal.com pela landing page de um vendedor. Uma reserva nova vira um Deal (o lead) em Meeting Scheduled, ou passa para Meeting Scheduled o Deal aberto da pessoa com aquele vendedor, mais uma Interaction (a reunião) para o vendedor certo, e a pessoa é criada no Core quando ninguém tem aquele e-mail. Um reagendamento move a mesma reunião; um cancelamento ou recusa a marca como cancelada e põe o próximo contato da reunião em hoje. Um único webhook atende a conta do Cal.com de todos os vendedores.
O maintainer do CRM cuida do roteamento por vendedor e da recuperação; o maintainer do Core revisa as pessoas que o workflow cria.
Fluxo
Gerado da versão do n8n do quadro acima: cada caixa é um nó e cada seta é uma ligação no n8n. Pílulas rosa são gatilhos, hexágonos amarelos são nós If e Switch com as saídas nas setas, cilindros azuis leem (Airtable, Data Table, HTTP GET), cilindros verdes gravam, caixas roxas chamam outro workflow e setas tracejadas são saídas de erro.
Código Mermaid
flowchart TD
subgraph g0["Receber e conferir"]
n0(["Cal.com Webhook"]):::trigger
n26["Sign Body"]:::logic
n29["Verify & Parse"]:::logic
n13{{"Is New Booking?"}}:::decision
n14{{"Is Reschedule?"}}:::decision
end
subgraph g1["Reserva nova"]
n8[("Find Duplicate<br/><small>GET CRM Interaction</small>")]:::read
n12[("Get Lead Sources<br/><small>GET CRM Lead Source</small>")]:::read
n11[("Find Person (CRM)<br/><small>GET CRM People</small>")]:::read
n25["Resolve"]:::logic
n17{{"Needs Core?"}}:::decision
n10[("Find Person (Core)<br/><small>GET Core People</small>")]:::read
n5{{"Exists in Core?"}}:::decision
n3[("Create Person (Core)<br/><small>POST Core People</small>")]:::write
n30["Wait 1 min"]:::wait
n24[("Recheck Person (CRM)<br/><small>GET CRM People</small>")]:::read
n27["Sync Check"]:::logic
n19{{"Person Ready?"}}:::decision
n7[("Find Deals<br/><small>GET CRM Deal</small>")]:::read
n21["Prepare Deal"]:::logic
n4{{"Existing Deal?"}}:::decision
n28[("Update Deal<br/><small>PATCH CRM Deal</small>")]:::write
n1[("Create Deal<br/><small>POST CRM Deal</small>")]:::write
n22["Prepare Interaction"]:::logic
n2[("Create Interaction<br/><small>POST CRM Interaction</small>")]:::write
end
subgraph g2["Reagendamento"]
n9[("Find Original Meeting<br/><small>GET CRM Interaction</small>")]:::read
n23["Prepare Reschedule"]:::logic
n18{{"Original Found?"}}:::decision
n16[("Move Meeting<br/><small>PATCH CRM Interaction</small>")]:::write
end
subgraph g3["Cancelamento e recusa"]
n6[("Find Booked Meeting<br/><small>GET CRM Interaction</small>")]:::read
n20["Prepare Cancellation"]:::logic
n15[("Mark Meeting Cancelled<br/><small>PATCH CRM Interaction</small>")]:::write
end
n0 --> n26
n26 --> n29
n29 --> n13
n13 -->|sim| n8
n13 -->|não| n14
n14 -->|sim| n9
n14 -->|não| n6
n9 --> n23
n23 --> n18
n18 -->|sim| n16
n18 -->|não| n8
n6 --> n20
n20 --> n15
n8 --> n12
n12 --> n11
n11 --> n25
n25 --> n17
n17 -->|sim| n10
n17 -->|não| n7
n10 --> n5
n5 -->|sim| n30
n5 -->|não| n3
n3 --> n30
n30 --> n24
n24 --> n27
n27 --> n19
n19 -->|sim| n7
n19 -->|não| n30
n7 --> n21
n21 --> n4
n4 -->|sim| n28
n4 -->|não| n1
n1 --> n22
n22 --> n2
n28 --> n22
classDef trigger fill:#fde7f3,stroke:#d6409f,color:#111
classDef decision fill:#fff7e0,stroke:#c99a00,color:#111
classDef read fill:#e8f1ff,stroke:#3b82f6,color:#111
classDef write fill:#e7f8ee,stroke:#16a34a,color:#111
classDef subflow fill:#f3e8ff,stroke:#9333ea,color:#111
classDef wait fill:#f1f1f1,stroke:#888,color:#111
classDef logic fill:#ffffff,stroke:#999,color:#111Sistemas que ele toca
| Sistema | Tabela | Lê | Grava |
|---|---|---|---|
| CRM | Interaction | Find Duplicate, Find Original Meeting, Find Booked Meeting | Create Interaction, Move Meeting, Mark Meeting Cancelled |
| CRM | Deal | Find Deals | Create Deal, Update Deal |
| CRM | People (mirror de People do Core) | Find Person (CRM), Recheck Person (CRM) | Nada |
| CRM | Automation: Lead Source | Get Lead Sources | Nada |
| Core | People | Find Person (Core) | Create Person (Core) |
Toda chamada ao Airtable é um nó HTTP Request com uma credencial de token do Airtable; o workflow não usa o nó Airtable. As leituras filtram pelo nome do campo ({Booking UID}, {Email} no mirror do CRM, {Email*} no Core); as gravações usam IDs de campo. Renomear um desses três campos quebra as buscas sem erro: a fórmula falha e o nó não devolve nada.
Os outros sistemas só registram o workflow. Communication o lista como o canal Webhook cal-com-bookings → n8n → CRM. Systems Central tem uma linha de System para ele. O Login e o Tool Identifier que ele compara são lookups que chegam ao CRM vindos de Tooling and Access pelo mirror Tool Account.
Gatilho e assinatura
O Cal.com Webhook aceita um POST num path fixo. Ele usa o modo de resposta padrão, então o Cal.com recebe HTTP 200 assim que a requisição chega, antes de qualquer conferência ou gravação. Um 200 no log de webhooks do Cal.com só diz que o n8n recebeu a chamada.
O Sign Body calcula o HMAC-SHA256 de JSON.stringify(body) com o segredo do webhook do Cal.com. O Verify & Parse compara o resultado com o header x-cal-signature-256 e lança “Invalid Cal.com signature (x-cal-signature-256)” quando eles diferem ou o header falta. Essa execução termina em erro e não grava nada.
O Verify & Parse só segue para BOOKING_CREATED, BOOKING_RESCHEDULED, BOOKING_CANCELLED e BOOKING_REJECTED. Qualquer outro evento não devolve itens e a execução termina ali.
O que o Verify & Parse lê do payload
| Valor | De onde vem, nesta ordem | Quando falta |
|---|---|---|
| Nome e sobrenome | responses.name (objeto com firstName/lastName, ou texto separado no primeiro espaço), depois o nome do primeiro participante |
O nome completo vira “Cal.com lead” |
| E-mail do lead | responses.email, depois o e-mail do primeiro participante; sem espaços e em minúsculas |
Toda busca por e-mail vira FALSE() |
| Telefone | responses.attendeePhoneNumber, responses.phone, depois o telefone do participante |
Não é gravado |
| Notas do lead | additionalNotes, responses.notes, depois description |
Não são gravadas |
| Chaves da conta do vendedor | E-mail, username e id do organizador, mais externalId e primaryEmail de cada calendário de destino; em minúsculas |
Nenhum vendedor identificado |
| Início da reunião | startTime |
O horário em que o n8n processou a chamada |
| Dia da reunião e hoje | Data no fuso do organizador | Fuso America/Sao_Paulo; um fuso inválido cai na data UTC |
| Booking UID | uid, depois bookingId |
As buscas de duplicata e de cancelamento viram FALSE() |
| UID original (reagendamento) | rescheduleUid, fromReschedule, depois originalRescheduledBooking.uid |
A busca do original vira FALSE() |
| Motivo | cancellationReason, depois rejectionReason |
Não é anexado |
| URL da reunião | metadata.videoCallUrl, videoCallData.url, location quando começa com http, depois https://app.cal.com/booking/<uid> |
Não é gravada |
| Título | title |
“Cal.com meeting” |
Reserva nova
BOOKING_CREATED passa por estes passos, nesta ordem.
- Checagem de duplicata. O Find Duplicate procura qualquer Interaction com o mesmo Booking UID. O Resolve para a execução quando encontra uma.
- Roteamento por vendedor. O Get Lead Sources lê Automation: Lead Source. O Resolve pega a primeira linha que está Active, cujo Tool inclui
cal.come cujo Tool Identifier ou Login é igual a uma das chaves da conta do vendedor. O Follow up Target: Person da linha vira a pessoa responsável e o Follow up Target: Company, a empresa responsável. Quando nenhuma linha bate, o fluxo segue sem vendedor. - Pessoa no CRM. O Find Person (CRM) procura o e-mail no mirror People do CRM. Quando acha, o fluxo vai direto para o passo 5.
- Pessoa no Core. Sem pessoa no CRM e com e-mail, o Find Person (Core) procura em People do Core. Quando a pessoa também não está lá, o Create Person (Core) a cria com os campos de Campos gravados. Nos dois casos o fluxo então espera o mirror do CRM: Wait 1 min, Recheck Person (CRM), Sync Check. Ele sai do loop quando a pessoa aparece ou depois da décima tentativa, cerca de dez minutos. Sem e-mail, os passos 3 e 4 não acham ninguém e nada é criado no Core.
- Deal. O Find Deals lê os Deals ligados à pessoa (o Deals (Counterparty) dela). O Prepare Deal fica com os abertos (Deal Status Open) e reaproveita o primeiro cujo Accountable (Person)* contém a pessoa roteada ou, sem vendedor roteado, o primeiro sem pessoa responsável. O Update Deal passa esse Deal para Meeting Scheduled quando o Deal Stage* é Lead, Contacted ou vazio; um estágio posterior (Meeting Scheduled, Pitched, Proposal Sent, Negotiation) é mantido. Sem correspondência, ou sem pessoa no CRM, o Create Deal cria um Deal novo em Meeting Scheduled.
- Interaction. O Create Interaction cria a reunião, ligada a esse Deal em Deal° e à pessoa em People°, com Result Meeting booked. Nenhum Relationship é criado, e o fluxo não preenche Campaign° no Deal.
Reagendamento
BOOKING_RESCHEDULED roda o Find Original Meeting com o UID original. O Prepare Reschedule só aceita uma Interaction cujo Result é Meeting booked.
Quando acha uma, o Move Meeting atualiza essa mesma Interaction: Date*, Title*, Booking UID (agora o UID novo), Next Contact Date° e Next Action°, a Evidence URL quando o payload traz URL da reunião, e uma linha anexada a Note. O Deal não muda, nem Result e Accountable.
Quando não acha nenhuma (o original foi cancelado ou nunca chegou ao CRM), o item vai para o Find Duplicate; veja em Lacunas conhecidas o que acontece ali.
Cancelamento e recusa
BOOKING_CANCELLED e BOOKING_REJECTED rodam o Find Booked Meeting com o UID atual. O Prepare Cancellation escolhe a Interaction cujo Result é Meeting booked. Quando não há nenhuma (já cancelada, ou a reserva nunca chegou ao CRM), a execução termina sem gravar.
O Mark Meeting Cancelled põe Result em Meeting cancelled, Next Contact Date° em hoje, Next Action° em “Meeting cancelled, reach out to rebook” (“rejected” numa recusa) e anexa a Note uma linha com o motivo. É nessa Interaction que o follow-up aparece. O Deal não muda: o Deal Stage* continua em Meeting Scheduled, ou onde estava.
Campos gravados
People do Core (Create Person (Core))
| Campo | Valor |
|---|---|
| First Name* | Nome, ou o nome completo quando não há nome |
| Last Name* | Sobrenome, só quando existe |
| Email* | E-mail do lead |
| Telephone* | Telefone, só quando existe |
| Status | Proposed |
| Tags | cal-lead-n8n |
| Notes | “Created by n8n from a Cal.com booking (uid …) on <data>.” mais o nome da Lead Source e a conta do Cal.com |
Deal do CRM
O workflow não grava nada no Relationship, que foi apagado em 04/11/2026.
| Campo | Create Deal | Update Deal |
|---|---|---|
| Deal Stage* | Meeting Scheduled | Meeting Scheduled quando estava em Lead, Contacted ou vazio; senão, o estágio atual |
| Source° | Inbound | Não muda |
| Notes° | Cartão do lead, notas do lead e avisos quando faltam a pessoa ou o vendedor | Não muda |
| Tags | cal-lead-n8n | Não muda |
| People° | A pessoa do CRM, quando encontrada | Não muda |
| Accountable (Person)* | Pessoa roteada, quando encontrada | Não muda |
| Accountable (Company)* | Empresa roteada, quando encontrada | Não muda |
O Company° não é gravado; o vendedor preenche quando souber a empresa do lead.
Interaction do CRM
| Campo | Create Interaction | Move Meeting | Mark Meeting Cancelled |
|---|---|---|---|
| Deal° | O Deal do passo 5 | Não muda | Não muda |
| People° | A pessoa do CRM, quando encontrada | Não muda | Não muda |
| Date* | Início da reunião | Novo início | Não muda |
| Title* | Título da reunião | Novo título | Não muda |
| Type | Meeting | Não muda | Não muda |
| Result | Meeting booked | Não muda | Meeting cancelled |
| Note | Cartão do lead, notas do lead, local, conta do Cal.com, nome da Lead Source | Anexa “Rescheduled via Cal.com on <hoje> from <início antigo>.” | Anexa “Meeting cancelled via Cal.com on <hoje>.” e o motivo |
| Booking UID | Booking UID | UID novo | Não muda |
| Evidence URL | URL da reunião, quando existe | Nova URL da reunião, quando existe | Não muda |
| Next Contact Date° | Dia da reunião | Novo dia da reunião | Hoje |
| Next Action° | “Meeting booked via Cal.com: <título>” | “Meeting rescheduled via Cal.com: <título>” | “Meeting cancelled (ou rejected), reach out to rebook” |
| Accountable (Person) | Pessoa roteada, quando encontrada | Não muda | Não muda |
| Accountable (Company) | Empresa roteada, quando encontrada | Não muda | Não muda |
| Tags | cal-lead-n8n | Não muda | Não muda |
O Company° não é gravado. As gravações em Deal, Interaction e People do Core mandam typecast: true, então um valor de select que ainda não existe (um Result ou Deal Stage* renomeado, por exemplo) é criado como opção nova em vez de falhar.
Nós
O workflow tem 32 nós: 31 que executam e uma nota adesiva. Num nó If, a saída 0 é verdadeiro e a saída 1 é falso.
| Nó | Tipo | O que faz |
|---|---|---|
| Cal.com Webhook | webhook | Recebe o POST do Cal.com e responde na hora |
| Sign Body | crypto | HMAC-SHA256 do body já parseado com o segredo do Cal.com |
| Verify & Parse | code | Confere a assinatura, descarta outros eventos, normaliza a reserva |
| Is New Booking? | if | BOOKING_CREATED ou não |
| Find Duplicate | httpRequest | Interaction do CRM com o mesmo Booking UID |
| Get Lead Sources | httpRequest | Todas as linhas de Automation: Lead Source do CRM (primeira página) |
| Find Person (CRM) | httpRequest | Mirror People do CRM pelo e-mail |
| Resolve | code | Para em duplicata, identifica o vendedor, prepara a pessoa do Core |
| Needs Core? | if | Sem pessoa no CRM e com e-mail |
| Find Person (Core) | httpRequest | People do Core pelo e-mail |
| Exists in Core? | if | Pessoa do Core encontrada ou não |
| Create Person (Core) | httpRequest | POST de uma pessoa Proposed em People do Core |
| Wait 1 min | wait | Espera um minuto pela sincronização do mirror |
| Recheck Person (CRM) | httpRequest | Mirror People do CRM pelo e-mail de novo |
| Sync Check | code | Conta tentativas; termina ao achar ou após 10 |
| Person Ready? | if | Sai do loop ou espera de novo |
| Find Deals | httpRequest | Os Deals do CRM da pessoa |
| Prepare Deal | code | Escolhe o Deal aberto do vendedor e o novo estágio, ou monta um Deal novo |
| Existing Deal? | if | Atualizar ou criar |
| Update Deal | httpRequest | PATCH do Deal Stage* do Deal reaproveitado |
| Create Deal | httpRequest | POST de um Deal novo em Meeting Scheduled |
| Prepare Interaction | code | Monta a Interaction da reunião |
| Create Interaction | httpRequest | POST da Interaction |
| Is Reschedule? | if | BOOKING_RESCHEDULED ou cancelamento/recusa |
| Find Original Meeting | httpRequest | Interaction do CRM pelo Booking UID original |
| Prepare Reschedule | code | Escolhe a Interaction Meeting booked e monta o patch dela |
| Original Found? | if | Interaction original em Meeting booked encontrada ou não |
| Move Meeting | httpRequest | PATCH na Interaction original |
| Find Booked Meeting | httpRequest | Interaction do CRM pelo Booking UID atual |
| Prepare Cancellation | code | Escolhe a Interaction Meeting booked e monta o patch dela |
| Mark Meeting Cancelled | httpRequest | PATCH na Interaction para Meeting cancelled |
| Sticky Note | stickyNote | Nota de configuração no canvas; não executa |
Ligações
| De | Saída | Para |
|---|---|---|
| Cal.com Webhook | 0 | Sign Body |
| Sign Body | 0 | Verify & Parse |
| Verify & Parse | 0 | Is New Booking? |
| Is New Booking? | 0 | Find Duplicate |
| Is New Booking? | 1 | Is Reschedule? |
| Find Duplicate | 0 | Get Lead Sources |
| Get Lead Sources | 0 | Find Person (CRM) |
| Find Person (CRM) | 0 | Resolve |
| Resolve | 0 | Needs Core? |
| Needs Core? | 0 | Find Person (Core) |
| Needs Core? | 1 | Find Deals |
| Find Person (Core) | 0 | Exists in Core? |
| Exists in Core? | 0 | Wait 1 min |
| Exists in Core? | 1 | Create Person (Core) |
| Create Person (Core) | 0 | Wait 1 min |
| Wait 1 min | 0 | Recheck Person (CRM) |
| Recheck Person (CRM) | 0 | Sync Check |
| Sync Check | 0 | Person Ready? |
| Person Ready? | 0 | Find Deals |
| Person Ready? | 1 | Wait 1 min |
| Find Deals | 0 | Prepare Deal |
| Prepare Deal | 0 | Existing Deal? |
| Existing Deal? | 0 | Update Deal |
| Existing Deal? | 1 | Create Deal |
| Update Deal | 0 | Prepare Interaction |
| Create Deal | 0 | Prepare Interaction |
| Prepare Interaction | 0 | Create Interaction |
| Is Reschedule? | 0 | Find Original Meeting |
| Is Reschedule? | 1 | Find Booked Meeting |
| Find Original Meeting | 0 | Prepare Reschedule |
| Prepare Reschedule | 0 | Original Found? |
| Original Found? | 0 | Move Meeting |
| Original Found? | 1 | Find Duplicate |
| Find Booked Meeting | 0 | Prepare Cancellation |
| Prepare Cancellation | 0 | Mark Meeting Cancelled |
Configurações e tratamento de erro
| Configuração | Valor |
|---|---|
| Ordem de execução | v1 |
| Workflow de erro | Nenhum |
| Retry on fail | Desligado em todos os nós |
| On error | Para o workflow (padrão) em todos os nós |
| Caller policy | Workflows do mesmo dono |
| Disponível no MCP | Sim |
| Retenção dos dados de execução | Padrão da instância; este workflow não define |
Não há agendamento; o workflow só roda quando o Cal.com o chama. Uma chamada ao Airtable que falha para a execução onde está, depois que as gravações anteriores já foram salvas.
Lacunas conhecidas
Estas vêm da configuração dos nós. A versão com Deal foi publicada em 04/11/2026 e testada só numa simulação local (quatro casos); nenhuma reserva real passou por ela ainda. Na versão anterior, com Relationship, três caminhos rodaram no n8n: uma reserva nova para uma pessoa existente, um reagendamento com o original encontrado e um cancelamento.
| Lacuna | Efeito |
|---|---|
| Um reagendamento cujo original não é encontrado chega ao Find Duplicate sem a fórmula de duplicata | O Find Duplicate filtra com fórmula vazia, o Airtable devolve a primeira Interaction da tabela e o Resolve para. O reagendamento não grava nada |
| O segredo do webhook fica nos parâmetros do Sign Body, não numa credencial do n8n | Quem abre o workflow consegue ler, e uma exportação do workflow o leva junto |
| A assinatura é calculada sobre o body já parseado, não sobre a requisição crua | Um payload cujo JSON re-serializado difere do que o Cal.com assinou falha como assinatura inválida |
| O Cal.com recebe 200 antes da conferência da assinatura | Uma entrega recusada ou que falhou parece bem-sucedida no Cal.com; só a lista de execuções do n8n mostra o erro |
| A checagem de duplicata é uma busca, não uma trava | Duas entregas da mesma reserva no mesmo instante podem criar registros as duas |
| Get Lead Sources e Find Deals leem uma página (até 100 registros) | Acima de 100 linhas de Lead Source, ou 100 Deals de uma pessoa, uma correspondência pode ser perdida |
| Uma reserva sem e-mail, ou uma pessoa que ainda não está no mirror depois de dez minutos, cria um Deal sem People° e uma Interaction sem People° | A linha de Notes° sempre diz que a pessoa não chegou ao mirror a tempo, mesmo quando a causa é a falta de e-mail. Uma reserva seguinte da mesma pessoa cria outro Deal |
| Só Deals abertos são reaproveitados | Uma reserva de uma pessoa cujo Deal com aquele vendedor está Won, Lost ou Abandoned cria um Deal novo em Meeting Scheduled |
| O Company° nunca é gravado no Deal nem na Interaction | O Name do Deal mostra só a pessoa até o vendedor ligar a empresa |
Recuperar uma gravação parcial
Veja o que foi salvo antes de reenviar uma entrega.
| O que foi salvo | O que fazer |
|---|---|
| Pessoa criada no Core, mirror do CRM ainda sem ela | Espere ou corrija a sincronização, depois ligue a pessoa em People° do Deal novo e da Interaction |
| Deal gravado, Interaction falhou | Procure a Interaction pelo Booking UID antes de reenviar; o reenvio reaproveita o Deal aberto quando ele tem People° e o mesmo vendedor, e cria a Interaction |
| Dois registros para um Booking UID | Fique com um, mova os links dele, depois apague o outro; nada os junta automaticamente |
Manter esta página em dia
O quadro no topo mostra a versão do n8n que esta página descreve. Abra o workflow no n8n: quando o último salvamento dele é mais novo que a data do quadro, esta página está desatualizada até a próxima revisão. Todos os nós e todas as ligações dessa versão estão em Nós e Ligações.