Skip to main content

Webhooks in Terminal49

Webhooks provide real-time notifications about changes to your shipments and containers, letting you build responsive applications that update as soon as new information becomes available.

How Webhooks Work

Terminal49 Webhook Flow
  1. You register a webhook with a URL endpoint that can receive POST requests
  2. An event occurs on Terminal49 (e.g., a vessel arrives or customs status changes)
  3. Terminal49 sends a notification to your registered URL with details about the event
  4. Your application processes the notification and takes appropriate action

Setting Up Webhooks

Step 1: Create a Webhook Endpoint

First, you need to create an endpoint on your server that can receive POST requests. Here’s an example using Node.js with Express:
Webhook Endpoint
const express = require('express');
const app = express();
app.use(express.json());

app.post('/terminal49-webhook', (req, res) => {
  // Extract the event data
  const webhookData = req.body;

  // Process the webhook (recommended to do this asynchronously)
  processWebhook(webhookData);

  // Always return a 200 status code quickly to acknowledge receipt
  res.status(200).send('Webhook received');
});

function processWebhook(webhookData) {
  // Extract the event type
  const event = webhookData.data.attributes.event;

  // Extract the reference object (what the event is about)
  const referenceType = webhookData.data.relationships.reference_object.data.type;
  const referenceId = webhookData.data.relationships.reference_object.data.id;

  console.log(`Processing ${event} for ${referenceType} ${referenceId}`);

  // Handle different event types
  switch (event) {
    case 'container.transport.vessel_arrived':
      handleVesselArrival(webhookData);
      break;
    case 'container.available_for_pickup':
      handleContainerAvailable(webhookData);
      break;
    case 'container.pickup_lfd.changed':
      handleLastFreeDayChanged(webhookData);
      break;
    // Add cases for other events you're interested in
    default:
      console.log(`Unhandled event type: ${event}`);
  }
}

app.listen(3000, () => {
  console.log('Webhook server running on port 3000');
});
Your webhook endpoint must be publicly accessible over the internet for Terminal49 to send notifications to it. During development, you can use tools like ngrok to create a temporary public URL.

Step 2: Register Your Webhook with Terminal49

Once your endpoint is ready, register it with Terminal49’s API:
Register Webhook
async function registerWebhook() {
  try {
    const response = await fetch('https://api.terminal49.com/v2/webhooks', {
      method: 'POST',
      headers: {
        'Authorization': 'Token YOUR_API_KEY',
        'Content-Type': 'application/vnd.api+json'
      },
      body: JSON.stringify({
        data: {
          type: "webhook",
          attributes: {
            url: "https://your-domain.com/terminal49-webhook",
            event_types: [
              "container.transport.vessel_arrived",
              "container.transport.vessel_discharged",
              "container.available_for_pickup",
              "container.pickup_lfd.changed",
              "container.holds.changed",
              "tracking_request.succeeded",
              "tracking_request.failed"
            ]
          }
        }
      })
    });

    const result = await response.json();
    console.log('Webhook registered:', result);
    return result;

  } catch (error) {
    console.error('Error registering webhook:', error);
    throw error;
  }
}
cURL Example
curl -X POST \
  https://api.terminal49.com/v2/webhooks \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Content-Type: application/vnd.api+json' \
  -d '{
    "data": {
      "type": "webhook",
      "attributes": {
        "url": "https://your-domain.com/terminal49-webhook",
        "event_types": [
          "container.transport.vessel_arrived",
          "container.transport.vessel_discharged",
          "container.available_for_pickup",
          "container.pickup_lfd.changed",
          "container.holds.changed",
          "tracking_request.succeeded",
          "tracking_request.failed"
        ]
      }
    }
  }'

Step 3: Verify and Test Your Webhook

After registering, you can test your webhook using Terminal49’s test tracking numbers:
Test Webhook
async function testWebhook() {
  try {
    // Create a tracking request with a test number
    const response = await fetch('https://api.terminal49.com/v2/tracking_requests', {
      method: 'POST',
      headers: {
        'Authorization': 'Token YOUR_API_KEY',
        'Content-Type': 'application/vnd.api+json'
      },
      body: JSON.stringify({
        data: {
          type: "tracking_request",
          attributes: {
            tracking_number: "TEST-ARRIVAL-TODAY", // Test number
            reference_number: "TEST-ORDER-123"
          }
        }
      })
    });

    const result = await response.json();
    console.log('Test tracking request created:', result);

  } catch (error) {
    console.error('Error creating test tracking request:', error);
  }
}

Managing Webhooks

Listing Your Webhooks

You can retrieve a list of all registered webhooks:
List Webhooks
async function listWebhooks() {
  try {
    const response = await fetch('https://api.terminal49.com/v2/webhooks', {
      headers: {
        'Authorization': 'Token YOUR_API_KEY'
      }
    });

    const result = await response.json();
    console.log('Your webhooks:', result.data);
    return result.data;

  } catch (error) {
    console.error('Error listing webhooks:', error);
    throw error;
  }
}

Updating a Webhook

To update an existing webhook (e.g., to change the URL or event types):
Update Webhook
async function updateWebhook(webhookId) {
  try {
    const response = await fetch(`https://api.terminal49.com/v2/webhooks/${webhookId}`, {
      method: 'PATCH',
      headers: {
        'Authorization': 'Token YOUR_API_KEY',
        'Content-Type': 'application/vnd.api+json'
      },
      body: JSON.stringify({
        data: {
          id: webhookId,
          type: "webhook",
          attributes: {
            url: "https://your-updated-domain.com/webhook",
            event_types: [
              "container.transport.vessel_arrived",
              "container.available_for_pickup"
              // Updated list of events
            ]
          }
        }
      })
    });

    const result = await response.json();
    console.log('Webhook updated:', result);
    return result;

  } catch (error) {
    console.error('Error updating webhook:', error);
    throw error;
  }
}

Deleting a Webhook

If you no longer need a webhook, you can delete it:
Delete Webhook
async function deleteWebhook(webhookId) {
  try {
    const response = await fetch(`https://api.terminal49.com/v2/webhooks/${webhookId}`, {
      method: 'DELETE',
      headers: {
        'Authorization': 'Token YOUR_API_KEY'
      }
    });

    if (response.status === 204) {
      console.log('Webhook deleted successfully');
      return true;
    } else {
      console.error('Failed to delete webhook:', response.status);
      return false;
    }

  } catch (error) {
    console.error('Error deleting webhook:', error);
    throw error;
  }
}

Webhook Payload Structure

When an event occurs, Terminal49 sends a webhook notification with a JSON:API structured payload:
Example Payload
{
  "data": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "type": "webhook_notification",
    "attributes": {
      "event": "container.transport.vessel_arrived",
      "created_at": "2023-05-15T14:23:12Z"
    },
    "relationships": {
      "reference_object": {
        "data": {
          "id": "789e4567-e89b-12d3-a456-426614174000",
          "type": "container"
        }
      }
    }
  },
  "included": [
    {
      "id": "789e4567-e89b-12d3-a456-426614174000",
      "type": "container",
      "attributes": {
        "number": "MSCU1234567",
        "seal_number": "SEAL123456",
        "created_at": "2023-05-10T10:23:12Z",
        "updated_at": "2023-05-15T14:23:12Z"
      },
      "relationships": {
        "shipment": {
          "data": {
            "id": "456e4567-e89b-12d3-a456-426614174000",
            "type": "shipment"
          }
        }
      }
    }
  ]
}

Webhook Event Types

Terminal49 supports multiple event types that you can subscribe to:
  • container.created: A new container is tracked in the system
  • container.estimated_arrival_changed: The estimated arrival date has changed
  • container.transport.vessel_arrived: The vessel carrying the container has arrived at port
  • container.transport.vessel_discharged: The container has been unloaded from vessel
  • container.available_for_pickup: The container is available for pickup from terminal
  • container.pickup_lfd.changed: Last Free Day for pickup has changed
  • container.holds.changed: Customs or terminal holds have changed
  • container.picked_up: The container has been picked up from terminal
  • container.empty_returned: The empty container has been returned to terminal
  • tracking_request.succeeded: A tracking request was successful
  • tracking_request.failed: A tracking request failed to find container information
  • shipment.created: A new shipment is created in the system
  • shipment.updated: A shipment’s details have been updated

Best Practices

Your webhook endpoint should acknowledge receipt of a webhook by returning a 2xx HTTP status code (200, 201, 202, or 204) as quickly as possible. Process the webhook data asynchronously after acknowledging receipt.
Design your webhook handler to be idempotent, meaning it handles duplicate webhook deliveries gracefully. Terminal49 may retry webhook delivery if your server doesn’t respond with a success code.
  • Use HTTPS for your webhook endpoint
  • Consider implementing webhook signature validation if available
  • Only accept webhooks from Terminal49’s IP ranges if possible
Handle errors gracefully and set up monitoring for your webhook endpoint. If webhooks aren’t being received as expected, check your server logs and Terminal49 dashboard.
Implement a polling fallback that periodically checks the API directly in case webhooks are missed or delayed. This ensures your data stays up-to-date even if webhook delivery fails.

Troubleshooting

  • Check if your webhook URL is accessible from the internet
  • Verify your server is returning 2xx status codes
  • Check if the webhook is registered correctly
  • Look for any firewalls or security measures blocking incoming requests
Terminal49 may retry webhook delivery if your server doesn’t respond or returns an error. Implement idempotency in your handler to prevent processing the same event multiple times.
Use Terminal49’s test tracking numbers to simulate events and test your webhook handling without waiting for real events to occur.

Next Steps