Platform / WebhooksSign inGet an API key

Webhooks

Get a signed request when a transcription finishes, instead of polling.

Register an endpoint

Create an endpoint in the console (Webhooks → Add endpoint) or with the API. The URL must be HTTPS and publicly reachable. The response includes the signing secret — shown once.

curl "$TWENTYSIX_BASE_URL/v1/webhook_endpoints" \
  -H "Authorization: Bearer $TWENTYSIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/hooks/transcripts"}'

Then name the endpoint in each transcription that should notify it:

JSON
{ "audio": { "upload_id": "upl_…" }, "language": "bn-IN", "webhook_endpoint_id": "wh_…" }

Events

When the transcription finishes we send one POST with a small JSON body. It never contains the transcript — fetch that with your API key.

JSON
{ "type": "transcription.completed", "data": { "id": "tr_…", "state": "completed" } }
EventWhen
transcription.completedThe full transcript is ready.
transcription.partialA partial transcript is ready (see partial results).
transcription.failedIt could not be transcribed; data.error.code says why.
transcription.cancelledIt was cancelled.

Reply with any 2xx status within ten seconds. Other replies, timeouts and redirects count as failures and are retried with increasing delays for about a day. Every attempt is listed in the console under Webhooks. A transcription's state never depends on your endpoint: if deliveries fail, the result is still there to fetch.

Verify the signature

Every request carries three headers:

HeaderValue
webhook-idUnique per event — use it to ignore duplicates.
webhook-timestampUnix seconds when it was sent.
webhook-signaturev1, followed by the base64 HMAC-SHA256 of {webhook-id}.{webhook-timestamp}.{raw body}, keyed with the base64-decoded part of your secret after whsec_.

Check the signature against the raw body before parsing it, and reject timestamps more than five minutes old.

from twentysix import verify_webhook

@app.post("/hooks/transcripts")
def hook(request):
    if not verify_webhook(SECRET, request.headers, request.body):
        return Response(status=400)
    event = json.loads(request.body)
    if event["type"] in ("transcription.completed", "transcription.partial"):
        result = client.result(event["data"]["id"])
    return Response(status=204)

Delivery behaviour

  • Events can arrive more than once; deduplicate on webhook-id.
  • Events are not guaranteed to arrive in order. Fetch the transcription if you need its latest state.
  • Endpoints that resolve to private or internal addresses are refused, at registration and on every delivery.
Scriba is a product of 26AI Private Limited, Pune, India.© 2026 26AI Private Limited