All Webhook Errors

Webhook Timeout Error — Causes & Fixes

A webhook timeout means your endpoint did not respond within the provider's timeout window. The provider marks the delivery as failed and retries — sometimes with exponential backoff over hours. If your handler is slow, you will receive duplicate events. The fix is always the same: respond immediately with HTTP 200, then process asynchronously.

Provider Timeout Windows

ProviderTimeoutRetry Policy
Stripe30 secondsExponential backoff, up to 3 days
Slack3 seconds3 retries over 1 hour
Shopify5 seconds19 retries over 48 hours
GitHub10 secondsManual redelivery only
Twilio15 seconds4 retries
HubSpot5 seconds3 retries

Why Your Handler Is Slow

Handlers that do synchronous work inside the request/response cycle will hit timeouts:

  • Writing to a database without a connection pool
  • Calling external APIs (sending emails, Slack notifications, payment confirmations)
  • Running image processing or file conversion
  • Querying multiple tables without indexes

The Fix: Respond First, Process Later

Return HTTP 200 as the first thing your handler does, then process the event in the background.

Node.js (Express + queue)

const queue = []

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  res.status(200).json({ received: true })  // respond immediately
  const event = JSON.parse(req.body)
  queue.push(event)  // process later
})

async function processQueue() {
  while (true) {
    const event = queue.shift()
    if (event) await handleEvent(event)
    await new Promise(r => setTimeout(r, 100))
  }
}
processQueue()

Python (FastAPI + BackgroundTasks)

from fastapi import FastAPI, BackgroundTasks, Request

app = FastAPI()

async def process_event(payload: dict):
    await send_confirmation_email(payload)
    await update_database(payload)

@app.post('/webhook')
async def webhook(request: Request, background_tasks: BackgroundTasks):
    payload = await request.json()
    background_tasks.add_task(process_event, payload)
    return {'received': True}

Idempotency: Handle Retries Safely

Since providers retry on timeout, your handler will receive the same event multiple times. Use the provider's event ID to deduplicate:

const redis = require('redis')
const client = redis.createClient()

async function handleEventOnce(event) {
  const key = `webhook:processed:${event.id}`
  const set = await client.set(key, '1', { NX: true, EX: 86400 })
  if (!set) return  // already processed
  await processEvent(event)
}

FAQ

My handler responds in under 1 second locally but times out in production — why?

Cold starts, connection pool exhaustion, or DNS resolution delays add latency that doesn't appear locally. Add connection pooling (e.g. pg-pool for Postgres), keep your DB connection alive, and profile the handler with timing logs.

How do I know if timeouts are causing duplicate events?

Check whether the provider's event ID appears more than once in your processing logs. WebhookWhisper shows retry deliveries separately — if you see the same payload delivered multiple times, your handler is timing out.

Should I return 200 even if processing fails?

Return 200 as soon as you have successfully enqueued the event. Only return non-2xx if you cannot safely enqueue it. Never return non-2xx because downstream processing failed — the provider will retry and you will get duplicate events.

Debug This Error in Real Time

WebhookWhisper captures every webhook request with full headers, body, and timing — so you can see exactly what the provider sent and reproduce the error instantly.

Start Debugging Free
Webhook Timeout Error — Causes & Fixes (2026) | WebhookWhisper