Callbacks
With callbacks, the API also POSTs a session’s messages to your server: to store transcripts as they come, or process them away from the client that streams the audio.
{ "callback": true, "callback_config": { "url": "https://hooks.example.com/gladia", "receive_final_transcripts": true, "receive_post_processing_events": true, "receive_lifecycle_events": true }}callback_config chooses the messages independently of the WebSocket’s messages_config; its defaults differ
(final transcripts, post-processing and lifecycle events on, the rest off). The URL must be https, on a public
address.
Payload
Section titled “Payload”Each POST carries the session id, the event and the WebSocket message:
{ "id": "0199d7a2-5c3e-7b41-9f0a-3c2d1e4f5a6b", "event": "live.transcript", "payload": { "type": "transcript", "session_id": "0199d7a2-...", "data": { "id": "00_00000003", "is_final": true, "utterance": { "text": "..." } } }}event is live. followed by the message type: live.start_session (callbacks only, when the WebSocket opens),
live.start_recording, live.transcript, live.speech_start, live.speech_end, live.translation,
live.named_entity_recognition, live.sentiment_analysis, live.audio_chunk, live.stop_recording,
live.end_recording, live.post_transcript, live.post_final_transcript, live.end_session, live.error. The
payloads are the WebSocket messages. pii and end_of_turn messages
are not sent to callbacks.
Verify the signature
Section titled “Verify the signature”Each POST is signed with your workspace’s callback signing secret, in the header:
x-gladia-signature: t=<unix seconds>,v1=<hex>v1 is the hex HMAC-SHA256, keyed with the secret, of <t>. followed by the raw request body (the bytes as
received, before any JSON parsing). To verify a callback:
- Split the header on
,and readtandv1. - Compute
HMAC-SHA256(secret, "<t>." + raw body)and compare it withv1in constant time. - Refuse a
tolder than your replay window (5 minutes is a good default).
import hashlib, hmac, time
def verify(secret: str, header: str, raw_body: bytes, window_s: int = 300) -> bool: parts = dict(p.split("=", 1) for p in header.split(",")) t, v1 = parts.get("t", ""), parts.get("v1", "") if not t.isdigit() or abs(time.time() - int(t)) > window_s: return False expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, v1)import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(secret, header, rawBody, windowS = 300) { const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2))); if (!/^\d+$/.test(parts.t ?? "") || Math.abs(Date.now() / 1000 - Number(parts.t)) > windowS) return false; const expected = createHmac("sha256", secret).update(`${parts.t}.`).update(rawBody).digest("hex"); return expected.length === parts.v1?.length && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));}The secret (whsec_…) is created at your workspace’s first callback. Owners and admins rotate it in Gladia
Workspace, Settings → Security → Callback signing secret: the new secret is shown once, and the previous one
stops signing at once.
Delivery
Section titled “Delivery”- Messages are sent in order, one at a time per connection; a slow or failing receiver never slows the session.
- Network errors and
5xxanswers are retried, 3 attempts in all with a growing delay;4xxanswers are not. - Each POST times out after 5 seconds.
- After a session ends, its remaining messages are delivered for up to 30 seconds.
- Up to 1,024 messages wait per connection; beyond, messages are dropped.
- After a reconnection, messages of the old and new connections may interleave: order them
by the utterance
id.
Answer 2xx quickly and process the message afterwards.