ShipVeryFastShipVeryFast
Documentation

Webhooks & background jobs

ShipVeryFast already handles Stripe and Mailgun webhooks, so the pattern for receiving events is established. Background work needs a little more, because serverless functions are short-lived.

Add a webhook endpoint

Verify, then act

Copy the shape of app/api/webhooks/stripe/route.ts: read the raw body, verify the signature against a secret, then handle the event. Never trust an unverified webhook.

app/api/webhooks/acme/route.ts
export async function POST(req: Request) {
  const raw = await req.text();
  if (!verifySignature(raw, req.headers.get("x-acme-signature"), env.ACME_WEBHOOK_SECRET)) {
    return new Response("Bad signature", { status: 400 });
  }
  // handle the event, return 200 fast
  return new Response("ok");
}

Return fast

Acknowledge quickly and do slow work elsewhere. A webhook that does heavy processing inline will time out and the sender will retry.

Run background jobs

Serverless functions stop when the response is sent, so you cannot rely on work continuing after that. Two options:

  • Scheduled functions (cron), for recurring work like digests or cleanup. Most hosts support a cron trigger that calls a route on a schedule.
  • A queue (for example QStash or a hosted queue), for work triggered by an event. Enqueue from your route, process in a dedicated handler.

Idempotency matters

Webhooks and queues retry. Make handlers idempotent (safe to run twice) by keying on the event id, so a retry never double-charges or double-sends.