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

APIHostAuthDocumented in
TOVR REST APItovr.euOAuth 2.0 client-credentials → bearer JWTTOVR › Reference
APEX APIapex.tovr.euX-API-Key headerAPEX › Reference
fOS Agent APImcp.tovr.euAPI 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.

EnvironmentTOVR RESTAPEXfOS Agent
Productionhttps://tovr.euhttps://apex.tovr.euhttps://mcp.tovr.eu
Staginghttps://staging.tovr.euhttps://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 form tovr_… 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 like apex_….

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:

StatusMeaning
400Malformed request (bad JSON, missing required field).
401Missing or invalid credentials.
402Monthly quota exhausted — see below. Waiting does not clear it.
403Authenticated but not permitted (bad key, insufficient scope).
404Resource not found.
409Conflict — the natural key is taken. See code for which case.
422Understood but unprocessable (unknown parameter, invalid cursor, illegal status transition).
429Rate limit exceeded — see below. Retry after Retry-After.
500internal_error — a fault on our side. Retrying will not help; report the request_id.
503service_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.

WindowOn exceedingClears by
Rate limitper minute429 · rate_limitedwaiting — see Retry-After
Quotaper calendar month402 · quota_exceededupgrading 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.

HeaderMeaning
X-RateLimit-LimitRequests allowed per minute.
X-RateLimit-RemainingRequests remaining in the current minute.
X-RateLimit-ResetWhen the minute window resets (epoch seconds).
RateLimit-Limit / -Remaining / -ResetThe same three, in the IETF draft's unprefixed spelling. RateLimit-Reset is seconds remaining, not an epoch.
Retry-AfterSeconds 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.

PlanRequests / minuteRequests / month
Hobby6010,000
Pro300200,000
Scale1,2002,000,000
Enterpriseunmeteredunmetered

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.

On this page