On this page

API Documentation

Last updated: October 2026

Learn how to integrate with the DwellZero Container Tracking API

Quick Start

  1. Create an account and an API key under Settings → API in your dashboard
  2. Add containers to tracking with their terminal code
  3. Read their status from the API, or get it pushed to you by email alerts and webhooks

Base URL

https://api.dwellzero.com

Authentication

Every request except the terminal list and health check needs an API key. Create keys under Settings → API in your dashboard (up to 10 per account). A key is shown once when you create it, so store it safely.

Sending the key

API keys look like 550e8400-e29b-41d4-a716-446655440000. Send one in the Authorization header, or in an X-API-Key header:

Authorization: Bearer your-api-key
# or
X-API-Key: your-api-key

Example Request

curl -X GET \
  -H "Authorization: Bearer your-api-key" \
  "https://api.dwellzero.com/tracking"

Container States

Each tracked container has a lifecycle (where the box is) and a status (what DwellZero is doing with it).

lifecycle

NOT_FOUNDThe terminal does not list the container yet (usually still on the water).
IN_TRANSITThe terminal knows the container, but it is not off the vessel yet.
DISCHARGEDOff the vessel, not available for pickup yet (holds, releases or yard moves).
AVAILABLEAvailable for pickup.
GATE_OUTLeft the terminal. Tracking ends.

status

syncingJust added; the first check is running.
activeFound at the terminal and checked on a schedule.
pendingNot found at the terminal yet; checked every few hours. After 7 days it expires and the credit is returned.
errorThe terminal could not be reached. DwellZero keeps retrying; after 3 days of errors it expires. When the whole terminal service is down, it waits in error until the service is back and does not expire.
completedThe container left the terminal.
expiredTracking stopped without a gate-out. A container that never returned data gets its credit back.

How often containers are checked

The schedule follows the container: several times a day, more often while it is arriving and as the Last Free Day gets close, and less often before the terminal lists it. Checks pause at night in the terminal's time zone. nextSyncAt on a container says when it is checked next.

Dates

lastFreeDay is a calendar day (YYYY-MM-DD) in the terminal's local calendar; it is the earliest of the terminal's and the shipping line's free days. Moments such as dischargedAt and gateOutAt are UTC timestamps. terminalTimeZone on a container is the terminal's time zone.

Container Tracking

Add containers to tracking. DwellZero checks each one with its terminal on a schedule (see Container States) until it leaves the terminal, and can tell you about changes by email or webhook. Adding a container uses one credit or one of your 10 free containers a month; checks are never charged.

POST/trackingAdd container to tracking

Request Body

{
  "terminalCode": "NWK-MAH",
  "containerNumber": "BEAU6666666",
  "notifyEmails": ["user@example.com", "team@example.com"],  // optional, up to 10
  "billOfLading": "MEDU1234567"  // optional; CSX ramps return the Last Free Day only with it
}

Code Examples

curl -X POST \
  -H "Authorization: Bearer your-api-key-uuid" \
  -H "Content-Type: application/json" \
  -d '{
    "terminalCode": "NWK-MAH",
    "containerNumber": "BEAU6666666",
    "notifyEmails": ["user@example.com"]
  }' \
  "https://api.dwellzero.com/tracking"

Response

// 201 Created (200 when the container is already tracked, with "isNew": false and no charge)
{
  "success": true,
  "trackingId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "syncing",
  "isNew": true,
  "message": "Container added to tracking. Initial sync in progress.",
  "terminal": {
    "code": "NWK-MAH",
    "name": "Maher Terminal",
    "location": "New Jersey"
  },
  "notifyEmails": ["user@example.com", "team@example.com"],
  "billOfLading": "MEDU1234567",
  "charge": {
    "credits": 0,
    "freeQuota": true,
    "alreadyCharged": false,
    "balance": 42,
    "freeRemaining": 6
  },
  "usage": { "containersThisMonth": 4, "freeRemaining": 6, "balance": 42 },
  "timestamp": "2026-10-26T10:30:00.000Z"
}
GET/trackingList all tracked containers

Code Examples

curl -X GET \
  -H "Authorization: Bearer your-api-key-uuid" \
  "https://api.dwellzero.com/tracking"

Response

{
  "success": true,
  "count": 1,
  "stats": {
    "total": 1, "syncing": 0, "active": 1, "pending": 0, "completed": 0, "expired": 0, "error": 0,
    "isSyncing": false,
    "lastSyncTime": "2026-10-26T10:15:00.000Z",
    "nextSyncTime": "2026-10-26T11:15:00.000Z"
  },
  "data": [
    {
      "trackingId": "550e8400-e29b-41d4-a716-446655440000",
      "containerNumber": "BEAU6666666",
      "terminalCode": "NWK-MAH",
      "terminal": "Maher Terminal",
      "location": "New Jersey",
      "terminalTimeZone": "America/New_York",
      "status": "active",
      "lifecycle": "AVAILABLE",
      "createdAt": "2026-10-25T08:00:00.000Z",
      "updatedAt": "2026-10-26T10:15:00.000Z",
      "notifyEmails": ["user@example.com"],
      "billOfLading": null,
      "shareUrl": "https://app.dwellzero.com/c/0123456789abcdef0123456789abcdef",
      "lfdRequiresBillOfLading": false,
      "billOfLadingMismatch": false,
      "lastSyncAt": "2026-10-26T10:15:00.000Z",
      "lastChangeAt": "2026-10-26T10:15:00.000Z",
      "available": true,
      "lastFreeDay": "2026-10-30",
      "transitState": "IN YARD",
      "freightReleased": true,
      "customsReleased": true,
      "hasHolds": false,
      "holds": [],
      "dischargedAt": "2026-10-25T14:20:00.000Z",
      "gateOutAt": null,
      "vessel": { "name": "OCEAN ANNA", "voyage": "123W" },
      "errorMessage": null
    }
  ],
  "timestamp": "2026-10-26T10:30:00.000Z"
}
GET/tracking/:trackingIdGet container details

The container with everything the terminal reported. data is the latest answer, previousData the one before it. shareUrl is a read-only page you can send to anyone, no login needed.

Code Examples

curl -X GET \
  -H "Authorization: Bearer your-api-key-uuid" \
  "https://api.dwellzero.com/tracking/550e8400-e29b-41d4-a716-446655440000"

Response

{
  "success": true,
  "trackingId": "550e8400-e29b-41d4-a716-446655440000",
  "containerNumber": "BEAU6666666",
  "terminalCode": "NWK-MAH",
  "terminal": "Maher Terminal",
  "location": "New Jersey",
  "terminalTimeZone": "America/New_York",
  "status": "active",
  "lifecycle": "AVAILABLE",
  "createdAt": "2026-10-25T08:00:00.000Z",
  "updatedAt": "2026-10-26T10:15:00.000Z",
  "lastSyncAt": "2026-10-26T10:15:00.000Z",
  "nextSyncAt": "2026-10-26T11:15:00.000Z",
  "notifyEmails": ["user@example.com"],
  "billOfLading": null,
  "shareUrl": "https://app.dwellzero.com/c/0123456789abcdef0123456789abcdef",
  "lfdRequiresBillOfLading": false,
  "billOfLadingMismatch": false,
  "data": {
    "containerNumber": "BEAU6666666",
    "found": true,
    "lifecycle": "AVAILABLE",
    "available": true,
    "transitState": "IN YARD",
    "lastFreeDay": "2026-10-30",
    "portLfd": "2026-10-30",
    "lineLfd": null,
    "freightReleased": true,
    "customsReleased": true,
    "hasHolds": false,
    "holds": [],
    "dischargedAt": "2026-10-25T14:20:00.000Z",
    "gateOutAt": null,
    "vessel": { "name": "OCEAN ANNA", "voyage": "123W" },
    "line": { "name": "Ocean Line" },
    "containerType": { "iso": "45G1", "description": "40' High Cube" },
    "terminal": "Maher Terminal",
    "location": "New Jersey"
  },
  "previousData": { "...": "the answer before this one, same fields" },
  "lastChangeAt": "2026-10-26T10:15:00.000Z",
  "timestamp": "2026-10-26T10:30:00.000Z"
}
PATCH/tracking/:trackingId/notifyUpdate notification emails

Code Examples

curl -X PATCH \
  -H "Authorization: Bearer your-api-key-uuid" \
  -H "Content-Type: application/json" \
  -d '{
    "notifyEmails": ["new@example.com", "another@example.com"]
  }' \
  "https://api.dwellzero.com/tracking/550e8400-e29b-41d4-a716-446655440000/notify"

Request Body

{
  "notifyEmails": ["new@example.com", "another@example.com"]
}

Response

{
  "success": true,
  "trackingId": "550e8400-e29b-41d4-a716-446655440000",
  "notifyEmails": ["new@example.com", "another@example.com"],
  "message": "Notification emails updated. 2 email(s) configured.",
  "timestamp": "2026-01-16T10:30:00Z"
}
PATCH/tracking/:trackingId/bill-of-ladingSet or clear the bill of lading

CSX rail ramps return the Last Free Day only when the search includes the bill of lading. Tracking responses include lfdRequiresBillOfLading: true when that is why the LFD is missing. Send null to clear it.

Request Body

{
  "billOfLading": "MEDU1234567"
}

Response

{
  "success": true,
  "trackingId": "550e8400-e29b-41d4-a716-446655440000",
  "billOfLading": "MEDU1234567",
  "timestamp": "2026-10-23T10:30:00Z"
}
DELETE/tracking/:trackingIdRemove from tracking

Remove a container from tracking. This will stop all syncing and notifications.

Code Examples

curl -X DELETE \
  -H "Authorization: Bearer your-api-key-uuid" \
  "https://api.dwellzero.com/tracking/550e8400-e29b-41d4-a716-446655440000"

Response

{
  "success": true,
  "message": "Container removed from tracking",
  "timestamp": "2026-01-16T10:30:00Z"
}
POST/tracking/bulkBulk add containers

Add up to 500 containers in one request. Each item is checked and charged on its own, so some can succeed while others fail. Items take the same fields as POST /tracking.

Request Body

{
  "containers": [
    {
      "terminalCode": "NWK-MAH",
      "containerNumber": "MSDU1234560",
      "notifyEmails": ["user@example.com"]  // optional, up to 10
    },
    {
      "terminalCode": "LALB-APM",
      "containerNumber": "TCNU7654328"
    }
  ]
}

Code Examples

curl -X POST \
  -H "Authorization: Bearer your-api-key-uuid" \
  -H "Content-Type: application/json" \
  -d '{
    "containers": [
      { "terminalCode": "NWK-MAH", "containerNumber": "MSDU1234560", "notifyEmails": ["user@example.com"] },
      { "terminalCode": "LALB-APM", "containerNumber": "TCNU7654328" },
      { "terminalCode": "BAL-SGT", "containerNumber": "CMAU9876540" }
    ]
  }' \
  "https://api.dwellzero.com/tracking/bulk"

Response

{
  "success": true,
  "summary": {
    "total": 3,
    "created": 2,
    "alreadyTracked": 1,
    "failed": 0,
    "insufficientCredits": 0
  },
  "results": [
    { "containerNumber": "MSDU1234560", "status": "created", "trackingId": "550e8400-..." },
    { "containerNumber": "TCNU7654328", "status": "created", "trackingId": "660f9500-..." },
    { "containerNumber": "CMAU9876540", "status": "already_tracked", "trackingId": "770a0600-..." }
  ],
  "timestamp": "2026-01-16T10:30:00Z"
}

Per-Container Status Values

StatusDescription
createdNew tracking created successfully
already_trackedContainer was already being tracked
insufficient_creditsFree containers used up and not enough credits. Later new containers in the same request are skipped.
failedValidation error (see the error field)
GET/tracking/statsGet tracking statistics

Your containers by status, and when they were last and are next checked.

Code Examples

curl -X GET \
  -H "Authorization: Bearer your-api-key-uuid" \
  "https://api.dwellzero.com/tracking/stats"

Response

{
  "success": true,
  "data": {
    "total": 10,
    "syncing": 0,
    "active": 6,
    "pending": 2,
    "completed": 1,
    "expired": 0,
    "error": 1,
    "isSyncing": false,
    "lastSyncTime": "2026-10-26T10:15:00.000Z",
    "nextSyncTime": "2026-10-26T10:42:00.000Z"
  },
  "timestamp": "2026-10-26T10:30:00.000Z"
}

Arrival Notices

Upload an arrival notice PDF and DwellZero reads the containers, the terminal (or the rail ramp for boxes moving inland) and the bill of lading, then starts tracking them. Each new container is charged like POST /tracking. You can also forward notices by email to the import address on your dashboard.

Upload

POST /tracking/import-an/upload, multipart form with a file field (PDF, up to 10 MB, 20 uploads an hour). Reading happens in the background: the answer is an import id to check.

curl -X POST \
  -H "Authorization: Bearer your-api-key" \
  -F "file=@arrival-notice.pdf" \
  "https://api.dwellzero.com/tracking/import-an/upload"

# 202 Accepted
{ "success": true, "import_id": "7d0f3c1e-2b8a-4e57-9a5e-1f2c3d4e5f60" }

Check the import

GET /tracking/imports/:importId. Poll every few seconds until status is completed, partial or failed.

{
  "success": true,
  "data": {
    "id": "7d0f3c1e-2b8a-4e57-9a5e-1f2c3d4e5f60",
    "source": "upload",
    "filename": "arrival-notice.pdf",
    "status": "completed",
    "containersFound": 2,
    "containersCreated": 2,
    "containersUpdated": 0,
    "errorMessage": null,
    "createdAt": "2026-10-26T10:30:00.000Z"
  }
}

Credits

Each account gets 10 free containers a month; after that, adding a container uses one credit. Re-adding the same container at the same terminal in the same month is free, and a container that never returned any data gets its credit back. When both are used up, new containers answer 402 INSUFFICIENT_CREDITS; containers already tracked keep updating. See pricing to top up.

Check your balance

GET /billing/credits. GET /billing/credits/transactions lists the history (?limit=50&before=<id>).

{
  "success": true,
  "balance": 42,
  "freeQuota": { "limit": 10, "used": 3, "remaining": 7, "resetsAt": "2026-11-01T00:00:00.000Z" },
  "containersThisMonth": 3,
  "estimatedContainersRemaining": 49,
  "expiring": { "credits": 15, "at": "2026-11-15T10:00:00.000Z" },
  "timestamp": "2026-10-26T10:30:00.000Z"
}

Email Notifications

Get notified via email when your container status changes or when the Last Free Day is approaching.

Notification Types

  • Now TrackingSent when DwellZero first finds the container at the terminal, with its state and Last Free Day
  • Status ChangeSent when availability, holds, the Last Free Day or the terminal status change, and at gate-out
  • LFD ReminderSent 2 days and 1 day before the Last Free Day, and on the day
  • LFD PassedDaily while the container is past its Last Free Day and still at the terminal
  • Tracking StoppedSent when tracking expires: the container never showed up, or the terminal kept failing

Adding Notification Emails

You can add up to 10 email addresses when creating tracking or update them later:

// When creating tracking
{
  "terminalCode": "NWK-MAH",
  "containerNumber": "BEAU6666666",
  "notifyEmails": ["dispatch@company.com", "logistics@company.com"]
}

// Update emails later
PATCH /tracking/:trackingId/notify
{
  "notifyEmails": ["new@company.com"]
}

Auto-Stop Notifications

Emails stop when the container leaves the terminal (lifecycle GATE_OUT); the gate-out itself is the last one. Each email has a "View container" button to the container's read-only page, so people on the list need no DwellZero account.

Webhooks

DwellZero can POST container events to your own system. Add up to 5 endpoints under Settings → API or through the API, and pick the events each one receives. Webhooks cover every container on your account, whether or not it has notification emails.

Setting up a webhook

  1. Build a receiver. An https URL on your server that accepts a JSON POST, checks the signature and answers 200 within 10 seconds. The example below is a complete receiver in Node.js.
  2. Make it reachable. DwellZero only sends to public https addresses. For a first test on your own machine, run the example and open a tunnel to it, e.g. cloudflared tunnel --url http://localhost:4000, which prints an https URL. Add /dwellzero/webhook to it.
  3. Add the endpoint. Under Settings → API, click Add endpoint, paste the URL and tick the events you want. Copy the signing secret it shows: it is shown only once. Put it in your receiver's configuration (DWELLZERO_WEBHOOK_SECRET in the example).
  4. Send a test event. Click Send test event. DwellZero sends a ping with a sample container and shows the status your server answered. Recent deliveries lists every event and its result.
  5. Go live. Events now arrive as your containers change, and LFD events once a day, in the morning. Move the receiver to your own server when you are done testing, and update the URL with Edit.

Good practice: check the signature on the raw body, answer before doing slow work, and skip an id you already handled (a retry sends the same event again).

// npm install express
const express = require('express');
const crypto = require('crypto');

const SECRET = process.env.DWELLZERO_WEBHOOK_SECRET; // whsec_... from Settings > API in your dashboard
const seen = new Set(); // use your database in production

function verify(rawBody, headers) {
  const id = headers['webhook-id'];
  const timestamp = headers['webhook-timestamp'];
  const ts = Number(timestamp);
  if (!id || !timestamp || !Number.isFinite(ts) || Math.abs(Date.now() / 1000 - ts) > 300) return false;
  const key = Buffer.from(SECRET.replace(/^whsec_/, ''), 'base64');
  const expected = crypto.createHmac('sha256', key).update(`${id}.${timestamp}.${rawBody}`).digest();
  return (headers['webhook-signature'] || '').split(' ').some((entry) => {
    const sig = Buffer.from(entry.slice(3), 'base64');
    return entry.startsWith('v1,') && sig.length === expected.length && crypto.timingSafeEqual(sig, expected);
  });
}

const app = express();

// Raw body: the signature covers the exact bytes DwellZero sent
app.post('/dwellzero/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const rawBody = req.body.toString('utf8');
  if (!verify(rawBody, req.headers)) return res.status(401).end();

  const event = JSON.parse(rawBody);
  res.status(200).end(); // answer first, then do the work

  if (seen.has(event.id)) return; // a retry of an event you already handled
  seen.add(event.id);

  const box = event.container;
  switch (event.event) {
    case 'ping':
      console.log('DwellZero test event received');
      break;
    case 'container.changed':
      console.log(`${box.containerNumber}: ${event.changes.map((c) => c.field).join(', ')} changed`);
      break;
    case 'container.lfd_reminder':
    case 'container.lfd_passed':
      console.log(`${box.containerNumber}: LFD ${event.lfd.day} (${event.lfd.daysUntil} days)`);
      break;
    default:
      console.log(event.event, box.containerNumber);
  }
});

app.listen(4000, () => console.log('Listening on http://localhost:4000/dwellzero/webhook'));

Events

container.tracking_startedDwellZero found the container at the terminal. Has "lfd" ({ day, daysUntil }) when the terminal already gives one.
container.changedAvailability, holds, Last Free Day, terminal status or gate-out changed. Has "changes".
container.lfd_reminderThe Last Free Day is in 2 days, tomorrow or today (sent once a day, in the morning). Has "lfd".
container.lfd_passedDaily while the container is past its Last Free Day and still at the terminal. Has "lfd" (negative daysUntil).
container.tracking_stoppedTracking expired. Has "reason": "not_found" or "error".
pingThe test event from "Send test event". Sent to that endpoint whatever it subscribes to.

Payload

A JSON POST. container is the container as it is after the event. Use id to skip an event you already handled.

{
  "id": "evt_8sKq2mXbV3rT0yPzL1nW4eHd",
  "event": "container.changed",
  "createdAt": "2026-10-26T18:00:05.000Z",
  "trackingId": "tracking-uuid",
  "container": {
    "containerNumber": "MSCU1234566",
    "terminalCode": "NWK-MAH",
    "terminal": "Maher Terminal",
    "location": "New Jersey",
    "status": "active",
    "lifecycle": "AVAILABLE",
    "lastFreeDay": "2026-10-30",
    "available": true,
    "transitState": "IN YARD",
    "freightReleased": true,
    "customsReleased": true,
    "hasHolds": false,
    "holds": [],
    "dischargedAt": "2026-10-25T10:00:00Z",
    "gateOutAt": null,
    "vessel": { "name": "OCEAN ANNA", "voyage": "123W" },
    "line": { "name": "Ocean Line" },
    "lastCheckedAt": "2026-10-26T18:00:00Z",
    "lastChangeAt": "2026-10-26T18:00:00Z",
    "billOfLading": "MEDU1234567"
  },
  "changes": [{ "field": "available", "oldValue": false, "newValue": true }],
  "shareUrl": "https://app.dwellzero.com/c/<token>"
}

Verifying the signature

Each request has webhook-id, webhook-timestamp and webhook-signature headers (the Standard Webhooks scheme). The signature is an HMAC-SHA256 of id.timestamp.body with your endpoint's signing secret. Check it against the raw body, before parsing the JSON:

const crypto = require('crypto');

// Express: app.post('/dwellzero/webhook', express.raw({ type: 'application/json' }), handler)
function verifyDwellZeroWebhook(rawBody, headers, secret) {
  const id = headers['webhook-id'];
  const timestamp = headers['webhook-timestamp'];
  const signatures = headers['webhook-signature'] || '';
  const ts = Number(timestamp);
  if (!id || !timestamp || !Number.isFinite(ts) || Math.abs(Date.now() / 1000 - ts) > 300) return false; // missing, or older than 5 minutes

  const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
  const expected = crypto.createHmac('sha256', key)
    .update(`${id}.${timestamp}.${rawBody}`)
    .digest();

  return signatures.split(' ').some((entry) => {
    const [version, sig] = entry.split(',');
    const candidate = Buffer.from(sig || '', 'base64');
    return version === 'v1' && candidate.length === expected.length
      && crypto.timingSafeEqual(candidate, expected);
  });
}

Delivery and retries

  • Answer with any 2xx status within 10 seconds. The response body is ignored and redirects are not followed.
  • Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours.
  • After 10 events in a row fail every retry, the endpoint is turned off and the account owner gets an email.
  • Endpoints must be public https URLs.

Managing endpoints through the API

GET    /webhooks                    # your endpoints (up to 5)
POST   /webhooks                    # { "url", "events"?, "description"? } -> returns "secret" once
PATCH  /webhooks/:id                # { "url"?, "events"?, "description"?, "enabled"? }
POST   /webhooks/:id/secret         # new signing secret, returned once
POST   /webhooks/:id/test           # send a test event (event "ping") right away
GET    /webhooks/:id/deliveries     # recent deliveries, newest first
DELETE /webhooks/:id
GET    /webhooks/events             # the event names

Terminal Codes

DwellZero covers 162 terminals (91 ports and 71 rail ramps) in the US and Canada. Use the code from the terminal list when adding a container; some terminals give less data than others (for example, CSX ramps need the bill of lading for the Last Free Day).

Port Terminals (90+)

Major container ports across North America.

NWK-MAH, NWK-PNCT, LALB-LBCT, POV-NIT, BAL-SGT...

Rail Terminals (70+)

Intermodal rail yards across the country.

NS-ATLANTA, NS-CHICAGO_47TH, CSX-CHARLOTTE...

Get Terminal List

GET /terminals, no key needed. Add ?type=port or ?type=rail to filter. GET /terminals/locations returns the same terminals grouped by location.

Code Examples

curl -X GET "https://api.dwellzero.com/terminals"

# Filter by type
curl -X GET "https://api.dwellzero.com/terminals?type=port"

Response

{
  "success": true,
  "data": {
    "count": 161,
    "stats": {
      "total": 166,
      "byType": { "port": 93, "rail": 73 }
    },
    "terminals": [
      {
        "code": "NWK-MAH",
        "name": "Maher Terminal",
        "location": "New Jersey",
        "type": "port",
        "timeZone": "America/New_York"
      },
      ...
    ]
  },
  "timestamp": "2026-10-26T10:30:00.000Z"
}

View Full Terminal Directory →

Error Handling

Errors use standard HTTP status codes and the same JSON shape. Some errors add fields (the credits error below adds the balance).

Error Response Format

{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Your free containers for this month are used up and you have 0 credits. Top up to keep adding containers.",
    "balance": 0,
    "freeRemaining": 0,
    "topupUrl": "/dashboard/billing",
    "timestamp": "2026-01-16T10:30:00Z"
  }
}

Common Error Codes

CodeHTTPDescription
MISSING_API_KEY401No API key in the Authorization or X-API-Key header
INVALID_API_KEY401The API key is wrong or expired
INVALID_API_KEY_FORMAT401The API key is malformed
REVOKED_API_KEY401The API key was revoked
ACCOUNT_SUSPENDED403The account is suspended
INSUFFICIENT_CREDITS402Free containers for the month used up and no credits left
INVALID_REQUEST400A required field is missing or has the wrong type
INVALID_TERMINAL_CODE400Unknown terminal code (see GET /terminals)
TERMINAL_UNAVAILABLE400The terminal is listed, but tracking there is paused
INVALID_CONTAINER_FORMAT400Not 4 letters and 7 digits
INVALID_CHECK_DIGIT400The last digit does not match the container number (usually a typo)
INVALID_EMAIL400Invalid email format in notifyEmails
INVALID_BILL_OF_LADING400Bill of lading must be letters, digits and dashes, up to 40 characters
TOO_MANY_EMAILS400Maximum 10 notification emails allowed
TRACKING_NOT_FOUND404No container with that tracking id on your account
FILE_REQUIRED400Arrival notice upload has no file field
INVALID_FILE_TYPE400Arrival notice upload is not a PDF
FILE_TOO_LARGE400Arrival notice upload is over 10 MB
IMPORT_NOT_FOUND404No import with that id on your account
INVALID_WEBHOOK_URL400Webhook URL is not public https, or has a user name and password
INVALID_WEBHOOK_EVENTS400Unknown webhook event, or no events picked
WEBHOOK_LIMIT_REACHED400The account already has 5 webhooks
WEBHOOK_NOT_FOUND404No webhook with that id on your account
RATE_LIMIT_EXCEEDED429Too many requests; wait and retry

Rate limits

  • Requests without an API key (terminal list, health): 20 a minute per IP address.
  • Requests with an API key have no general limit.
  • Arrival notice uploads: 20 an hour per account.
  • Webhook test events: 10 a minute per account.
  • Over a limit, the answer is 429 RATE_LIMIT_EXCEEDED; wait a minute and retry.

Contact us

Have a question or need help? Send us a message and we'll get back to you shortly.