Webhooks for AI Workflows: Background Jobs, Agents, and Reliable Callbacks
A webhook is an HTTP request that one service sends to your application when an event occurs. Instead of repeatedly asking whether a long model response, batch, evaluation, or agent run has finished, your app registers an endpoint and receives a callback.
Webhooks are often called reverse APIs because the provider initiates the request. They are particularly useful when AI work outlives the browser request that started it.
Polling versus a webhook
With polling, your application repeatedly asks for status. That is easy to understand but adds delay, empty requests, and rate-limit pressure. With a webhook, the provider sends one or more event notifications when state changes.
| Question | Polling | Webhook |
|---|---|---|
| Who initiates? | Your app | Event provider |
| Delivery | On the next poll | Push after an event |
| Failure mode | Missed/slow polls | Retries and duplicates |
| Best AI fit | simple status screen | background responses, batches, agent jobs |
Neither is universally better. A reliable system often receives a webhook, then fetches the canonical resource state from the provider API.
AI example: a background response completes
OpenAI documents webhook events such as response.completed, response.failed, and response.cancelled for background responses in its webhook event reference.
A production flow looks like this:
- Create a job and persist your own job ID plus the provider resource ID.
- Return immediately to the user with a pending state.
- Receive a signed event at a public HTTPS endpoint.
- Verify the signature against the raw body.
- Store the event ID transactionally so duplicates become no-ops.
- Acknowledge quickly with a
2xxresponse. - Queue heavier work and fetch current provider state if correctness depends on it.
- Update your job and notify the user.
This separates durable job state from delivery. A webhook is a notification, not your database.
Verify with the provider SDK
Do not invent a generic signature scheme when the provider publishes one. The official OpenAI Python SDK can verify and parse the raw payload:
from flask import Flask, request
from openai import OpenAI
app = Flask(__name__)
client = OpenAI() # reads OPENAI_WEBHOOK_SECRET from the environment
@app.post("/webhooks/openai")
def receive_openai_event():
event = client.webhooks.unwrap(request.get_data(as_text=True), request.headers)
enqueue_event(event.id, event.type, event.data)
return "", 200
Keep the raw body unchanged until verification. Use the exact SDK/header instructions for your provider; Stripe, GitHub, and other services have different formats and replay protections.
Idempotency is mandatory
Providers retry when your endpoint times out or returns a failure. Delivery can be duplicated or reordered. A database uniqueness constraint on provider plus event ID is stronger than an in-memory Set, which disappears on restart and is not shared across instances.
create table webhook_events (
provider text not null,
event_id text not null,
event_type text not null,
received_at timestamptz not null default now(),
primary key (provider, event_id)
);
Insert the receipt and enqueue the next step in one durable transaction or use an outbox pattern. If an agent tool may charge money or mutate external data, give that action its own idempotency key as well.
Respond fast, process later
Signature verification, validation, and durable receipt belong in the request path. Model calls, retrieval, document processing, email, and agent continuation belong on a queue or durable workflow.
This avoids timeout-driven retries and lets workers apply independent concurrency, retry, and cost limits. The long-running agent guide explains why an hours-long run should not depend on one HTTP connection.
Security checklist
- Accept HTTPS only.
- Verify signatures and enforce a replay window where supported.
- Keep webhook secrets in a secret manager and rotate them deliberately.
- Validate event type and schema before dispatch.
- Allowlist supported events; ignore unknown types safely.
- Never authorize a destructive agent action solely because text in the payload requested it.
- Redact sensitive prompts and outputs from logs.
- Rate-limit endpoints without blocking legitimate provider retries.
Webhook, stream, or WebSocket?
- Use a webhook for asynchronous service-to-service completion or state changes.
- Use HTTP streaming/SSE for one-way incremental model output to a client.
- Use a WebSocket or WebRTC when a realtime session is bidirectional, such as a voice agent.
For broader delivery, retry, and ordering patterns, read webhook architecture patterns. For agent failure policy, use AI agent error handling.
Webhooks make AI workflows reliable when treated as signed, retryable event notifications. The durable job record, idempotent action, and current provider state remain the sources of truth.