{
  "openapi": "3.0.3",
  "info": {
    "title": "Cargovate API — integrations",
    "version": "0.2.0",
    "description": "The part of the Cargovate API an integration can use: every operation below is reachable with an\n**integration key** (`x-vexalus-key`) carrying the scope shown, or — for device readings — a **device key**.\nKeys are issued by the customer's administrator, choosing the scopes. Anything not listed here is not available to\nkeys: organisation administration, billing and credential management never are.\n\n**Errors** carry a stable `code` (branch on it) and a human `error` (never match on it). **Lists** page with\n`limit`/`offset` and report `x-has-more`. **Ids** may be strings or numbers — treat them as opaque, and prefer\nyour own references. **Rate limit:** 600 requests a minute by default; back off on `429` and `5xx` only.\n\nExamples are recorded from a real instance with anchoring switched off. Within `/v1` fields are added, never\nremoved or repurposed — parse permissively."
  },
  "servers": [
    {
      "url": "{baseUrl}",
      "variables": {
        "baseUrl": {
          "default": "https://api.cargovate.com",
          "description": "Hosted customers (https://api.vexalus.com answers identically). A dedicated or on-premises deployment uses its own address."
        }
      }
    }
  ],
  "tags": [
    {
      "name": "Shipments",
      "description": "Create, read and move shipments, keyed on your own reference."
    },
    {
      "name": "Checkpoints",
      "description": "Events recorded against a shipment."
    },
    {
      "name": "Custody",
      "description": "Who holds the goods. Keys propose transfers; people approve them."
    },
    {
      "name": "Audit",
      "description": "The organisation's append-only, hash-chained audit trail."
    },
    {
      "name": "Reporting",
      "description": "Read-only figures for an ERP or finance system."
    },
    {
      "name": "Devices",
      "description": "Readings from field devices you build, authenticated with a device key."
    },
    {
      "name": "Manufacturing",
      "description": "Orders in from a CRM or ERP, the order feed back out, and test station results."
    }
  ],
  "paths": {
    "/v1/audit/events": {
      "get": {
        "tags": [
          "Audit"
        ],
        "summary": "Read the audit trail",
        "description": "Who did what in this organisation, including reads and refusals, newest first. Actions by your key appear under the label the customer gave it. Each entry is hash-chained to the one before.\n\n**Scope:** `audit:read`",
        "operationId": "getAuditEvents",
        "x-scope": "audit:read",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "parameters": [
          {
            "name": "outcome",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "success",
                "denied",
                "error"
              ]
            }
          },
          {
            "name": "actorId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "resourceType",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "For example `shipment`."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Rows to skip. Follow the `link` header rather than computing this where you can."
          }
        ],
        "responses": {
          "200": {
            "description": "Success (example abbreviated)",
            "headers": {
              "x-has-more": {
                "description": "`true` if another page exists. Read this — a full page does not mean there is more, and a short one is the only proof there is not.",
                "schema": {
                  "type": "boolean"
                }
              },
              "x-limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-offset": {
                "schema": {
                  "type": "integer"
                }
              },
              "link": {
                "description": "`rel=\"next\"` URL when `x-has-more` is true.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": [
                  {
                    "seq": "14",
                    "occurred_at": "2026-10-01T04:20:58.748Z",
                    "actor_type": "user",
                    "actor_label": "ops-mup10y0x@example.com",
                    "credential": "jwt",
                    "action": "create",
                    "resource_type": "approvals.approve",
                    "resource_id": "2392",
                    "route": "/v1/approvals/:id/approve",
                    "method": "POST",
                    "status_code": 200,
                    "outcome": "success",
                    "entry_hash": "3182750afb4058a2778ffab52192610c1dbf229e3ba96c9f42e2ca41758dfa5f"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `audit:read`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/checkpoint/{collection}/batch": {
      "post": {
        "tags": [
          "Checkpoints"
        ],
        "summary": "Record a batch of checkpoints",
        "description": "`collection` names the shipment: **your reference**, or its checkpoint collection address. Up to 500 events. Give each event a `uuid` you generate and keep it on retry: a resent event comes back as `duplicate` and is not recorded twice, even when two resends race. Events without a `uuid` are recorded every time. `type` is a checkpoint type such as `LocationUpdate`, `TemperatureReading`, `SealVerification` or `DelayNotice` (casing and separators are ignored). **A 201 can contain rejected events** — read `results`. A device clock that is unset or more than 48 hours ahead is replaced by the server's time. `anchored` is `false` where anchoring is not enabled; the checkpoints are recorded either way.\n\n**Scope:** `checkpoints:write`",
        "operationId": "postCheckpointCollectionBatch",
        "x-scope": "checkpoints:write",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "parameters": [
          {
            "name": "collection",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your shipment reference, or the shipment's checkpoint collection address."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "events"
                ],
                "properties": {
                  "events": {
                    "type": "array",
                    "maxItems": 500,
                    "items": {
                      "type": "object",
                      "required": [
                        "type"
                      ],
                      "properties": {
                        "type": {
                          "type": "string"
                        },
                        "uuid": {
                          "type": "string",
                          "maxLength": 128,
                          "description": "Your idempotency key for this event."
                        },
                        "metadata": {
                          "type": "object",
                          "properties": {
                            "timestamp": {
                              "type": "string",
                              "format": "date-time",
                              "description": "When it happened (or epoch milliseconds)."
                            },
                            "lat": {
                              "type": "number"
                            },
                            "lng": {
                              "type": "number"
                            },
                            "location_name": {
                              "type": "string"
                            },
                            "value_celsius": {
                              "type": "number"
                            },
                            "device": {
                              "type": "string"
                            },
                            "operator": {
                              "type": "string"
                            },
                            "notes": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  },
                  "shipmentId": {
                    "type": "string",
                    "description": "Your shipment reference; overrides `collection`."
                  }
                }
              },
              "example": {
                "events": [
                  {
                    "uuid": "3f1c2a8e-5b7d-4e21-9c0a-7d2e4b6f8a10",
                    "type": "TemperatureReading",
                    "metadata": {
                      "value_celsius": 4.2,
                      "device": "logger-7",
                      "timestamp": "2026-09-16T09:15:00Z"
                    }
                  },
                  {
                    "uuid": "3f1c2a8e-5b7d-4e21-9c0a-7d2e4b6f8a11",
                    "type": "LocationUpdate",
                    "metadata": {
                      "lat": 51.92,
                      "lng": 4.48,
                      "location_name": "Rotterdam",
                      "timestamp": "2026-09-16T09:15:00Z"
                    }
                  },
                  {
                    "uuid": "3f1c2a8e-5b7d-4e21-9c0a-7d2e4b6f8a12",
                    "type": "Teleport"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success (example abbreviated)",
            "content": {
              "application/json": {
                "example": {
                  "shipmentId": "ACME-0001",
                  "recorded": 2,
                  "duplicate": 0,
                  "rejected": 1,
                  "results": [
                    {
                      "index": 0,
                      "uuid": "3f1c2a8e-5b7d-4e21-9c0a-7d2e4b6f8a10",
                      "status": "recorded"
                    },
                    {
                      "index": 1,
                      "uuid": "3f1c2a8e-5b7d-4e21-9c0a-7d2e4b6f8a11",
                      "status": "recorded"
                    },
                    {
                      "index": 2,
                      "uuid": "3f1c2a8e-5b7d-4e21-9c0a-7d2e4b6f8a12",
                      "status": "rejected",
                      "error": "unknown checkpoint type 'Teleport'"
                    }
                  ],
                  "anchored": false
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `checkpoints:write`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`shipment.not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "`checkpoint.batch_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/checkpoint/by-shipment/{shipmentId}": {
      "get": {
        "tags": [
          "Checkpoints"
        ],
        "summary": "List a shipment's checkpoints",
        "description": "Newest first. An empty array means the shipment exists and has no checkpoints; an unknown reference is a 404, never an empty list.\n\n**Scope:** `checkpoints:read`",
        "operationId": "getCheckpointByShipmentShipmentId",
        "x-scope": "checkpoints:read",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "parameters": [
          {
            "name": "shipmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your shipment reference."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "description": "Page size."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Rows to skip. Follow the `link` header rather than computing this where you can."
          }
        ],
        "responses": {
          "200": {
            "description": "Success (example abbreviated)",
            "headers": {
              "x-has-more": {
                "description": "`true` if another page exists. Read this — a full page does not mean there is more, and a short one is the only proof there is not.",
                "schema": {
                  "type": "boolean"
                }
              },
              "x-limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-offset": {
                "schema": {
                  "type": "integer"
                }
              },
              "link": {
                "description": "`rel=\"next\"` URL when `x-has-more` is true.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": [
                  {
                    "id": 637,
                    "checkpoint_type": "LocationUpdate",
                    "checkpoint_type_id": 0,
                    "lat": 51.92,
                    "lng": 4.48,
                    "location_name": "Rotterdam",
                    "value_celsius": null,
                    "device_id": null,
                    "operator": null,
                    "notes": null,
                    "recorded_at": "2026-09-16T09:15:00.000Z"
                  },
                  {
                    "id": 636,
                    "checkpoint_type": "TemperatureReading",
                    "checkpoint_type_id": 1,
                    "lat": null,
                    "lng": null,
                    "location_name": null,
                    "value_celsius": 4.2,
                    "device_id": "logger-7",
                    "operator": null,
                    "notes": null,
                    "recorded_at": "2026-09-16T09:15:00.000Z"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `checkpoints:read`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`shipment.not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/custody": {
      "get": {
        "tags": [
          "Custody"
        ],
        "summary": "List custody transfers",
        "description": "Newest first, with `shipment_ref` — your reference — on each. The example is abbreviated.\n\n**Scope:** `custody:read`",
        "operationId": "getCustody",
        "x-scope": "custody:read",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "For example `proposed`."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "description": "Page size."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Rows to skip. Follow the `link` header rather than computing this where you can."
          }
        ],
        "responses": {
          "200": {
            "description": "Success (example abbreviated)",
            "headers": {
              "x-has-more": {
                "description": "`true` if another page exists. Read this — a full page does not mean there is more, and a short one is the only proof there is not.",
                "schema": {
                  "type": "boolean"
                }
              },
              "x-limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-offset": {
                "schema": {
                  "type": "integer"
                }
              },
              "link": {
                "description": "`rel=\"next\"` URL when `x-has-more` is true.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": [
                  {
                    "id": 2392,
                    "shipment_ref": "ACME-0001",
                    "status": "proposed",
                    "from_party_id": null,
                    "to_party_id": 19649,
                    "location": "Felixstowe",
                    "proposed_at": "2026-10-01T04:20:58.732Z",
                    "proposed_by": "Acme SI middleware"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `custody:read`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/custody/transfer": {
      "post": {
        "tags": [
          "Custody"
        ],
        "summary": "Propose a custody transfer",
        "description": "Records that custody should pass to another party. It is created as `proposed` and is **decided by a signed-in person** in the customer's organisation — a key can propose a transfer and never approve one. `toPartyId` is a party in the customer's organisation.\n\n**Scope:** `custody:write`",
        "operationId": "postCustodyTransfer",
        "x-scope": "custody:write",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "shipmentRef",
                  "toPartyId"
                ],
                "properties": {
                  "shipmentRef": {
                    "type": "string",
                    "description": "Your shipment reference."
                  },
                  "toPartyId": {
                    "type": "integer",
                    "description": "Who receives custody."
                  },
                  "fromPartyId": {
                    "type": "integer"
                  },
                  "location": {
                    "type": "string"
                  },
                  "conditionNotes": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "shipmentRef": "ACME-0001",
                "toPartyId": 19649,
                "location": "Felixstowe",
                "conditionNotes": "Seal intact"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success (example abbreviated)",
            "content": {
              "application/json": {
                "example": {
                  "id": 2392,
                  "status": "proposed",
                  "from_party_id": null,
                  "to_party_id": 19649,
                  "location": "Felixstowe",
                  "condition_notes": "Seal intact",
                  "proposed_at": "2026-10-01T04:20:58.732Z",
                  "proposed_by": "Acme SI middleware"
                }
              }
            }
          },
          "400": {
            "description": "`shipment.not_found` or `party.not_found` — not found **in this organisation**",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `custody:write`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/manufacturing/evidence/import": {
      "post": {
        "tags": [
          "Manufacturing"
        ],
        "summary": "Import test station results",
        "description": "A test station's results file, as CSV text. Each row is matched to a unit by serial: a passing row becomes that unit's evidence of the named type, a failing row raises a nonconformance and holds the unit. Rows are reported one by one; a bad row never stops the others.\n\n**Idempotent:** importing the same file again changes nothing (`evidenceUnchanged`, `ncUnchanged`). **`dryRun: true`** reports every row's outcome and writes nothing — send it first. `verdict.pass` lists the values that mean a pass (default `PASS`, `P`, `OK`, `PASSED`, in any case); anything else is a failure. At most 5000 rows per file.\n\nThe example is a dry run naming a serial that does not exist — it shows how a refused row is reported.\n\n**Scope:** `evidence:write`",
        "operationId": "postManufacturingEvidenceImport",
        "x-scope": "evidence:write",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "typeKey",
                  "csv",
                  "mapping",
                  "verdict"
                ],
                "properties": {
                  "typeKey": {
                    "type": "string",
                    "description": "The evidence type the results are recorded as. Must be a unit-level type."
                  },
                  "csv": {
                    "type": "string",
                    "description": "The file, with a header row."
                  },
                  "filename": {
                    "type": "string",
                    "description": "Shown with each record so a row can be traced to its file."
                  },
                  "sourceSystem": {
                    "type": "string",
                    "description": "Defaults to `tester`."
                  },
                  "mapping": {
                    "type": "object",
                    "required": [
                      "serial"
                    ],
                    "properties": {
                      "serial": {
                        "type": "string",
                        "description": "The column holding the unit serial."
                      },
                      "partNumber": {
                        "type": "string",
                        "description": "A part number column, needed only where two parts share serials."
                      },
                      "fields": {
                        "type": "object",
                        "additionalProperties": {
                          "type": "string"
                        },
                        "description": "Evidence property → column."
                      }
                    }
                  },
                  "verdict": {
                    "type": "object",
                    "required": [
                      "column"
                    ],
                    "properties": {
                      "column": {
                        "type": "string"
                      },
                      "pass": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    }
                  },
                  "dryRun": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "typeKey": "functional_test",
                "filename": "ft-line2-2026-10-01.csv",
                "dryRun": true,
                "csv": "serial,result,supply_v,current_ma\nSN-0001,PASS,12.02,184\n",
                "mapping": {
                  "serial": "serial",
                  "fields": {
                    "supply_v": "supply_v",
                    "current_ma": "current_ma"
                  }
                },
                "verdict": {
                  "column": "result"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "dryRun": true,
                  "typeKey": "functional_test",
                  "rowsTotal": 1,
                  "evidenceCreated": 0,
                  "evidenceUnchanged": 0,
                  "ncRaised": 0,
                  "ncUnchanged": 0,
                  "invalid": 1,
                  "ragged": [],
                  "rows": [
                    {
                      "rowNum": 2,
                      "serial": "SN-0001",
                      "outcome": "invalid",
                      "issues": [
                        "no unit SN-0001"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`request.field_required` / `request.field_invalid` — a missing column is named",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `evidence:write`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`mfg.evidence_type_unknown`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/manufacturing/feed/orders": {
      "get": {
        "tags": [
          "Manufacturing"
        ],
        "summary": "Follow orders (the order feed)",
        "description": "Orders changed after a feed position, oldest change first — for an ERP to keep its own copy current.\n\n**How to read it.** Start with `since=0`. Store `nextSince` only after you have saved the page, and send it as the next `since`. Every change to an order — its status, its customer fields, any of its lines — moves the order to a new position, so **the same order arrives again: upsert it by `id`, never append.** A page shorter than `limit` means you are caught up.\n\n**Held back.** A change becomes visible about five seconds after it is made (`heldBackMs`), so that a reader never moves past a change still being written. Do not poll faster than that.\n\n**Prices.** `acceptedQuote` is present only once the customer has accepted a quote; drafts and quotes still awaiting a decision are never sent.\n\n**Scope:** `orders:read`",
        "operationId": "getManufacturingFeedOrders",
        "x-scope": "orders:read",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]{1,15}$"
            },
            "description": "The `nextSince` of your last page; `0` to start."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            },
            "description": "Page size, default 100."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "orders": [
                    {
                      "id": 1993,
                      "feedSeq": "1768",
                      "orderRef": "SO-10442",
                      "status": "submitted",
                      "externalId": "SO-10442",
                      "sourceSystem": "acme-erp",
                      "customer": null,
                      "customerReference": "PO-7781",
                      "customerNotes": null,
                      "submittedAt": null,
                      "createdAt": "2026-10-01T04:20:58.783Z",
                      "changedAt": "2026-10-01T04:20:58.804Z",
                      "lines": [
                        {
                          "id": 866,
                          "lineNo": 1,
                          "customerPartRef": "BRK-220",
                          "description": "Mounting bracket, anodised",
                          "partNumber": null,
                          "revision": null,
                          "quantity": "50.0000",
                          "uom": "ea",
                          "needBy": "2026-11-24",
                          "notes": null,
                          "cancelled": false,
                          "externalId": "SO-10442:10",
                          "unitsCreated": 0
                        },
                        {
                          "id": 867,
                          "lineNo": 2,
                          "customerPartRef": "HSG-118",
                          "description": "Sensor housing",
                          "partNumber": null,
                          "revision": null,
                          "quantity": "12.0000",
                          "uom": "ea",
                          "needBy": null,
                          "notes": null,
                          "cancelled": false,
                          "externalId": "SO-10442:20",
                          "unitsCreated": 0
                        },
                        {
                          "id": 868,
                          "lineNo": 3,
                          "customerPartRef": "GSK-040",
                          "description": "Gasket set",
                          "partNumber": null,
                          "revision": null,
                          "quantity": "40.0000",
                          "uom": "ea",
                          "needBy": null,
                          "notes": null,
                          "cancelled": false,
                          "externalId": "SO-10442:30",
                          "unitsCreated": 0
                        }
                      ],
                      "acceptedQuote": null
                    }
                  ],
                  "nextSince": "1768",
                  "heldBackMs": 5000
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `orders:read`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/manufacturing/orders": {
      "post": {
        "tags": [
          "Manufacturing"
        ],
        "summary": "Record an order (with lines)",
        "description": "The intake door for a CRM or ERP. `status: \"submitted\"` asks to be quoted; the default `\"open\"` is an order already agreed.\n\n**Idempotent on your `externalId`** (with `sourceSystem`, default `api`): sending the same order again returns it with `201` → `200` and `created: false`, and each line that carries its own `externalId` is created, updated or left `unchanged` — the outcome is reported per line. A resend may not carry a line *without* an `externalId` (it would duplicate): that is refused with `mfg.order_exists`. A line id need only be unique within its order. Lines are frozen once a quote for them has been sent (`mfg.order_line_frozen`). Nothing is saved when any line is refused.\n\n**Scope:** `orders:write`",
        "operationId": "postManufacturingOrders",
        "x-scope": "orders:write",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "externalId": {
                    "type": "string",
                    "description": "Your id for the order. Strongly recommended — it is what makes a resend safe."
                  },
                  "sourceSystem": {
                    "type": "string",
                    "description": "Which of your systems sent it. Defaults to `api`."
                  },
                  "orderRef": {
                    "type": "string",
                    "description": "The reference people see. Defaults to `externalId`."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "open",
                      "submitted"
                    ]
                  },
                  "customerPartyId": {
                    "type": "integer",
                    "description": "The customer, by their id here."
                  },
                  "customerExternalId": {
                    "type": "string",
                    "description": "Or the customer by YOUR id for them, if they were imported with one."
                  },
                  "customerReference": {
                    "type": "string",
                    "description": "The customer's own reference, such as their PO number."
                  },
                  "notes": {
                    "type": "string"
                  },
                  "lines": {
                    "type": "array",
                    "maxItems": 500,
                    "items": {
                      "type": "object",
                      "required": [
                        "customerPartRef",
                        "quantity"
                      ],
                      "properties": {
                        "externalId": {
                          "type": "string",
                          "description": "Your id for the line, unique within the order."
                        },
                        "customerPartRef": {
                          "type": "string",
                          "description": "The part as the customer names it."
                        },
                        "description": {
                          "type": "string"
                        },
                        "partNumber": {
                          "type": "string",
                          "description": "Optional: map it to one of the manufacturer's parts now."
                        },
                        "revision": {
                          "type": "string"
                        },
                        "quantity": {
                          "type": "number",
                          "description": "Positive, at most 4 decimals."
                        },
                        "uom": {
                          "type": "string",
                          "description": "Defaults to `ea`."
                        },
                        "needBy": {
                          "type": "string",
                          "format": "date"
                        },
                        "notes": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "externalId": "SO-10442",
                "sourceSystem": "acme-erp",
                "status": "submitted",
                "customerReference": "PO-7781",
                "lines": [
                  {
                    "externalId": "10",
                    "customerPartRef": "BRK-220",
                    "description": "Mounting bracket, anodised",
                    "quantity": 40,
                    "needBy": "2026-12-01"
                  },
                  {
                    "externalId": "20",
                    "customerPartRef": "HSG-118",
                    "description": "Sensor housing",
                    "quantity": 12
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success (example abbreviated)",
            "content": {
              "application/json": {
                "example": {
                  "id": "1993",
                  "order_ref": "SO-10442",
                  "status": "submitted",
                  "external_id": "SO-10442",
                  "source_system": "acme-erp",
                  "customer_party_id": null,
                  "customer_reference": "PO-7781",
                  "created_at": "2026-10-01T04:20:58.783Z",
                  "created": true,
                  "lines": [
                    {
                      "lineId": 866,
                      "externalId": "10",
                      "outcome": "created"
                    },
                    {
                      "lineId": 867,
                      "externalId": "20",
                      "outcome": "created"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`request.field_required` / `request.field_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `orders:write`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`mfg.customer_not_found` / `mfg.part_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`mfg.order_exists` / `mfg.order_state` / `mfg.order_line_frozen`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      },
      "get": {
        "tags": [
          "Manufacturing"
        ],
        "summary": "List orders",
        "description": "Newest first, with the line count and the latest quote's version and status. To follow changes, use the order feed rather than re-reading this list.\n\n**Scope:** `orders:read`",
        "operationId": "getManufacturingOrders",
        "x-scope": "orders:read",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success (example abbreviated)",
            "content": {
              "application/json": {
                "example": [
                  {
                    "id": "1993",
                    "order_ref": "SO-10442",
                    "status": "submitted",
                    "external_id": "SO-10442",
                    "source_system": "acme-erp",
                    "customer_party_id": null,
                    "customer_reference": "PO-7781",
                    "created_at": "2026-10-01T04:20:58.783Z",
                    "line_count": 2,
                    "latest_quote": null
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `orders:read`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/manufacturing/orders/{id}": {
      "get": {
        "tags": [
          "Manufacturing"
        ],
        "summary": "Get an order",
        "description": "The order with its lines, its quotes (a draft quote is never shown to a key) and any customer requirements flowed down to it. The example is abbreviated; parse permissively.\n\n**Scope:** `orders:read`",
        "operationId": "getManufacturingOrdersId",
        "x-scope": "orders:read",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The order's id, as returned when it was recorded or read from the feed."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "order": {
                    "id": "1993",
                    "order_ref": "SO-10442",
                    "status": "submitted",
                    "external_id": "SO-10442",
                    "source_system": "acme-erp",
                    "customer_party_id": null,
                    "customer_name": null,
                    "customer_reference": "PO-7781",
                    "accepted_quote_id": null,
                    "created_at": "2026-10-01T04:20:58.783Z"
                  },
                  "lines": [
                    {
                      "id": "866",
                      "order_id": "1993",
                      "line_no": 1,
                      "customer_part_ref": "BRK-220",
                      "description": "Mounting bracket, anodised",
                      "part_definition_id": null,
                      "quantity": "40.0000",
                      "uom": "ea",
                      "need_by": "2026-12-01",
                      "notes": null,
                      "cancelled_at": null,
                      "created_at": "2026-10-01T04:20:58.783Z",
                      "part_number": null,
                      "revision": null,
                      "units_created": 0
                    },
                    {
                      "id": "867",
                      "order_id": "1993",
                      "line_no": 2,
                      "customer_part_ref": "HSG-118",
                      "description": "Sensor housing",
                      "part_definition_id": null,
                      "quantity": "12.0000",
                      "uom": "ea",
                      "need_by": null,
                      "notes": null,
                      "cancelled_at": null,
                      "created_at": "2026-10-01T04:20:58.783Z",
                      "part_number": null,
                      "revision": null,
                      "units_created": 0
                    }
                  ],
                  "quotes": [],
                  "flowdowns": []
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `orders:read`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`mfg.order_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/manufacturing/orders/{id}/lines": {
      "post": {
        "tags": [
          "Manufacturing"
        ],
        "summary": "Add a line",
        "description": "With an `externalId` this is an upsert: resending the same line changes nothing, a changed one is updated (`outcome` says which). Only while the order is `submitted`, `quoted` or `open`.\n\n**Scope:** `orders:write`",
        "operationId": "postManufacturingOrdersIdLines",
        "x-scope": "orders:write",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The order's id, as returned when it was recorded or read from the feed."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "customerPartRef",
                  "quantity"
                ],
                "properties": {
                  "externalId": {
                    "type": "string"
                  },
                  "sourceSystem": {
                    "type": "string"
                  },
                  "customerPartRef": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "partNumber": {
                    "type": "string"
                  },
                  "revision": {
                    "type": "string"
                  },
                  "quantity": {
                    "type": "number"
                  },
                  "uom": {
                    "type": "string"
                  },
                  "needBy": {
                    "type": "string",
                    "format": "date"
                  },
                  "notes": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "externalId": "30",
                "sourceSystem": "acme-erp",
                "customerPartRef": "GSK-040",
                "description": "Gasket set",
                "quantity": 40
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "id": "868",
                  "order_id": "1993",
                  "line_no": 3,
                  "customer_part_ref": "GSK-040",
                  "description": "Gasket set",
                  "part_definition_id": null,
                  "quantity": "40.0000",
                  "uom": "ea",
                  "need_by": null,
                  "notes": null,
                  "cancelled_at": null,
                  "created_at": "2026-10-01T04:20:58.797Z",
                  "part_number": null,
                  "revision": null,
                  "units_created": 0,
                  "outcome": "created"
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `orders:write`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`mfg.order_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`mfg.order_state` / `mfg.order_line_frozen`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/manufacturing/orders/{id}/lines/{lineId}": {
      "patch": {
        "tags": [
          "Manufacturing"
        ],
        "summary": "Change a line",
        "description": "Send only the fields that change. `cancelled: true` cancels a line (refused once units exist for it). Quantity, part reference, unit and cancellation are what a quote prices, so they are refused once a quote has been sent (`mfg.order_line_frozen`); dates and notes stay editable.\n\n**Scope:** `orders:write`",
        "operationId": "patchManufacturingOrdersIdLinesLineId",
        "x-scope": "orders:write",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The order's id, as returned when it was recorded or read from the feed."
          },
          {
            "name": "lineId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The line's id, from the order or the feed."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "quantity": {
                    "type": "number"
                  },
                  "customerPartRef": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "partNumber": {
                    "type": "string",
                    "nullable": true
                  },
                  "revision": {
                    "type": "string"
                  },
                  "uom": {
                    "type": "string"
                  },
                  "needBy": {
                    "type": "string",
                    "format": "date",
                    "nullable": true
                  },
                  "notes": {
                    "type": "string",
                    "nullable": true
                  },
                  "cancelled": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "quantity": 50,
                "needBy": "2026-11-24"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "id": "866",
                  "order_id": "1993",
                  "line_no": 1,
                  "customer_part_ref": "BRK-220",
                  "description": "Mounting bracket, anodised",
                  "part_definition_id": null,
                  "quantity": "50.0000",
                  "uom": "ea",
                  "need_by": "2026-11-24",
                  "notes": null,
                  "cancelled_at": null,
                  "created_at": "2026-10-01T04:20:58.783Z",
                  "part_number": null,
                  "revision": null,
                  "units_created": 0
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `orders:write`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`mfg.order_not_found` / `mfg.order_line_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`mfg.order_state` / `mfg.order_line_frozen` / `mfg.order_line_in_use` / `mfg.units_exceed_line`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/reporting/operations": {
      "get": {
        "tags": [
          "Reporting"
        ],
        "summary": "Operational counts",
        "description": "Shipment counts by status and how many are late against their original ETA. Counts only — no references, parties or locations.\n\n**Scope:** `reporting:operational`",
        "operationId": "getReportingOperations",
        "x-scope": "reporting:operational",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "`YYYY-MM-DD`"
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "`YYYY-MM-DD`"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "from": null,
                  "to": null,
                  "total": 1,
                  "late": 0,
                  "onTimePct": 100,
                  "byStatus": [
                    {
                      "status": "in_transit",
                      "count": 1,
                      "late": 0
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`request.field_invalid` — a date that is not `YYYY-MM-DD`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `reporting:operational`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/reporting/statements": {
      "get": {
        "tags": [
          "Reporting"
        ],
        "summary": "Closed billing statements",
        "description": "Closed periods only, newest first — an open period's figures still move. Money is in integer minor units (cents) with the currency alongside.\n\n**Scope:** `reporting:financial`",
        "operationId": "getReportingStatements",
        "x-scope": "reporting:financial",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "`YYYY-MM-DD`"
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "`YYYY-MM-DD`"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "statements": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "periodStart": {
                            "type": "string",
                            "format": "date"
                          },
                          "periodEnd": {
                            "type": "string",
                            "format": "date"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "totalMinor": {
                            "type": "integer"
                          },
                          "closedAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "lines": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "kind": {
                                  "type": "string"
                                },
                                "description": {
                                  "type": "string"
                                },
                                "quantity": {
                                  "type": "number"
                                },
                                "unitMinor": {
                                  "type": "integer"
                                },
                                "amountMinor": {
                                  "type": "integer"
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "statements": []
                }
              }
            }
          },
          "400": {
            "description": "`request.field_invalid` — a date that is not `YYYY-MM-DD`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `reporting:financial`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/shipment": {
      "post": {
        "tags": [
          "Shipments"
        ],
        "summary": "Create a shipment",
        "description": "`metadata.shipmentId` is **your** reference and is how every other call finds the shipment. Keep it identical to the reference your own system uses. Creating a shipment with a reference that already exists updates that shipment rather than failing. `anchored` says whether the record was also anchored; `false` is normal where anchoring is not enabled.\n\n**Scope:** `shipments:write`",
        "operationId": "postShipment",
        "x-scope": "shipments:write",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "metadata"
                ],
                "properties": {
                  "metadataUri": {
                    "type": "string",
                    "description": "Where your own copy of the metadata lives, if anywhere. `local://none` is fine."
                  },
                  "metadata": {
                    "type": "object",
                    "required": [
                      "shipmentId"
                    ],
                    "properties": {
                      "shipmentId": {
                        "type": "string",
                        "description": "Your reference."
                      },
                      "origin": {
                        "type": "object",
                        "properties": {
                          "port": {
                            "type": "string"
                          },
                          "country": {
                            "type": "string"
                          }
                        }
                      },
                      "destination": {
                        "type": "object",
                        "properties": {
                          "port": {
                            "type": "string"
                          },
                          "country": {
                            "type": "string"
                          }
                        }
                      },
                      "carrier": {
                        "type": "string"
                      },
                      "vessel": {
                        "type": "string"
                      },
                      "incoterms": {
                        "type": "string"
                      },
                      "contents": {
                        "type": "string"
                      },
                      "weight_kg": {
                        "type": "number"
                      },
                      "value": {
                        "type": "number"
                      },
                      "currency": {
                        "type": "string",
                        "description": "ISO 4217. Defaults to your organisation's currency."
                      },
                      "eta": {
                        "type": "string",
                        "format": "date-time"
                      }
                    }
                  }
                }
              },
              "example": {
                "metadataUri": "local://none",
                "metadata": {
                  "shipmentId": "ACME-0001",
                  "origin": {
                    "port": "NLRTM",
                    "country": "NL"
                  },
                  "destination": {
                    "port": "GBFXT",
                    "country": "GB"
                  },
                  "carrier": "Maersk",
                  "contents": "Machine parts",
                  "weight_kg": 1200
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "shipmentId": "ACME-0001",
                  "anchored": false,
                  "anchoringSkipped": "anchoring not configured (no RPC endpoint)"
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `shipments:write`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      },
      "get": {
        "tags": [
          "Shipments"
        ],
        "summary": "List shipments",
        "description": "Newest first. Each item is a summary; read one shipment for the full record.\n\n**Scope:** `shipments:read`",
        "operationId": "getShipment",
        "x-scope": "shipments:read",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Only shipments with this status."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "description": "Page size."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Rows to skip. Follow the `link` header rather than computing this where you can."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-has-more": {
                "description": "`true` if another page exists. Read this — a full page does not mean there is more, and a short one is the only proof there is not.",
                "schema": {
                  "type": "boolean"
                }
              },
              "x-limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-offset": {
                "schema": {
                  "type": "integer"
                }
              },
              "link": {
                "description": "`rel=\"next\"` URL when `x-has-more` is true.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": [
                  {
                    "id": "ACME-0001",
                    "reference": "ACME-0001",
                    "origin": "NLRTM, NL",
                    "destination": "GBFXT, GB",
                    "status": "created",
                    "carrier": "Maersk",
                    "created_at": "2026-10-01T04:20:58.697Z",
                    "eta": null
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `shipments:read`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/shipment/by-owner/{owner}": {
      "get": {
        "tags": [
          "Custody"
        ],
        "summary": "List the shipments a party holds",
        "description": "Shipments whose latest **approved** custody transfer went to this party, most recent first. `owner` is the party's id, its `Vx` or `0x` address, or its `.vex` name. A proposed transfer does not move custody until a person approves it.\n\n**Scope:** `shipments:read`",
        "operationId": "getShipmentByOwnerOwner",
        "x-scope": "shipments:read",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "parameters": [
          {
            "name": "owner",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "A party: its id, Vx or 0x address, or .vex name."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "description": "Page size."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Rows to skip. Follow the `link` header rather than computing this where you can."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-has-more": {
                "description": "`true` if another page exists. Read this — a full page does not mean there is more, and a short one is the only proof there is not.",
                "schema": {
                  "type": "boolean"
                }
              },
              "x-limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-offset": {
                "schema": {
                  "type": "integer"
                }
              },
              "link": {
                "description": "`rel=\"next\"` URL when `x-has-more` is true.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": [
                  {
                    "id": "ACME-0001",
                    "reference": "ACME-0001",
                    "origin": "NLRTM, NL",
                    "destination": "GBFXT, GB",
                    "status": "in_transit",
                    "carrier": "Maersk",
                    "created_at": "2026-10-01T04:20:58.697Z",
                    "eta": null,
                    "custodianPartyId": 19649,
                    "custodySince": "2026-10-01T04:20:58.743Z"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `shipments:read`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`party.not_found` — not a party **in this organisation**",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/shipment/detail/{shipmentId}": {
      "get": {
        "tags": [
          "Shipments"
        ],
        "summary": "Get a shipment",
        "description": "By your reference. Returns the full record; the example is abbreviated, and more fields may be present than are shown. Parse permissively.\n\n**Scope:** `shipments:read`",
        "operationId": "getShipmentDetailShipmentId",
        "x-scope": "shipments:read",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "parameters": [
          {
            "name": "shipmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your shipment reference."
          }
        ],
        "responses": {
          "200": {
            "description": "Success (example abbreviated)",
            "content": {
              "application/json": {
                "example": {
                  "shipment_id": "ACME-0001",
                  "status": "created",
                  "current_location": null,
                  "origin_port": "NLRTM",
                  "origin_country": "NL",
                  "dest_port": "GBFXT",
                  "dest_country": "GB",
                  "carrier": "Maersk",
                  "vessel": null,
                  "incoterms": null,
                  "contents": "Machine parts",
                  "weight_kg": "1200",
                  "value_amount": null,
                  "value_currency": "USD",
                  "original_eta": null,
                  "current_eta": null,
                  "created_at": "2026-10-01T04:20:58.697Z",
                  "updated_at": "2026-10-01T04:20:58.697Z"
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `shipments:read`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`shipment.not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/shipment/detail/{shipmentId}/status": {
      "put": {
        "tags": [
          "Shipments"
        ],
        "summary": "Update a shipment's status",
        "description": "Records the change and its previous value. The web application uses: `created`, `booked`, `in_transit`, `at_port`, `customs_hold`, `customs_cleared`, `warehoused`, `out_for_delivery`, `delivered`.\n\n**Scope:** `shipments:write`",
        "operationId": "putShipmentDetailShipmentIdStatus",
        "x-scope": "shipments:write",
        "security": [
          {
            "integrationKey": []
          }
        ],
        "parameters": [
          {
            "name": "shipmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your shipment reference."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "type": "string"
                  },
                  "location": {
                    "type": "string",
                    "description": "Where the shipment is now."
                  },
                  "notes": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "status": "in_transit",
                "location": "Rotterdam",
                "notes": "Loaded"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "shipmentId": "ACME-0001",
                  "oldStatus": "created",
                  "newStatus": "in_transit"
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`integration.scope_missing` — the key lacks `shipments:write`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`shipment.not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    },
    "/v1/telemetry/readings": {
      "post": {
        "tags": [
          "Devices"
        ],
        "summary": "Send device readings",
        "description": "For a device you build yourself, authenticated with its own **device key** (provisioned by the customer). Up to 500 readings per request. `seq` is yours, must only ever increase — persist it across reboots — and makes a resent batch safe: already-recorded readings come back as `duplicate`. A position is a reading with `metric: \"position\"` and `lat`/`lng` instead of `value`. **A 200 can contain rejected readings** — read `results`.",
        "operationId": "postTelemetryReadings",
        "security": [
          {
            "deviceKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "readings"
                ],
                "properties": {
                  "readings": {
                    "type": "array",
                    "maxItems": 500,
                    "items": {
                      "type": "object",
                      "required": [
                        "seq",
                        "metric"
                      ],
                      "properties": {
                        "seq": {
                          "type": "integer"
                        },
                        "metric": {
                          "type": "string",
                          "description": "`temperature`, `humidity`, `position`, …"
                        },
                        "value": {
                          "type": "number"
                        },
                        "unit": {
                          "type": "string"
                        },
                        "lat": {
                          "type": "number"
                        },
                        "lng": {
                          "type": "number"
                        },
                        "acc": {
                          "type": "number",
                          "description": "Horizontal accuracy in metres."
                        },
                        "fix": {
                          "type": "string",
                          "enum": [
                            "gnss",
                            "cell",
                            "wifi"
                          ]
                        },
                        "t": {
                          "type": "string",
                          "format": "date-time",
                          "description": "Device time, when it has a real clock."
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "readings": [
                  {
                    "seq": 1041,
                    "metric": "temperature",
                    "value": 4.2,
                    "t": "2026-09-16T09:15:00Z"
                  },
                  {
                    "seq": 1042,
                    "metric": "position",
                    "lat": 51.92,
                    "lng": 4.48,
                    "acc": 6,
                    "fix": "gnss",
                    "t": "2026-09-16T09:15:00Z"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "device": "Truck 12 logger",
                  "accepted": 2,
                  "duplicate": 0,
                  "rejected": 0,
                  "positions": 1,
                  "alerts": 0,
                  "results": [
                    {
                      "seq": 1041,
                      "status": "accepted"
                    },
                    {
                      "seq": 1042,
                      "status": "accepted"
                    }
                  ],
                  "serverTime": "2026-10-01T04:20:58.779Z"
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or wrong credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "`device.batch_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — back off and retry"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "integrationKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-vexalus-key",
        "description": "Issued by the customer's administrator with chosen scopes. Shown once."
      },
      "deviceKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-vexalus-device-key",
        "description": "One per device, issued when the device is provisioned."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error",
          "code"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable. Branch on this.",
            "example": "integration.scope_missing"
          },
          "error": {
            "type": "string",
            "description": "For people. Reworded without notice."
          },
          "requiredScope": {
            "type": "string",
            "description": "On a missing scope: what to ask the customer for."
          }
        }
      }
    }
  },
  "x-scopes": [
    "shipments:read",
    "shipments:write",
    "custody:read",
    "custody:write",
    "checkpoints:read",
    "checkpoints:write",
    "audit:read",
    "reporting:financial",
    "reporting:operational",
    "orders:read",
    "orders:write",
    "evidence:write"
  ],
  "x-generated": {
    "at": "2026-10-01",
    "by": "build-integrator-reference"
  }
}
