Webhooks
Buluthat envía los resultados a su dirección sin esperar a que usted los consulte POST se envía: resultados de llamadas automáticas, resúmenes de conversaciones del asistente de voz, estados de códigos de verificación por voz. El mismo sobre, la misma firma, la misma política de reintentos.
Sobre
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": { }
}
| Encabezado | Descripción |
|---|---|
X-Buluthat-Event | Nombre del evento |
X-Buluthat-Delivery | Identificador de entrega; se mantiene igual en los reintentos (úselo como clave de idempotencia) |
X-Buluthat-Signature | sha256= + firma HMAC-SHA256 del cuerpo; en el registro webhook_secret si se indica, se envía |
Verificación de firma
Firma cuerpo sin procesar se calcula sobre; no lo calcule volviendo a serializar el JSON.
$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);
Respuesta y reintento
Su destinatario 2xx debe devolver; el cuerpo no se lee. Ponga el procesamiento en cola y devuelva de inmediato 200 lo más saludable es responder (10 s de tiempo de espera).
2xx si no llega, la entrega se reintenta con estos intervalos: 1 min, 5 min, 15 min, 1 h, 3 h, 6 h. Tras el sexto intento se abandona y en el panel failed aparece como. El mismo evento puede llegar más de una vez; X-Buluthat-Delivery elimine los duplicados con.
Eventos
Llamadas automáticas
| Evento | Cuándo | data |
|---|---|---|
call_finished | La llamada terminó, el resultado es definitivo (una vez por número) | results los mismos campos que: phone, external_id, status, dtmf, dtmf_label, amd_result, talk_seconds, custom_fields… |
dtmf | En el momento en que se pulsa la tecla | phone, external_id, digit, label |
campaign_finished | Todos los números completados | campaign_id, cifras resumen |
Qué eventos se enviarán en la campaña webhook_events se selecciona con (call_finished,dtmf).
Asistente de voz
| Evento | Cuándo | data |
|---|---|---|
session_ended | La conversación terminó y se generó el resumen | session_id, bot_id, caller_number, direction, duration_seconds, summary, intent, sentiment_label, outcome, tools (herramientas invocadas), task_id (si es una llamada de tarea) y los campos de resultado |
La dirección y el secreto del webhook del asistente se definen en el formulario del asistente.
Código de verificación por voz
| Evento | Cuándo |
|---|---|
voice_otp.delivered | Código leído |
voice_otp.verified | Código verificado |
voice_otp.no_answer, voice_otp.busy, voice_otp.failed, voice_otp.expired | Resultados fallidos |
data en lugar de request se usa la clave: { "id", "status", "reference", "phone" }.
Recomendaciones de seguridad
- Indique solo direcciones HTTPS; no se entrega a una dirección con certificado erróneo.
webhook_secretdefínalo siempre y verifique la firma.- Si va a restringir al destinatario por IP, solicite la IP del servidor del panel de Buluthat en el Centro de Soporte.
- Trate los datos del cuerpo del evento como "datos" y no como "instrucciones"; lo que dijo el llamante aparece en los resúmenes.
