OpenAI webhooks that survive a 45-second handler.
Your handler is doing real work, RAG, another model call, a DB transaction. OpenAI gives it a few seconds. AnyHook returns 200 in about 1s and lets your handler finish on its own time.
The timing problem
OpenAI's background responses, batch jobs and fine-tuning jobs all report back over HTTP and expect a 2xx within a few seconds, then retry for up to 72 hours. The moment your handler does something interesting, chain a second model call, do a vector search, hit Stripe, you blow past it.
When you blow past it, OpenAI retries. Your handler runs again. Now you're paying for tokens twice and your downstream system sees duplicates. The fix is not a longer timeout; it's a separate ack-and-process pattern.
Drop-in replacement: change the webhook URL
Your existing handler doesn't need to change. AnyHook acks OpenAI in about 1s at the edge, then forwards the verified payload to your endpoint with full retry, replay, and event log.
// 1. Point OpenAI's webhook URL at your AnyHook inbound:
// https://in.anyhook.net/{user-slug}/{app-slug}
//
// 2. Keep your existing handler, it now has all the time it needs.
// AnyHook re-signs every delivery with AnyHook-Signature.
import OpenAI from "openai";
import { verifyWebhook } from "anyhook-verify";
const openai = new OpenAI();
export async function POST(req: Request) {
const ok = await verifyWebhook(req, process.env.ANYHOOK_SIGNING_SECRET!);
if (!ok) return new Response("invalid signature", { status: 401 });
// OpenAI's own event, byte for byte: { type: "response.completed", data: { id }, ... }
const event = await req.json();
if (event.type !== "response.completed") return new Response("ok");
const response = await openai.responses.retrieve(event.data.id);
// Run anything here. AnyHook already returned 200 to OpenAI.
// If you fail, AnyHook retries with backoff (up to 10 attempts on Scale).
await ragPipeline(response); // 25s
await secondModelCall(response); // 18s
await persistResult(response); // 2s
return new Response("ok", { status: 200 });
}Questions teams ask
- Do I lose OpenAI's signature verification?
- No. OpenAI signs with the Standard Webhooks scheme (webhook-id, webhook-timestamp, webhook-signature). Set the app's source to Standard Webhooks and paste the webhook's signing secret (whsec_...): AnyHook checks it at the edge and rejects forgeries, then re-signs every delivery to your endpoint with AnyHook-Signature.
- What happens if my handler is down for an hour?
- AnyHook queues the event and retries on exponential backoff: up to 3 attempts on Free, 5 on Pro, 10 on Scale. Every attempt is logged and replayable from the dashboard. OpenAI sees one 200 and moves on.
- Which OpenAI events does this cover?
- Whatever OpenAI sends by webhook: background responses (response.completed and its failed, cancelled and incomplete siblings), batch jobs, fine-tuning jobs and eval runs. Streaming is not a webhook: it is a stream your code reads directly, so it never passes through AnyHook.
Change one URL. Keep your OpenAI handler.
Free tier covers 10K events / month. No SDK: point OpenAI at your AnyHook inbound URL.