Documentação dos webhooks · versão 1

Webhooks de saída

Receba no seu CRM, na hora, cada passo do lead: quando ele chega, qualifica, vai para um atendente, muda de etapa e vira venda. Cada aviso sai assinado, é tentado até 6 vezes e fica no registro de entregas por 30 dias.

13 eventosJSON · UTF-8Assinatura HMAC SHA-256Atualizada em 25/09/2026
POSThttps://seu-sistema.com.br/avisosJSON
{  "event": "lead.qualified",  "event_id": "5b1e2c7a-6a1f-4c1e-9a0e-3f2d9b7c1a10",  "timestamp": "2026-09-24T17:02:11.482913+00:00",  "occurred_at": "2026-09-24T17:02:10.917204Z",  "lead_id": "7f0c2a9e-1b3d-4e8f-9a01-6c5d4e3f2a1b",  "external_id": "DL-88213",  "score": 82,  "band": "hot",  "qualified_by": "ia"}
Nesta página · Visão geral

Visão geral

Um webhook é um endereço do seu sistema que recebe um aviso a cada evento que você escolheu. O painel manda um POST com um JSON, assinado com o segredo daquele webhook. Se o seu sistema não responder, tentamos de novo sozinhos, e cada tentativa fica no registro de entregas.

  1. Evento no painel
  2. Aviso assinado
  3. Seu sistema
  4. Registro de entregas
1

Crie o webhook

No painel, em Integrações → Webhooks → Novo webhook: o endereço (https) e os eventos, em grupos.

2

Guarde o segredo

O segredo de assinatura (whsec_…) aparece uma vez, quando o webhook é criado. É com ele que o seu sistema confere cada aviso.

3

Mande um teste

O botão Testar do cartão manda um exemplo com dados fictícios. O resultado aparece no próprio cartão.

Receber um aviso

POSThttps://seu-sistema.com.br/avisos

O aviso chega como POST no endereço que você cadastrou, com o corpo em JSON (UTF-8). Responda com qualquer código 2xx em até 5 segundos: é o que conta como entregue. Se o processamento do seu lado demora, responda primeiro e processe depois.

  • Só endereços https, na porta padrão, na internet. Endereço de rede interna é bloqueado.
  • Não seguimos redirecionamento: cadastre o endereço final.
  • Guardamos até 500 caracteres do começo da sua resposta no registro de entregas. Uma mensagem curta de erro ajuda você mesmo a descobrir o que houve.

Conferir a assinatura

Qualquer um que descubra o endereço pode mandar um POST para ele. A assinatura prova que o aviso saiu do painel e não foi mexido no caminho.

  1. Leia os cabeçalhos X-Webhook-Timestamp e X-Webhook-Signature-V2.
  2. Calcule o HMAC SHA-256 de timestamp.corpo (o carimbo, um ponto e o corpo cru, exatamente como chegou) com o segredo de assinatura, em hexadecimal, e compare com o que vem depois de sha256=.
  3. Recuse avisos com mais de 5 minutos e use X-Webhook-Id para não processar o mesmo aviso duas vezes.
import crypto from "node:crypto";

// corpo: o texto cru da requisição, exatamente como chegou (não o JSON já lido).
function avisoValido(corpo, cabecalhos, segredo) {
  const ts = Number(cabecalhos["x-webhook-timestamp"]);
  if (!ts || Math.abs(Date.now() / 1000 - ts) > 300) return false;
  const esperada =
    "sha256=" + crypto.createHmac("sha256", segredo).update(`${ts}.${corpo}`).digest("hex");
  const recebida = cabecalhos["x-webhook-signature-v2"] ?? "";
  return (
    esperada.length === recebida.length &&
    crypto.timingSafeEqual(Buffer.from(esperada), Buffer.from(recebida))
  );
}
Confira sobre o corpo cru
Ler o JSON e montar o texto de novo muda espaços e a ordem das chaves, e a assinatura deixa de bater. Guarde os bytes que chegaram e assine exatamente eles.

A assinatura antiga, X-Webhook-Signature (HMAC SHA-256 só do corpo), continua saindo, para quem já integrou. Para integração nova, use a V2: ela leva o carimbo de hora, e um aviso copiado não pode ser repetido depois.

Perdeu o segredo? Em Editar, Gerar segredo novo troca na hora: o anterior para de valer, e o seu sistema precisa ser atualizado logo depois.

Envelope

Todo aviso começa com os mesmos campos. Depois deles vêm os campos do evento.

CampoO que é
eventA chave do evento, como lead.qualified. É a mesma do cabeçalho X-Webhook-Event.
event_idO ID único do evento (uuid). O reenvio usa o mesmo: é por ele que o seu sistema não duplica.
occurred_atQuando o evento aconteceu no painel, em UTC com Z (2026-09-24T17:02:10.917204Z). Use para ordenar.
timestampQuando o corpo foi montado, em UTC no formato de sempre (2026-09-24T17:02:11.482913+00:00).
lead_idO ID do lead no painel (menos em order.imported, que é um lote).
external_idO ID do lead no seu sistema, quando ele veio por uma integração de entrada. Senão, null.
testtrue no evento de teste. Não vem nos avisos de verdade.

Os 13 eventos

Cada webhook escolhe os eventos que quer receber. Os avisos saem sem dados pessoais, como nos exemplos abaixo; veja .

Leads

EventoQuando saiCampos
lead.created
Lead criado
Chegou um lead novo por conversa, integração, cadastro manual, comentário ou story. Não sai em importação por planilha nem para lead de teste.
  • external_id O ID do lead no seu sistema, da entrada mais recente que trouxe um. Vem null quando o lead nunca veio por integração.
  • source Por onde o lead chegou: whatsapp, instagram, integracao, manual e outros.
  • integration O nome da integração de entrada, quando veio por uma.
  • origin O anúncio de origem: platform, campaign_name, adset_name, ad_name e ad_id.
  • created_at Quando o lead nasceu no painel.
lead.qualified
Lead qualificado
A IA qualificou o lead, ou alguém levou o cartão para a coluna de qualificado. Sai uma vez por lead: reaberto e qualificado de novo, ele não sai outra vez.
  • external_id O ID do lead no seu sistema, da entrada mais recente que trouxe um. Vem null quando o lead nunca veio por integração.
  • score A nota, de 0 a 100.
  • band A temperatura: hot, warm, cold ou unscored (sem nota: alguém levou à coluna de qualificado um lead cuja conversa ainda não trouxe informação; aí o score vem 0).
  • bant_filled need, budget, authority e timing: true quando a conversa já trouxe aquela informação.
  • qualified_by ia ou pessoa.
lead.hot
Lead quente
O lead virou quente. Não sai de novo enquanto ele continuar quente.
  • band hot.
  • score A nota, de 0 a 100.
lead.opted_out
Pediu para não receber mensagens
O lead pediu para parar de receber mensagens. Se outra ferramenta sua dispara mensagens, avise-a no mesmo minuto.
  • opted_out_at Quando o pedido foi registrado.
lead.anonymized
Dados apagados (LGPD)
Os dados do lead foram apagados a pedido do titular. O external_id é lido antes de apagar, para o seu sistema achar a cópia dele e apagar também.
  • external_id O ID do lead no seu sistema, da entrada mais recente que trouxe um. Vem null quando o lead nunca veio por integração.
{
  "event": "lead.created",
  "event_id": "5b1e2c7a-6a1f-4c1e-9a0e-3f2d9b7c1a10",
  "timestamp": "2026-09-24T17:02:11.482913+00:00",
  "occurred_at": "2026-09-24T17:02:10.917204Z",
  "lead_id": "7f0c2a9e-1b3d-4e8f-9a01-6c5d4e3f2a1b",
  "external_id": "DL-88213",
  "source": "integracao",
  "integration": "Distribuidor — anúncios",
  "origin": {
    "platform": "meta",
    "campaign_name": "Lançamento Alcântara",
    "adset_name": "Alcântara 25-40",
    "ad_name": "Vídeo varanda",
    "ad_id": "120210000000000003"
  },
  "created_at": "2026-09-24T17:02:10.917204Z"
}

Funil

EventoQuando saiCampos
lead.stage_changed
Mudou de etapa
O cartão mudou de coluna no Funil: pela IA, por uma pessoa, pelo desfecho, pela entrada de leads ou pela API.
  • board O quadro: id e name.
  • from A coluna de onde saiu: column_id, key, name e role. null quando o cartão acabou de nascer.
  • to A coluna para onde foi, com os mesmos campos.
  • by Quem moveu: ia, pessoa, desfecho, entrada, api ou sistema.
  • key, role e custom_fields O nome da coluna na API (o mesmo de stage), o papel dela no funil (entrada, qualificado, atendimento, intermediaria, ganho ou perdido) e os campos do negócio pelo Nome na API, moeda em centavos. Campo sensível nunca vem. role vem vazio enquanto a coluna não tiver papel.
lead.won
Venda (ganho)
O negócio foi marcado como ganho. Antes dele sai o lead.stage_changed da ida para a coluna de ganho, com by: "desfecho".
  • value_cents O valor da venda em centavos (R$ 389.000,00 vira 38900000).
  • currency BRL.
  • won_at Quando foi ganho.
  • by Quem marcou.
  • custom_fields Os campos do negócio pelo Nome na API, moeda em centavos. Campo sensível nunca vem.
lead.lost
Perdido
O negócio foi marcado como perdido, com o motivo. Antes dele sai o lead.stage_changed da ida para a coluna de perdido.
  • reason A chave do motivo.
  • reason_label O motivo em português, como aparece no painel.
  • lost_at Quando foi perdido.
  • by Quem marcou.
lead.reopened
Negócio reaberto
Um negócio ganho ou perdido voltou para o funil.
  • reopened_at Quando foi reaberto.
  • by Quem reabriu.
{
  "event": "lead.stage_changed",
  "event_id": "5b1e2c7a-6a1f-4c1e-9a0e-3f2d9b7c1a10",
  "timestamp": "2026-09-24T17:02:11.482913+00:00",
  "occurred_at": "2026-09-24T17:02:10.917204Z",
  "lead_id": "7f0c2a9e-1b3d-4e8f-9a01-6c5d4e3f2a1b",
  "external_id": "DL-88213",
  "board": {
    "id": "b1c8e0d4-2f6a-4e1b-9c3d-5a7e8f9b0c1d",
    "name": "Imobiliária"
  },
  "from": {
    "column_id": "c4a19f3e-7b2d-4c8a-9e1f-3d5b7a9c1e2f",
    "key": "visita",
    "name": "Visita",
    "role": "intermediaria"
  },
  "to": {
    "column_id": "c5b20e4f-8c3e-4d9b-af20-4e6c8b0d2f3a",
    "key": "pasta",
    "name": "Pasta",
    "role": "intermediaria"
  },
  "by": "pessoa",
  "custom_fields": {
    "valor_imovel": 45000000,
    "tipo_financiamento": "Financiamento bancário"
  }
}

Atendimento

EventoQuando saiCampos
lead.assigned
Atribuído a um atendente
O lead foi para um atendente: pelo rodízio, à mão, pela coluna do Funil ou porque o prazo para responder estourou.
  • conversation_id A conversa.
  • attendant id, name e email do atendente. Vão sempre: são dados da sua equipe, não do lead.
  • reason rodizio, manual, coluna ou sla (prazo estourado).
conversation.handoff
Transbordo para humano
A conversa passou da IA para uma pessoa. No transbordo automático com rodízio chegam dois avisos: um com reason: "ia" (a IA pediu uma pessoa) e outro com reason: "atribuicao" (quem recebeu), cada um com o seu event_id.
  • conversation_id A conversa.
  • reason ia, atribuicao ou manual (pedido na tela).
  • attendant id e name de quem recebeu, só em atribuicao.
lead.first_contact
Primeiro contato
Saiu o resultado da primeira mensagem automática da entrada de leads.
  • status sent (enviado), skipped (dispensado), suppressed (não saiu por regra), failed (falhou) ou uncertain (não confirmado).
  • reason O motivo em português, como aparece no registro de entradas.
{
  "event": "lead.assigned",
  "event_id": "5b1e2c7a-6a1f-4c1e-9a0e-3f2d9b7c1a10",
  "timestamp": "2026-09-24T17:02:11.482913+00:00",
  "occurred_at": "2026-09-24T17:02:10.917204Z",
  "lead_id": "7f0c2a9e-1b3d-4e8f-9a01-6c5d4e3f2a1b",
  "external_id": "DL-88213",
  "conversation_id": "e4f5a6b7-c8d9-4e0f-a1b2-c3d4e5f6a7b8",
  "attendant": {
    "id": "a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d",
    "name": "Juliana Prado",
    "email": "juliana@exemplo.com"
  },
  "reason": "rodizio"
}

Pedidos

EventoQuando saiCampos
order.imported
Pedidos importados
Chegaram pedidos pela API de pedidos ou por planilha. Não fala de um lead: é o resumo do lote.
  • source api ou csv (planilha).
  • imported Pedidos novos.
  • updated Pedidos que já existiam e foram atualizados.
  • total Linhas recebidas no lote.
{
  "event": "order.imported",
  "event_id": "5b1e2c7a-6a1f-4c1e-9a0e-3f2d9b7c1a10",
  "timestamp": "2026-09-24T17:02:11.482913+00:00",
  "occurred_at": "2026-09-24T17:02:10.917204Z",
  "source": "api",
  "imported": 12,
  "updated": 2,
  "total": 14
}

Dados pessoais

Os avisos levam identificadores: o ID do lead, a etapa, a nota, os valores e os campos do negócio que não são sensíveis (campo de texto livre leva o que o time escreveu nele). Nome, telefone, e-mail e o resumo da conversa não saem, e campo sensível nunca sai.

  • Quando o titular pede para apagar os dados, sai o lead.anonymized: apague do seu sistema o que você guardou sobre ele.

Cabeçalhos

POSTCabeçalhos de cada aviso
Content-Type: application/json
User-Agent: Webhooks/1.0
X-Webhook-Event: lead.qualified
X-Webhook-Id: 5b1e2c7a-6a1f-4c1e-9a0e-3f2d9b7c1a10
X-Webhook-Timestamp: 1790269331
X-Webhook-Signature: sha256=3f9a0c1b7e2d…
X-Webhook-Signature-V2: sha256=7d0c9a41e5b8…
CabeçalhoO que é
X-Webhook-EventA chave do evento.
X-Webhook-IdO event_id. Igual no reenvio.
X-Webhook-TimestampO carimbo de hora da assinatura, em segundos (Unix).
X-Webhook-Signature-V2sha256= e o HMAC de timestamp.corpo. É a que recomendamos conferir.
X-Webhook-Signaturesha256= e o HMAC só do corpo. A assinatura antiga, que continua saindo.
User-AgentWebhooks/1.0.

Até 09/02/2027, os avisos também levam X-AttendTo-Event e X-AttendTo-Signature, com os mesmos valores. Troque para os X-Webhook-* antes disso.

Novas tentativas

Resposta 2xx conta como entregue. Sem resposta em 5 s, erro no seu servidor (5xx), recusa (4xx) ou certificado com problema: tentamos de novo nestes intervalos, até 6 tentativas.

  1. 1Na hora
  2. 2+1 min
  3. 3+5 min
  4. 4+30 min
  5. 5+2 h
  6. 6+6 h
  7. Paramos
  • Uma resposta 410 faz a gente parar na hora: é o jeito de o seu sistema dizer que aquele endereço não existe mais.
  • Endereço que aponta para uma rede interna é bloqueado e não é tentado de novo.
  • Depois da 6ª tentativa, a entrega fica como Não entregue no registro, com o botão Reenviar.

Pausa automática

Pausa depois de
50
entregas seguidas sem sucesso
Tentativas
6
por aviso
Registro
30 dias
com o botão Reenviar

Depois de 50 avisos seguidos que esgotaram as tentativas, pausamos o webhook e avisamos o dono da conta por e-mail e no sino. Nada se perde: as entregas que ficaram de fora aparecem no registro, e os avisos do período em que ele ficou pausado também, como Não entregue. Corrija o endereço no sistema que recebe, clique em Reativar e reenvie as falhas.

Ordem e duplicidade

Os avisos podem chegar fora de ordem quando há novas tentativas, e o mesmo aviso pode chegar mais de uma vez (o reenvio, por exemplo). Então:

  • Ordene pelo campo occurred_at, e não pela hora em que o aviso chegou.
  • Guarde os event_id já processados e ignore um que chegar de novo.
  • No transbordo automático com rodízio chegam dois conversation.handoff: um com reason: "ia" e outro com reason: "atribuicao", junto do lead.assigned. São eventos diferentes, com event_id diferentes.
  • Na venda e na perda, primeiro sai o lead.stage_changed com by: "desfecho" e depois o lead.won ou o lead.lost.

Evento de teste

O botão Testar do cartão manda o evento que você escolher com dados fictícios (“Lead de teste”), a marca "test": true e um event_id novo a cada teste. É uma tentativa só, sem nova tentativa, e ela aparece no registro de entregas. O teste não conta para a pausa automática.

Registro de entregas

Em Integrações → Entregas fica tudo o que saiu nos últimos 30 dias. Abra uma entrega para ver o corpo enviado, o começo da resposta, os cabeçalhos e cada tentativa. Reenviar manda de novo com o mesmo event_id, para o seu sistema não duplicar. Depois de 30 dias, as entregas são apagadas sozinhas.

Esta página ajudou?

Precisa de ajuda no primeiro webhook?

Fale com a gente pelo chat do painel. A nossa equipe acompanha você até o primeiro aviso chegar.

Abrir Webhooks no painel