Webhooks
A Buluthat envia os resultados para o seu endereço sem esperar que os peça POST envia: resultados das chamadas automáticas, resumos de conversas do assistente de voz, estados dos códigos de verificação por voz. O mesmo envelope, a mesma assinatura, a mesma política de repetição.
Envelope
POST https://sizin-adresiniz/buluthat
Content-Type: application/json
X-Buluthat-Event: call_finished
X-Buluthat-Delivery: 4471
X-Buluthat-Signature: sha256=9f2b…
{
"event": "call_finished",
"sent_at": "2026-09-18T10:12:35+03:00",
"tenant_id": 1,
"data": { }
}
| Cabeçalho | Descrição |
|---|---|
X-Buluthat-Event | Nome do evento |
X-Buluthat-Delivery | Identificador de entrega; mantém-se igual nas novas tentativas (use-o como chave de idempotência) |
X-Buluthat-Signature | sha256= + assinatura HMAC-SHA256 do corpo; no registo webhook_secret se for indicado, é enviado |
Validação da assinatura
Assinatura corpo bruto é calculada a partir de; não volte a serializar o JSON para calcular.
$raw = file_get_contents('php://input');
$given = $_SERVER['HTTP_X_BULUTHAT_SIGNATURE'] ?? '';
$expect = 'sha256=' . hash_hmac('sha256', $raw, $secret);
if (!hash_equals($expect, $given)) { http_response_code(401); exit; }
$event = json_decode($raw, true);
// Node.js (express, raw body ile)
const crypto = require('crypto');
const expect = 'sha256=' + crypto.createHmac('sha256', secret).update(req.rawBody).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expect), Buffer.from(req.get('X-Buluthat-Signature') || ''))) return res.sendStatus(401);
Resposta e repetição
O seu interlocutor 2xx deve devolver; o corpo não é lido. O mais seguro é pôr o processamento em fila e devolver de imediato 200 (prazo de 10 s).
2xx se não chegar, a entrega é repetida nos seguintes intervalos: 1 min, 5 min, 15 min, 1 h, 3 h, 6 h. Após a sexta tentativa é abandonado e no painel failed aparece como. O mesmo evento pode chegar mais de uma vez; X-Buluthat-Delivery elimine a repetição com.
Eventos
Chamadas automáticas
| Evento | Quando | data |
|---|---|---|
call_finished | A chamada terminou, o resultado é definitivo (uma vez por número) | results campos iguais aos de: phone, external_id, status, dtmf, dtmf_label, amd_result, talk_seconds, custom_fields… |
dtmf | No momento em que a tecla é premida | phone, external_id, digit, label |
campaign_finished | Todos os números concluídos | campaign_id, totais resumidos |
Quais os eventos a enviar na campanha webhook_events escolhe-se com (call_finished,dtmf).
Assistente de voz
| Evento | Quando | data |
|---|---|---|
session_ended | A conversa terminou e o resumo foi gerado | session_id, bot_id, caller_number, direction, duration_seconds, summary, intent, sentiment_label, outcome, tools (ferramentas invocadas), task_id (se for uma chamada de tarefa) e os campos de resultado |
O endereço do webhook e o segredo do assistente são definidos no formulário do assistente.
Código de verificação por voz
| Evento | Quando |
|---|---|
voice_otp.delivered | Código lido |
voice_otp.verified | Código validado |
voice_otp.no_answer, voice_otp.busy, voice_otp.failed, voice_otp.expired | Resultados com falha |
data em vez de request é utilizada a chave: { "id", "status", "reference", "phone" }.
Recomendações de segurança
- Indique apenas endereços HTTPS; não se entrega a endereços com certificado inválido.
webhook_secretdefina sempre e valide a assinatura.- Se for restringir o destinatário por IP, peça ao Centro de Apoio o IP do servidor do painel Buluthat.
- Trate os dados do corpo do evento como "dados" e não como "instruções"; o texto dito por quem liga aparece nos resumos.
