Skip to main content
When TetraFi refuses a request, the answer is always the same JSON object, whatever the status and whichever call you made. One parser reads every refusal on this page. This includes 400, 401, 403, 404, 413 and 415 responses. A body-size refusal is JSON too, never a separate text format. Use the error-code reference for the complete declared list of codes and source reasons; the tables below group common refusals by what you are doing. The examples come from one script, samples/http/errors.sh. It provokes three refusals and places no order.
The API may change until launch. Every change is listed in the Changelog.

Read a refusal

details.value names the value that was refused, such as an unknown asset id. details.reason carries a token that says why, for example on REQUOTE_REFUSED. details.retryAfter says how long to wait. Here the script asks for a quote on an asset TetraFi does not list, prints the refusal and acts on its code:
Output

What next tells you

A refusal that you can recover from in a known way carries next. Its kind is the same set of steps a quote’s own next uses: More kinds can be added. Treat a kind you do not recognise as a step you cannot take automatically: show message and stop.

Retries

Whether to retry depends on the code, and for a code you do not know, on the status class.

Waits

A refusal that names a wait carries it twice:
  • the Retry-After header, in whole seconds;
  • details.retryAfter, an ISO 8601 duration such as PT3S. It can carry a fraction, such as PT1.5S.
Wait at least that long. Sending sooner is refused again.

The quote rate limit

You can send one live quote request every 3 seconds per API key. A requote counts as a quote request. A request answered entirely from cached prices does not count. A faster request answers 429 QUOTE_RATE_LIMITED. The script sends two quote requests at once, so one of them is refused. It waits details.retryAfter, then sends the refused one again:
Output
The two requests carry different amounts on purpose. A signed write is accepted once, so two identical quote requests signed in the same second would read as a replay.

Orders

POST /api/v1/orders is safe to retry only with the same Idempotency-Key and the same body: the retry returns the first answer and never places a second order. When you do not know whether an order was placed (a timeout, a dropped connection, a 5xx), never send it again with a new key. Retry with the same key, or read the order by its quote first. Idempotency has the full rules.

Codes you do not know

New codes can be added at any time. Act on the codes you know, and treat any other by its status class, as the compatibility policy says: retry a 429 or 503 after Retry-After, fix the request on any other 4xx, and retry a 5xx with backoff. Show message and log requestId either way.

The wrong workspace

An API key is bound to its workspace, so it may leave X-Workspace-Id out. When it sends one, it must be the key’s own: any other workspace answers 403 WORKSPACE_SCOPE_MISMATCH, and a value that is not a workspace id answers 400 INVALID_WORKSPACE_ID. A signed-in member’s session must always send the header, or it is refused with 400 MISSING_WORKSPACE. A few paths name a workspace, such as GET /api/v1/workspaces/{workspaceId}/webhooks. A path workspace that is not yours answers 404 WORKSPACE_NOT_FOUND, exactly as one that does not exist.

A workspace that cannot trade yet

A company, broker or liquidity provider workspace can request quotes, place orders and connect to counterparties once its company details are on file: legal name, country of incorporation, registration number and registered address. Until then, those requests answer 403 CAPABILITY_BLOCKED. Each entry in details.capabilityBlockers names one thing to fix:
Refusal
Both are done in the app, not through the API. When TetraFi cannot read the company details just now, the request answers 503 CAPABILITY_UNAVAILABLE with the blocker BASE_PROFILE_UNREADABLE: send it again shortly. Treat a blocker code you do not recognise by its message and unlockStep. To check before you quote, read GET /api/v1/workspaces/{workspaceId}/capabilities: each capability, such as request_quotes or submit_order, says whether it is allowed, with the same blockers when it is not.

Refusals by area

Each table lists the codes you can get from that part of the API with an API key. Unless a row says otherwise, nothing was placed or sent.

Authentication and workspace

These can answer any request. Authentication explains keys, permissions and signing. Every authentication failure is the one code 401 AUTH_REQUIRED. Its details.reason names the cause: More reasons may be added: treat one you do not recognise as “fix the credentials”.

Quote request

POST /api/v1/quotes for a new search, and GET /api/v1/quotes/requests/{quoteRequestId} to read a quote set again. How search works covers the request. A venue that cannot quote is not a refusal: the request succeeds and lists that venue in sources with its outcome (below).

Requote

POST /api/v1/quotes with requote. Requotes and expiry covers the request.

Orders

POST /api/v1/orders and the order reads. Orders covers the request.

Idempotency

The Idempotency-Key on POST /api/v1/orders, and GET /api/v1/orders/by-quote/{quoteId}. Idempotency has the full rules.

Prices and assets

GET /api/v1/prices, GET /api/v1/prices/stream and GET /api/v1/assets. Prices covers the request.

Source outcomes

A quote request that succeeds lists every venue it considered in sources, each with a status and, when it did not quote, a reason. These are outcomes, not refusals: the request worked, and the venue’s line says why it gave no quote. Some reasons you may see: Both lists are open. New statuses and reasons appear without notice. Reasons are camelCase tokens. Treat a status or reason you do not know as a source you cannot use: show its detail if you show anything, and carry on with the quotes you have.