Skip to main content
This recipe trades 5 USDC for USDT on one exchange account your workspace has connected, with an API key. TetraFi places the trade on that account, with that account’s balance, so no wallet signs anything. Every step comes from one script, samples/http/recipe-exchange-order.sh.
The API may change until launch. Every change is listed in the Changelog.

Before you begin

You need bash, curl, jq and openssl, and:
  • A connected exchange account. Someone who manages your workspace’s venues connects it in the app (Connect a venue account), and it must hold the asset you sell.
  • An API key that can trade on venue accounts. Besides Read quotes, Submit orders and Read orders, the key needs the venue trading scope, which is granted only when the key is created, and venue trading switched on for it by a workspace owner or an admin who manages credentials (Trading on your venue accounts). Without it, the venue is listed in sources as restricted with the reason venueTradeScopeRequired, and you get no quote from it.
  • The key’s signing secret. A key with venue trading signs every request. The script’s tetrafi helper signs each one when TETRAFI_SIGNING_SECRET is set (Sign requests).
Export your environment as in the Quickstart, plus your workspace id and, if you know it, the venue id of your account:
The script needs the workspace id for one step only: after it recovers the order by its quote, the trade’s status address contains the workspace id.
1

Find your account's venue

Quotes from your exchange account carry its venue id, such as binance-orderbook. If you do not set TETRAFI_VENUE, the script finds it with one search that keeps only off-chain quotes (filters.locations: ["offChain"]): only your connected venue accounts answer that. It takes the venue of the best Basic quote.
Output (shortened)
notConnected means your workspace has no account at that venue.
2

Search that venue only

filters.venues.include with one venue id asks that venue alone. Every other venue is listed in sources as filteredOut and is not asked. The quote trades on your account at that venue: its tradingSource has kind venueAccount, and fundingModel says whether the account must already hold the funds (prefunded) or takes them in (payIn).
Output (shortened)
You can send one live quote request every 3 seconds. The script waits details.retryAfter once when it gets 429 QUOTE_RATE_LIMITED.
3

Check the account holds enough

Each quote says whether the account holds enough to trade it, in readiness. To see everything the account holds, read its balances with the same key (Exchange account balances).
Output
To see the balances themselves, check in the app (below).
4

Place the order

POST /api/v1/orders with the quote id and an Idempotency-Key you generate and store before you send it. A Basic quote takes no signature.Some desks execute within your limit: when a quote reads minReceiveRequired: true, its order must carry minReceive, the least you accept (Orders). The script then sets minReceive to the quoted amount less 50 basis points. When it reads false, like the order-book quote above, minReceive is optional, and the script sends only the quote id. A refused order names its code, such as 409 FUNDING_SHORTFALL, and nothing was traded (Orders).The script places the order only when you set TETRAFI_PLACE_ORDER=1.
The script prints the order response’s kind, quoteId and operation:
Output (shortened)
5

Recover the order by its quote

If the order response never reaches you, after a timeout or a dropped connection, do not order again with a new key: that is a new order. Read the order by its quote with GET /api/v1/orders/by-quote/{quoteId}. It never places anything (Idempotency).The script reads the order this way every time, and takes statusUrl from the answer.
Output (shortened)
6

Read the status

Read the trade at its statusUrl until terminal is true, every 10 seconds, or after next.retryAfter when next says wait. outcomeUnknown is not a failure: the trade may have filled, so keep reading (Order status). The venue’s own detail is under operation.
Output
When the venue reports them, dispatch.inputSpent and dispatch.outputReceived are what the trade took from and added to the account. A missing or null amount is unknown, never zero. bounds.sell.amount (the most it spends) and bounds.buy.amount (the least it delivers) are your accepted limits, not fill amounts. dispatch.venueOrderId, when present, is the venue’s native order id.Check settlementStatus for any later money movement. To confirm the balance independently, open the account under Treasury in the app.

Run the whole script

Progress goes to stderr and the summary to stdout:
Output
Without TETRAFI_PLACE_ORDER=1, the summary reads "ordered": false.

In the app

The account’s page under Treasury shows its balances, and the trade appears under Trades.

Next steps

Orders

Venue quotes that execute within your limit, and every refusal.

Idempotency

Retry an order safely and find out whether it was placed.