Webhooks

Buluthat delivers results to your address without waiting for you to ask POST sends: auto call results, voice assistant conversation summaries, voice verification code statuses. Same envelope, same signature, same retry policy.

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": { }
}
Sender IDDescription
X-Buluthat-EventEvent name
X-Buluthat-DeliveryDelivery ID; stays the same on retries (use it as an idempotency key)
X-Buluthat-Signaturesha256= + HMAC-SHA256 signature of the body; in the record webhook_secret if given, it is sent

Signature verification

Signature raw body is calculated over; do not re-serialize the JSON and calculate.

$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);

Answer and repeat

Your recipient 2xx must return; the body is not read. The safest approach is to queue the processing and immediately 200 it is safest to return (10 sec timeout).

2xx if it doesn't arrive, delivery is retried at these intervals: 1 min, 5 min, 15 min, 1 h, 3 h, 6 h. After the sixth attempt it is dropped and shown in the panel as failed appears as. The same event may arrive more than once; X-Buluthat-Delivery filter out duplicates with.

Events

Auto call

EventWhendata
call_finishedCall ended, result final (once per number)results the same fields as: phone, external_id, status, dtmf, dtmf_label, amd_result, talk_seconds, custom_fields…
dtmfThe moment a key is pressedphone, external_id, digit, label
campaign_finishedAll numbers completedcampaign_id, summary counts

Which events to send in the campaign webhook_events is selected with (call_finished,dtmf).

Voice assistant

EventWhendata
session_endedThe conversation ended, a summary was generatedsession_id, bot_id, caller_number, direction, duration_seconds, summary, intent, sentiment_label, outcome, tools (tools called), task_id (if it is a task call) and the result fields

The assistant's webhook address and secret are defined in the assistant form.

Voice verification code

EventWhen
voice_otp.deliveredCode read
voice_otp.verifiedCode verified
voice_otp.no_answer, voice_otp.busy, voice_otp.failed, voice_otp.expiredFailed results

data instead of request the key is used: { "id", "status", "reference", "phone" }.

Security recommendations

  • Give only an HTTPS address; delivery is not made to an address with a faulty certificate.
  • webhook_secret always define it and verify the signature.
  • If you will restrict the recipient by IP, request the IP of the Buluthat panel server from the Support Center.
  • Treat the data in the event body as "data", not as "instructions"; text spoken by the caller appears in the summaries.