API reference
Signed webhooks
Receive call events, verify the raw body and deduplicate deliveries.
Configure endpoints in Settings → Webhooks. Endpoint management uses your signed-in workspace session; it is not part of the bearer-key public API.
Add an endpoint
- Select Add endpoint and enter a name and public HTTPS receiver URL.
- Create a private signing secret of at least 32 characters and store the same value in your receiver’s secret configuration.
- Subscribe only to needed events:
call.started,call.ended,call.failed,call.analyzedandalert.triggered. - Keep caller details and transcripts excluded unless your receiver needs and is authorized to process them.
- Enable deliveries. Inspect the Deliveries tab after an authorized test call or event.
Headers and body
X-Verbatik-Id: stable delivery identifier; also sent asIdempotency-Key.X-Verbatik-Event-Id: event identifier.X-Verbatik-Event: event type.X-Verbatik-Timestamp: Unix time in seconds for this delivery attempt.X-Verbatik-Attempt: attempt number.X-Verbatik-Signature:v1=followed by the HMAC-SHA256 hexadecimal digest.
The JSON body has id, type, occurredAt and data. An event and a delivery have different identifiers. Use the delivery header, not the body’s event ID, when verifying the signature.
Verify before processing
Compute HMAC-SHA256 using your signing secret over the UTF-8 string:
deliveryId.timestamp.rawBodyUse the original raw request body, before JSON parsing or reserialization. Reject missing or malformed headers, compare signatures with a constant-time comparison and reject timestamps more than five minutes from your server’s current time. Keep server clocks synchronized.
Node.js verifier:
import { createHmac, timingSafeEqual } from 'node:crypto';
export function validWebhook(rawBody, headers, secret) {
const id = headers.get('x-verbatik-id');
const timestamp = headers.get('x-verbatik-timestamp');
const signature = headers.get('x-verbatik-signature');
if (!id || !timestamp || !signature) return false;
const seconds = Number(timestamp);
if (!Number.isSafeInteger(seconds) ||
Math.abs(Date.now() / 1000 - seconds) > 300) return false;
const digest = createHmac('sha256', secret)
.update(id + '.' + timestamp + '.' + rawBody).digest('hex');
const expected = Buffer.from('v1=' + digest);
const actual = Buffer.from(signature);
return expected.length === actual.length &&
timingSafeEqual(expected, actual);
}After verification, atomically record the delivery ID and enqueue your own durable processing before returning a 2xx acknowledgement. If an ID was already accepted, acknowledge it without repeating side effects.
Retries and inspection
Delivery has a ten-second receiver deadline. Temporary responses such as 408, 425, 429 and 5xx are retryable; redirects are not followed. Numeric Retry-After values are bounded to one hour. Automatic attempts are bounded, so inspect failed deliveries and use Redeliver only after fixing the receiver.
Editing endpoint settings cancels queued attempts tied to the prior settings. Redacting saved caller content cancels pending sensitive deliveries; copies already received by another system cannot be recalled.
Do not assume event ordering or process the same delivery twice. A call-ended notification does not guarantee that call analysis or final billing is already complete.