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.
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.
403integration.scope_missing — the key lacks shipments:read
404shipment.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.
403integration.scope_missing — the key lacks shipments:write
404shipment.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.
403integration.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.
400shipment.not_found or party.not_found — not found in this organisation
401 Missing, revoked or wrong credential
403integration.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.
403integration.scope_missing — the key lacks shipments:read
404party.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.
400request.field_invalid — a date that is not YYYY-MM-DD
401 Missing, revoked or wrong credential
403integration.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
400request.field_invalid — a date that is not YYYY-MM-DD
401 Missing, revoked or wrong credential
403integration.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.
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.
400request.field_required / request.field_invalid — a missing column is named
401 Missing, revoked or wrong credential
403integration.scope_missing — the key lacks evidence:write
404mfg.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.
403integration.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.
403integration.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.
403integration.scope_missing — the key lacks orders:read
404mfg.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.
403integration.scope_missing — the key lacks orders:write
404mfg.order_not_found
409mfg.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.