Guide to OpenAI Webhooks: Features and Best Practices
OpenAI webhooks are HTTP callbacks that OpenAI uses to notify your application when long-running asynchronous work completes — a Batch job finishes, a fine-tuning run succeeds, a background Responses call returns a result, an evaluation run ends, or a Realtime SIP call comes in. They are the primary way to react to OpenAI activity without polling the API, and OpenAI shipped them in mid-2025 alongside the Deep Research API and the broader push toward longer-running, agent-shaped workloads.
This guide covers how OpenAI webhooks work, the features OpenAI provides, the limitations and pain points worth knowing about before you build, and the best practices that keep production integrations reliable.
How OpenAI webhooks work
When you create a webhook endpoint in the OpenAI Dashboard, OpenAI generates a whsec_-prefixed signing secret and starts delivering events for the types you've subscribed to. Each delivery is an HTTP POST with a JSON body and three Standard Webhooks headers: webhook-id (a unique delivery identifier), webhook-timestamp (Unix epoch seconds), and webhook-signature (an HMAC of the canonical signed payload). Your endpoint must return a 2xx status to acknowledge receipt; anything else — including 3xx redirects — is treated as a failure and retried with exponential backoff for up to 72 hours.
| Feature | Details |
|---|---|
| Webhook configuration | OpenAI Dashboard → Settings → Project → Webhooks, or via the API |
| Verification spec | Standard Webhooks, HMAC-SHA256 |
| Headers | webhook-id, webhook-timestamp, webhook-signature |
| Replay protection | Five-minute timestamp tolerance (Standard Webhooks default) |
| Acknowledgement | Any 2xx response; 3xx and 4xx/5xx are failures |
| Retry logic | At-least-once delivery, exponential backoff, up to 72 hours |
| Auto-disable | Not documented |
| Ordering | Not guaranteed |
| Payload shape | Thin — data typically contains only the resource ID |
| Endpoint scope | Per-project, not org-wide |
| SDK support | client.webhooks.unwrap() in Python and Node SDKs |
OpenAI's design choices here are deliberate. Adopting Standard Webhooks means tooling that works for one provider works for another. Thin payloads avoid serving stale data on retries and keep deliveries small. At-least-once delivery prioritises reaching your endpoint over avoiding duplicates. Each of these choices has a flip side, which we cover in the limitations section below.
OpenAI webhook features
Standard Webhooks compliance
OpenAI implements the Standard Webhooks specification, which means the verification process is identical to other Standard Webhooks adopters like Resend, Lob, and Clerk. Headers are lowercase and hyphenated (webhook-id, webhook-timestamp, webhook-signature), the signed payload is webhook-id + "." + webhook-timestamp + "." + raw_body, and the signing secret is base64-encoded with a whsec_ prefix. Tooling and SDKs that target the spec — including Hookdeck — work without OpenAI-specific configuration.
Five categories of event types
OpenAI emits webhook events across five product surfaces:
- Background Responses —
response.completed,response.cancelled,response.failed,response.incomplete(fired when a Responses API call run withbackground: truefinishes) - Batch API —
batch.completed,batch.failed,batch.cancelled,batch.expired - Fine-Tuning —
fine_tuning.job.succeeded,fine_tuning.job.failed,fine_tuning.job.cancelled - Eval Runs —
eval.run.succeeded,eval.run.failed,eval.run.canceled(note the single-l US spelling here, which differs from the double-lcancelledused elsewhere) - Realtime SIP —
realtime.call.incoming, fired when a SIP call arrives at a Realtime session
Each endpoint subscribes per-event-type. Streaming chat completions, file uploads, assistants/threads, image generation, and vector store events are not currently surfaced via webhooks.
Dashboard test events
The OpenAI Dashboard webhook settings page lets you trigger a test event with sample data — useful for confirming endpoint wiring without kicking off a real Batch or fine-tuning run. The dashboard also surfaces basic delivery information for recent attempts.
Thin-payload pattern with API hydration
Every delivery returns an object of event, an id prefixed evt_, a type, a created_at Unix timestamp, and a data object containing the resource's ID — and little else. To get the full Batch result, fine-tuning artefact, Responses output, or evaluation summary, your handler calls back into the OpenAI API. This avoids stale-data races on retries and keeps every webhook delivery small enough to verify and queue cheaply.
The exception is realtime.call.incoming, whose data payload includes the call_id plus SIP headers — your server then accepts or rejects the call via the Realtime server controls API.
SDK signature verification helpers
The OpenAI Python and Node SDKs ship a client.webhooks.unwrap() helper that takes the raw request body and headers, verifies the signature against the Standard Webhooks rules, enforces the five-minute timestamp tolerance, and returns a parsed event. The helper raises InvalidWebhookSignatureError on any failure, giving applications one safe entry point.
Security considerations
Verifying webhook signatures
Every webhook delivery includes the three Standard Webhooks headers and an HMAC-SHA256 signature computed with the endpoint's signing secret. Verification is non-optional in production: without it, your handler is an unauthenticated public endpoint that does work on receipt of any HTTP POST.
The recommended approach is the SDK helper — client.webhooks.unwrap(), available in the Python and Node SDKs — which takes the raw request body and headers, verifies the signature, enforces the timestamp tolerance, and returns a parsed event. The helper throws if the signature is invalid or if the timestamp is more than five minutes off. Both checks are important: signature verification alone doesn't protect against replay attacks where an attacker captures a valid signed delivery and re-sends it later.
For a code-level walk-through of both manual verification and the SDK helper, see our guide to securing and verifying OpenAI webhooks with Hookdeck.
Store webhook secrets securely
Treat the whsec_ secret like a database credential. Store it in environment variables or a secrets manager — never in source control, never hardcoded. OpenAI's SDKs read it from OPENAI_WEBHOOK_SECRET by default. The secret is shown once at endpoint creation and is not retrievable afterward, so capture it then or you'll have to recreate the endpoint.
OpenAI webhook limitations and pain points
Thin payloads require API round-trips
The Problem: Webhook payloads contain only the resource ID. To act on an event your handler usually has to make an additional API call to hydrate the full object (Batch results, fine-tuning artefacts, Responses output, evaluation reports) and during high-volume periods, those round-trips compound.
Why It Happens: OpenAI deliberately ships thin payloads to avoid serving stale data on retries: the resource you fetch is always the current state, not whatever was true at delivery time.
Workarounds:
- Cache hydrated resources by
idif your handler is likely to see the same resource multiple times in quick succession. - Defer the API call to an async worker so the synchronous handler can return 2xx fast.
- Skip hydration entirely for events where the
typeis enough to act on (e.g.batch.failedtriggering a Slack notification).
How Hookdeck Can Help: Event Gateway queues every event durably and lets your handler process them at whatever rate your downstream services tolerate. The 5-minute timestamp tolerance stops being a problem for the downstream side because Hookdeck verifies and acknowledges at the edge.
At-least-once delivery means duplicates
The Problem: OpenAI explicitly notes that "in rare cases, due to internal system issues, OpenAI may deliver duplicate copies of the same webhook event." Your handler will receive duplicate events, and acting on them more than once can mean duplicate notifications, duplicate database writes, or duplicate downstream API calls.
Why It Happens: OpenAI prioritises eventual delivery over exactly-once semantics. The webhook-id header is your idempotency key.
Workarounds:
- Track processed
webhook-ids in a fast key-value store with a TTL longer than the 72-hour retry window. - Make every side effect idempotent at the destination — use unique constraints, upserts, or natural keys instead of inserts.
- Don't rely on processing order to detect duplicates; events can arrive out of sequence.
How Hookdeck Can Help: Event Gateway dedupes events at the edge using configurable rules and only forwards unique events downstream. For deeper background, see our guide to webhook idempotency.
No ordering guarantees
The Problem: OpenAI makes no guarantees about the order in which events arrive. A response.completed event may land before its preceding lifecycle events, or a Batch's lifecycle events may arrive in scrambled sequence. State machines that assume sequential delivery will get into impossible states.
Why It Happens: OpenAI's delivery infrastructure is parallelised; ordering would require serialising deliveries per resource, which would limit throughput.
Workarounds:
- Use the
created_attimestamp on the event envelope to sort. Don't rely on receipt order. - Treat each event as a transition trigger that re-fetches the current state from the API rather than as the authoritative current state itself.
- Build state machines that tolerate out-of-order transitions — for example, treat
failedandcancelledas terminal regardless of what other events have arrived.
How Hookdeck Can Help: Event Gateway's queueing and replay let you re-process events in any order you choose during recovery. For deeper context on why ordering is hard and how to handle it, see our guide to webhook ordering.
Five-minute timestamp tolerance
The Problem: Webhook payloads with a webhook-timestamp more than five minutes off real time are rejected by the SDK helper as a replay-protection measure. If your handler is slow, queued behind a backlog, or briefly down, deliveries can age out before you process them.
Why It Happens: The freshness window is what makes signature verification resistant to replay attacks — without it, a captured signed payload could be re-delivered indefinitely.
Workarounds:
- Acknowledge fast (return 2xx within milliseconds) and process asynchronously off a queue.
- Monitor end-to-end latency from
webhook-timestampto processing and alert on regressions. - Avoid processing-time work in the synchronous webhook handler; defer everything except verification and enqueue.
How Hookdeck Can Help: Event Gateway verifies the signature once at the edge and acknowledges OpenAI immediately. Your handler then consumes from Hookdeck's queue at whatever rate suits — the freshness window doesn't apply to Hookdeck's downstream forwarding because the verification has already happened.
3xx redirects are treated as failures
The Problem: OpenAI doesn't follow redirects. If your endpoint URL changes and the old hostname returns a 301 or 302, OpenAI marks the delivery as failed and adds it to the 72-hour retry queue.
Why It Happens: Following redirects to arbitrary URLs is a security risk and adds delivery latency.
Workarounds:
- Update the endpoint URL in the Dashboard whenever your hostname changes. Don't rely on redirects.
- Use a stable, dedicated domain for webhook ingress so renames and migrations don't cascade into endpoint configuration.
How Hookdeck Can Help: Event Gateway gives you a stable HTTPS URL that doesn't change when your application's hostname does. Internal routing is a Hookdeck-side configuration, not an OpenAI-side endpoint update.
72-hour retry window means slow recovery
The Problem: OpenAI retries failed deliveries with exponential backoff for up to 72 hours. That's generous in one sense — short outages won't drop events — but it also means a delivery that's been failing for 24 hours is on long backoff intervals, so once you fix the bug you may wait hours before the next retry hits your endpoint.
Why It Happens: Exponential backoff protects OpenAI's delivery infrastructure from hammering broken endpoints.
Workarounds:
- Acknowledge anything you can immediately, even if you'll retry-process it later, to avoid getting put on long backoff.
- Build a reconciliation job that polls the API for resources you might have missed events for.
How Hookdeck Can Help: Event Gateway lets you replay any failed event on demand from the dashboard or CLI — no need to wait for OpenAI's next retry attempt. Combined with Event Gateway's own retry policies, you control the recovery cadence.
No documented auto-disable
The Problem: Unlike Stripe and Anthropic Managed Agents, for example, OpenAI's docs do not specify auto-disable behaviour for endpoints that fail consistently. That sounds like a feature, but in practice it means a misconfigured endpoint can silently fail forever within the 72-hour window with no automatic intervention.
Why It Happens: OpenAI may rely on the 72-hour retry ceiling as the bound rather than a failure-count threshold.
Workarounds:
- Monitor endpoint health and alerting yourself.
- Build runbooks for spotting silent endpoint failures.
How Hookdeck Can Help: Event Gateway's observability surfaces failed deliveries in real time with detailed error context, so you don't have to wait for a downstream symptom to discover an endpoint is broken.
No first-party CLI for local development
The Problem: Stripe ships stripe listen for forwarding webhooks to a local handler. OpenAI does not. The docs explicitly recommend ngrok or a cloud dev environment, neither of which gives you replay or durable inspection of historical events.
Why It Happens: OpenAI hasn't shipped a CLI for this use case.
Workarounds:
- Use ngrok for the public URL.
- Trigger test events from the Dashboard.
- Save raw payloads to disk and re-POST them manually with curl.
How Hookdeck Can Help: The Hookdeck CLI gives you a stable public HTTPS URL, full request inspection, and one-click replay — filling the gap OpenAI leaves. See our guide to testing and replaying OpenAI webhooks locally.
Best practices
Always verify webhook signatures
Use the OpenAI SDK helper to verify both the HMAC and the five-minute timestamp tolerance. Never trust an unsigned payload, never skip the freshness check, and never use a non-constant-time string comparison if you're verifying manually. Standard Webhooks supports overlapping signatures during secret rotation — your manual verification needs to handle that.
Return 2xx status codes quickly
Acknowledge within milliseconds and defer work to an async queue. The five-minute timestamp tolerance plus exponential backoff mean slow handlers compound problems — deliveries age out, retries pile up, and recovery slows.
Subscribe only to required events
Each endpoint subscribes per-event-type. Don't blanket-subscribe to every event family — it inflates your processing volume and forces your handler to filter events it never needed.
Dedupe on webhook-id
OpenAI explicitly says duplicates can occur. Track processed webhook-ids with a TTL longer than the 72-hour retry window. The webhook-id is unique per event but reused across retries, which is exactly what makes it usable as an idempotency key.
Don't trust event order
Don't rely on receipt order. If your handler needs to apply transitions in sequence, sort by the created_at field on the event envelope. Better still, treat each event as a "go check the current state" trigger rather than a sequential message.
Process events asynchronously
Verify, enqueue, and acknowledge synchronously; do everything else off a queue. This keeps your handler under the freshness window, makes retries safe, and lets you scale workers independently of webhook ingress. For background, see our guide on why to implement asynchronous processing for webhooks.
Implement reconciliation jobs
Webhooks are a notification mechanism, not a system of record. Periodically reconcile against the OpenAI API to catch any events that were dropped or processed incorrectly. The thin-payload pattern actually helps here — your reconciliation job is already shaped like the rest of your handler, since both pull full state from the API.
Validate payloads
Even after signature verification, validate the parsed event against an expected schema. OpenAI adds new event types over time (the Realtime SIP events landed after the initial June 2025 launch), and a handler that assumes every payload has fields it expects will break the first time a new event family ships.
Conclusion
OpenAI webhooks are how long-running async work (Batch, fine-tuning, background Responses, Deep Research, evals, Realtime SIP) becomes addressable from the rest of your stack. OpenAI ships them on top of the Standard Webhooks specification, so the verification model is interoperable with other adopters and well-supported by SDKs in Python and Node. But the operational responsibilities of running them in production are non-trivial: deduplication, ordering tolerance, the 72-hour retry window, secret rotation, and the local-development feedback loop are all problems your team will own unless you offload them.
For teams that want to focus on what their AI workloads do rather than on the infrastructure that delivers their events, Hookdeck Event Gateway handles verification, deduplication, queueing, retries, replay, and observability at the edge — so your application only ever sees verified, deduplicated, durably-queued events.
Get started with Hookdeck Event Gatewayfor free and start handling OpenAI webhooks reliably in minutes.