Webhooks — integration guide
This guide covers production-grade webhook handling. For the API reference (event types, payload format, signature verification), see API → Webhooks.
Local development
Webhook endpoints need to be publicly reachable. During development, use the NahuPay CLI to forward events to your local server:
# Install the CLI
npm install -g nahupay-cli
# Log in
nahupay login
# Forward to your local handler
nahupay listen --forward-to localhost:3000/api/webhooks/nahupayThe CLI prints a webhook secret specific to your forwarding session. Use this as NAHUPAY_WEBHOOK_SECRET in your .env.local.
Register an endpoint
- Go to Dashboard → Settings → Webhooks → Add endpoint
- Enter your HTTPS URL (e.g.
https://api.myshop.com/webhooks/nahupay) - Select which events to receive (or choose "All events")
- Copy the generated webhook secret — you won't see it again
You can also register endpoints programmatically:
const endpoint = await client.webhookEndpoints.create({url: 'https://api.myshop.com/webhooks/nahupay',events: ['payment.completed', 'payment.refunded'],});console.log(endpoint.secret); // store this immediatelyIdempotency
NahuPay will retry events if your endpoint is temporarily unreachable. Your handler must be idempotent — safe to call multiple times with the same event. The simplest approach is to store processed event IDs in a database:
export async function POST(req: Request) {const event = verifyAndParse(req);// Deduplicate using the event IDconst seen = await db.webhookEvents.findUnique({where: { id: event.id },});if (seen) return Response.json({ received: true });await db.webhookEvents.create({data: { id: event.id, type: event.type, processed_at: new Date() }});await processEvent(event);return Response.json({ received: true });}Async processing
Your endpoint has a 5-second window to return 200. For operations that take longer (database writes, sending emails, calling third-party APIs), return 200 immediately and process in the background:
export async function POST(req: Request) {const event = verifyAndParse(req);// Enqueue for background processing (e.g. BullMQ, Inngest, Trigger.dev)await queue.add('process-payment-event', event);return Response.json({ received: true }); // instant reply}Replaying events
If an event fails all retry attempts, you can manually replay it from Dashboard → Webhooks → [your endpoint] → Failed events → Replay.
You can also replay any event by ID via the API, regardless of its delivery status — useful for backfilling after a deployment:
await client.webhookEndpoints.deliverEvent('we_endpoint_abc',{ event_id: 'evt_01hxbcdef' });