# Best Practices (/docs/developer-resources/guides/webhook/best-practices)

Building a reliable webhook integration requires more than a working endpoint. This page covers architectural patterns, operational practices, and defensive techniques that help your integration handle real-world conditions: retries, failures, traffic spikes, and edge cases.

## Respond quickly, process later

The most important architectural decision for webhook reliability is to separate receiving events from processing them. Your endpoint should acknowledge receipt immediately and defer actual processing to a background system.

When Monime sends a webhook, it waits for your response. If your endpoint does database queries, makes external API calls, or performs any slow operation before responding, you risk timeouts. Timeouts trigger retries, and retries compound the load on your system.

The solution is to return 202 Accepted as soon as you have stored the event somewhere durable—a database, a message queue, or a job system. A background worker then picks up the event and processes it at its own pace.

```javascript
app.post('/webhooks/monime', express.raw({ type: 'application/json' }), async (req, res) => {
  // Verify signature first
  if (!verifySignature(req)) {
    return res.status(401).send('Unauthorized');
  }

  const payload = JSON.parse(req.body.toString());

  // Store in queue for later processing
  await queue.enqueue('webhook-events', payload);

  // Respond immediately
  res.status(202).send('Accepted');
});
```

This pattern has several benefits. Your endpoint responds within milliseconds, well under any timeout. Processing failures do not cause webhook delivery failures. You can retry processing locally without waiting for Monime to redeliver. You can scale workers independently of the endpoint.

## Handle duplicate events

Monime may deliver the same event more than once. Duplicates occur when:

- A delivery attempt times out, but your endpoint actually processed it. Monime does not know it succeeded and retries.
- You replay a delivery from the dashboard for debugging.
- Network conditions cause a request to be duplicated in transit.

Your handler must be idempotent: processing the same event twice should produce the same result as processing it once. The standard approach is to track which event IDs have been processed.

```javascript
async function handleEvent(payload) {
  const eventId = payload.event.id;

  // Check if already processed
  const exists = await db.events.findOne({ eventId });
  if (exists) {
    console.log('Duplicate event, skipping:', eventId);
    return;
  }

  // Process the event
  await processPayment(payload.data);

  // Mark as processed
  await db.events.insertOne({ eventId, processedAt: new Date() });
}
```

Store event IDs with a time-to-live. The TTL should exceed Monime's retry window (about 24 hours) to catch duplicates during that period. Older entries can be safely cleaned up because Monime will not deliver events from the distant past.

For some operations, idempotency can be achieved without storing IDs. For example, if you update a record to a specific state, applying the same update twice is harmless. But for operations that accumulate (like incrementing a counter or sending a notification), explicit duplicate detection is necessary.

## Handle out-of-order delivery

Events can arrive in a different order than they occurred. This happens when an earlier delivery fails and is retried while later events succeed immediately.

Consider a payment that transitions from `created` to `processing` to `completed`. If the `processing` delivery fails and is retried an hour later, you might receive `completed` before `processing`.

Design your handlers to tolerate this. Options include:

**Ignore stale events.** Each payload includes a timestamp. If you receive an event with an older timestamp than what you have already processed for that resource, skip it.

**Fetch current state from the API.** Instead of relying on the event payload, use it as a trigger to fetch the current state of the resource from the Monime API. This ensures you always work with the latest data.

**Use optimistic locking.** If your local database tracks resource states, include a version or timestamp field. Update only if the incoming event is newer than the stored version.

## Verify every delivery

No exceptions. Verify signatures before doing anything else, including parsing the JSON body. An endpoint that processes unverified requests is vulnerable to forged events.

Signature verification should be the first thing your handler does after receiving the raw body bytes. If verification fails, return 401 and stop processing.

```javascript
app.post('/webhooks/monime', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifySignature(req.body, req.headers['monime-signature'])) {
    return res.status(401).send('Unauthorized');
  }

  // Only now do we trust the payload
  const payload = JSON.parse(req.body.toString());
  // ...
});
```

## Use the raw request body for verification

A common mistake is computing the signature over a parsed and re-serialized JSON object instead of the raw bytes. JSON parsers may reorder keys, normalize whitespace, or change number formatting. Any change to the bytes produces a different signature.

Ensure your web framework gives you access to the raw body. In Express, use `express.raw()`. In Flask, use `request.get_data()`. In Go, read from `r.Body` before any middleware parses it.

## Design for bursty traffic

Webhook traffic is inherently unpredictable. A popular product launch might generate thousands of events in seconds. Your endpoint must handle these bursts without falling over.

Strategies for handling traffic spikes:

**Horizontal scaling.** Run multiple instances of your endpoint behind a load balancer. Stateless handlers scale easily.

**Rate limiting on the processing side.** Your queue or job system can smooth out bursts. The endpoint accepts events quickly; workers process them at a sustainable rate.

**Circuit breakers.** If a downstream service (like your database or an external API) becomes overwhelmed, a circuit breaker prevents your handler from making things worse by continuing to hammer it.

**Graceful degradation.** In extreme cases, accept events into a durable queue and alert your team. Processing can catch up once the surge subsides.

## Organize webhooks by purpose

As your integration grows, you might create multiple webhooks. Organize them thoughtfully.

**Separate environments.** Create distinct webhooks for development, staging, and production. Use different secrets for each. This prevents test events from affecting production and vice versa.

**Separate systems.** If different systems handle different events, consider separate webhooks. Your accounting system might receive `payment.processing_completed` events while your CRM receives `checkout_session.completed` events. This lets you configure different alert emails and makes debugging easier.

**Use clear names.** A webhook named "Production Payment Events to Accounting" is much easier to manage than "Webhook 1."

Avoid creating too many webhooks for the same endpoint. Each webhook adds configuration overhead and makes monitoring harder. If one endpoint handles all events, one webhook per environment is usually sufficient.

## Configure alert emails thoughtfully

Monime sends alert emails when deliveries fail repeatedly. These alerts are your first line of defense against data loss.

Configure alert emails to reach people who can investigate: an on-call engineer, a team inbox, or an alerting system like PagerDuty. Do not route alerts to an unmonitored mailbox.

Respond to alerts promptly. A few transient failures are normal—networks hiccup, servers restart. But persistent failures mean events are being lost. Investigate immediately, fix the issue, and replay failed deliveries.

## Monitor the Event Deliveries table

The dashboard's Event Deliveries table shows the health of your webhook at a glance. Check it regularly, especially after deploying changes to your endpoint.

Look for patterns:

- A spike in Failed deliveries indicates a systemic problem.
- Many Scheduled deliveries suggest your endpoint is struggling but retries may succeed.
- Consistent Success means everything is working.

For automated monitoring, consider building a dashboard or alert that tracks delivery success rates. The Monime API provides the data you need.

## Test before going live

Before enabling a webhook in production, test it thoroughly:

**Unit test your handler.** Mock webhook payloads and verify your handler processes them correctly. Test edge cases: missing fields, unexpected values, duplicate events.

**Integration test with a tunnel.** During development, use ngrok or localtunnel to receive real webhook deliveries on your local machine. Verify signature verification works.

**Test in a staging environment.** Mirror your production setup as closely as possible. Trigger events and confirm they flow through your system correctly.

**Review the pre-launch checklist.** The [pre-launch checklist](/docs/developer-resources/guides/webhook/pre-launch-checklist) covers everything you should verify before going live.

## Use the right API release

Events are scoped to the API release you select when creating the webhook. The release determines which events exist, the payload structure, and field types.

Confirm the release matches what your code expects. If you upgrade to a new release, review the release notes for breaking changes and update your handler accordingly.

When Monime introduces a new release with changes you need, create a new webhook with the new release rather than modifying the existing one. This lets you test the new payload format before switching over.

## Replay judiciously

The replay feature lets you resend a delivery after fixing an issue. It is powerful but should be used carefully.

Before replaying:

- Confirm the underlying issue is fixed. Replaying into a broken handler just creates another failed delivery.
- Consider whether your handler is idempotent. If it already processed the event through another path, replaying may cause duplicates.
- Verify you are replaying the right deliveries. Replaying hundreds of events indiscriminately can overwhelm your system or cause unintended side effects.

Replay is most useful for recovering from brief outages: your endpoint was down for 30 minutes, deliveries failed, you fixed the issue, and now you replay to catch up.

## Document your integration

Maintain documentation that describes:

- Which webhooks exist and their purpose.
- What events each webhook subscribes to.
- How your handler processes each event type.
- Where secrets are stored.
- Who is responsible for monitoring and responding to alerts.

This documentation is invaluable when onboarding new team members, debugging issues at 3 AM, or auditing your integration.

## Related topics

- [Delivery and retries](/docs/developer-resources/guides/webhook/delivery-and-retries) explains the retry mechanism in detail.
- [Monitoring and debugging](/docs/developer-resources/guides/webhook/monitoring-and-debugging) covers using the dashboard to investigate issues.
- [Pre-launch checklist](/docs/developer-resources/guides/webhook/pre-launch-checklist) provides a verification checklist for production readiness.
