API conventions
Conventions shared by all three TOVR APIs — base URLs and environments, authentication header formats, the error response shape, rate-limit headers, and versioning. Each pillar's API reference links here.
TOVR exposes three distinct APIs, one per pillar. They are separate services with different auth mechanisms, but they share the conventions on this page. Each pillar's reference documents only its own specifics and links back here for the shared parts — so these conventions are defined once.
The three APIs
| API | Host | Auth | Documented in |
|---|---|---|---|
| TOVR REST API | tovr.eu | OAuth 2.0 client-credentials → bearer JWT | TOVR › Reference |
| APEX API | apex.tovr.eu | X-API-Key header | APEX › Reference |
| fOS Agent API | mcp.tovr.eu | API key or OAuth 2.1 (for MCP) | fOS › Reference |
Pick by intent: manage freight resources → TOVR REST API; query market prices → APEX; drive the AI dispatcher / tool-calling from an external assistant → fOS Agent API.
Environments
Each API has a staging and a production host. Staging is a full, isolated environment for integration testing.
| Environment | TOVR REST | APEX | fOS Agent |
|---|---|---|---|
| Production | https://tovr.eu | https://apex.tovr.eu | https://mcp.tovr.eu |
| Staging | https://staging.tovr.eu | https://apex-staging.tovr.eu | (staging MCP host) |
Authentication
Two header styles are used depending on the API:
- Bearer token (TOVR REST, fOS OAuth):
Authorization: Bearer <token>. TOVR REST tokens are short-lived JWTs obtained from an OAuth 2.0 client-credentials exchange. fOS accepts either a long-lived API key of the formtovr_…as a bearer token, or an OAuth 2.1 access token for MCP clients. - API key header (APEX):
X-API-Key: <key>. APEX keys are issued per consumer and look likeapex_….
Never embed a secret in client-side code. Treat client secrets and API keys like passwords; rotate them if exposed.
Versioning
Resource APIs are versioned in the path (/api/v1/…). A version is additive-stable: new
optional fields may appear, but breaking changes ship under a new version prefix. The APEX
and fOS tool surfaces are versioned the same way (/api/v1/…).
Errors
Errors return a non-2xx HTTP status and a JSON body with a machine-readable code and a
human-readable message:
{ "error": { "code": "KEY_INVALID", "message": "The provided API key is not valid." } }Common status codes across all three APIs:
| Status | Meaning |
|---|---|
400 | Malformed request (bad JSON, missing required field). |
401 | Missing or invalid credentials. |
402 | Monthly quota exhausted — see below. Waiting does not clear it. |
403 | Authenticated but not permitted (bad key, insufficient scope). |
404 | Resource not found. |
409 | Conflict — the natural key is taken. See code for which case. |
422 | Understood but unprocessable (unknown parameter, invalid cursor, illegal status transition). |
429 | Rate limit exceeded — see below. Retry after Retry-After. |
500 | internal_error — a fault on our side. Retrying will not help; report the request_id. |
503 | service_unavailable — a dependency (APEX, today) is unreachable or unconfigured. Your request was fine; retry with backoff. |
The 500 / 503 split is worth wiring into your client rather than lumping both into "5xx":
they ask for opposite behaviour. A 503 clears on its own and a 500 does not.
fOS tools additionally return in-band result codes for agent flows — for example
CONFIRMATION_REQUIRED when a write needs explicit approval. Those are documented in
fOS › Guardrails.
Rate limiting and quota
There are two ceilings, and they fail differently. Getting this distinction into your client is the difference between a backoff that works and a retry loop that never ends.
| Window | On exceeding | Clears by | |
|---|---|---|---|
| Rate limit | per minute | 429 · rate_limited | waiting — see Retry-After |
| Quota | per calendar month | 402 · quota_exceeded | upgrading the plan |
A 402 is deliberately not a 429: no amount of waiting inside the month makes a
month's quota pass, so a client that treats it as "slow down" will retry until the month
ends. Only the 429 carries Retry-After.
Every response carries the headers below — including refusals and errors, so you can back off before hitting the wall rather than after.
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed per minute. |
X-RateLimit-Remaining | Requests remaining in the current minute. |
X-RateLimit-Reset | When the minute window resets (epoch seconds). |
RateLimit-Limit / -Remaining / -Reset | The same three, in the IETF draft's unprefixed spelling. RateLimit-Reset is seconds remaining, not an epoch. |
Retry-After | Seconds to wait (on 429 only). |
Limits are per credential, not per organization: one integration going wide does not throttle your others, and a misbehaving one can be rotated on its own.
| Plan | Requests / minute | Requests / month |
|---|---|---|
| Hobby | 60 | 10,000 |
| Pro | 300 | 200,000 |
| Scale | 1,200 | 2,000,000 |
| Enterprise | unmetered | unmetered |
A refused request still counts against both windows — otherwise flooding would be free.
Batch endpoints (where available, e.g. APEX estimate batch) are the right tool for high-volume workloads — prefer one batched call over many single calls.
Idempotency
Send Idempotency-Key: <your-unique-string> on any POST or PATCH. If the request is
retried within 24 hours, the original response is replayed and the work does not run
again. Replays carry Idempotent-Replayed: true.
One key per operation, not per retry loop. The key is bound to the request that used it
— method, path and body — and reusing it for a different request is a 409
(idempotency_key_reuse) rather than a replay. That refusal is the point: without it, a
client whose key generator resets would silently receive an earlier request's response and
conclude that work happened which never did.
Bodies that differ only in key order or whitespace are the same request, so a client whose HTTP library re-serialises on retry is safe.
The claim, the work and the stored response commit together. There is no window in which the write landed and the record did not — which is the only way the guarantee is worth relying on.
Scopes
Where an API supports scopes, access is split into read (queries and lookups) and
write (actions that change state). Request the narrowest scope your integration needs.
The fOS Agent API gates write tools behind the write scope and behind an explicit
confirmation step for money-moving actions.
Concepts & Glossary
The shared vocabulary of TOVR — shipments, loads, freight chains, deadhead, cabotage, hours-of-service, price percentiles, and the org/tenant model. The single home for cross-pillar terms.
Platform overview
TOVR is a carrier-facing AI execution layer on top of Europe's freight exchanges. It runs one loop — ingest, match, book, extend, bill — and monetizes only on executed freight, with compliance checked before economics.