# Cal.com Booking Intake

Transforma agendamentos do Cal.com em leads e reuniões no CRM, criando a pessoa no Core quando preciso.

| Platform | Workflow ID | Status | Version | Last saved | Nodes | Systems |
| --- | --- | --- | --- | --- | --- | --- |
| n8n | QtaJdIPuUy8l6AXn | Active | d1323e82-7bd7-4179-b1d9-2c00b8a9f260 | 2026-11-06T21:25:19.572Z | 32 | CRM, Core |

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.

```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:#111
```

## Sistemas 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](/pt-br/systems/communication/automations) o lista como o canal Webhook `cal-com-bookings → n8n → CRM`. [Systems Central](/pt-br/systems/systems-central/entity-system) 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](/pt-br/systems/tooling/automations) 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.

1. **Checagem de duplicata.** O Find Duplicate procura qualquer Interaction com o mesmo Booking UID. O Resolve para a execução quando encontra uma.
2. **Roteamento por vendedor.** O Get Lead Sources lê Automation: Lead Source. O Resolve pega a primeira linha que está Active, cujo Tool inclui `cal.com` e 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.
3. **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.
4. **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](#fields-written). 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.
5. **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.
6. **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](#known-gaps) 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 &lt;data&gt;." 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 &lt;hoje&gt; from &lt;início antigo&gt;." | Anexa "Meeting cancelled via Cal.com on &lt;hoje&gt;." 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: &lt;título&gt;" | "Meeting rescheduled via Cal.com: &lt;título&gt;" | "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](#nodes) e [Ligações](#connections).