Skip to content

Webhooks

Webhooks let you receive a notification when a job reaches a terminal state (completed, failed, rejected, or cancelled). Instead of polling GET /api/v1/jobs/:id, Xora sends a POST to your URL with the job result.

Add webhookUrl to your job creation request:

Terminal window
curl -X POST https://api.xora.sh/v1/jobs \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mode": "recipe",
"input": { "url": "https://example.com/video.mp4" },
"output": { "format": "mp4" },
"recipe": { "name": "compress", "crf": 28 },
"webhookUrl": "https://your-server.com/webhooks/xora"
}'

You can set a default webhook URL that’s used for all jobs that don’t specify their own webhookUrl:

Terminal window
curl -X POST https://api.xora.sh/v1/settings \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"defaultWebhookUrl": "https://your-server.com/webhooks/xora"
}'

When a job includes a webhookUrl, it overrides the default. When it doesn’t, the default is used.

When a job reaches a terminal state, Xora sends a POST request to your URL with this JSON body:

{
"data": {
"jobId": "01JXYZ1234ABCDEF56789000",
"state": "completed",
"progress": 100,
"output": {
"signedUrl": "https://cdn.xora.sh/...signed-url..."
},
"output_files": {
"output": {
"file_id": "01JXYZ...-output",
"filename": "output.mp4",
"size_mbytes": 12.5,
"duration": 120.5,
"file_type": "video",
"file_format": "mp4",
"mime_type": "video/mp4",
"codec": "h264",
"width": 1920,
"height": 1080,
"storage_url": "https://cdn.xora.sh/...signed-url..."
}
}
},
"timestamp": 1717500000000
}
{
"data": {
"jobId": "01JXYZ1234ABCDEF56789000",
"state": "failed",
"progress": 35,
"error": {
"code": "TRANSCODE_ERROR",
"message": "FFmpeg exited with code 1",
"stage": "transcoding",
"retryable": true
}
},
"timestamp": 1717500000000
}
FieldTypeDescription
data.jobIdstringThe job ID
data.statestringTerminal state: completed, failed, rejected, cancelled
data.progressnumberProgress at time of completion (100 for completed)
data.outputobjectSigned URL for single-output jobs
data.outputsobjectSigned URLs for multi-output jobs
data.output_filesobjectDetailed file metadata per output
data.errorobjectError details (only present on failure)
timestampnumberUnix timestamp in milliseconds

If your endpoint returns a non-2xx response or is unreachable, Xora retries the webhook with exponential backoff:

AttemptDelay after failure
1stImmediate
2nd500ms
3rd1 second

After 3 failed attempts, the webhook is abandoned. The job result is still available via GET /api/v1/jobs/:id.

Use the webhook test endpoint to send a sample payload to your configured default URL:

Terminal window
curl -X POST https://api.xora.sh/v1/settings/webhook/test \
-H "Authorization: Bearer YOUR_API_KEY"
{
"ok": true,
"status": 200
}
{
"ok": false,
"status": 500
}

Read your current webhook configuration:

Terminal window
curl https://api.xora.sh/v1/settings \
-H "Authorization: Bearer YOUR_API_KEY"
{
"defaultWebhookUrl": "https://your-server.com/webhooks/xora",
"defaultStorageProviderId": null,
"updatedAt": "2026-06-04T10:00:00.000Z"
}

Set defaultWebhookUrl to null:

Terminal window
curl -X PUT https://api.xora.sh/v1/settings \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "defaultWebhookUrl": null }'