Skip to main content
O Webhook de Atendimento notifica seu sistema externo sempre que uma conversa é finalizada no Talk. A cada encerramento, o Talk envia um POST em JSON para a URL configurada, contendo os dados da conversa e do contato. Opcionalmente, o payload pode incluir o resumo gerado por IA, o histórico de mensagens e a nota CSAT. A configuração fica em Configurações → Webhooks → Finalização de Atendimento (talk.saninternet.com/settings/webhooks).
O disparo acontece quando a conversa é finalizada de fato. Isso inclui a finalização manual pelo atendente, o encerramento automático por inatividade e a finalização após a avaliação CSAT (com ou sem resposta do cliente).

Configuração

1

Ative o webhook

Em Configurações → Webhooks → Finalização de Atendimento, ligue a chave Ativar webhook.
2

Informe a URL de destino

O endpoint do seu sistema que receberá o POST. Use sempre https:// em produção.
3

Gere o secret de assinatura

Clique em Gerar novo e guarde o valor com segurança. Ele será usado para validar a autenticidade de cada entrega.
4

Escolha o conteúdo do payload

Conversa e contato são sempre enviados. Você pode incluir também o resumo da IA, o histórico de mensagens e a nota CSAT.
5

Defina o filtro de relevância (opcional)

Com Registrar apenas atendimentos relevantes ativo, conversas triviais (como um cumprimento sem continuação) não são enviadas ao seu sistema.
6

Teste a entrega

O botão Testar webhook envia um payload de exemplo (com test: true) para validar a integração antes de ativar.

Identificando o cliente: o ID externo

Para o seu sistema saber a qual cliente o atendimento pertence, cada contato do Talk possui um campo ID externo: um código livre que referencia o cliente no seu sistema (por exemplo, o ID dele no seu ERP ou CRM). O atendente define esse valor no painel de detalhes do contato, dentro do Inbox (seção Conversa, campo ID externo). Uma vez preenchido, ele acompanha o contato em todas as finalizações futuras e chega no payload como contact.externalId.
Quando o ID externo não estiver preenchido, contact.externalId chega como null. Nesse caso, use contact.phoneNumber como alternativa de vínculo.

Formato do payload

{
  "event": "conversation.finished",
  "version": "v1",
  "requestId": "f3a1c2d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "sentAt": "2026-06-12T15:40:49.716Z",
  "data": {
    "conversation": {
      "id": "c0a8012e-7d3b-4f1a-9e2c-5b6d8f0a1c3e",
      "createdAt": "2026-06-12T14:02:11.000Z",
      "closedAt": "2026-06-12T15:40:49.000Z",
      "closeReason": "Dúvida resolvida pelo atendente",
      "attendant": { "id": "...", "name": "João Atendente" },
      "sector": { "id": "...", "name": "Suporte" },
      "channel": { "id": "...", "type": "ROOKIE", "name": "whatsapp-principal" }
    },
    "contact": {
      "id": "...",
      "name": "Camila Ribeiro",
      "phoneNumber": "5511999999999",
      "externalId": "ERP-12345"
    },
    "summary": {
      "text": "Cliente entrou em contato com dúvida sobre fatura...",
      "score": 9.2,
      "generatedAt": "2026-06-12T15:41:30.000Z"
    },
    "csat": {
      "rating": 5,
      "comment": "Atendimento excelente!",
      "respondedAt": "2026-06-12T15:43:02.000Z"
    },
    "messages": [
      { "sender": "USER", "content": "Oi, tenho uma dúvida sobre minha fatura", "sentAt": "2026-06-12T14:02:11.000Z" }
    ]
  }
}

Campos especiais

CampoSignificado
summary: nullResumo desativado na configuração. Se vier acompanhado de summaryPending: true, o resumo foi solicitado mas não ficou pronto a tempo.
csat: { rating: null }O cliente não respondeu a avaliação antes do tempo limite.
contact.externalId: nullNenhum ID externo definido para o contato.
messages: nullHistórico de mensagens desativado na configuração.
test: trueEntrega gerada pelo botão Testar webhook, com dados fictícios. Não registre como atendimento real.
Quando o resumo da IA está habilitado, a entrega aguarda a geração do resumo e pode atrasar alguns minutos em relação ao encerramento da conversa.

Entrega e resposta

  • O POST é enviado com Content-Type: application/json.
  • Seu endpoint deve responder com status 2xx em até 30 segundos.
  • Recomendação: receba, salve e responda. Deixe processamentos demorados para depois da resposta.

Headers enviados

HeaderConteúdo
X-Webhook-EventNome do evento (conversation.finished).
X-Webhook-SignatureAssinatura do corpo: sha256=<HMAC-SHA256(corpo bruto, secret)>.
X-Request-IdIdentificador único da entrega.

Validando a assinatura

Toda entrega é assinada com HMAC-SHA256 usando o secret configurado. Recalcule o HMAC sobre o corpo bruto da requisição (antes do parse do JSON) e compare com o valor do header X-Webhook-Signature usando comparação de tempo constante. Requisições com assinatura inválida devem ser rejeitadas.
const crypto = require('crypto');

// use o corpo bruto (antes do JSON.parse) para calcular o HMAC
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const esperado =
    'sha256=' + crypto.createHmac('sha256', SECRET).update(req.body).digest('hex');

  const recebido = req.headers['x-webhook-signature'] || '';
  const valido =
    recebido.length === esperado.length &&
    crypto.timingSafeEqual(Buffer.from(recebido), Buffer.from(esperado));

  if (!valido) return res.status(401).end();

  const evento = JSON.parse(req.body);
  res.status(200).end();
});
O secret nunca trafega nas requisições. Se ele vazar, gere um novo na tela de configuração e salve. As próximas entregas passam a usar o novo valor imediatamente.