Skip to main content

Overview

Use webhooks to receive real-time notifications when a task changes state. This lets you react to long-running media processing without polling. To set up a webhook, go to our dashboard and create a new endpoint.
  • Provider: Svix
  • Events: task.progress, task.completed, task.failed
  • Payload envelope: common event fields plus data.task

Event payload

All webhooks share the same envelope. The event_type matches the event you subscribe to and the data object contains the resource payloads. For task events, only data.task is populated.
{
  "timestamp": "2025-01-01T12:00:00Z",
  "user_id": "3f5d7e38-8c7c-4a22-9a7d-5702a8c3a111",
  "event_id": "8b7a2e8f-3a1c-4ff9-9f5e-4b8e0a9ee000",
  "event_type": "task.progress",
  "data": {
    "task": {
      "id": "2e1c7e42-4f8d-4e10-8d7a-0a6c0d3b1234",
      "asset_id": "c4b6b5f1-6a2d-4d75-9a90-1f6f4a0a2222",
      "features": {
        "transcription": { "state": "processing" },
        "visual": { "state": "queued" },
        "preview": { "state": "completed" },
        "proxy": { "state": "na" },
      },
      "user_id": "3f5d7e38-8c7c-4a22-9a7d-5702a8c3a111",
      "created": "2025-01-01T11:55:00Z",
      "updated": "2025-01-01T12:00:00Z"
    }
  }
}

Envelope schema

{
  "timestamp": "string (ISO 8601)",
  "user_id": "uuid",
  "event_id": "uuid",
  "event_type": "'task.progress'|'task.completed'|'task.failed'",
  "data": {
    "task": { "Task": "see schema below" }
  }
}

Task schema

This matches the API response TaskGetResponse used throughout the server.
{
  "id": "uuid",
  "asset_id": "uuid",
  "features": {
    "proxy": { "state": "queued|processing|completed|failed|cancelled|na" },
    "preview": { "state": "queued|processing|completed|failed|cancelled|na" },
    "visual": { "state": "queued|processing|completed|failed|cancelled|na" },
    "transcription": { "state": "queued|processing|completed|failed|cancelled|na" }
  },
  "user_id": "uuid",
  "created": "string (ISO 8601)",
  "updated": "string (ISO 8601)"
}

Supported events

  • task.progress: One or more features changed state (e.g., queued → processing)
  • task.completed: All user-facing features reached a terminal success state
  • task.failed: One or more user-facing features failed

Set up an endpoint

Create and manage webhook endpoints via API. We use Svix applications per user; your first endpoint creation will provision one if needed. The create response includes the endpoint secret.

Create endpoint

POST /webhooks/endpoints Request
{
  "url": "https://your-domain.com/webhooks/aspect",
  "description": "Prod listener",
  "event_types": ["task.*"],
  "rate_limit": 120,
  "version": 1
}
Response
{
  "endpoint": {
    "id": "ep_123",
    "url": "https://your-domain.com/webhooks/aspect",
    "description": "Prod listener",
    "event_types": ["task.progress","task.completed","task.failed"],
    "rate_limit": 120,
    "disabled": false,
    "version": 1,
    "created_at": "2025-01-01T00:00:00Z",
    "updated_at": "2025-01-01T00:00:00Z"
  },
  "secret": "whsec_xxx",
  "app": { "id": "app_123", "is_new": true }
}

Update endpoint

PUT /webhooks/endpoints/{endpoint_id}
{
  "url": "https://your-domain.com/webhooks/aspect",
  "description": "Prod listener",
  "event_types": ["task.progress","task.completed","task.failed"],
  "rate_limit": 120,
  "version": 1,
  "disabled": false
}

Delete endpoint

DELETE /webhooks/endpoints/{endpoint_id}{ "success": true }

Inspect logs (optional)

  • GET /webhooks/messages — recent webhook messages
  • GET /webhooks/delivery-attempts?message_id=...|endpoint_id=... — delivery attempts
  • GET /webhooks/endpoints/{endpoint_id}/deliveries — legacy endpoint attempts list

Verify signatures

Svix signs requests with headers svix-id, svix-timestamp, and svix-signature. Always verify before processing. Your endpoint secret comes from the create response (keep it safe). Remove the whsec_ prefix when using verification helpers that expect the raw secret.

Node (TypeScript)

import { Webhook } from 'svix'
import express from 'express'

const app = express()
app.use(express.json({ type: '*/*' }))

app.post('/webhooks/aspect', (req, res) => {
  const svixId = req.header('svix-id')
  const svixTimestamp = req.header('svix-timestamp')
  const svixSignature = req.header('svix-signature')

  if (!svixId || !svixTimestamp || !svixSignature) return res.status(400).send('Missing Svix headers')

  const secret = process.env.ASPECT_WEBHOOK_SECRET || ''
  const wh = new Webhook(secret.replace(/^whsec_/, ''))

  try {
    const payload = wh.verify(JSON.stringify(req.body), {
      'svix-id': svixId,
      'svix-timestamp': svixTimestamp,
      'svix-signature': svixSignature
    })
    // payload matches the envelope schema above
    res.status(204).end()
  } catch (err) {
    res.status(400).send('Invalid signature')
  }
})

Python (FastAPI)

from fastapi import FastAPI, Request, HTTPException
from svix.webhooks import Webhook

app = FastAPI()

@app.post('/webhooks/aspect')
async def handler(request: Request):
    svix_id = request.headers.get('svix-id')
    svix_timestamp = request.headers.get('svix-timestamp')
    svix_signature = request.headers.get('svix-signature')
    if not (svix_id and svix_timestamp and svix_signature):
        raise HTTPException(status_code=400, detail='Missing Svix headers')

    secret = (os.getenv('ASPECT_WEBHOOK_SECRET') or '').removeprefix('whsec_')
    payload = await request.body()
    try:
        evt = Webhook(secret).verify(payload, {
            'svix-id': svix_id,
            'svix-timestamp': svix_timestamp,
            'svix-signature': svix_signature,
        })
        return { 'ok': True }
    except Exception:
        raise HTTPException(status_code=400, detail='Invalid signature')

Testing

Use the Svix dashboard to send test events to your endpoint. If you need a simple echo endpoint for local testing, expose it and verify signatures using the steps above.

Notes

  • Only task.* events are currently published to webhooks
  • All events are also available via SSE for live dashboards
  • Delivery attempts and logs are queryable using our Webhooks API