Webhooks
Get called when a task finishes, instead of polling for it.
Pass a callback_url when you create a task:
{
"model": "image-generator",
"input": { "prompt": "..." },
"callback_url": "https://client.example.com/webhooks/mager"
}Mager sends one POST to that URL once the task reaches finished or failed.
The payload
{
"event": "generation.finished",
"task_id": "cmagr01hxyz123",
"status": "finished",
"model": "image-generator",
"result": [
{
"id": "result_01hxyz",
"status": "SUCCESS",
"file_url": "https://cdn.example.com/generated-image.png",
"file_type": "image/png"
}
],
"error": null,
"created_at": "2026-07-03T06:00:00.000Z",
"completed_at": "2026-07-03T06:01:00.000Z"
}event is generation.finished or generation.failed. On failure, error carries the reason and
result is empty.
The request is sent as content-type: application/json with the user agent
Mager-Developer-Webhooks/1.0.
Responding
Return any 2xx within 10 seconds. Anything else — a non-2xx status, a timeout, a connection
error — counts as a failed delivery.
Acknowledge first, work second. Write the payload somewhere durable, return 200, and do the
downloading and processing on your own time. Handlers that download the file before responding are
the usual cause of timeout-driven retries.
Retries
A failed delivery is retried up to 3 attempts total, 60 seconds apart. After the third
failure the delivery is marked FAILED and Mager stops.
Delivery state is visible on the task itself, under webhooks:
{
"id": "delivery_01hxyz",
"status": "RETRYING",
"attempts": 2,
"max_attempts": 3,
"last_attempt_at": "2026-07-03T06:02:00.000Z",
"next_retry_at": "2026-07-03T06:03:00.000Z",
"last_status_code": 502,
"last_error": "Request failed with status code 502"
}If your endpoint was down for the whole retry window, queue a fresh delivery by hand:
curl https://api.mageran.ai/api/v1/generation-tasks/cmagr01hxyz123/webhooks/retry \
--request POST \
--header 'x-mager-api-key: YOUR_API_KEY'The task must belong to your account, must have a callback_url, and must already be terminal.
Securing your endpoint
Mager does not sign webhook requests. There is no shared secret and no signature header, so your endpoint cannot verify from the request alone that Mager sent it.
Two things to do about that:
- Put a secret in the URL. Use a long, unguessable path or query value —
https://client.example.com/webhooks/mager/8f2c…— and reject anything that does not match. It is coarse, but it stops anyone who does not already have the URL. - Verify before you trust. Treat the payload as a hint, not as fact. Call
GET /generation-tasks/{taskId}with your API key and use that response as the source of truth. This also covers the duplicate-delivery case: a retry after a timeout can arrive for a task you already handled.
Design the handler to be idempotent. Keying on task_id is enough.
When to use which
| Situation | Approach |
|---|---|
| Server-side job, you control the endpoint | Webhook |
| Local development, no public URL | Polling |
| User is watching a progress bar | Poll, and update from webhooks |