Saltar al contenido principal

Best Practices

Respond quickly​

Your webhook endpoint should:

  • Respond with HTTP 200 within 10 seconds
  • Process webhooks asynchronously (queue for background processing)
  • Avoid long-running operations before responding

Example pattern:

app.post('/webhooks/minteo', async (req, res) => {
const event = req.body;
// 1. Validate signature (fast)
if (!validateChecksum(event, secret)) {
return res.status(401).send('Unauthorized');
}
// 2. Queue for background processing
await queue.add('process-webhook', event);
// 3. Respond immediately
res.status(200).send('OK');
// 4. Process in background worker (not blocking the response)
});

Implement idempotency​

The same event may be delivered multiple times due to retries. Use event_id to deduplicate.

Example:

async function processWebhook(event: WebhookEvent) {
const { event_id, event_type, data } = event;
// Check if already processed
const exists = await db.processedEvents.findOne({ event_id });
if (exists) {
console.log(`Event ${event_id} already processed`);
return;
}
// Process the event
await handleEvent(event_type, data);
// Mark as processed
await db.processedEvents.insert({ event_id, processed_at: new Date() });
}
info

Use event_id for deduplication, not hook_id. The hook_id changes with each retry attempt, but event_id remains constant for the same business event.

Verify signatures on every request​

Always verify the webhook signature before processing. This prevents:

  • Forged requests from malicious actors
  • Accidental processing of invalid data
  • Replay attacks

See Verifying Signatures for implementation details.

Use HTTPS endpoints​

Your webhook URL must use HTTPS. HTTP endpoints are not supported.

Handle failures gracefully​

If your endpoint experiences an error:

  • Return a non-200 status code to trigger a retry
  • Log the error for debugging
  • Minteo will retry up to 14 times over ~64 hours

Example:

app.post('/webhooks/minteo', async (req, res) => {
try {
const event = req.body;
if (!validateChecksum(event, secret)) {
return res.status(401).send('Unauthorized');
}
await processWebhook(event);
res.status(200).send('OK');
} catch (error) {
console.error('Webhook processing failed:', error);
// Return 500 to trigger retry
res.status(500).send('Internal Server Error');
}
});

Monitor webhook delivery​

Check the webhook history in Board to monitor:

  • Delivery success rates
  • Failed webhooks
  • Retry attempts

Testing webhooks​

Sandbox testing​

Test webhooks in Sandbox by performing real operations:

EventHow to triggerFinal states
order.updatedCreate an orderSUCCEEDED, FAILED
payout.updatedCreate a payoutFULFILLED, ABORTED
payin.updatedCreate a payinFULFILLED, ABORTED
payout.item.updatedCreate a payout with itemsSUCCEEDED, REJECTED

For payin.updated, you'll also receive a webhook when the gateway confirms payment (GATEWAY_CONFIRM_PAYIN).

info

Manual webhook triggering is not currently supported. You must perform actual operations to generate webhook events.

Local development​

For local development, expose your localhost as an HTTPS endpoint using ngrok:

# Install ngrok
npm install -g ngrok
# Expose local port 3000
ngrok http 3000
# Use the HTTPS URL in Board webhook configuration
# Example: https://abc123.ngrok.io/webhooks/minteo
aviso

Remember to update your webhook URL in Board when switching between local development and deployed environments.

Next steps​