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": { }
}
EncabezadoDescripción
X-Buluthat-EventNombre del evento
X-Buluthat-DeliveryIdentificador de entrega; se mantiene igual en los reintentos (úselo como clave de idempotencia)
X-Buluthat-Signaturesha256= + 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

EventoCuándodata
call_finishedLa 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…
dtmfEn el momento en que se pulsa la teclaphone, external_id, digit, label
campaign_finishedTodos los números completadoscampaign_id, cifras resumen

Qué eventos se enviarán en la campaña webhook_events se selecciona con (call_finished,dtmf).

Asistente de voz

EventoCuándodata
session_endedLa conversación terminó y se generó el resumensession_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

EventoCuándo
voice_otp.deliveredCódigo leído
voice_otp.verifiedCódigo verificado
voice_otp.no_answer, voice_otp.busy, voice_otp.failed, voice_otp.expiredResultados 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_secret defí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.