{
  "openapi": "3.1.0",
  "info": {
    "title": "TrackingMCP REST API",
    "version": "1.0.0",
    "description": "DCSA-aligned ocean container tracking across 227 carriers. Every lookup is cross-checked against 2–4 independent sources (110 direct carrier connectors, fallback providers with automatic failover, 4 fused AIS networks, and sailing schedules). Authenticated endpoints take `Authorization: Bearer <API key>` — create a key at https://trackingmcp.com after signing up (free tier: 5 containers + 100 MCP calls/mo, no card). AI agents can use the hosted MCP server at https://mcp.trackingmcp.com/mcp instead (registry name io.github.lxxmng/container-tracking).",
    "contact": {
      "name": "TrackingMCP",
      "url": "https://trackingmcp.com",
      "email": "hello@trackingmcp.com"
    }
  },
  "servers": [{ "url": "https://api.trackingmcp.com" }],
  "paths": {
    "/v1/track/public": {
      "post": {
        "operationId": "trackPublic",
        "summary": "Anonymous one-off tracking lookup (no API key)",
        "description": "Free, unauthenticated lookup of a container, bill of lading, or booking. Per-browser allowance of 1 lookup per reference type; returns HTTP 429 with `gated: true` once spent. Send a stable per-browser UUID in `X-Anon-Id`.",
        "security": [],
        "parameters": [
          {
            "name": "X-Anon-Id",
            "in": "header",
            "required": false,
            "description": "Stable anonymous browser id (e.g. a UUID persisted in localStorage) used to meter the free allowance.",
            "schema": { "type": "string", "maxLength": 80 }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["reference"],
                "properties": {
                  "reference": {
                    "type": "string",
                    "description": "Container number (4 letters + 7 digits), bill of lading, or booking reference."
                  },
                  "reference_type": {
                    "type": "string",
                    "enum": ["container_id", "bill_of_lading", "booking_number"],
                    "description": "Client-side guess; the server refines it."
                  },
                  "carrier_code": { "type": "string", "description": "Optional SCAC hint." }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Live tracking result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": { "type": "boolean" },
                    "data": { "$ref": "#/components/schemas/PublicTrackResult" }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid reference (e.g. bad ISO 6346 container number)."
          },
          "404": { "description": "No live data found for the reference." },
          "429": {
            "description": "Free anonymous allowance spent — create a free account to continue.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": { "type": "boolean", "const": false },
                    "gated": { "type": "boolean", "const": true }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/containers": {
      "post": {
        "operationId": "addContainer",
        "summary": "Start tracking a container, bill of lading, or booking",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["identifier", "identifier_type"],
                "properties": {
                  "identifier": { "type": "string", "minLength": 1, "maxLength": 50 },
                  "identifier_type": {
                    "type": "string",
                    "enum": ["container_id", "bill_of_lading", "booking"]
                  },
                  "carrier_code": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 10,
                    "description": "Optional SCAC hint; auto-resolved from the container prefix when omitted."
                  },
                  "tags": { "type": "array", "items": { "type": "string" }, "maxItems": 20 }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Container accepted and tracking started.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/OkEnvelope" } }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } }
            }
          },
          "401": { "description": "Missing or invalid API key." },
          "409": { "description": "Already in your tracking list." }
        }
      },
      "get": {
        "operationId": "listContainers",
        "summary": "List tracked containers",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": { "type": "string" },
            "description": "Filter by container status."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "default": 50, "maximum": 500 }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": { "type": "string" },
            "description": "Pagination cursor from the previous page."
          },
          {
            "name": "tags",
            "in": "query",
            "schema": { "type": "string" },
            "description": "Comma-separated tag filter."
          },
          {
            "name": "archived",
            "in": "query",
            "schema": { "type": "boolean", "default": false },
            "description": "true → archived (delivered/removed) boxes instead of the active fleet."
          }
        ],
        "responses": {
          "200": {
            "description": "Page of tracked containers.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/OkEnvelope" } }
            }
          },
          "401": { "description": "Missing or invalid API key." }
        }
      }
    },
    "/v1/containers/summary": {
      "get": {
        "operationId": "getContainersSummary",
        "summary": "Portfolio summary — counts, exceptions, arriving soon",
        "responses": {
          "200": {
            "description": "Aggregated fleet summary.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/OkEnvelope" } }
            }
          },
          "401": { "description": "Missing or invalid API key." }
        }
      }
    },
    "/v1/containers/demurrage": {
      "get": {
        "operationId": "getDemurrageRisk",
        "summary": "Demurrage risk across all tracked containers",
        "responses": {
          "200": {
            "description": "Containers at demurrage/detention risk with free days remaining.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/OkEnvelope" } }
            }
          },
          "401": { "description": "Missing or invalid API key." }
        }
      }
    },
    "/v1/containers/lookup/{identifier}": {
      "get": {
        "operationId": "lookupContainer",
        "summary": "Look up a tracked container by its identifier",
        "parameters": [
          { "name": "identifier", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Container detail.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/OkEnvelope" } }
            }
          },
          "401": { "description": "Missing or invalid API key." },
          "404": { "description": "Not in your tracking list." }
        }
      }
    },
    "/v1/containers/{id}": {
      "get": {
        "operationId": "getContainer",
        "summary": "Full container detail — route, ETA, vessel, events",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Container detail with normalized DCSA events.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/OkEnvelope" } }
            }
          },
          "401": { "description": "Missing or invalid API key." },
          "404": { "description": "Unknown container id." }
        }
      },
      "delete": {
        "operationId": "deleteContainer",
        "summary": "Stop tracking (archive) a container",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Archived.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/OkEnvelope" } }
            }
          },
          "401": { "description": "Missing or invalid API key." },
          "404": { "description": "Unknown container id." }
        }
      }
    },
    "/v1/containers/{id}/recheck": {
      "post": {
        "operationId": "recheckContainer",
        "summary": "Force an immediate re-poll of a container",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Recheck queued.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/OkEnvelope" } }
            }
          },
          "401": { "description": "Missing or invalid API key." },
          "404": { "description": "Unknown container id." }
        }
      }
    },
    "/v2/events": {
      "get": {
        "operationId": "listDcsaEvents",
        "summary": "DCSA Track & Trace 2.2 event feed",
        "description": "Standards-aligned event feed (DCSA T&T 2.2.0). Returns a bare JSON array of event objects for the containers on your account, newest first. Filter with the DCSA query parameters. IMPORTANT — two fields customers ask about:\n\n1. `emptyIndicatorCode` is present ONLY on EQUIPMENT events and ONLY when the carrier reported the fill state. Per DCSA it is a required `EMPTY | LADEN` enum with no 'unknown' member, so rather than fabricate a value we OMIT the field when the line didn't report it (most equipment events). Do NOT rely on it as your end-of-shipment trigger — many carriers (e.g. MSC) never stamp it on the empty return. Where the carrier described the move only in words ('Empty received at CY'), we now infer `EMPTY` from that phrase, but coverage is still partial.\n\n2. There is NO container-level status in this feed — it is events only. `delivered` / `returned_empty` are CONTAINER statuses, not events; read them from `GET /v1/containers/{id}` (the `status` field). To detect end-of-shipment, poll the container status: `delivered` = laden box handed to the consignee at the destination port; `returned_empty` = the empty box was returned to the depot (the true, terminal end of shipment, which happens AFTER `delivered`).",
        "parameters": [
          {
            "name": "equipmentReference",
            "in": "query",
            "required": false,
            "description": "Filter to a single container number (e.g. MSCU1234567).",
            "schema": { "type": "string" }
          },
          {
            "name": "eventType",
            "in": "query",
            "required": false,
            "description": "DCSA event type filter.",
            "schema": { "type": "string", "enum": ["SHIPMENT", "TRANSPORT", "EQUIPMENT"] }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "default": 100, "maximum": 500 }
          }
        ],
        "responses": {
          "200": {
            "description": "DCSA event array (the `API-Version` response header reports the T&T version, e.g. 2.2.0).",
            "content": {
              "application/json": {
                "schema": { "type": "array", "items": { "$ref": "#/components/schemas/DcsaEvent" } }
              }
            }
          },
          "401": { "description": "Missing or invalid API key." }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "TrackingMCP API key (or a session JWT). Create keys in the dashboard — https://trackingmcp.com/developers."
      }
    },
    "schemas": {
      "OkEnvelope": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean", "const": true },
          "data": { "description": "Endpoint-specific payload." }
        },
        "required": ["ok"]
      },
      "ErrorEnvelope": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean", "const": false },
          "error": {
            "type": "object",
            "properties": {
              "code": { "type": "string" },
              "message": { "type": "string" },
              "severity": { "type": "string" }
            }
          }
        }
      },
      "ContainerStatus": {
        "type": "string",
        "description": "Derived container status (from `GET /v1/containers/{id}`). Ocean lifecycle order — `discharged` → `available` → `delivered` → `returned_empty`. Two terminal end-of-shipment values: `delivered` (laden container gated out to the consignee at the destination port) and `returned_empty` (the empty box was returned to the depot AFTER delivery — the true end of shipment). `returned_empty` outranks `delivered`; a box that has been returned reads `returned_empty` even though it was `delivered` earlier.",
        "enum": [
          "pre_departure",
          "in_transit",
          "at_transshipment",
          "at_anchor",
          "at_berth",
          "arriving",
          "discharged",
          "customs_hold",
          "available",
          "delivered",
          "returned_empty",
          "cancelled",
          "unknown"
        ]
      },
      "DcsaEvent": {
        "type": "object",
        "description": "A DCSA Track & Trace 2.2 event. Common fields plus a per-type block (SHIPMENT / TRANSPORT / EQUIPMENT). Null/absent fields are omitted from the payload rather than sent as null.",
        "properties": {
          "eventID": { "type": "string", "format": "uuid" },
          "eventCreatedDateTime": { "type": "string", "format": "date-time" },
          "eventType": { "type": "string", "enum": ["SHIPMENT", "TRANSPORT", "EQUIPMENT"] },
          "eventClassifierCode": {
            "type": "string",
            "enum": ["ACT", "PLN", "EST"],
            "description": "ACT = actual (happened), EST = estimated, PLN = planned. Never assume a PLN/EST move has occurred."
          },
          "eventDateTime": { "type": "string", "format": "date-time" },
          "equipmentEventTypeCode": {
            "type": "string",
            "description": "EQUIPMENT events only. E.g. GTOT (gate-out), GTIN (gate-in), LOAD, DISC, PICK, DROP.",
            "enum": [
              "LOAD",
              "DISC",
              "GTIN",
              "GTOT",
              "STUF",
              "STRP",
              "PICK",
              "DROP",
              "INSP",
              "RSEA",
              "RMVD"
            ]
          },
          "emptyIndicatorCode": {
            "type": "string",
            "enum": ["EMPTY", "LADEN"],
            "description": "EQUIPMENT events only. Present ONLY when the carrier reported the fill state (a minority of events). Absent when not reported — DCSA has no 'unknown' value and we do not fabricate one. Not a reliable end-of-shipment signal; use the container `status` field instead."
          },
          "equipmentReference": { "type": "string", "description": "Container number." },
          "ISOEquipmentCode": { "type": ["string", "null"] },
          "transportEventTypeCode": {
            "type": "string",
            "description": "TRANSPORT events only. E.g. ARRI (arrival), DEPA (departure).",
            "enum": ["ARRI", "DEPA"]
          },
          "shipmentEventTypeCode": {
            "type": ["string", "null"],
            "description": "SHIPMENT events only."
          },
          "transportCall": {
            "type": ["object", "null"],
            "description": "Location / vessel / voyage of the call."
          }
        },
        "required": ["eventID", "eventType", "eventClassifierCode", "eventDateTime"]
      },
      "PublicTrackResult": {
        "type": "object",
        "properties": {
          "identifier": { "type": "string" },
          "identifier_type": {
            "type": "string",
            "enum": ["container_id", "bill_of_lading", "booking_number"]
          },
          "carrier_code": { "type": ["string", "null"] },
          "carrier_name": { "type": ["string", "null"] },
          "status": { "type": "string" },
          "eta": { "type": ["string", "null"], "format": "date-time" },
          "origin": { "type": ["string", "null"] },
          "destination": { "type": ["string", "null"] },
          "vessel": {
            "type": ["object", "null"],
            "properties": {
              "name": { "type": ["string", "null"] },
              "lat": { "type": ["number", "null"] },
              "lng": { "type": ["number", "null"] }
            }
          },
          "events": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "description": { "type": "string" },
                "location": { "type": ["string", "null"] },
                "datetime": { "type": ["string", "null"], "format": "date-time" },
                "actual": { "type": "boolean" }
              }
            }
          },
          "observability": {
            "type": ["object", "null"],
            "properties": {
              "lenses": {
                "type": "object",
                "properties": {
                  "actuals": { "type": "boolean" },
                  "position": { "type": "boolean" },
                  "plan": { "type": "boolean" },
                  "probability": { "type": "boolean" }
                }
              },
              "active_count": { "type": "integer" }
            }
          }
        }
      }
    }
  },
  "security": [{ "bearerAuth": [] }]
}
