Setup and Verify Webhooks
This guide shows you how to set up a webhook endpoint, register it with NextAPI, and verify incoming signatures.
Step 1: Create Your Endpoint
Your webhook endpoint must:
- Accept
POSTrequests - Return a
2xxstatus within 30 seconds - Be accessible via HTTPS
- Node.js (Express)
- Python (Flask)
import express from 'express';
import { Webhook, WebhookVerificationError } from 'svix';
const app = express();
app.use('/webhooks/nextpay', express.raw({ type: 'application/json' }));
app.post('/webhooks/nextpay', (req, res) => {
const secret = process.env.NEXTAPI_WEBHOOK_SECRET;
const wh = new Webhook(secret);
let event;
try {
event = wh.verify(req.body.toString(), {
'svix-id': req.headers['svix-id'],
'svix-timestamp': req.headers['svix-timestamp'],
'svix-signature': req.headers['svix-signature'],
});
} catch (err) {
return res.status(401).json({ error: 'Invalid signature' });
}
switch (event.event || event.type) {
case 'v2.payment_intent.succeeded':
// v2-prefixed events use event.event and event.payload
// Handle successful payment
break;
case 'payout_request.processed':
// Handle processed payout
break;
case 'payout.failed':
// Handle failed payout
break;
}
res.status(200).send('OK');
});
app.listen(3000);
from flask import Flask, request
from svix.webhooks import Webhook, WebhookVerificationError
import os
app = Flask(__name__)
@app.route('/webhooks/nextpay', methods=['POST'])
def handle_webhook():
raw_body = request.get_data()
secret = os.environ['NEXTAPI_WEBHOOK_SECRET']
wh = Webhook(secret)
try:
event = wh.verify(raw_body, {
'svix-id': request.headers.get('svix-id', ''),
'svix-timestamp': request.headers.get('svix-timestamp', ''),
'svix-signature': request.headers.get('svix-signature', ''),
})
except WebhookVerificationError:
return 'Invalid signature', 401
event_name = event.get('event') or event.get('type')
if event_name == 'v2.payment_intent.succeeded':
pass # v2-prefixed events use event['event'] and event['payload']
elif event_name == 'payout_request.processed':
pass # Handle processed payout
elif event_name == 'payout.failed':
pass # Handle failed payout
return 'OK', 200
Step 2: Register Your Webhook
Use the API to register your endpoint URL and select which events to receive:
curl -X POST "https://api.partners.nextpay.world/v2/webhooks" \
-H "Content-Type: application/json" \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-d '{
"url": "https://your-domain.com/webhooks/nextpay",
"events": [
"v2.payment_intent.succeeded",
"payout_request.processed",
"payout.failed"
]
}'
The response includes your webhook id and the secret used for signature verification. Store the secret securely — it is only shown once.
Step 3: Handle Deduplication
Webhook events may be delivered more than once. Use the event id to prevent duplicate processing:
const processedEvents = new Set();
function handleEvent(event) {
if (processedEvents.has(event.id)) {
return; // Already processed
}
// Process the event...
processedEvents.add(event.id);
}
In production, store processed event IDs in your database rather than in-memory.
Step 4: Test Locally
Use a tunneling tool like ngrok to expose your local server for testing:
ngrok http 3000
Then register the ngrok URL as your webhook endpoint in the sandbox environment.
Troubleshooting
| Issue | Solution |
|---|---|
| 401 Unauthorized | Check your webhook secret and signature verification logic |
| Timeout errors | Acknowledge quickly, process async. Return 200 before heavy work. |
| Missing events | Verify your webhook is registered for the correct event types |
| Duplicate events | Implement deduplication using event IDs |
Related
- Understanding Webhooks — how webhooks work
- API Reference: Create Webhook — full endpoint docs
- API Reference: List Webhooks — manage your webhooks