Skip to main content

Why webhooks

Asset processing, cuts, and renders all finish asynchronously. Rather than poll GET /v1/assets/{id}, GET /v1/cuts/{id}, or GET /v1/renders/{id}, register an endpoint and Roughy POSTs you a signed event when each one reaches a terminal state. Delivery is handled by Svix: HMAC-signed payloads, automatic retries with backoff, and a dashboard / CLI for inspection and replay. Webhooks are a convenience layer, not the source of truth — if delivery is ever degraded, the read endpoints remain authoritative.

Managing endpoints

# Create an endpoint subscribed to a set of event types
curl https://api.roughy.ai/v1/webhook-endpoints \
  -H "Authorization: Bearer $ROUGHY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/roughy",
    "event_types": ["cut.completed", "render.completed"]
  }'

# List your endpoints
curl https://api.roughy.ai/v1/webhook-endpoints \
  -H "Authorization: Bearer $ROUGHY_API_KEY"

# Remove one
curl -X DELETE https://api.roughy.ai/v1/webhook-endpoints/{endpoint_id} \
  -H "Authorization: Bearer $ROUGHY_API_KEY"

Event catalog

Event typeFired when
cut.completedA cut finished computing and its kept-segment plan is available.
cut.failedA cut reached failed.
render.completedA render finished and its output is available.
render.failedA render failed.
render.cancelledA render was cancelled.
asset.readyAn asset reached ready and is usable (cuttable and renderable). data.previous_state is processing or pending_payment.
asset.pending_paymentAn asset finished processing but the charge couldn’t be settled — it awaits a top-up.
asset.failedAn asset reached failed — its media could not be ingested. The reason is on the asset’s error_code / error_message.

Payload shape

Each delivery has a JSON body of exactly two top-level keys — type and data:
{
  "type": "cut.completed",
  "data": { "entity_id": "<cut_id>", "asset_id": "<asset_id>" }
}
The Svix message id and send timestamp ride in the svix-id and svix-timestamp headers, not in the JSON body.

Idempotency + verification

Each message carries the standard Svix headers (svix-id, svix-timestamp, svix-signature) — verify the signature with your endpoint’s signing secret, and dedupe on svix-id. Roughy also sets a stable idempotency key per terminal event, so an internal retry never delivers the same terminal state to your endpoint twice.

Browser live-updates

For an in-app editor, first-party browser clients receive live updates over a separate Server-Sent Events stream. Public API integrations use webhooks.
  • Cut / Render — the operations whose terminal states you subscribe to.
  • Errors — the synchronous failure envelope (webhooks cover async outcomes).