# Webhooks

Add `"webhook_url": "https://..."` to `POST /videos`, `POST /jobs` or `POST /trade/submit` (MCP: `generate_video`, `build`, `trade_submit`) instead of polling. When the work ends you get one POST: `{ id, type, created, data }` with type `video.completed`, `video.failed`, `job.completed`, `job.failed`, `trade.completed` or `trade.failed`; `data` is what the matching GET returns.

Verify every delivery (Standard Webhooks):
- `webhook-signature` is `v1,<base64>`: the HMAC-SHA256 of `{webhook-id}.{webhook-timestamp}.{raw body}`, keyed with your `whsec_...` secret (base64-decode the part after `whsec_`). Compare in constant time.
- Reject timestamps older than 5 minutes, de-duplicate on `webhook-id`, answer 2xx fast. Retries back off for about 43 h.
- The secret is returned once as `webhook_secret` the first time, except to a scoped key or a key with limits: then your human reveals it in the dashboard. Store it like a password.

Details and verification code: https://omnirail.org/docs#webhooks
