API Reference
With the TMS add-on, another system of yours (a BI tool, an accounting program, a visibility platform) can read your organization's TMS orders, contractors, vehicles and invoices through the TMS API, and your own telematics system can send vehicle positions. In the app's words: "A key lets another system read TMS data, for example orders for reports, or send vehicle positions from its telematics. Besides those positions it changes nothing."
Everything else in Quanty is used through the app at app.quanty.ai. The app's own private API is not offered for integration: it needs a signed-in session and changes without notice.
Before you start
- The organization has the TMS add-on and a paid plan. Without the add-on every request is refused with
addon_required; while the plan is unpaid, withpayment_required. - Only an organization admin creates and revokes API keys, in TMS settings, API and webhooks.
- The system that calls the API needs the key, the address of your Quanty API (shown in the key dialog's example) and this page.
API keys
- Open TMS settings, API and webhooks and click Create key under API keys.
- Fill in the dialog and click Create key.
- The key is shown once, with Copy and Example: the first 50 orders (Copy example): "We show this key only once. The list keeps only its beginning; if it is lost, revoke it and create a new one."
- Paste the key into the system that will use it and click Done.
| Field | What it means | Example |
|---|---|---|
| Name | "The name tells you which key is which in the list." Up to 120 characters. | BI reports |
| What it can read or send | Orders, Contractors, Vehicles and Invoices (ticked by default), and Telematics: sending for a system that sends positions (never ticked by default; keys made earlier do not have it). Tick at least one. | Orders, Invoices |
The list of keys shows Name, Access, Created, Last used ("not yet" until the first request) and Status: Active or Revoked (with who revoked it). Revoke in a key's menu stops it at once: "Systems that use it lose access at once. This cannot be undone."
| Key rule | Value |
|---|---|
| Active keys per organization | at most 20 ("Revoke a key nobody uses to create a new one.") |
| What a key looks like | starts with qk_; the list shows only its beginning |
| A lost key | cannot be shown again: revoke it and create a new one |
| Last used | updated at most every 5 minutes |
| Chat | keys are never made, shown or revoked through the chat |
Keep the key secret
Anyone who has the key can read what it allows. Store it as a secret in the system that uses it, and revoke it when that system no longer needs it.
Reading data
Use the address from the key dialog's example and send the key as a bearer token:
GET /api/v1/ext/tms/orders?limit=50
Authorization: Bearer qk_…| Path | The key needs |
|---|---|
GET /api/v1/ext/tms/orders | Orders |
GET /api/v1/ext/tms/contractors | Contractors |
GET /api/v1/ext/tms/vehicles | Vehicles |
GET /api/v1/ext/tms/invoices | Invoices |
Query parameters
| Parameter | What it means | Example |
|---|---|---|
limit | Rows per page, 1 to 200. Default 50. | 200 |
cursor | The next_cursor of the previous page. | MjAyNi0xMS0wMlQw… |
updated_since | Only rows changed at or after this moment: an ISO moment, or a bare date, which means its midnight in Polish time. | 2026-10-21T08:00:00Z |
Pages and polling
The answer is one page, oldest change first:
{
"items": [ { "id": "…", "number": "…", "updated_at": "…" } ],
"next_cursor": "…"
}- Read the first page, with
updated_sinceif you only want recent changes. - While
next_cursoris notnull, read again withcursorset to it (keep the sameupdated_since). - To poll for changes later, start the next run with
updated_sincea few minutes before the time of your last run.
Rows that changed in the last 10 seconds are not served yet; they come on the next read, which is why a poll goes back a few minutes. Rows with the same updated_at are never skipped or repeated across pages. Deleted rows are left out; cancelled ones keep their status. Answers are never cached.
Fields
Each row has id, created_at and updated_at, and the fields of the matching table in TMS data (see Tables in Quanty). Money and other decimals come as strings ("1234.50"), whole numbers as numbers, dates as YYYY-MM-DD (the day in Polish time), moments in UTC, and choices as codes. A value that is not set is null.
orders
| Field | What it holds |
|---|---|
number, client_reference | The order number and the client's own number. |
status | draft, confirmed, planned, in_progress, delivered, documents (waiting for documents), invoiced, settled or cancelled. |
client, route | The client's short name and the route. |
loading_date, unloading_date | The first loading and the last unloading. |
execution | own (own fleet) or carrier. |
vehicle_plate, trailer_plate, driver | For own fleet the TMS vehicle, trailer and drivers; for a carrier the plate and driver typed on the order. |
carrier | The carrier, for a carrier order. |
freight_amount, currency, freight_pln | The freight in its currency and in PLN. |
carrier_cost_pln, margin_pln | The carrier's cost and the margin, for a carrier order. |
documents_status | missing, partial, complete or not_required. |
owner, branch, tags | The person in charge, the branch and the tags (comma-separated). |
is_adr, km, co2_kg | ADR (true or false), the kilometres and the CO2 in kg. |
contractors
| Field | What it holds |
|---|---|
name, short_name, nip, city, country | The company and its country code. |
roles | A list: client, carrier or both. |
verification | positive, negative, neutral or unverified. |
credit_limit, exposure_pln | The credit limit and, for a client, what it owes now in PLN. |
blocked_as_client, blocked_as_carrier | true or false. |
payment_days, owner, last_order_on, archived | The payment term, the person in charge, the last order's day, and whether it is archived. |
vehicles
| Field | What it holds |
|---|---|
plate, kind, make_model, vin | kind is tractor, trailer, truck, van or car. |
status | active, service, inactive or sold. |
current_trailer, current_driver | The set the vehicle is in now. |
insurance_until, inspection_until, tachograph_until, next_expiry | The deadlines and the earliest of them. A trailer's tachograph date is not a deadline: next_expiry leaves it out (so does the Vehicles table in Tables in Quanty). |
odometer_km | The odometer. |
invoices
| Field | What it holds |
|---|---|
number, series | The number (none on a draft) and the numbering series. |
status | draft, issued, sent, partially_paid, paid or cancelled. |
buyer, issue_date, due_date | The buyer as on the invoice, and the dates. |
gross, currency, paid, outstanding, net_pln | The amounts. outstanding is set once the invoice is issued. |
days_overdue | Days past the due date while something is left to pay, else 0. |
ksef_status | queued, sent, accepted, rejected, or null when the invoice did not go to KSeF. |
orders | The order numbers on the invoice (comma-separated). |
No internal notes, files, people's ids or coordinates are served. Records that come from Marcos TMS are included like any other.
Errors
A refusal carries a code and a sentence (error_code, message), never anything of the key.
| Status | Code | Meaning |
|---|---|---|
| 401 | invalid_api_key | The key is missing, wrong or revoked. |
| 402 | addon_required | The organization does not have the TMS add-on. |
| 402 | payment_required | The organization's subscription is not paid. |
| 403 | scope_missing | The key may not read this kind of data. |
| 422 | invalid_cursor | The cursor is not one the API gave out. |
| 422 | invalid_updated_since | The moment cannot be read. |
| 429 | Too many requests. Wait and try again. |
Limits
| Limit | Value |
|---|---|
| Reading, per key | 120 requests a minute and 5 000 an hour |
| Rows per page | at most 200 |
| Requests with a wrong key, per address | 30 a minute; after that a wrong key is answered 429 for a while |
| Sending positions, per key | 60 requests a minute and 2 000 an hour |
| Positions per request | at most 500, and at most 1 MB |
A valid key is never slowed down by other people's wrong keys.
Sending vehicle positions
For a telematics Quanty does not read itself, your own system can send the positions. The walkthrough, with every field, how vehicles are matched and all the answers, is on Your own system (API). In short:
- An admin connects Your own system (API) under Telematics: "Nothing to fill in here." The connection shows Receiving: Through the API, Last data, and How to send data with the address, the key it needs and an example request.
- Make an API key with Telematics: sending.
- Your system sends the positions:
POST /api/v1/ext/tms/telematics/positions
Authorization: Bearer qk_…
Content-Type: application/json{
"items": [
{
"vehicle": { "plate": "PO 1AB12" },
"at": "2026-11-01T10:00:00Z",
"lat": 52.40,
"lng": 16.92,
"speed_kmh": 82,
"heading": 270,
"odometer_km": 423434.5,
"fuel_pct": 61,
"fuel_l": 410,
"driver": { "id": "D-17", "name": "Jan Kowalski" },
"trailer": { "plate": "PO 9TR" }
}
]
}vehiclenames the vehicle by its Quantyid, itsvinor itsplate. A vehicle with no telematics yet is linked on its first position; one that takes its positions from another telematics connection is refused for that item.atis an ISO moment (without an offset it is read as UTC) or Unix seconds; a time more than 15 minutes ahead of Quanty's clock is refused, so check the sender's clock.latandlngare required; the rest is optional. The driver's name is personal data, as with any telematics.- A field an item leaves out keeps the vehicle's last value (fuel, driver), and
nullclears it. The vehicle's odometer only goes up, so send a real reading. An optional value that is not a reading (a speed of -1, an odometer of 0, a fuel of "n/a") is left out and listed underignored; the position still counts.
The answer is 200 when at least one item was stored:
{
"accepted": 1,
"positions_added": 1,
"older_than_stored": 0,
"vehicles_linked": 0,
"rejected": [ { "index": 3, "status": 404, "error_code": "…", "message": "…" } ],
"ignored": [ { "index": 0, "field": "speed_kmh" } ]
}Each item is answered on its own: a refused item is listed under rejected with its index, and the others are stored. The same body sent twice is stored once, and a report older than the vehicle's newest one is added to its trail without moving the vehicle back.
A whole request is refused with:
| Status | Code | Meaning |
|---|---|---|
| 401, 402 | as above | The key, the add-on or the plan. |
| 403 | scope_missing | The key does not have Telematics: sending. |
| 409 | push_not_connected, push_paused | No Your own system (API) connection, or it is paused. |
| 413 | body_too_large | More than 1 MB. Split the items. |
| 422 | invalid_json, items_required, too_many_items | Not JSON, no items, or more than 500. |
| 429 | push_busy | Another push of your organization is being stored: wait the seconds in Retry-After and send again. Also when the key sends too many requests. |
Other ways to move data
- Google Sheets: import rows from a Google spreadsheet on a schedule, with a key column against duplicates, or export a table the other way.
- Export: any table exports to CSV, Excel or PDF from More actions in the grid (see Projects, tables and views), and the TMS lists and reports have their own export.
- Automations (beta): turn e-mails, WhatsApp messages and KSeF invoices into records of a table, and act on new and changed records.
- Share links: dashboards and presentations can be shared read-only by link (see Dashboards and Presentations).
Questions about the API: support@quanty.ai.