Amount precision
- An amount with more fractional digits than its asset has is refused before any quote or funding check, with
400 AMOUNT_PRECISION_EXCEEDED;detailsnames thefield, thedecimalssent and theallowedprecision (Conventions).
Firm only, and repeated signed reads
- The search filter
filters.executableOnlyis renamedfilters.firmOnly, and it now keeps only firm quotes (firm: true): executable estimates held tominOut, such as order-book and AMM quotes, are left out too, as are indicative quotes. A request still sendingfilters.executableOnlyis refused with400 INVALID_REQUEST(Firm only). - A signed read (
GETorHEAD) sent again unchanged inside the 300-second window is accepted, so polling needs no nonce. A signed write is still accepted once (Sign requests). - An API key placing an order without an
Idempotency-Keyheader is refused with400 IDEMPOTENCY_KEY_REQUIRED(Idempotency). - The source refusal
BINDING_NOT_ON_CHAINis renamedWALLET_NOT_ON_CHAIN, with the same meaning (Wallets and accounts). - Every path that names a workspace calls its parameter
workspaceId, for exampleGET /api/v1/workspaces/{workspaceId}/venue-enablements. The URLs themselves are unchanged. - A new source reason,
venueEnablementRechecking: the venue’s readiness for your workspace is being re-checked, so it wasn’t asked. Request quotes again shortly (Error codes).
What an order did, one trade history, and exchange account balances
- Every order, Basic and Advanced, carries what it actually did:
fills,quotedBuyAmountandsettledBuyAmount, the fees actually charged infees(each withincludedInPrice), the kinds whose amount was not recorded inunrecordedFees, andsettlesBy(What the order did). Show an unrecorded fee as “not recorded”, never as 0. - The search filter
filters.commitmentis replaced byfilters.executableOnly, a boolean: sendtrueto leave indicative quotes out (Firm only). A request still sendingfilters.commitmentis refused with400 INVALID_REQUEST,details.fieldfilters.commitment. GET /api/v1/pricesrows no longer carrycommitment: they carryfirmandlastLook, both alwaysfalse(Prices before you quote).GET /api/v1/workspaces/{workspaceId}/tradeslists the workspace’s orders and exchange-account trades in one history, with cursor paging and CSV export. The key needs Read trades (Trade history).GET /api/v1/workspaces/{workspaceId}/trades/{tradeId}reads one trade. A failed or refunded order’ssettlement.failureReasonsays why (Read one trade). A trade read with an API key no longer carries the compliance records attached to it: read those in the app.- An API key with Read treasury reads an exchange account’s balances, deposit instructions and deposit history at
…/venue-accounts/{venueAccountId}/balances,/deposit-instructionsand/deposit-history; these used to answer403 JWT_REQUIRED. A read whose authority is withdrawn while it runs answers403 VENUE_READ_AUTHORITY_REFUSED(Exchange account balances). GET /api/v1/workspaces/{workspaceId}/venue-enablementslists your exchange accounts with the same key, with cursor paging: each item’saccountIdis the{venueAccountId}of those reads (List your exchange accounts). The deposit history is one bounded window,lookbackDays1 to 30, with no cursor; more than 1,000 records answersDEPOSIT_HISTORY_WINDOW_TOO_LARGE.
Service-account tokens, key rules and response headers
- A service account’s API key can be exchanged for a bearer token of at most 15 minutes, with the OAuth 2.0 client-credentials grant at
POST /api/v1/oauth/token.GET /api/v1/mewith the token answers for the key it was exchanged from:authoauthClientCredentials,keyId,workspaceId, its livescopesandorganisationWallets(Service accounts and tokens). - A key that requires request signing or holds venue trading is refused by the exchange with
400 unauthorized_client,error_uriending#SIGNED_KEY_NOT_EXCHANGEABLE: sign each request with it instead. - Turning signing off on a key that can trade venues answers
409 VENUE_TRADE_KEY_SIGNING_REQUIRED; the key is unchanged. - A service account’s organisation-wallet trades stop with
409 SERVICE_ACCOUNT_SPONSOR_NOT_CURRENTwhen its sponsor is no longer a current member (Its sponsor). - Every answer to a key with a per-minute limit carries
X-RateLimit-Limit,X-RateLimit-RemainingandX-RateLimit-Reset, refusals included. A deprecated operation’s answers will carryDeprecation,SunsetandLink; none is deprecated (Conventions). - Reading a quote set with
GET /api/v1/quotes/requests/{quoteRequestId}moments after it was returned answers503 QUOTE_REQUEST_NOT_READYinstead of404: read it again after about a second. - A venue-trading key that is revoked, expired or no longer current for its workspace is refused with
VENUE_TRADE_KEY_NOT_CURRENT, and nothing is placed (Error codes). - Every quote carries
firm(the venue is bound to the price untilexpiresAt) andlastLook, alwaysfalse. Read firmness fromfirmandexpiresAt:commitmentsaid the same (How search works). - Quote cards no longer carry
commitment, inPOST /quotesand requotes alike: readfirmandexpiresAt. - Past a quote’s
retainUntil, a requote or an order answers410 REQUOTE_WINDOW_CLOSEDwithnext.kind: "search": search again.410 QUOTE_EXPIREDnow means only that the quote lapsed and can still be requoted (Requotes and expiry).
A key reads its own organisation wallets; fuller filtered order pages
GET /api/v1/mewith a workspace key answersorganisationWallets:venueTradeScope,venueTradeEnabled,requestSigningRequired, thegrantswith each wallet’sfinancialAccountId, andblockedBy, the code an organisation-wallet order would be refused with right now (Check what a key can trade from).GET /api/v1/orderswith astatusfilter fills its page before answering, up to a scan limit. A short page with anextCursorsaysscanLimitReached: true; a filter that matches nothing answers one page with nonextCursor.
The last pay and receive words become sell and buy
What to change:
- Match the source reason
buyAmountUnsupportedwhere you matchedreceiveExactUnsupported, on quote sources, quote cards, price rows andnetReceived. - Match
sellChainInvalidandbuyChainInvalidwhere you matchedpayChainInvalidandreceiveChainInvalid. - Read
sizeLimits.fixedAmount(sellAmountorbuyAmount, the request field the limits bound) where you readsizeLimits.amountSide(payExactorreceiveExact).
Refused orders, and on-chain orders that answer before signing
What to change:
- Handle the order
statusrefused, which is final (terminal: true). A pre-trade check refused the order before anything was sent, andoperation.complianceRefusalnames why. Such an order used to readpending. It cannot be confirmed, continued or cancelled: once the cause is cleared, request a new quote and place a new order.GET /api/v1/orders/by-quote/{quoteId}still answersorderedfor its quote. - A Basic order on an on-chain venue now answers
workingas soon as it is recorded, before its wallet signs. A wallet that then refuses to sign ends the orderunfilled, withoperation.dispatch.notSentCause, instead of answering409. Follow the order or its webhook toterminal. - Read
outcomeUnknownonly as a lost outcome. An on-chain trade waiting for its transaction to confirm readsworking, notoutcomeUnknown.
Advanced order detail in sell and buy
An Advanced order’s detail, the
order of a kind advancedTrade answer, states its amounts as sell and buy, in decimals.What to change:- Read
order.route.sellandorder.route.buy, where you read the route’s pay and receive sides. Each carries its asset, chain anddecimalAmount. When TetraFi cannot work out the decimals, the side carriesunresolved(assetNotInRegistryoramountUnavailable) and nodecimalAmount: decimals are never guessed. - Stop reading
order.inputAmountsandorder.outputAmounts. Outputs that pay a fee are listed inorder.feeOutputs, in decimals. - Read each of
order.legs[]assell,buy(the leg’s estimate) andmeasuredBuy, withmeasuredBuySource, where you readinputAsset,inputAmount,outputAsset,outputAmountandmeasuredOutputAmount. - Read what was delivered from
order.deliveredBuy, withdeliveredBuyProvenance.
Prices in sell and buy
GET /api/v1/prices, GET /api/v1/prices/stream and every referenceMid use sell and buy too. The old names are gone, with no aliases.What to change:- Query prices with
sell,buyand exactly one ofsellAmount,buyAmountorsizes, where you sentpay,receive,amountandside. Pin chains withsell.chainandbuy.chain, and filter variants withvariants.sellandvariants.buy. An old parameter is refused with400 INVALID_REQUESTnaming it. - Read the price response’s
sell,buyandsellBalances, each row’ssellVariantandbuyVariant, and each size’ssellandbuy, where you read the pay and receive names. - Read
referenceMidassellUsd,buyUsdandbuyPerSell, on prices and on quote sets, where you readpayUsd,receiveUsdandreceivePerPay.
Company details before trading
A company, broker or liquidity provider workspace trades once its company details are on file: legal name, country of incorporation, registration number and registered address. TetraFi’s business verification and members’ identity verification no longer hold such a workspace back.What to change:
- Expect
403 CAPABILITY_BLOCKEDon quotes, orders and new counterparty connections while company details are missing. Itsdetails.capabilityBlockersholdsBASE_PROFILE_INCOMPLETE, with the missing fields incurrentlyDue. Add them in the TetraFi app under Company profile. - Retry
503 CAPABILITY_UNAVAILABLEwith the blockerBASE_PROFILE_UNREADABLE: the details could not be read just now. KYC_NOT_VERIFIEDnow blocks only a direct user’s own workspace.
Venue account fields renamed
What to change:
- On a venue account order (
kindbasicTrade), readoperation.labelwhere you readoperation.accountLabel. - On a venue connection, read
labelandvenueEnablementIdwhere you readaccountLabelandaccountBindingId.
Sell and buy
A quote request names what you sell and what you buy, and fixes exactly one amount. The old
pay, receive, amount and side fields are gone, with no aliases.What to change:- Send
sellwhere you sentpay, andbuywhere you sentreceive. Each is still{asset, chain?}. - Send
sellAmountwhere you sentamountwithside: "payExact", andbuyAmountwhere you sentamountwithside: "receiveExact". Both are decimal strings in Asset units. - Send exactly one of
sellAmountandbuyAmount. A body with both, or with neither, is refused with400 INVALID_REQUESTnaming them. - Send
filters.variantsas{"sell": …, "buy": …}, where it was{"pay": …, "receive": …}. - Read each quote’s
sellandbuy(asset,chain,variant,amount) where you readpayandreceive.buy.amountis what lands atdeliverTo. - Stop sending
pay,receive,amountandsideonPOST /api/v1/quotes: they are unknown fields now, refused with400 INVALID_REQUEST.
List and cancel orders
- List your orders with
GET /api/v1/orders: newest first, in the order envelope, filtered bystatus,kind,sellAsset,buyAsset,fromandto, and paged withcursor. A page can hold fewer thanlimitorders, even none: follownextCursoruntil it isnull. - Cancel an order before it is sent to a venue, or a pay-in waiting for funds, with
POST /api/v1/orders/{id}/canceland anIdempotency-Key. Afterwards it answers409 ORDER_NOT_CANCELLABLEwithdetails.status. TetraFi never cancels at the venue or refunds. GET /api/v1/workspaces/{workspaceId}/tradesis no longer part of the integrator API: use the order list.
orderId on every order answer; minReceiveRequired on quotes
What to change:
- Read an order’s id from the top-level
orderIdof the order answer, the status read and the recovery answer. It is the idGET /api/v1/orders/{id}takes, for Basic and Advanced orders alike. - Send
minReceivewhen the quote’sminReceiveRequiredistrue, instead of working it out fromcommitment. A missing one is refused with400 MIN_RECEIVE_REQUIRED, withdetails.fieldminReceive.
One order read, one 401 code, and renamed refusals
An order has one read, and every refusal has one code with one status.What to change:
- Read every order at the
statusUrlits answer gives:GET /api/v1/orders/{id}. It answers the same envelope asPOST /api/v1/orders, for Basic and Advanced orders alike:kind,status,terminal,statusUrl,nextwhen there is something to do, and the trade’s own detail underoperationororder. The recovery answer ofGET /api/v1/orders/by-quote/{quoteId}also carriesstatusUrl. - Read
statusfrom one camelCase list:pending,working,filled,settled,unfilled,cancelled,failed,refunded,reviewRequiredandoutcomeUnknown. Stop polling onterminal. While an order is not final,nextsayswait(read again afterretryAfter) orcontact(contact support withreference). - Stop building the Basic trade’s address from your workspace, connection and operation ids.
- Match
401 AUTH_REQUIREDfor every authentication failure, and read the cause indetails.reason, for exampleMISSING_CREDENTIALS,INVALID_API_KEYorSIGNATURE_INVALID. - Match the renamed codes:
QUOTE_PRICE_MOVED(wasPRICE_MOVED_BEYOND_PROTECTION),VENUE_FIRM_QUOTE_LIMITED(wasFIRM_QUOTE_LIMITED),QUOTE_ALREADY_ORDERED,QUOTE_ID_INVALIDfor a malformed quote id, andWALLET_SIGNATURE_REQUIREDfor a missing wallet signature on an order. - Read
docUrlon any refusal: it links the code’s entry in the error-code reference. - Deliver trade output to an address or one of your own accounts: no trade can deliver to a payee.
An API key implies its workspace
An API key is bound to the workspace it was created in, and every request it sends now works in that workspace without
X-Workspace-Id.What to change:- Stop sending
X-Workspace-Idwith an API key, or keep sending it: when present it must be the key’s own workspace, or it is refused with403 WORKSPACE_SCOPE_MISMATCH. - A signed-in member’s session still sends
X-Workspace-Id. Without it, the request is refused with400 MISSING_WORKSPACE. - Read a placed order’s status at the
statusUrlitsPOST /api/v1/ordersanswer returns, instead of building the address yourself. - Filter
GET /api/v1/workspaces/{workspaceId}/tradeswith its camelCase parameter names only, for examplenextActor,dateFrom,amountMinandsortBy. The snake_case names are refused with400.
Trading source and funding replace bind and binding
A quote request names what a trade executes with in
source, and where its output goes in a top-level deliverTo. Each quote states its trading source and its funding. The old bind, binding and bindingOptions fields are gone, with no aliases.What to change:-
Read
sourceOptionswhere you readbindingOptions. It is listed on the quote set and on each quote. Candidates carryfinancialAccountId,kind,displayNameandisDefaultinstead ofidandlabel. -
Send
source.tradingWallets(CAIP-2 chain id tofinancialAccountId) where you sentbind.wallets,source.venueAccountIdwhere you sentbind.account, andsource.fundingAccountIdwhere you sentbind.bankAccount. -
Send
deliverToat the top level of the request or requote, not underbind. Its kinds arefinancialAccount,payeeandaddress. -
Read each quote’s
tradingSource,fundingModel,fundingAccount,settlementanddeliverTowhere you readbinding. A quote with no usable source carriessourceRefusal. -
Read a quote’s
locationas{ "kind": … }only:singleChain,crossChain,offChainormixed. -
On a
walletRefusedsource, readrecovery.defaultWalletsand send it assource.tradingWallets, where you sentrecovery.defaultBindingasbind. -
Match
SOURCE_NOT_OWNED(403) where you matchedBINDING_NOT_OWNED. It now refuses adeliverToorsource.fundingAccountIdnaming a wallet or account you cannot use. -
A trading wallet in
source.tradingWalletsthat a venue account has not linked is no longer refused. That venue readsunavailablewith the reasontradingSourceNotLinked, and the others still quote.
BINDING_NOT_ON_CHAIN. Wallets and accounts covers the new fields.Consistent refusals, order status and source reasons
Every refusal of a quote, price, order, preflight, asset or order-status request, at every status, is now one JSON object:
What to change:
- Read
requestIdwhere you readtraceIdbefore. - Read waits from
details.retryAfter, an ISO 8601 duration such asPT2S. TheRetry-Afterheader still carries the same wait in seconds. - Fields a refusal used to send at its top level now sit under
details. - A request body that is too large now answers JSON,
413 REQUEST_BODY_TOO_LARGE, instead of plain text. - A missing or empty
X-Workspace-Idanswers400 MISSING_WORKSPACEeverywhere. A malformed one answers400 INVALID_WORKSPACE_ID. - Send credentials on
GET /api/v1/orders/{orderId}. Without them, you get401 MISSING_CREDENTIALSbefore any order lookup. This one is rolling out: until it reaches a deployment, a read without credentials answers404 ORDER_NOT_FOUND. - Read order
statusas a string, including"failed". Failure details are infailure.phase,failure.codeandfailure.message, not insidestatus. - Source reasons on quotes and prices are camelCase tokens, for example
pairNotServedinstead ofpair_unsupportedandapiKeyOrganisationWalletScopeRequiredinstead ofAPI_KEY_ORGANISATION_WALLET_SCOPE_REQUIRED. - Match
receiveExactUnsupportedwhen a source cannot quote a fixed receive amount. Quotes and prices now use the same token. Read it fromreason, or fromdetailon afilteredOutrow.
Idempotency-Key rules for POST /api/v1/orders are now documented in full: see Idempotency.New developer docs
These docs now describe one API for search, quotes, requotes and orders, with a runnable sample on every page. They start from the Quickstart.The separate RFQ API, Router API, Price API and Trade History API pages are gone. Their old addresses redirect to the pages that replace them.
Venue identity on quotes
Each quote leg and each entry in
sources names the venue it comes from with venueId and venueName. Group sources and quotes by venueId for display. The existing id still identifies the route that executes, and it is the id filters.venues takes (Venue types and venues). Pools and solvers that are not a named venue leave both fields out.Quotes by asset pair, requotes, and orders by quote id
A quote request names the asset you pay and the asset you receive, not chains or wallets:
POST /api/v1/quotes with pay, receive, amount and side. Every quote states where it executes and how firm it is.- Changing a quote’s wallet, account or route, or turning an indicative price executable, is a requote:
POST /api/v1/quoteswithrequoteset to the quote id. See Requotes and expiry. POST /api/v1/orderstakes the quote id. The server holds the quote’s terms, so you never send them back. See Orders.GET /api/v1/assetslists what your workspace can trade, andGET /api/v1/pricesreturns indicative prices without asking any venue for a quote.