Cargovate Request a demo

Home  /  Documentation  /  API reference

Documentation 07

API reference for integrations.

Every operation an integration can call, with a request and the response it actually returned. If an operation is not on this page, integration keys cannot call it.

The part of the Cargovate API an integration can use: every operation below is reachable with an integration key (x-vexalus-key) carrying the scope shown, or — for device readings — a device key. Keys are issued by the customer's administrator, choosing the scopes. Anything not listed here is not available to keys: organisation administration, billing and credential management never are.

Errors carry a stable code (branch on it) and a human error (never match on it). Lists page with limit/offset and report x-has-more. Ids may be strings or numbers — treat them as opaque, and prefer your own references. Rate limit: 600 requests a minute by default; back off on 429 and 5xx only.

Examples are recorded from a real instance with anchoring switched off. Within /v1 fields are added, never removed or repurposed — parse permissively.

Base URL
https://api.cargovate.com — Hosted customers (https://api.vexalus.com answers identically). A dedicated or on-premises deployment uses its own address.
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
Headers
x-vexalus-key for an integration key, x-vexalus-device-key for a device
Machine-readable
openapi.json (OpenAPI 3.0) — generate a client from it
Recorded
2026-10-01

Shipments

Create, read and move shipments, keyed on your own reference.

POST /v1/shipment

Create a shipment

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.

Auth
integration key with shipments:write
metadataUri body
string — Where your own copy of the metadata lives, if anywhere. local://none is fine.
metadata body (required)
object

Request

{
  "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
  }
}

Response 201 Success

{
  "shipmentId": "ACME-0001",
  "anchored": false,
  "anchoringSkipped": "anchoring not configured (no RPC endpoint)"
}

Errors

  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks shipments:write
  • 429 Rate limited — back off and retry

GET /v1/shipment

List shipments

Newest first. Each item is a summary; read one shipment for the full record.

Auth
integration key with shipments:read
status query
string — Only shipments with this status.
limit query
integer — Page size.
offset query
integer — Rows to skip. Follow the link header rather than computing this where you can.

Response 200 Success

[
  {
    "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
  }
]

Paged: x-has-more, x-limit, x-offset, link.

Errors

  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks shipments:read
  • 429 Rate limited — back off and retry

GET /v1/shipment/detail/{shipmentId}

Get a shipment

By your reference. Returns the full record; the example is abbreviated, and more fields may be present than are shown. Parse permissively.

Auth
integration key with shipments:read
shipmentId path (required)
string — Your shipment reference.

Response 200 Success (example abbreviated)

{
  "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"
}

Errors

  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks shipments:read
  • 404 shipment.not_found
  • 429 Rate limited — back off and retry

PUT /v1/shipment/detail/{shipmentId}/status

Update a shipment's status

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.

Auth
integration key with shipments:write
shipmentId path (required)
string — Your shipment reference.
status body (required)
string
location body
string — Where the shipment is now.
notes body
string

Request

{
  "status": "in_transit",
  "location": "Rotterdam",
  "notes": "Loaded"
}

Response 200 Success

{
  "shipmentId": "ACME-0001",
  "oldStatus": "created",
  "newStatus": "in_transit"
}

Errors

  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks shipments:write
  • 404 shipment.not_found
  • 429 Rate limited — back off and retry

Checkpoints

Events recorded against a shipment.

POST /v1/checkpoint/{collection}/batch

Record a batch of checkpoints

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.

Auth
integration key with checkpoints:write
collection path (required)
string — Your shipment reference, or the shipment's checkpoint collection address.
events body (required)
array
shipmentId body
string — Your shipment reference; overrides collection.

Request

{
  "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"
    }
  ]
}

Response 201 Success (example abbreviated)

{
  "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
}

Errors

  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks checkpoints:write
  • 404 shipment.not_found
  • 413 checkpoint.batch_too_large
  • 429 Rate limited — back off and retry

GET /v1/checkpoint/by-shipment/{shipmentId}

List a shipment's checkpoints

Newest first. An empty array means the shipment exists and has no checkpoints; an unknown reference is a 404, never an empty list.

Auth
integration key with checkpoints:read
shipmentId path (required)
string — Your shipment reference.
limit query
integer — Page size.
offset query
integer — Rows to skip. Follow the link header rather than computing this where you can.

Response 200 Success (example abbreviated)

[
  {
    "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"
  }
]

Paged: x-has-more, x-limit, x-offset, link.

Errors

  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks checkpoints:read
  • 404 shipment.not_found
  • 429 Rate limited — back off and retry

Custody

Who holds the goods. Keys propose transfers; people approve them.

GET /v1/custody

List custody transfers

Newest first, with shipment_ref — your reference — on each. The example is abbreviated.

Auth
integration key with custody:read
status query
string — For example proposed.
limit query
integer — Page size.
offset query
integer — Rows to skip. Follow the link header rather than computing this where you can.

Response 200 Success (example abbreviated)

[
  {
    "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"
  }
]

Paged: x-has-more, x-limit, x-offset, link.

Errors

  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks custody:read
  • 429 Rate limited — back off and retry

POST /v1/custody/transfer

Propose a custody transfer

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.

Auth
integration key with custody:write
shipmentRef body (required)
string — Your shipment reference.
toPartyId body (required)
integer — Who receives custody.
fromPartyId body
integer
location body
string
conditionNotes body
string

Request

{
  "shipmentRef": "ACME-0001",
  "toPartyId": 19649,
  "location": "Felixstowe",
  "conditionNotes": "Seal intact"
}

Response 201 Success (example abbreviated)

{
  "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"
}

Errors

  • 400 shipment.not_found or party.not_found — not found in this organisation
  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks custody:write
  • 429 Rate limited — back off and retry

GET /v1/shipment/by-owner/{owner}

List the shipments a party holds

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.

Auth
integration key with shipments:read
owner path (required)
string — A party: its id, Vx or 0x address, or .vex name.
limit query
integer — Page size.
offset query
integer — Rows to skip. Follow the link header rather than computing this where you can.

Response 200 Success

[
  {
    "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"
  }
]

Paged: x-has-more, x-limit, x-offset, link.

Errors

  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks shipments:read
  • 404 party.not_found — not a party in this organisation
  • 429 Rate limited — back off and retry

Audit

The organisation's append-only, hash-chained audit trail.

GET /v1/audit/events

Read the audit trail

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.

Auth
integration key with audit:read
outcome query
string
actorId query
string
resourceType query
string — For example shipment.
limit query
integer
offset query
integer — Rows to skip. Follow the link header rather than computing this where you can.

Response 200 Success (example abbreviated)

[
  {
    "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"
  }
]

Paged: x-has-more, x-limit, x-offset, link.

Errors

  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks audit:read
  • 429 Rate limited — back off and retry

Reporting

Read-only figures for an ERP or finance system.

GET /v1/reporting/operations

Operational counts

Shipment counts by status and how many are late against their original ETA. Counts only — no references, parties or locations.

Auth
integration key with reporting:operational
from query
string — YYYY-MM-DD
to query
string — YYYY-MM-DD

Response 200 Success

{
  "from": null,
  "to": null,
  "total": 1,
  "late": 0,
  "onTimePct": 100,
  "byStatus": [
    {
      "status": "in_transit",
      "count": 1,
      "late": 0
    }
  ]
}

Errors

  • 400 request.field_invalid — a date that is not YYYY-MM-DD
  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks reporting:operational
  • 429 Rate limited — back off and retry

GET /v1/reporting/statements

Closed billing statements

Closed periods only, newest first — an open period's figures still move. Money is in integer minor units (cents) with the currency alongside.

Auth
integration key with reporting:financial
from query
string — YYYY-MM-DD
to query
string — YYYY-MM-DD
limit query
integer

Response 200 Success

{
  "statements": []
}

Errors

  • 400 request.field_invalid — a date that is not YYYY-MM-DD
  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks reporting:financial
  • 429 Rate limited — back off and retry

Devices

Readings from field devices you build, authenticated with a device key.

POST /v1/telemetry/readings

Send device readings

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.

Auth
device key
readings body (required)
array

Request

{
  "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"
    }
  ]
}

Response 200 Success

{
  "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"
}

Errors

  • 401 Missing, revoked or wrong credential
  • 413 device.batch_too_large
  • 429 Rate limited — back off and retry

Manufacturing

Orders in from a CRM or ERP, the order feed back out, and test station results.

POST /v1/manufacturing/evidence/import

Import test station results

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.

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.

The example is a dry run naming a serial that does not exist — it shows how a refused row is reported.

Auth
integration key with evidence:write
typeKey body (required)
string — The evidence type the results are recorded as. Must be a unit-level type.
csv body (required)
string — The file, with a header row.
filename body
string — Shown with each record so a row can be traced to its file.
sourceSystem body
string — Defaults to tester.
mapping body (required)
object
verdict body (required)
object
dryRun body
boolean

Request

{
  "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"
  }
}

Response 200 Success

{
  "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"
      ]
    }
  ]
}

Errors

  • 400 request.field_required / request.field_invalid — a missing column is named
  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks evidence:write
  • 404 mfg.evidence_type_unknown
  • 429 Rate limited — back off and retry

GET /v1/manufacturing/feed/orders

Follow orders (the order feed)

Orders changed after a feed position, oldest change first — for an ERP to keep its own copy current.

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.

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.

Prices. acceptedQuote is present only once the customer has accepted a quote; drafts and quotes still awaiting a decision are never sent.

Auth
integration key with orders:read
since query
string — The nextSince of your last page; 0 to start.
limit query
integer — Page size, default 100.

Response 200 Success

{
  "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
}

Errors

  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks orders:read
  • 429 Rate limited — back off and retry

POST /v1/manufacturing/orders

Record an order (with lines)

The intake door for a CRM or ERP. status: "submitted" asks to be quoted; the default "open" is an order already agreed.

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.

Auth
integration key with orders:write
externalId body
string — Your id for the order. Strongly recommended — it is what makes a resend safe.
sourceSystem body
string — Which of your systems sent it. Defaults to api.
orderRef body
string — The reference people see. Defaults to externalId.
status body
string
customerPartyId body
integer — The customer, by their id here.
customerExternalId body
string — Or the customer by YOUR id for them, if they were imported with one.
customerReference body
string — The customer's own reference, such as their PO number.
notes body
string
lines body
array

Request

{
  "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
    }
  ]
}

Response 201 Success (example abbreviated)

{
  "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"
    }
  ]
}

Errors

  • 400 request.field_required / request.field_invalid
  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks orders:write
  • 404 mfg.customer_not_found / mfg.part_not_found
  • 409 mfg.order_exists / mfg.order_state / mfg.order_line_frozen
  • 429 Rate limited — back off and retry

GET /v1/manufacturing/orders

List orders

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.

Auth
integration key with orders:read

Response 200 Success (example abbreviated)

[
  {
    "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
  }
]

Errors

  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks orders:read
  • 429 Rate limited — back off and retry

GET /v1/manufacturing/orders/{id}

Get an order

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.

Auth
integration key with orders:read
id path (required)
string — The order's id, as returned when it was recorded or read from the feed.

Response 200 Success

{
  "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": []
}

Errors

  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks orders:read
  • 404 mfg.order_not_found
  • 429 Rate limited — back off and retry

POST /v1/manufacturing/orders/{id}/lines

Add a line

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.

Auth
integration key with orders:write
id path (required)
string — The order's id, as returned when it was recorded or read from the feed.
externalId body
string
sourceSystem body
string
customerPartRef body (required)
string
description body
string
partNumber body
string
revision body
string
quantity body (required)
number
uom body
string
needBy body
string
notes body
string

Request

{
  "externalId": "30",
  "sourceSystem": "acme-erp",
  "customerPartRef": "GSK-040",
  "description": "Gasket set",
  "quantity": 40
}

Response 201 Success

{
  "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"
}

Errors

  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks orders:write
  • 404 mfg.order_not_found
  • 409 mfg.order_state / mfg.order_line_frozen
  • 429 Rate limited — back off and retry

PATCH /v1/manufacturing/orders/{id}/lines/{lineId}

Change a line

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.

Auth
integration key with orders:write
id path (required)
string — The order's id, as returned when it was recorded or read from the feed.
lineId path (required)
string — The line's id, from the order or the feed.
quantity body
number
customerPartRef body
string
description body
string
partNumber body
string
revision body
string
uom body
string
needBy body
string
notes body
string
cancelled body
boolean

Request

{
  "quantity": 50,
  "needBy": "2026-11-24"
}

Response 200 Success

{
  "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
}

Errors

  • 401 Missing, revoked or wrong credential
  • 403 integration.scope_missing — the key lacks orders:write
  • 404 mfg.order_not_found / mfg.order_line_not_found
  • 409 mfg.order_state / mfg.order_line_frozen / mfg.order_line_in_use / mfg.units_exceed_line
  • 429 Rate limited — back off and retry
Hit a route you need that is not here? Tell us at sales@cargovate.com. Requests from integrators decide which routes open next.