GET endpoint, you
can pass a webhook_url and Samsa will POST a signed event to it the moment the
job reaches a terminal status.
Requesting a webhook
Addwebhook_url to any generation request. It is per request — different jobs
can target different URLs.
Webhooks are a convenience, not the source of truth. If every delivery attempt
fails, the job result is still available from its
GET endpoint. Poll as a fallback
for anything critical.Events
Webhooks fire only on terminal transitions —completed or failed. Jobs that
are cancelled do not emit a webhook.
| Event | Fires when |
|---|---|
image.generation.completed / image.generation.failed | An image generation reaches a terminal status. |
image.edit.completed / image.edit.failed | A Magic Edit reaches a terminal status. |
video.generation.completed / video.generation.failed | A video generation reaches a terminal status. |
model.completed / model.failed | A model creation reaches a terminal status. |
Payload
The request body is JSON. Thedata object is the same shape you get from the job’s
GET status endpoint.
failed event, status is "failed" and data carries an error object
using the same inner shape as the error envelope.
Signature headers
Every delivery carries three headers:| Header | Description |
|---|---|
webhook-id | Unique id for the event, stable across retries (use it to deduplicate). |
webhook-timestamp | Unix seconds when the event was sent. Reject if it is more than 300s from now (replay protection). |
webhook-signature | Space-separated list of v1,<base64> signatures. Verify against each v1, candidate. |
{webhook-id}.{webhook-timestamp}.{raw-body}, base64-encoded, prefixed with v1,.
Verify the signature
Your per-keywebhook_secret lives in the app under Settings → API Keys (each key
has its own secret; rotate it independently of the key). The snippets below are
verified against a fixed test vector — run them as-is and they return true, so
you can confirm your implementation reproduces the signature byte-for-byte before
wiring in a real secret.
The test vector’s body is a fixed reference string, so its exact bytes never change
and the signature stays reproducible — it is intentionally not the live event
payload shown above. Signature verification always runs over the exact
raw bytes you receive, whatever their shape, so this is purely a self-test for your
verifier. Likewise,
webhook-id is an opaque, stable-per-event string — treat it as
a token, never parse it.|now − webhook-timestamp| > 300 seconds.
Retries and delivery rules
- Success is any
2xxresponse returned within a 10-second timeout (connect timeout 5s). A3xxis treated as a failure — Samsa does not follow redirects. - On failure, Samsa retries with the initial attempt plus 5 retries — 6 delivery
attempts total — backing off
5s → 30s → 2m → 15m → 1h. - After the final attempt the delivery is dropped (and logged). The job result stays
queryable from its
GETendpoint. - Endpoints must be HTTPS. Respond quickly (
2xx) and do heavy processing asynchronously so you never exceed the 10-second window.