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

Acknowledge quickly with a success status

Terminal49 expects your endpoint to return a success status (200, 201, 202, or 204). Acknowledge quickly โ€” but only after you have durably accepted the event. Persist the raw payload or push it onto a queue first, then return the response and process the event asynchronously.
app.post("/webhooks/terminal49", async (req, res) => {
  // Durably accept the event first (persist or enqueue the raw payload)
  await enqueueWebhook(req.body);

  // Acknowledge; process asynchronously from the queue
  res.sendStatus(202);
});
If you acknowledge before storing the event and your process crashes mid-handling, the notification is lost โ€” Terminal49 sees a success response and does not retry. Any other response โ€” including a timeout โ€” triggers retries. If your endpoint consistently fails, Terminal49 retries up to 12 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:
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() });
}
If you use a message queue (SQS, RabbitMQ, etc.), enqueue the raw payload and return a success status as soon as the enqueue succeeds. Your queue consumer can handle deduplication and processing at its own pace.

Verify the webhook source

Terminal49 publishes the IP addresses that webhook notifications originate from. Use the List Webhook IPs endpoint to fetch the current list and validate incoming requests:
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);
});
Cache the IP list and refresh it periodically (e.g., daily). The list rarely changes, but checking the endpoint ensures you stay current.

Monitor delivery status

Use the Webhook Notifications API to check delivery status and catch any notifications your endpoint may have missed:
curl -s "https://api.terminal49.com/v2/webhook_notifications" \
  -H "Authorization: Token YOUR_API_KEY" \
  -H "Content-Type: application/vnd.api+json"
Each notificationโ€™s delivery_status attribute reports the delivery state (pending, succeeded, or failed). Page through recent notifications and review the ones marked failed regularly to identify issues with your endpoint before they cause data gaps.

Test with the Trigger endpoint

Use the Trigger 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.
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.

Handle downtime gracefully

If your endpoint goes down, Terminal49 retries failed deliveries. When your endpoint recovers:
  1. Use the Webhook Notifications API to list recent notifications and identify the ones with delivery_status: failed.
  2. Re-fetch the current state of the affected shipments and containers from the REST API. Webhook notifications cannot be replayed โ€” the Trigger Webhook endpoint only sends a one-time sample payload for testing.
  3. For longer outages, list recent shipments via the API to catch up on any missed state changes.

Keep your webhook active

Terminal49 may deactivate a webhook after repeated delivery failures. Check your webhookโ€™s active status periodically:
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 to set active: true.

Summary checklist

  • Return a success status (200, 201, 202, or 204) after durably accepting the event, then 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