call.started
Fires when an inbound or outbound call is initiated and the call leg enters the platform.
Status: This event will start emitting once gateway-go upstream signals land. The contract documented here is stable.
call.started fires as soon as a call leg is registered in the platform, before any ringing or answer occurs. It is routed per-number via voice_callback_url / events_url and is not subscribable as a workspace webhook; configure it on the Routing tab for the relevant number.
{
"kind": "call.started",
"event_id": "01900000-0000-7000-8000-000000000001",
"workspace_id": "01900000-0000-7000-8000-000000000002",
"occurred_at": "2026-06-27T10:00:00.000Z",
"data": {
"call_id": "01900000-0000-7000-8000-000000000003",
"direction": "inbound",
"from": "+254700000001",
"to": "+254700000002",
"number_id": "01900000-0000-7000-8000-000000000004",
"status": "started"
}
}Every webhook delivery includes the following request headers:
| Header | Description |
|---|---|
X-Sautikit-Signature | HMAC-SHA256 of the raw body, hex-encoded. Verify with your subscription secret. |
X-Sautikit-Idempotency-Key | Unique delivery ID for deduplication. |
X-Sautikit-Event | Literal event kind: call.started. |
event_id or the X-Sautikit-Idempotency-Key header.dead_letter.import { createHmac } from "node:crypto";
export async function POST(req) {
const sig = req.headers["x-sautikit-signature"];
const body = await req.text();
const expected = createHmac("sha256", process.env.WEBHOOK_SECRET)
.update(body)
.digest("hex");
if (sig !== expected) return new Response("Forbidden", { status: 403 });
const event = JSON.parse(body);
if (event.kind === "call.started") {
const { call_id, direction, from, to } = event.data;
console.log(`Call ${call_id} started: ${direction} ${from} -> ${to}`);
}
return new Response("OK", { status: 200 });
}voice_callback_url / events_url on the Numbers Routing tab.