Webhooks
Webhooks notify your application when events occur in Transcodely. Configure a webhook endpoint on your App to receive HTTP POST callbacks for job lifecycle events.
Events
| Event | Description |
|---|---|
job.completed | Job finished processing all outputs. |
job.failed | Job failed with an error. |
job.progress | Job progress updated. |
job.canceled | Job was canceled. |
output.completed | Individual output rendition completed. |
output.failed | Individual output rendition failed. |
Webhook payload
Webhook payloads are sent as JSON POST requests to your configured URL.
| Field | Type | Description |
|---|---|---|
event | string | Event type (e.g., job.completed). |
job_id | string | Job ID (e.g., job_a1b2c3d4e5f6). |
app_id | string | App ID (e.g., app_k1l2m3n4o5). |
timestamp | string | ISO 8601 event timestamp. |
data | object | Event-specific payload containing the full job or output object. |
Example: job.completed
{
"event": "job.completed",
"job_id": "job_a1b2c3d4e5f6",
"app_id": "app_k1l2m3n4o5",
"timestamp": "2025-02-28T10:05:45Z",
"data": {
"id": "job_a1b2c3d4e5f6",
"status": "completed",
"progress": 100,
"total_actual_cost": 0.42,
"currency": "EUR",
"outputs": [
{
"id": "out_r4s5t6u7v8",
"status": "completed",
"format": "mp4",
"file_size": 15728640,
"duration": 120.5
}
],
"completed_at": "2025-02-28T10:05:45Z"
}
}Example: output.failed
{
"event": "output.failed",
"job_id": "job_a1b2c3d4e5f6",
"app_id": "app_k1l2m3n4o5",
"timestamp": "2025-02-28T10:05:30Z",
"data": {
"id": "out_w9x0y1z2a3",
"status": "failed",
"format": "webm",
"error_code": "CODEC_UNSUPPORTED",
"error_message": "VP9 encoding failed: input codec incompatible"
}
}Verifying signatures
If you generated a signing secret when configuring your app’s webhook, each request includes an X-Webhook-Signature header containing an HMAC-SHA256 hex digest of the raw request body.
Bash
SECRET="whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
BODY='{"event":"job.completed","job_id":"job_a1b2c3d4e5f6"}'
EXPECTED=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
RECEIVED="<value from X-Webhook-Signature header>"
if [ "$EXPECTED" = "$RECEIVED" ]; then
echo "Signature valid"
else
echo "Signature invalid"
fiTypeScript
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyWebhookSignature(
body: string,
signature: string,
secret: string,
): boolean {
const expected = createHmac("sha256", secret).update(body).digest("hex");
return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
// In your webhook handler:
const body = await request.text();
const signature = request.headers.get("X-Webhook-Signature") ?? "";
const secret = process.env.WEBHOOK_SECRET;
if (!verifyWebhookSignature(body, signature, secret)) {
return new Response("Invalid signature", { status: 401 });
}
const payload = JSON.parse(body);
// Process the event...Configuring webhooks
Webhooks are configured per-app. Set the webhook URL, events, and optionally generate a signing secret when creating or updating an app.
curl -X POST https://api.transcodely.com/transcodely.v1.AppService/Create
-H "Authorization: Bearer ak_live_abc123"
-H "X-Organization-ID: org_f6g7h8i9j0"
-H "Content-Type: application/json"
-d '{
"org_id": "org_f6g7h8i9j0",
"name": "Production",
"webhook": {
"url": "https://api.example.com/webhooks/transcodely",
"generate_secret": true,
"events": ["job.completed", "job.failed", "output.failed"]
}
}'The response includes a webhook_secret field. Store it securely — it is only returned once.
{
"app": {
"id": "app_k1l2m3n4o5",
"name": "Production",
"webhook": {
"url": "https://api.example.com/webhooks/transcodely",
"secret_hint": "o5p6",
"has_secret": true,
"events": ["job.completed", "job.failed", "output.failed"]
},
"status": "active",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
},
"webhook_secret": "whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
}To update the webhook URL or events, or to regenerate the signing secret:
curl -X POST https://api.transcodely.com/transcodely.v1.AppService/Update
-H "Authorization: Bearer ak_live_abc123"
-H "X-Organization-ID: org_f6g7h8i9j0"
-H "Content-Type: application/json"
-d '{
"id": "app_k1l2m3n4o5",
"webhook": {
"url": "https://api.example.com/v2/webhooks/transcodely",
"events": ["job.completed", "job.failed", "job.canceled", "output.failed"],
"regenerate_secret": true
}
}'Retry policy
Failed deliveries (non-2xx responses or timeouts after 30 seconds) are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | ~1 minute |
| 2nd retry | ~10 minutes |
| 3rd retry | ~1 hour |
After 3 failed retries, the delivery is marked as failed. No further attempts are made.