Skip to content

Next.js

In the App Router, a route handler reads the raw body with request.text() and passes the Fetch Headers straight to verify.

Terminal window
npm install ricochet-webhooks
app/webhooks/route.ts
import { verify, WebhookVerificationError } from 'ricochet-webhooks';
// ricochet-webhooks uses node:crypto.
export const runtime = 'nodejs';
export async function POST(request: Request) {
const body = await request.text();
let id: string;
try {
({ id } = verify(body, request.headers, process.env.RICOCHET_WEBHOOK_SECRET!));
} catch (error) {
if (error instanceof WebhookVerificationError) {
return new Response(error.reason, { status: 401 });
}
throw error;
}
// Skip the delivery if `id` was already handled (store it in your database).
const event = JSON.parse(body);
console.log('received', id, event.type);
return new Response(null, { status: 200 });
}

The route is POST /webhooks; point your destination at https://<your app>/webhooks.

For the Pages Router, turn off the body parser for the route (export const config = { api: { bodyParser: false } }) and read the raw body from the request stream before calling verify(body, req.headers, secret).

Terminal window
npm run dev
ricochet listen --forward localhost:3000/webhooks

Deliveries through the CLI carry no webhook-signature (a CLI destination has no signing secret), so turn the check off in local development, for example with an environment variable. Never skip it just because the header is missing: that would accept unsigned requests in production too.