GET /v3/jobs/{job_id} until a job finishes, register a webhook and Hedra POSTs the result to you. A job fires exactly one terminal event — job.completed or job.failed — chosen from its final status when the delivery goes out.
Register a webhook
You can configure and test the default endpoint in the console. The same page shows recent deliveries for the selected team. There are two ways to say where deliveries go:- Per job — pass
webhookat submit, next toinput. This URL applies to that job only. - Account default — store one endpoint with
PUT /webhooks/default. It receives the terminal event for every job that names no per-jobwebhook.
enabled: false pauses the default endpoint without discarding the URL; DELETE /webhooks/default removes it entirely. To check the wiring before running a real job, POST /webhooks/default/test fires a test delivery at the stored endpoint and reports what it answered:
The payload
The body is the same envelopeGET /v3/jobs/{job_id} returns, minus its poll-only fields (logs, cost, currency) — poll the job if you need those.
job.failed, status is FAILED and error carries the same error envelope the poll endpoint returns.
Verify the signature
Every delivery is signed with Hedra’s ed25519 key — the same key for every account, so you can fetch it once and cache it:
The signature covers a canonical string of five newline-separated fields, in this order:
Retries and deduplication
Delivery is at-least-once. A2xx from your endpoint is success; anything else — including a redirect, which is never followed — is retried. Hedra makes up to 12 attempts over approximately 6 hours, backing off 10s, 30s, 90s, 4m30s, 13m30s, 40m30s, then hourly. Because the retry window is bounded, acknowledge with a 2xx first and do your own processing asynchronously.
Deduplicate on X-Hedra-Webhook-Id. It identifies the event — it is the job’s own id and is byte-identical across every retry and replay. Do not hash the request body: each attempt re-signs the output URLs, so the body legitimately differs between attempts of the same event.
The one exception is X-Hedra-Webhook-Redelivery: true: an operator asked for this event to be sent again, so process it even if you have already recorded that id — that request is the whole reason it was sent.
Inspect and replay deliveries
GET /v3/webhooks/deliveries lists every delivery with its status (PENDING, DELIVERING, DELIVERED, FAILED), its source (per_job or default), cumulative attempts, the latest outcome (last_response_status, last_error), and its replay history.
FAILED and not retried again. Replay it:
X-Hedra-Webhook-Redelivery: true, and the previous outcome is archived in the delivery’s redeliveries list. A replay answers 409 while a delivery for the job is still in flight.
last_error is a structured error envelope, not free text: a stable code (DEADLINE_EXCEEDED, UNAVAILABLE, RESOURCE_EXHAUSTED, and so on — the same vocabulary the rest of the API uses), a fixed message, and retryable, which tells you whether replaying is likely to help. One code is permanent and stops the ladder immediately: FAILED_PRECONDITION means the URL resolves to a blocked address range (or redirects to one) — fix the URL; replaying will not help. Your endpoint’s URL, headers, and response body are never echoed back, so treat your own logs as the record of what your endpoint returned.