Webhooks
Set webhook_url when you create a job, and the API sends one POST request, a callback, to that URL when the job ends. Use a webhook when you don't want to poll. This page covers jobs created with an API key. x402 jobs have their own webhooks.
Delivery
- The API makes one attempt and doesn't retry. If your endpoint is down or returns an error, that webhook is lost. Get the job to recover its status and output.
- The request has no signature, and its only header is
Content-Type. Anyone who knows your webhook URL can send it requests. Treat a webhook as a notification and confirm the result by getting the job. You can add a secret to yourwebhook_url, such ashttps://your-domain.com/webhook?token=SOME_SECRET, and check it. - The API sends final results only. It doesn't send progress. To receive progress and segments as they're transcribed, use
can_stream_output. - A job that completes or fails sends a webhook. A job you cancel may also send one.
- Make your handler safe to run twice for the same job
id. The API makes one attempt, but a canceled job can send a webhook after its result, and anyone who knows the URL can repeat a request.
Payloads
Completed
The body is the same JSON as the completed job response, shortened here:
{
"id": "00000000-1111-7222-b333-444444444444-sic",
"output": {
"duration": 1246.679375,
"request": { "input_url": "https://example.com/audio-file.m4a" },
"segments": [
{
"confidence": 0.988,
"end": 7.296,
"id": 1,
"language": "en (99.60%)",
"processing_duration_in_s": 0.029,
"start": 0,
"text": "Imagine staring at um just a massive hard drive filled with gigabytes of raw video files.",
"words": []
}
]
},
"status": "COMPLETED"
}
If you created the job with an output_format, the body is the transcript in that format instead, with Content-Type: application/x-subrip for SRT or text/vtt for VTT. That body doesn't contain the job ID. To tell jobs apart, add your own identifier to each job's webhook_url, for example https://your-domain.com/webhook?ref=episode-42. Failed and canceled jobs always send JSON.
The webhook carries the full transcript even for a job created with is_private. Only request.input_url is redacted. See Private Jobs.
Failed
{
"id": "00000000-1111-7222-b333-444444444444-sic",
"output": {
"error": "Expected at least 6 seconds; got 2.5 seconds"
},
"status": "FAILED"
}
output.error is a human-readable message, not a code. The webhook is the only place to get it, because getting a failed job returns an empty output. See Failures After a Job Is Accepted for the common messages.
Two failures happen while you create the job. The create request returns 400 Bad Request when the input_url isn't reachable, and 500 Internal Server Error with Failed to start a new transcribe job when the API can't start the job. In both cases the webhook also receives a FAILED payload with the same message, such as Input URL is not reachable: https://example.com/audio-file.mp3 (HTTP 403). The API doesn't save that job ID, so getting it returns 404 Not Found.
Canceled
{
"id": "00000000-1111-7222-b333-444444444444-sic",
"output": {},
"status": "CANCELED"
}
The API sends this when a job you canceled stops. output can contain an error message that describes the cancellation.
Compared With x402 Webhooks
| Jobs API | x402 API | |
|---|---|---|
| Attempts | One | Up to 12 within 24 hours |
| Signature and headers | No signature. Content-Type only | No signature. Content-Type, X-SiC-Event-ID, and X-SiC-Event-Type |
| Event types | None. Every webhook is a job result | x402.outcome for a job result and x402.payment for a refund update |