Sada Files

Developer docs

  1. Developers

Webhooks

Instead of polling, ask the API to POST to your server when a background job finishes.

Asking for a callback

Add callbackUrl and callbackSecret as form fields on POST /v1/tools/:slug/jobs or POST /v1/pipeline. There is nothing to register in advance. Callbacks need an API key.

Run in the background and call back
curl -X POST "https://api.sadafiles.com/v1/tools/compress-video/jobs" \
  -H "X-API-Key: $SADAFILES_API_KEY" \
  -F "files[]=@clip.mp4" \
  -F "callbackUrl=https://example.com/hooks/sada" \
  -F "callbackSecret=$WEBHOOK_SECRET"

Use these exact field names

The API reads camelCase callbackUrl and callbackSecret; snake_case form fields are ignored. Both SDKs send the right fields for you: callbackUrl in TypeScript and callback_url in Python.

What you receive

One POST with JSON metadata for job.completed or job.failed. It never contains file contents; download the result with the job endpoints.

HeaderValue
X-Sada-Eventjob.completed or job.failed
X-Sada-TimestampUnix seconds when this attempt was sent
X-Sada-Signaturesha256=<hex>, present when you set a secret
Body
{
  "event": "job.completed",
  "job": {
    "id": "de01bc4a-0686-400b-a594-20f031191629",
    "status": "done",
    "steps": [
      "compress-video"
    ],
    "expiresAt": "2026-11-02T16:31:37.708Z"
  },
  "sentAt": "2026-11-01T16:32:02.114Z"
}

The job object is the same summary GET /v1/jobs/:id returns; this example is shortened.

Verifying the signature

The signature is HMAC-SHA256 of timestamp.rawBody with your secret. Check it against the raw bytes before parsing the JSON, and reject old timestamps to stop replays.

# The signature covers "<X-Sada-Timestamp>.<raw body>"
printf '%s.%s' "$TIMESTAMP" "$BODY" | openssl dgst -sha256 -hmac "$SECRET"

Retries

Answer with any 2xx within 10 seconds. Otherwise the delivery is retried, up to 6 attempts in all, after 30 s, 2 min, 10 min, 30 min, 1 h. Retries are checked once a minute, so these delays are minimums. Because a delivery can repeat, make your handler safe to run twice, using the job id. The callback settings expire with the job, so nothing is sent after that.

What's next?