Webhook Best Practices
Return 200 Quickly
Your webhook endpoint should return a 200 status code as fast as possible. Do the heavy processing asynchronously.
app.post('/webhooks/inflow', express.raw({ type: 'application/json' }), (req, res) => {
const raw = req.body.toString('utf8');
// Verify signature first — see Verifying signatures
res.status(200).json({ received: true });
const { data } = JSON.parse(raw);
processEvent(data).catch(console.error);
});If your endpoint doesn't respond with a 200 within 15 seconds (the hard timeout), the delivery is considered failed. As a best practice, aim to respond within 5 seconds by processing events asynchronously.
Handle Duplicate Events
Your endpoint may receive the same event more than once. Always implement idempotent processing.
Deduplicate on payload.webhookEventId (mwe_…), the id Inflow gives each event. It is the same value across retry attempts of a delivery and when the event is replayed:
app.post('/webhooks/inflow', express.raw({ type: 'application/json' }), async (req, res) => {
// Respond immediately
res.status(200).json({ received: true });
const { data } = JSON.parse(req.body);
for (const event of data) {
const eventId = event.payload.webhookEventId;
// Check if already processed
const existing = await db.webhookEvents.findOne({ eventId });
if (existing) continue;
// Process the event
await handleEvent(event.eventType, event.payload);
// Record that we've processed this event
await db.webhookEvents.create({ eventId, processedAt: new Date() });
}
});A delivery is a batch: the
svix-idheader identifies the HTTP request, not one event, and a replayed event arrives under a newsvix-id. Keep verifying it as part of the signature, but deduplicate per event onwebhookEventId.Events sent before
webhookEventIdwas introduced do not carry it — fall back to the resource id plus its status (e.g.pay_…+CHECKOUT_SUCCESS) for those.
Verifying webhook signatures
Inflow signs every delivery with Svix. You must verify svix-id, svix-timestamp, and svix-signature using your endpoint secret before processing the body.
See the dedicated guide: Verifying signatures (SDK example, manual HMAC steps, test vectors, and GET /api/webhook/{webhookId}/secret).
Handle All Event Types
Even if you only care about specific events, your endpoint should handle unexpected event types gracefully:
const { data } = JSON.parse(rawBody);
for (const event of data) {
switch (event.eventType) {
case 'payment.created':
case 'payment.authorized':
case 'payment.settled':
case 'connect.payment.authorized':
case 'connect.payment.settled':
handlePayment(event.payload);
break;
default:
console.log('Unhandled event type:', event.eventType);
break;
}
}Always return 200 even for event types you don't process — otherwise Inflow may consider the delivery failed.
Never Infer State From a Sequence
Deliveries can arrive out of order. If you receive subscription.canceled and then subscription.active, do not conclude that the subscription is active — the second event may simply be an older delivery landing late. Treat each event as a trigger to reconcile, and read the authoritative state from the API when the two disagree.
To order or date events, use payload.occurredAt, the time of the change the event reports, recorded when the change was made. It is the same whether the delivery is on time, late, retried or replayed. Neither the delivery time nor the resource's updatedAt is the event's time: a retried delivery lands later, and updatedAt is the resource's last write, which may be an earlier change than the one the event reports.
Events sent before
occurredAtwas introduced do not carry it — fall back to the delivery time for those.
Granting access from webhooks? Granting Access with Webhooks maps each event to a grant or revoke decision, and Refreshing Entitlements shows the reconciliation fallback when a delivery is late or lost.
Use HTTPS
Your webhook URL must use HTTPS. HTTP endpoints are not supported.
Monitor Webhook Health
Periodically check your webhook status:
curl https://api.inflowpay.com/api/webhook/{webhookId}/status \
-H "X-Inflow-Api-Key: inflow_prod_your_key"If the status shows disabled or a failureReason, investigate and fix the issue, then re-enable the webhook.
In the Dashboard, Settings → Developers shows each webhook's Status. Error appears when the last delivery failed; click it to see why.
Recommended Architecture
Inflow Webhook
|
v
[Your Endpoint] --> [Message Queue / Job] --> [Business Logic]
|
v
Return 200
- Receive the webhook event.
- Acknowledge immediately with
200. - Queue the event for asynchronous processing.
- Process the event in a background worker.
This architecture ensures you never miss events due to processing delays.
Checklist
- Endpoint uses HTTPS
- Returns 200 within 5 seconds (hard limit: 15s)
- Handles duplicate events (idempotent)
- Processes events asynchronously
- Handles unknown event types gracefully
- Logs all received events for debugging
- Monitors webhook status regularly
Updated 9 days ago
