> ## Documentation Index
> Fetch the complete documentation index at: https://terminal49-codex-update-typescript-sdk-cli-current.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Best Practices

> Handle retries, ensure idempotency, verify signatures, and build reliable webhook consumers for Terminal49 shipment and container events.

Webhooks are the primary way to receive tracking updates from Terminal49. Follow these practices to build a consumer that handles every edge case reliably.

## Respond quickly with a 2xx

Terminal49 expects your endpoint to return one of these status codes: `200`, `201`, `202`, or `204`. Return the response **before** doing any heavy processing.

```javascript theme={null}
app.post("/webhooks/terminal49", (req, res) => {
  // Acknowledge immediately
  res.sendStatus(200);

  // Process asynchronously
  processWebhookAsync(req.body).catch(console.error);
});
```

Any other response — including a timeout — triggers retries. If your endpoint consistently fails, Terminal49 will retry up to a dozen times before marking the notification as failed.

## Handle retries and duplicate deliveries

Terminal49 retries failed deliveries, which means your endpoint may receive the same notification more than once. Design your consumer to be **idempotent**.

Every webhook notification has a unique `id` in `data.id`. Use it to deduplicate:

```javascript theme={null}
async function processWebhook(payload) {
  const notificationId = payload.data.id;

  // Check if already processed
  const alreadyProcessed = await db.webhookLog.findOne({
    where: { notificationId },
  });
  if (alreadyProcessed) {
    return; // Skip duplicate
  }

  // Process the event
  await handleEvent(payload);

  // Record that we processed it
  await db.webhookLog.create({ notificationId, processedAt: new Date() });
}
```

<Tip>
  If you use a message queue (SQS, RabbitMQ, etc.), enqueue the raw payload immediately and return `200`. Your queue consumer can handle deduplication and processing at its own pace.
</Tip>

## Verify the webhook source

Terminal49 publishes the IP addresses that webhook notifications originate from. Use the [List Webhook IPs](/api-docs/api-reference/webhooks/list-webhook-ips) endpoint to fetch the current list and validate incoming requests:

```javascript theme={null}
const ALLOWED_IPS = await fetchTerminal49WebhookIPs();

app.post("/webhooks/terminal49", (req, res) => {
  const sourceIp = req.ip;
  if (!ALLOWED_IPS.includes(sourceIp)) {
    return res.sendStatus(403);
  }

  // Process the webhook
  res.sendStatus(200);
});
```

<Info>
  Cache the IP list and refresh it periodically (e.g., daily). The list rarely changes, but checking the endpoint ensures you stay current.
</Info>

## Monitor delivery status

Use the [Webhook Notifications API](/api-docs/api-reference/webhook-notifications/list-webhook-notifications) to check delivery status and catch any notifications your endpoint may have missed:

```bash theme={null}
curl -s "https://api.terminal49.com/v2/webhook_notifications?filter[delivery_status]=failed" \
  -H "Authorization: Token YOUR_API_KEY" \
  -H "Content-Type: application/vnd.api+json"
```

Review failed notifications regularly to identify issues with your endpoint before they cause data gaps.

## Test with the Trigger endpoint

Use the [Trigger Webhook](/api-docs/api-reference/webhooks/trigger-a-webhook) endpoint to send a one-time sample webhook notification to an HTTPS URL. This is useful when you want to validate your signature verification, parsing, queueing, and event routing before subscribing a production webhook.

```bash theme={null}
curl -s -X POST "https://api.terminal49.com/v2/webhooks/trigger" \
  -H "Authorization: Token YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/terminal49",
    "event": "tracking_request.succeeded",
    "secret": "optional-test-secret"
  }'
```

The Trigger endpoint sends an example payload for the event you choose. If you include `secret`, Terminal49 signs the test request with the same `X-T49-Webhook-Signature` header used by real webhook deliveries.

## Keep your webhook active

Terminal49 may deactivate a webhook after repeated delivery failures. Check your webhook's `active` status periodically:

```bash theme={null}
curl -s "https://api.terminal49.com/v2/webhooks" \
  -H "Authorization: Token YOUR_API_KEY" \
  -H "Content-Type: application/vnd.api+json"
```

If your webhook has been deactivated, fix the endpoint issue and [update the webhook](/api-docs/api-reference/webhooks/edit-a-webhook) to set `active: true`.

## Summary checklist

* [ ] Return `2xx` immediately, process asynchronously
* [ ] Deduplicate using the notification `id`
* [ ] Validate the source IP against the webhook IPs list
* [ ] Monitor failed notifications
* [ ] Subscribe only to the events you need
* [ ] Log raw payloads for debugging

## Related

* [Setting up webhooks](/api-docs/in-depth-guides/webhooks) — create and configure endpoints
* [Event catalog](/api-docs/webhooks/event-catalog) — all available events
* [Webhook API Reference](/api-docs/api-reference/webhooks/create-a-webhook) — CRUD operations for webhooks
