webhook_url when you create a job and Voyager sends it a signed POST when the job finishes. Use webhooks to avoid holding a poll open for long jobs such as video and 3D.
webhook_url must be a public https:// URL of up to 2,048 characters, with no credentials and not a private or local address. Voyager checks it when you create the job (400 invalid_webhook_url if it isn’t) and again on every delivery. It doesn’t follow redirects: a 3xx counts as a failed delivery.
Events
Webhooks fire once per job, when it reaches a terminal status (
job.failed with outcome_unknown means Voyager couldn’t confirm the generation: ask for its status again, and never submit it again, or it may be charged twice). They don’t fire for queued or running, and they don’t fire again when the job’s charge settles later: read credits.state with GET /v1/jobs/{id} if you need the final charge.
Payload
data is the full job, rendered fresh for each delivery attempt. Its output links expire one hour after that; if you download later, call GET /v1/jobs/{id} for fresh ones.
Headers
Voyager follows the Standard Webhooks specification:The signing secret
Each API key has its own webhook secret,whsec_…, shown once next to the key when you create it on voyager.so/dashboard/api. Jobs created with a key are signed with that key’s secret. Store it like the key itself. If you lose it, create a new key.
Verify the signature
Always verify before trusting the body. Use the raw request body: parsing and re-serializing the JSON changes the bytes and breaks the signature.Verify by hand
The signature is HMAC-SHA256 over{webhook-id}.{webhook-timestamp}.{raw body}, keyed with the base64-decoded part of the secret after whsec_, and base64-encoded:
Python
Respond and retries
Return any2xx within 10 seconds, then do the work. Any other response, a redirect, or no answer in 10 seconds counts as a failure, and Voyager retries with backoff for up to 24 hours. After that the delivery is dropped; the job itself is unaffected.
- Deliveries can arrive more than once and out of order. Deduplicate on
webhook-id, and checkdata.statusrather than assuming order. - Revoking a key stops its deliveries: webhooks for its jobs that haven’t been delivered yet are dropped.
GET /v1/jobs/{id}is authoritative. If you miss a webhook, or need the final charge, poll.