{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://navo24.com/developers/webhooks/tracking-delivery.schema.json",
  "title": "Navo24 TrackingMCP webhook delivery",
  "description": "Body of every HTTP POST TrackingMCP sends to a registered webhook endpoint. The shape of data depends on type. Derived from https://trackingmcp.com/openapi.json (OpenAPI 3.1) and the producing code; test deliveries use type \"ping\".",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "id",
    "type",
    "occurred_at",
    "data"
  ],
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "description": "Delivery id. Stable across retries of the same delivery: use it to de-duplicate."
    },
    "type": {
      "type": "string",
      "enum": [
        "api_change_announced",
        "container_arrived",
        "container_available",
        "container_delivered",
        "container_misrouted",
        "customs_hold",
        "demurrage_started",
        "demurrage_warning",
        "eta_changed",
        "schedule_disruption",
        "shipment_updated",
        "vessel_departed",
        "ping"
      ],
      "description": "Event type, or ping for a test delivery triggered from POST /v1/webhooks/{id}/test."
    },
    "occurred_at": {
      "type": "string",
      "format": "date-time",
      "description": "When the event was recorded (UTC, ISO 8601)."
    },
    "data": {
      "type": "object",
      "description": "Event payload; shape depends on type (see allOf rules)."
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "type": {
            "enum": [
              "container_arrived",
              "container_available",
              "container_delivered",
              "customs_hold",
              "demurrage_started",
              "demurrage_warning",
              "vessel_departed"
            ]
          }
        }
      },
      "then": {
        "properties": {
          "data": {
            "$ref": "#/$defs/WebhookStatusEventData"
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "type": {
            "enum": [
              "eta_changed"
            ]
          }
        }
      },
      "then": {
        "properties": {
          "data": {
            "$ref": "#/$defs/WebhookEtaChangedData"
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "type": {
            "enum": [
              "schedule_disruption"
            ]
          }
        }
      },
      "then": {
        "properties": {
          "data": {
            "$ref": "#/$defs/WebhookScheduleDisruptionData"
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "type": {
            "enum": [
              "container_misrouted"
            ]
          }
        }
      },
      "then": {
        "properties": {
          "data": {
            "$ref": "#/$defs/WebhookMisroutedData"
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "type": {
            "enum": [
              "api_change_announced"
            ]
          }
        }
      },
      "then": {
        "properties": {
          "data": {
            "$ref": "#/$defs/WebhookApiChangeData"
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "type": {
            "const": "ping"
          }
        }
      },
      "then": {
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "message": {
                "type": "string"
              }
            },
            "additionalProperties": true
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "type": {
            "const": "shipment_updated"
          }
        }
      },
      "then": {
        "properties": {
          "data": {
            "type": "object",
            "description": "Fresh carrier data changed the shipment. Fetch the shipment REST resource for the canonical state. Shape taken from the producer (services/carriers/persist-direct.ts); not yet in the OpenAPI spec.",
            "required": [
              "container_id",
              "changed_fields"
            ],
            "properties": {
              "container_id": {
                "type": "string",
                "format": "uuid"
              },
              "identifier": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "identifier_type": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "container_id",
                  "bill_of_lading",
                  "booking",
                  null
                ]
              },
              "changed_fields": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "status",
                    "eta",
                    "vessel",
                    "discharge",
                    "carrier"
                  ]
                }
              }
            },
            "additionalProperties": true
          }
        }
      }
    }
  ],
  "$defs": {
    "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"
      ]
    },
    "WebhookStatusEventData": {
      "type": "object",
      "description": "`data` for the status family: container_arrived, container_available, container_delivered, customs_hold, vessel_departed, demurrage_started, demurrage_warning. The demurrage figures are null unless demurrage alerting is armed and applicable.",
      "properties": {
        "container_id": {
          "type": "string",
          "format": "uuid",
          "description": "Our internal shipment UUID. Not a shipping reference; useful only if you store our ids."
        },
        "identifier": {
          "type": [
            "string",
            "null"
          ],
          "description": "The reference the shipment is tracked by: a container number, a bill of lading or a booking number, whichever it was added with."
        },
        "identifier_type": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "container_id",
            "bill_of_lading",
            "booking",
            null
          ],
          "description": "What kind of reference `identifier` is. Null when unknown (shipments recorded before the type was stored). Additive field, added 2026-08-19; absent on deliveries produced by older builds."
        },
        "previous_status": {
          "$ref": "#/$defs/ContainerStatus"
        },
        "new_status": {
          "$ref": "#/$defs/ContainerStatus"
        },
        "eta": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "Current ETA at the moment the event fired. UTC."
        },
        "demurrage_days_overdue": {
          "type": [
            "integer",
            "null"
          ]
        },
        "demurrage_accrued_usd": {
          "type": [
            "number",
            "null"
          ]
        }
      },
      "required": [
        "container_id",
        "previous_status",
        "new_status"
      ],
      "additionalProperties": true
    },
    "WebhookEtaChangedData": {
      "type": "object",
      "description": "`data` for eta_changed: the carrier moved the ETA by 24 hours or more. Fires at most once per box per day.",
      "properties": {
        "container_id": {
          "type": "string",
          "format": "uuid",
          "description": "Our internal shipment UUID. Not a shipping reference; useful only if you store our ids."
        },
        "identifier": {
          "type": [
            "string",
            "null"
          ],
          "description": "The reference the shipment is tracked by: a container number, a bill of lading or a booking number, whichever it was added with."
        },
        "identifier_type": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "container_id",
            "bill_of_lading",
            "booking",
            null
          ],
          "description": "What kind of reference `identifier` is. Null when unknown (shipments recorded before the type was stored). Additive field, added 2026-08-19; absent on deliveries produced by older builds."
        },
        "old_eta": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "new_eta": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "delta_hours": {
          "type": "number",
          "description": "Signed shift in hours; positive means later."
        }
      },
      "required": [
        "container_id",
        "new_eta"
      ],
      "additionalProperties": true
    },
    "WebhookScheduleDisruptionData": {
      "type": "object",
      "description": "`data` for schedule_disruption: the matched sailing slipped materially or did not depart. Fired once per distinct disruption per box.",
      "properties": {
        "container_id": {
          "type": "string",
          "format": "uuid",
          "description": "Our internal shipment UUID. Not a shipping reference; useful only if you store our ids."
        },
        "identifier": {
          "type": [
            "string",
            "null"
          ],
          "description": "The reference the shipment is tracked by. Additive, added 2026-08-19."
        },
        "identifier_type": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "container_id",
            "bill_of_lading",
            "booking",
            null
          ],
          "description": "What kind of reference `identifier` is. Null when unknown (shipments recorded before the type was stored). Additive field, added 2026-08-19; absent on deliveries produced by older builds."
        },
        "kind": {
          "type": "string",
          "description": "slipped or not_departed."
        },
        "dedup_key": {
          "type": "string"
        },
        "headline": {
          "type": "string",
          "description": "One human-readable sentence for the disruption."
        },
        "delay_hours": {
          "type": [
            "number",
            "null"
          ]
        }
      },
      "required": [
        "container_id",
        "kind",
        "headline"
      ],
      "additionalProperties": true
    },
    "WebhookMisroutedData": {
      "type": "object",
      "description": "`data` for container_misrouted: a laden box was discharged at a port that is neither its destination nor a transshipment on its route.",
      "properties": {
        "container_id": {
          "type": "string",
          "format": "uuid",
          "description": "Our internal shipment UUID. Not a shipping reference; useful only if you store our ids."
        },
        "identifier": {
          "type": [
            "string",
            "null"
          ],
          "description": "The reference the shipment is tracked by: a container number, a bill of lading or a booking number, whichever it was added with."
        },
        "identifier_type": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "container_id",
            "bill_of_lading",
            "booking",
            null
          ],
          "description": "What kind of reference `identifier` is. Null when unknown (shipments recorded before the type was stored). Additive field, added 2026-08-19; absent on deliveries produced by older builds."
        },
        "discharge_port": {
          "type": [
            "string",
            "null"
          ],
          "description": "UN/LOCODE of the actual discharge."
        },
        "expected_port": {
          "type": [
            "string",
            "null"
          ],
          "description": "UN/LOCODE of the expected destination."
        }
      },
      "required": [
        "container_id"
      ],
      "additionalProperties": true
    },
    "WebhookApiChangeData": {
      "type": "object",
      "description": "`data` for api_change_announced: an API change announced ahead of its effective date. The fields mirror the public changelog entry.",
      "properties": {
        "notice_days": {
          "type": "integer",
          "description": "Days between this announcement and the effective date."
        }
      },
      "required": [
        "notice_days"
      ],
      "additionalProperties": true
    }
  }
}