api reference
webhooks
terse machine contract. for the walkthrough with reasoning and per-language signature-verify code, see the webhooks guide.
attach
add callback_url when you create a generation. one gen → one
POST on terminal state.
POST https://app.maginary.ai/api/gens/
Content-Type: application/json
Authorization: Bearer <API_KEY>
{
"prompt": "cyberpunk samurai --ar 16:9",
"callback_url": "https://your-app.example.com/webhooks/maginary"
} URL must be https://. loopback / private / metadata IPs are
rejected at delivery time.
request
| field | value |
|---|---|
method | POST |
content-type | application/json; charset=utf-8 |
user-agent | maginary-webhook/1 |
timeout budget | 10 seconds per attempt |
headers
| header | value |
|---|---|
X-Maginary-Event | gen.done | gen.failed |
X-Maginary-Event-Id | gen uuid. stable across retries. dedupe key. mirrored at body.uuid. |
X-Maginary-Delivery-Attempt | integer, 1-indexed. debug only. do not dedupe on this. |
X-Maginary-Signature | sha256=<hex> — HMAC-SHA256 of raw body bytes,
keyed by your account webhook_secret. |
payload (abbreviated)
same envelope as GET /api/gens/{uuid}/. full schema
in the openapi spec at /docs/api.
{
"uuid": "0c8c1f3a-...",
"processing_state": "DONE" | "FAILED",
"processing_result": {
"job_id": "mj_...",
"slots": [
{ "index": 0, "status": "success" | "failed", "url": "https://...", "charged": true }
],
"error_message": null | string,
"available_actions": { "0": ["upscale", "vary", ...], "global": ["reroll"] }
},
"image_urls": ["https://..."],
"prompt": "...",
"action_type": "txt2img" | "img2img" | "txt2vid" | "img2vid" | ...,
"expected_output_count": 1 | 2 | 3 | 4,
"created_at": "2026-06-01T12:34:50.112Z",
"processing_started_at": "2026-06-01T12:34:51.004Z",
"processing_ended_at": "2026-06-01T12:35:18.776Z"
}retry policy
retryable: 5xx, 408, 429, network / timeout. other 4xx = give up (receiver
refused).
| attempt | wait before |
|---|---|
| 1 | immediate |
| 2 | 1 min |
| 3 | 5 min |
| 4 | 30 min |
| 5 | 2 h |
| 6 | 8 h |
total budget ~10h45m. after that, fall back to polling GET /api/gens/{uuid}/.
signature
signature = "sha256=" + hex(HMAC_SHA256(webhook_secret, raw_body_bytes))
verify : constant_time_equal(header_value, signature) hash the raw request bytes, not a re-serialized JSON. use hmac.compare_digest / crypto.timingSafeEqual / hmac.Equal. secret lives at GET /api/api-keys/ → webhook_secret.
retention
- successful delivery state (timestamps, attempt count, last status) kept 30 days then nulled.
callback_urlalso cleared at 30-day mark.- undelivered / exhausted retries kept indefinitely — forensics stays intact.
need the reasoning, per-language verify code, ngrok testing, and troubleshooting? read the guide →