Skip to main content

Overview

Webhooks notify your application when parcel events occur in real-time, eliminating the need to poll for updates.

Setup

  1. Go to DashboardSettingsWebhooks
  2. Add your endpoint URL (must be HTTPS)
  3. Select the events you want to receive
  4. Save and test the webhook
Configure separate URLs for sandbox and production environments.

Webhook Format

All webhooks follow this structure:
{
  "id": "evt_abc123xyz",
  "event": "parcel.status_changed",
  "timestamp": "2024-12-21T14:30:00Z",
  "environment": "production",
  "data": {
    // Event-specific payload
  }
}

Event Types

parcel.status_changed

Triggered when a parcel’s status changes.
{
  "event": "parcel.status_changed",
  "data": {
    "trackingNumber": "CBECUSD123456789",
    "previousStatus": "IN_TRANSIT",
    "newStatus": "OUT_FOR_DELIVERY",
    "timestamp": "2024-12-21T14:30:00Z",
    "location": "Guayaquil, EC"
  }
}

parcel.delivered

Triggered when a parcel is delivered.
{
  "event": "parcel.delivered",
  "data": {
    "trackingNumber": "CBECUSD123456789",
    "deliveredAt": "2024-12-21T16:45:00Z",
    "signature": "M. García",
    "location": "Guayaquil, EC",
    "proofOfDelivery": "https://..."
  }
}

parcel.exception

Triggered when a delivery exception occurs.
{
  "event": "parcel.exception",
  "data": {
    "trackingNumber": "CBECUSD123456789",
    "exceptionType": "DELIVERY_FAILED",
    "reason": "Recipient not available",
    "timestamp": "2024-12-21T17:00:00Z",
    "location": "Guayaquil, EC",
    "nextAttempt": "2024-12-22T09:00:00Z"
  }
}

manifest.submitted

Triggered when a manifest is successfully submitted.
{
  "event": "manifest.submitted",
  "data": {
    "shipmentId": "shp_abc123",
    "awbNumber": "180-12345678",
    "parcelCount": 50,
    "country": "EC"
  }
}

Webhook Handling

Basic Handler

const express = require('express');
const crypto = require('crypto');

const app = express();

app.post('/webhooks/crossborderly', express.json(), (req, res) => {
  // Verify signature
  const signature = req.headers['x-crossborderly-signature'];
  if (!verifySignature(req.body, signature)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process event
  const event = req.body;
  
  switch (event.event) {
    case 'parcel.status_changed':
      handleStatusChange(event.data);
      break;
    case 'parcel.delivered':
      handleDelivery(event.data);
      break;
    case 'parcel.exception':
      handleException(event.data);
      break;
  }
  
  // Always respond 200 quickly
  res.status(200).json({ received: true });
});

function verifySignature(payload, signature) {
  const expected = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(JSON.stringify(payload))
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Async Processing

Process webhooks asynchronously to respond quickly:
app.post('/webhooks/crossborderly', express.json(), async (req, res) => {
  // Respond immediately
  res.status(200).json({ received: true });
  
  // Process in background
  try {
    await processWebhook(req.body);
  } catch (error) {
    console.error('Webhook processing failed:', error);
    // Add to retry queue
    await queue.add('webhook-retry', req.body);
  }
});

Security

Signature Verification

All webhooks include a signature header for verification:
X-CrossBorderly-Signature: sha256=abc123...
Always verify this signature to ensure the webhook is authentic.

IP Whitelist

Webhooks are sent from these IP addresses:
34.198.12.45
52.44.78.123
18.215.99.67
Optionally whitelist these IPs in your firewall.

Retry Policy

AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry12 hours
Final24 hours
After all retries fail, the webhook is marked as failed. Check the dashboard for failed webhooks.

Best Practices

Return 200 within 5 seconds. Process asynchronously if needed.
Use the id field to deduplicate webhooks. Same event may be sent multiple times.
Always verify the webhook signature to prevent spoofing.
Store raw webhook payloads for debugging and audit purposes.

Testing

Send test webhooks from the dashboard:
  1. Go to WebhooksTest
  2. Select event type
  3. Click Send Test Webhook
Or use the API:
curl -X POST https://api.crbtrack.com/api/v1/webhooks/test \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"event": "parcel.delivered", "trackingNumber": "CBECUSD123456789"}'