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.
