Webhook protocol
The webhook notification contract — payload shape, HMAC signature, retries, and the safety circuit breaker.
When a job finishes (completed, failed, or cancelled), we send a POST notification to your installation's callback_url. This page describes the notification contract; for a hands-on guide with verification code, see the receiving results tutorial.
Notification format
{
"event": "job.completed",
"delivered_at": "2026-06-03T08:00:00.000Z",
"job": {
"id": "00000000-0000-0000-0000-0000000000a1",
"status": "completed",
"order_id": "00000000-0000-0000-0000-000000000099",
"completed_at": "2026-06-03T07:59:40.000Z",
"error": null
},
"outputs": [
{
"url": "https://…?token=…",
"type": "image/jpeg",
"width": 1024,
"height": 1280
}
],
"external_metadata": { "your": "tag" }
}
event—job.completed,job.failed, orjob.cancelled.outputs[].url— download URLs valid for at least 7 days; get fresh ones withPOST /jobs/{id}/refresh-url.external_metadata— returns exactly what you sent when creating the session.
Signature
Every notification carries a header:
X-Qamera-Signature: t=<unix-time>,v1=<hmac_sha256_hex>
The string <t>.<raw-body> is signed with the installation's HMAC secret (algorithm HMAC-SHA256, result in hex). Reject notifications whose t is older than 5 minutes. Ready-made verification functions for Node.js and PHP are in the tutorial.
Secret rotation window
After you rotate the secret via POST /installations/{id}/rotate-hmac, the previous secret keeps working for another 48 hours. During that window notifications have two v1= segments — one for each secret; accept either of them:
X-Qamera-Signature: t=1746777600,v1=e3b0…,v1=44a0…
Once the window ends, we sign only with the new secret.
Reliability guarantees
- Up to 8 delivery attempts, with a growing interval (and random jitter), at most 1 hour between attempts.
- 5 consecutive failed deliveries pause sending for 30 minutes (safety circuit breaker).
- 3 such cycles suspend the installation — an administrator is needed to reinstate it.
- Once the attempts are used up, the notification gets the
abandonedstatus and can be re-sent withPOST /webhooks/{delivery_id}/replay.
Your endpoint should:
- Respond with any
2xxstatus for notifications it accepts. - Process idempotently — after a slow
200response we may re-send a notification that was already delivered. - Record
(event, job.id, delivered_at)for diagnostics — we never re-send already-acknowledged notifications on our own initiative.