Skip to content
Last updated

Webhooks Overview

Webhooks enable you to receive near real-time notifications when events occur in your MVMNT account. Instead of polling the API for changes, MVMNT sends HTTP POST requests to your server when important events happen.

What Are Webhooks?

A webhook is an HTTP callback - an HTTP POST request sent to a URL you configure. When an event occurs in MVMNT (like an order status change), we send a JSON payload to your webhook endpoint.

┌──────────────┐         Event Occurs        ┌──────────────┐
│    MVMNT     │      (Order Booked)         │  Your MVMNT  │
│    System    │────────────────────────────▶│   Account    │
└──────────────┘                             └──────────────┘


                                       ┌──────────────▼──────────────┐
                                       │  Event Processing Pipeline   │
                                       │   (EventBridge + Lambda)     │
                                       └──────────────┬──────────────┘

                                                      │ HTTP POST

                                       ┌──────────────▼──────────────┐
                                       │   Your Webhook Endpoint      │
                                       │   https://your-app.com/hooks │
                                       └──────────────────────────────┘

Why Use Webhooks?

✅ Benefits

  • Real-Time Updates: Get notified within seconds of events occurring
  • Reduced API Calls: No need to poll for changes (saves on rate limits)
  • Better Performance: Lower latency than polling
  • Event-Driven Architecture: Build reactive, event-driven integrations
  • Bandwidth Efficient: Only receive data when something changes

Without webhooks, you'd need to poll:

// ❌ Don't do this - wastes API calls and has high latency
setInterval(async () => {
  const shipments = await api.get('/v1/shipments?status=in_transit');
  shipments.forEach(checkForUpdates);
}, 30000); // Poll every 30 seconds
// ✅ Do this - real-time, efficient
app.post('/webhooks/mvmnt', (req, res) => {
  const { events } = req.body;

  events.forEach((event) => {
    if (event.event === 'SHIPMENT_DELIVERED') {
      handleShipmentUpdate(event.data);
    }
  });

  res.status(200).send('OK');
});

How Webhooks Work

1. Configure Webhook in MVMNT UI

Set up your webhook in the MVMNT application:

  • URL: Your endpoint (must be HTTPS)
  • Token: Secret token that MVMNT will send in the x-api-key header
  • Events: Select which event types to receive (e.g., SHIPMENT_DELIVERED, SHIPMENT_BOOKED)

The webhook configuration is managed through the MVMNT UI settings page.

2. Receive Webhook Requests

When an event occurs, MVMNT sends an HTTP POST request to your URL:

POST /webhooks/mvmnt HTTP/1.1
Host: your-app.com
Content-Type: application/json
x-api-key: your-webhook-token-from-ui

{
  "sentAt": "2025-01-15T14:30:00Z",
  "events": [
    {
      "event": "SHIPMENT_DELIVERED",
      "timestamp": "2025-01-15T14:30:00Z",
      "data": {
        "id": "660e8400-e29b-41d4-a716-446655440000",
        "friendlyId": "SHP-12345",
        "key": "ERP-SHIP-789"
      }
    }
  ]
}

3. Verify Token

Always verify the x-api-key header matches your configured webhook token:

app.post('/webhooks/mvmnt', (req, res) => {
  const receivedToken = req.headers['x-api-key'];
  const expectedToken = process.env.WEBHOOK_TOKEN; // Token you configured in MVMNT UI

  if (receivedToken !== expectedToken) {
    return res.status(401).send('Invalid token');
  }

  // Token is valid - process webhook
  console.log('Valid webhook:', req.body);
  res.status(200).send('OK');
});

4. Respond Quickly

Respond with 200 OK within 5 seconds:

app.post('/webhooks/mvmnt', async (req, res) => {
  // Verify token
  if (req.headers['x-api-key'] !== process.env.WEBHOOK_TOKEN) {
    return res.status(401).send('Invalid token');
  }

  // Respond quickly
  res.status(200).send('OK');

  // Process asynchronously
  processWebhookAsync(req.body);
});

5. Handle Retries

If your endpoint fails or doesn't respond, MVMNT automatically retries with exponential backoff:

  • 1st retry: 1 minute
  • 2nd retry: 5 minutes
  • 3rd retry: 15 minutes
  • 4th retry: 1 hour
  • 5th retry: 4 hours

After 5 failed attempts, the webhook delivery is abandoned.

Webhook Payload Structure

All webhook deliveries follow this structure:

{
  "sentAt": "2025-01-15T14:30:00Z",
  "events": [
    {
      "event": "SHIPMENT_DELIVERED",
      "timestamp": "2025-01-15T14:30:00Z",
      "data": {
        // Event-specific data
        // Always includes: id, friendlyId, key (if set)
      },
      "diff": null
    }
  ]
}

Top-Level Fields

Every webhook delivery includes:

FieldTypeDescription
sentAtstring (ISO 8601)When the webhook was sent from MVMNT
eventsarrayArray of events (typically one event)

Event Fields

Each event in the events array includes:

FieldTypeDescription
eventstringEvent type (e.g., SHIPMENT_DELIVERED)
timestampstring (ISO 8601)When the event occurred
dataobjectEvent-specific data
data.idstring (UUID)Resource ID
data.friendlyIdstringHuman-readable ID (e.g., SHP-12345)
data.keystring | nullYour system's reference ID (if set)
diffarray | nullChanges made (only for UPDATE events, else null)

Event-Specific Fields

Each event type includes additional relevant fields in the data object. The exact structure varies by event type.

Example: SHIPMENT_DELIVERED

{
  "sentAt": "2025-01-15T14:30:00Z",
  "events": [
    {
      "event": "SHIPMENT_DELIVERED",
      "timestamp": "2025-01-15T14:30:00Z",
      "data": {
        "id": "660e8400-e29b-41d4-a716-446655440000",
        "friendlyId": "SHP-12345",
        "key": "ERP-SHIP-789"
      },
      "diff": null
    }
  ]
}

Example: CARRIER_UPDATED (with diff)

{
  "sentAt": "2025-01-15T14:30:00Z",
  "events": [
    {
      "event": "CARRIER_UPDATED",
      "timestamp": "2025-01-15T14:30:00Z",
      "data": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "friendlyId": "CAR-12345",
        "key": "ERP-CARRIER-789",
        "status": "INACTIVE"
      },
      "diff": [
        {
          "type": "UPDATE",
          "key": "status",
          "value": "INACTIVE",
          "oldValue": "ACTIVE"
        }
      ]
    }
  ]
}

Note: See the API Reference for the complete schema of each webhook payload type.

Quick Start

1. Create a Test Endpoint

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

app.use(express.json());

app.post('/webhooks/mvmnt', (req, res) => {
  const { sentAt, events } = req.body;
  console.log(`Received ${events.length} event(s) sent at ${sentAt}`);

  events.forEach((event) => {
    console.log('Event:', event.event, 'Data:', event.data);
  });

  res.status(200).send('OK');
});

app.listen(3000, () => {
  console.log('Webhook server listening on port 3000');
});

2. Expose with ngrok (for testing)

ngrok http 3000

Copy the HTTPS URL (e.g., https://abc123.ngrok.io)

3. Configure in MVMNT UI

  1. Go to MVMNT Settings → Webhooks
  2. Add your ngrok URL: https://abc123.ngrok.io/webhooks/mvmnt
  3. Generate and copy a webhook token
  4. Select event types (e.g., SHIPMENT_DELIVERED, SHIPMENT_BOOKED)
  5. Save configuration

4. Update Your Code

// Use the token from MVMNT UI
const WEBHOOK_TOKEN = 'your-token-from-mvmnt-ui';

5. Trigger Test Event

Create or update a shipment in the MVMNT UI, and watch your endpoint receive the webhook!

Best Practices

✅ Do

  • Use HTTPS (required for production)
  • Verify token on all webhook requests using the x-api-key header
  • Respond quickly (< 5 seconds) with 200 OK
  • Process asynchronously - don't block the response
  • Use key for easy correlation with your system
  • Handle duplicate events (webhooks may be delivered more than once)
  • Log webhook deliveries for debugging
  • Test thoroughly before going to production

❌ Don't

  • Don't use HTTP (only HTTPS allowed in production)
  • Don't perform long operations before responding
  • Don't return errors for duplicate events (return 200)
  • Don't rely on event ordering (events may arrive out of order)
  • Don't skip token verification (security risk)

Managing Webhooks

All webhook configuration is managed through the MVMNT UI:

  1. Settings → Webhooks - Configure URL, token, and event types
  2. Enable/Disable - Toggle webhook delivery on/off
  3. Update Events - Add or remove event types to receive
  4. Rotate Token - Generate a new token for security
  5. View Logs - Monitor webhook deliveries and failures

Troubleshooting

Common Issues

  1. Token verification fails:

    • Ensure you're using the correct token from MVMNT UI
    • Check that x-api-key header is being read correctly
    • Verify no extra whitespace in token comparison
  2. Endpoint unreachable:

    • Verify URL and firewall settings
    • Ensure HTTPS certificate is valid
    • Check that your server is publicly accessible
  3. Timeout errors:

    • Endpoint must respond within 5 seconds
    • Move processing to background jobs
    • Return 200 OK immediately
  4. Duplicate webhooks:

    • This is expected behavior due to retries
    • Implement idempotency using event IDs
    • Always return 200 even for duplicates

Next Steps