Search Documentation
Search across all documentation pages
Webhooks

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

EventDescription
job.completedJob finished processing all outputs.
job.failedJob failed with an error.
job.progressJob progress updated.
job.canceledJob was canceled.
output.completedIndividual output rendition completed.
output.failedIndividual output rendition failed.

Webhook payload

Webhook payloads are sent as JSON POST requests to your configured URL.

FieldTypeDescription
eventstringEvent type (e.g., job.completed).
job_idstringJob ID (e.g., job_a1b2c3d4e5f6).
app_idstringApp ID (e.g., app_k1l2m3n4o5).
timestampstringISO 8601 event timestamp.
dataobjectEvent-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"
fi

TypeScript

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:

AttemptDelay
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.