# TxFlow Docs — full text Links are relative to the host serving this file. ## Appendix: integration rules for coding agents Hard rules for generating TxFlow client code. Each links the page that states it. ### Endpoints - Testnet (use it for every example and test): `https://rpc.txflow-testnet.xyz` serves `POST /info`, `POST /exchange`, `wss://…/ws`, `wss://…/api-ws`, and the faucet `POST /request-fund` with `{"address":"0x…"}`. Mainnet: `https://rpc.txflow.com`. TxRelay (`/api/v1/bridge`) is on its own host. — [Networks](/api.md) - Reads need no auth. There are no API keys: writes are signed locally. — [Builder API Reference](/api.md) - Send `x-api-version: 2026-08-16` on every `/info` and `/exchange` request; every documented response assumes it. — [Conventions](/api.md) ### Data - Prices and sizes are strings. They are hashed exactly as written, so `"50000.0"` and `"50000"` sign differently. — [Signing](/api/signing.md) - An instrument id is the `index` from `perpMeta`, or an Outcome market's `instrument_id` from `outcomeMeta`. In requests, `coin` and the `/info` `asset` filter take it as a numeric **string** (`"coin":"1"`) — every endpoint accepts that, and node reads such as `l2Book` accept nothing else; `instrumentId` / `instrumentIds` and the `/exchange` fields `a` / `asset` take it as an **integer** (`"a":1`). A string `a` changes the MessagePack bytes and breaks the signature. In responses `coin` is a display name whose form varies (`BTC-USDC`, `BTC`, `OUTCOME-4991183`): match rows on `asset` / `instrumentId`, compared as numbers (`asset` is `"1"` in some responses, `1` in others). — [Conventions](/api.md) - `accountType` is `perp` or `outcome`. - Private subscriptions and `/info` reads take the **account** address as `user`, never the agent's. - Outcome metadata responses are snake_case; everything else is camelCase. - Order prices must be a multiple of `priceTick` and sizes have at most `szDecimals` decimals (both from `perpMeta`). A cloid (`c`) is any string up to 64 bytes; do not start it with `0x` (reserved, rejected with `Invalid cloid format`). — [order](/api/exchange/order.md) - The Outcome book and balance come from the shared `l2Book` and `clearinghouseState` (`accountType: "outcome"`). — [Info API](/api/info.md) ### Signing - Two flows. Account key, direct EIP-712: `approveAgent`, `tokenSend`, `withdraw3`, `approveBuilderFee`, `revokeBuilderFee`. Agent key, MessagePack + `Agent` type: `order`, `cancel`, `cancelByCloid`, `modify2`, `outcomeOperate`, `update*`, `claimBuilderRewards`. — [Signing](/api/signing.md) - EIP-712 domain: `name` `TxFlow-Testnet` / `TxFlow-Mainnet`, `version` `"1"`, `chainId` `2511` / `869`, `verifyingContract` zero address. - Agent hash: MessagePack the action **without** `type` (exception: `claimBuilderRewards` keeps it) as a map with named keys, append the nonce as 8 bytes big-endian, then the vault flag (`0x00`, or `0x01` + 20 address bytes; `vaultTransfer` always uses `0x00`); `connectionId = keccak256(…)`. - Keep the node's key order — the key order of each action's request example, which is also the order its `.md` page lists the action fields in; for an order body `a, b, p, s, r, t, c, m, expiresAt`. Never sort keys. - Omit null optional fields instead of sending `null` (a plain `modify2` has no `tpsl` key). Extra keys break the signature. - The agent key must be authorized first with `approveAgent`, signed by the account key. The account must be funded first (testnet: the faucet), or it fails with `Please deposit first`. An agent address can be bound to only one account at a time (`Agent address is already used`); revoking frees it. — [approveAgent](/api/exchange/approveAgent.md) - Signatures must be low-S. `v` is `27`/`28` in JSON. Test vectors are on the Signing page. ### Nonces - Millisecond timestamp, unique per signer, within 2 days before / 1 day after the block time. Each address keeps its 100 highest nonces. Use a fresh `Date.now()` per request. — [Signing § Nonces](/api/signing.md) - For direct EIP-712 actions the nonce is also inside `action`; the envelope must repeat the same value. ### Responses - `/info` returns `{"success":true,"code":0,"data":…}`; on error `success` is `false` with `code` and `message`. Each reference page documents the `data` payload. - `/exchange` returns `{"status":"ok","code":0,"data":{"type":…,"data":{"statuses":["success"]}}}` — one status for the whole action. `success` means accepted by consensus, not executed; add `"confirm":"executed"` to the request body to wait for execution. The response never reports resting/filled/cancelled or an oid: read those from `orderUpdates` or `frontendOpenOrders`. `data.type` may differ from the request type (`order` → `PlaceOrder`). — [Exchange API § Response](/api/exchange.md) - A rejection is `{"status":"err","code":,"message":"…"}`; some gateway rejections (rate limits, disabled interfaces) carry the text in `response` instead. Read whichever is present. - An Outcome order can instead be acknowledged asynchronously as `{"status":"ok","traceId":"…"}` (no `data`), which means queued. With `"confirm":"executed"`, a timeout returns HTTP 202 `{"status":"pending","txHash":"…"}`. - If a queued Outcome order fails, WebSocket `outcomeOrderUpdate` on `/api-ws` pushes the reason under its `traceId`; successes are not pushed — follow fills on `outcomeOrderUpdates` (`/ws`). Outcome orders accept `Gtc`, `Gtd`, `Alo` only. — [order](/api/exchange/order.md) ### WebSocket - On `/ws`, every subscribe/unsubscribe frame needs the channel's subscribe type at the **top level** as well as inside `subscription`: `{"method":"subscribe","type":"orderUpdates","subscription":{"type":"orderUpdates","user":"0x…"}}`. Without it the node silently ignores the frame. `/api-ws` takes `subscription` alone. — [WebSocket API](/api/websocket.md) - Send `{"method":"ping"}` every 10 s on both `/ws` and `/api-ws`; a connection that sends nothing for ~16 s is closed (1006). Pongs are `{"method":"PONG"}` on `/ws` and `{"channel":"pong"}` on `/api-ws` — drop them before dispatching by `channel`. — [WebSocket API](/api/websocket.md) - On reconnect, re-subscribe every channel; subscriptions do not survive the socket. ### Limits - Per IP: `/info` 50 req/s, `/exchange` 10 req/s, 10 live connections each on `/ws` and `/api-ws`. Over the limit: HTTP 429 with `Retry-After`; a rate-limited action did not execute, so re-sign it with a fresh nonce. — [Rate limits](/api/rate-limits.md) - Prefer WebSocket streams to polling, batch orders (≤ 50) and cancels (≤ 200), and cache `perpMeta`. --- # What is TxFlow TxFlow is a **Layer 1 blockchain built for all finance** — its vision is to bring every financial scenario on-chain. Today it powers a decentralized derivatives exchange: spot, perpetual, and outcome (prediction market) trading, with margin, liquidation, funding rates, and risk management built into the protocol itself. **Its defining property is transparency.** A centralized exchange asks you to trust its private servers and internal books. TxFlow runs *every* part of trading on-chain as a deterministic state machine: each order, cancellation, fill, and liquidation is a public transaction, ordered by consensus and replayed identically by every node. Anyone can replay the chain and check exactly how the exchange behaved — **nothing is taken on trust.** That transparency rests on three guarantees: - **Verifiable** — any node can independently replay the block stream and confirm every balance, position, and fill. Results never depend on trusting a single operator. - **Atomic & consistent** — matching, clearing, and margin updates complete together inside one state machine, so there is never partial or out-of-sync state. - **Censorship-resistant** — ordering is handled by Tx-BFT, a DAG-based Byzantine Fault Tolerant consensus, so no single node can stall trading or tamper with results. - [Start Trading](/getting-started/start-trading.md) — Connect, deposit, and place your first order. - [Start Building](/builders.md) — Earn a fee on the order flow you route to TxFlow. --- # Key Concepts This page defines the vocabulary used throughout the rest of the documentation. ## Accounts Accounts are indexed by an **Ethereum-compatible address**. There is no registration flow — an account is created implicitly on first deposit. One address holds three independent sub-accounts. What distinguishes them is the funding model, not just what they hold: | Sub-account | Holds | Funding model | |---|---|---| | **Spot** | token balances | **Fully funded** — no leverage and no positions. An order can only lock balance you already hold, and a fill swaps one token balance for another. | | **Perp** | margin + perpetual positions | **Margin** — positions are collateralized by this account's assets, in [cross or isolated](#margin) mode, with tiered maintenance margin, mark-to-market PnL, funding, and liquidation. | | **Outcome** | margin + prediction-market shares | **Margin**, on the same path as perps — share positions are opened against this account's margin and redeemed to the margin asset at 0 or 1 when the market resolves. | The sub-accounts are margined and settled separately: cross margin pools only the assets held in the perp account, so a spot balance does not collateralize a perp position. **Vaults** are a separate account type, following a leader–follower copy-trading model: the leader trades the vault, and followers hold vault equity as *shares*, with a lock-up period and cost-basis accounting. ## Instruments Spot, perpetual, and outcome markets are unified under a single **instrument** abstraction. They share one configuration schema, with type-specific extensions where needed. This is documented in full under [TIP — Improvement Proposal](/tip/overview.md). | Type | What trades | Settlement | |---|---|---| | **Spot** | base asset vs. quote asset | instant swap of balances on fill | | **Perps** | perpetual contract | mark-to-market PnL + funding | | **Outcome** | prediction-market shares (synthetic) | redeemed to margin asset (0 or 1) at resolution | ## Channels Spot, perp, and outcome markets are grouped into **channels** — isolated trading venues that share the same L1 but independently control their own markets, fees, and operators. Documented in full under [TxCore § Channels](/txcore/channels.md). ## Margin - **Cross margin** — all assets in the perp account jointly collateralize all cross positions. - **Isolated margin** — each position locks its own margin, isolating risk. Outcome markets are isolated only. - **Position mode** — One-Way or Hedge (a single instrument can hold both long and short simultaneously). - **Tiered margin** — the maintenance margin rate (MMR) and max leverage are tiered by position size: larger positions have a higher MMR and lower available leverage. Full formulas are in [Margin System](/txcore/margin-system.md). ## Prices - **Mark price** — used for position valuation, unrealized PnL, and liquidation; smooths short-term wicks. - **Oracle price** — an aggregated external reference price for the underlying. Prices are fed by validators through on-chain oracle transactions, visible to and identical across all nodes each block. See [Oracle](/txcore/oracle.md). ## Networks TxFlow signing uses network-separated [EIP-712](https://eips.ethereum.org/EIPS/eip-712) domains: | Network | Chain ID | Domain name | |---|---|---| | Mainnet | 869 | `TxFlow-Mainnet` | | Testnet | 2511 | `TxFlow-Testnet` | See [Signing](/api/signing.md) for the full domain specification. ## Endpoints at a glance | Endpoint | Purpose | |---|---| | `POST /exchange` | submit signed transactions (all write operations) | | `POST /info` | read-only queries (balances, positions, order book, market metadata) | | `WS /ws` | 23 subscription channels for market data, account updates, and on-chain data | See the [Info API](/api/info.md) and [WebSocket API](/api/websocket.md) for the read reference, [Signing](/api/signing.md) for how writes are authenticated, and the [Exchange API](/api/exchange.md) for the write actions themselves. --- # Start Trading This page takes you from zero to your first trade. TxFlow is **non-custodial** — you keep control of your funds, and trading never hands your keys to an operator. Four steps get you trading: *Diagram: Four steps to start trading: connect, deposit, enable trading, trade.* *With email or social login, step 3 is automatic — enabling trading happens for you. With your own wallet, you approve it once with a signature.* ## Where you can trade TxFlow is available on four platforms: - **Web** — [app.txflow.com](https://app.txflow.com/) in a desktop browser. - **Mobile web (H5)** — [app.txflow.com](https://app.txflow.com/) in your wallet's built-in browser. - **iOS** — [App Store](https://apps.apple.com/app/txflow/id6759785006). - **Android** — [Google Play](https://play.google.com/store/apps/details?id=com.txflow.android). Your account is your address, so balances, positions, and orders are the same everywhere. > **Info:** The agent key from step 3 is per device — you enable trading again the first time you sign in on a new platform. ## 1. Connect Open the app and sign in. Two ways: - **Email or social** (Google, Twitter) — a secure wallet is created for you automatically, so you don't need any prior crypto setup. - **Your own wallet** — connect MetaMask, Rabby, or any WalletConnect wallet. Either way, you end up with an account identified by an Ethereum-style address. > **Info:** After you connect, you'll be asked for an **access code**. Request one in our [Discord](https://discord.com/invite/txflow) to get started. ## 2. Deposit Your trading collateral is **USDC**. Fund your account by: - depositing USDC directly from **Arbitrum**, or - bridging USDC in from another chain. Deposits are credited automatically once confirmed (about a minute or two). See [Deposits & Withdrawals](/getting-started/deposits-and-withdrawals.md) for the full flow, supported chains, and the minimum amount. ## 3. Enable trading Before your first order, you authorize trading once. You sign a single message that registers an **agent key** — a trading key your browser holds on your behalf. - After this, orders are signed instantly by the agent key — **no wallet pop-up on every trade.** - The agent key can **only trade**. It can never withdraw or move your funds, so your main key stays in control of the money. If you signed in with email or social login, this happens automatically. Wallet users tap **Enable trading** and approve the signature. See [Signing](/api/signing.md) for how it works. ## 4. Place your first trade 1. Pick a market — a perpetual (e.g. `BTC-USDC`), a spot pair, or an [outcome market](/tip/outcome.md). 2. Choose your side — **buy/long** if you expect the price to rise, **sell/short** if you expect it to fall. 3. Set **leverage** and **size**. On perpetuals you trade with USDC as collateral — you don't buy the underlying asset; your position size is up to `leverage × collateral`. 4. Place the order. A limit order rests on the book until it fills; a market order fills immediately against the best prices. That's it — your position, balance, and orders update live. > **Tip:** **No gas to trade.** Placing and canceling orders on TxFlow costs no gas. You only pay normal network fees on the external chain when you move funds in or out. ## Next steps - [Deposits & Withdrawals](/getting-started/deposits-and-withdrawals.md) — get funds in and out. - [Trade](/trade.md) — how margin, liquidation, funding, orders, and fees work. - [Key Concepts](/introduction/key-concepts.md) — accounts, balances, and the market types. --- # Deposits & Withdrawals **USDC** is your main collateral — it backs your perpetual and outcome positions. You can also deposit and withdraw the **spot assets** you trade (such as BTC or ETH). This page covers how to move funds in and out. ## Deposits How you deposit depends on how you signed in and which chain your funds are on. | Your situation | How to deposit | |---|---| | USDC on **Arbitrum**, signed in with a **wallet** | The app sends USDC from your wallet to the bridge — approve the transaction. | | USDC on **Arbitrum**, signed in with **email/social** | Send USDC to the deposit address shown (scan the QR or copy it). | | USDC (or other assets) on **another chain** | Use the bridge: pick your source chain and send to the address shown. A bridge fee and estimated arrival time are displayed before you send. | A few things to know: - **Minimum deposit** is small (currently 5 USDC). - Deposits are credited to your account **automatically** once they have enough confirmations on the source chain — typically a minute or two. - **USDC** is credited to your **perp account** by default, ready to use as collateral. **Spot assets** (such as ETH or BTC) go directly to your **spot account**. > **Tip:** Always send the **correct asset on the correct chain** to the address shown in the app. Sending an unsupported token, or the right token on the wrong chain, can result in lost funds. ### Depositing spot assets Spot assets are deposited to your **TxFlow Spot deposit address**, which the deposit popup shows for each token. You can send from a centralized exchange or from an on-chain wallet. **Option A: Deposit from a CEX** 1. Click **Deposit** in the app. 2. Select the token, and copy the address shown in the deposit popup — this is your TxFlow Spot deposit address. 3. Log in to your CEX account. 4. Go to **Withdraw**, select your token, and choose the correct network (for example, BTC on the Bitcoin network). 5. Paste the address into the CEX withdrawal page. 6. Enter the amount and confirm. Your funds should arrive in your TxFlow spot account within a few minutes. **Option B: Deposit from an on-chain wallet** (MetaMask, Trust Wallet, and so on) 1. Open your wallet. 2. Go to **Send**, and select your token and network (for example, ETH on Ethereum mainnet). 3. Copy your TxFlow Spot deposit address. 4. Paste the address, enter the amount, and confirm the transaction. Your funds should arrive in your TxFlow spot account within a few minutes. > **Info:** Always verify that both the **network** and the **destination address** are correct before confirming any transfer. ## Withdrawals ### Withdrawing USDC from your perp account Withdrawing USDC sends it back to your own wallet. It's built so that no single party can move your money: 1. **Request it** in the app — choose how much to withdraw, and pick where it goes: - **Address** — send to an external wallet, and choose the **withdrawal chain**: Arbitrum One, Ethereum, Base, Polygon PoS, or Solana. - **TxFlow Account** — transfer to another TxFlow account. 2. **The network approves it** — a large majority of TxFlow's validators have to agree, so no one operator can release your funds on their own. 3. **A short safety wait** — there's a built-in waiting period before the money is sent out. It's a security buffer that gives the network time to catch and stop anything that looks wrong. 4. **Funds arrive** — after the wait, your funds land in the wallet and on the chain you chose. Good to know: - A flat **1 USDC** fee is charged per withdrawal, whichever chain you withdraw to (this covers the network cost of paying you out). The minimum withdrawal is **2 USDC**. - Transfers to another TxFlow account are free, apart from a one-time **1 USDC** fee the first time you send to a given account. - Because of the safety wait, withdrawals aren't instant. See [Deposit & Withdrawal Fees](/trade/deposit-and-withdrawal-fees.md) for the full fee breakdown. ### Withdrawing spot assets To move a spot asset from TxFlow to a CEX or an on-chain wallet: 1. Click **Withdraw** in the app. 2. Select your token. 3. Enter the destination address — your CEX deposit address or your on-chain wallet address. 4. Enter the withdrawal amount and confirm. Funds typically arrive within minutes, depending on network conditions. > **Info:** Always verify that both the **network** and the **destination address** are correct before confirming any transfer. ## Need help? If a deposit or withdrawal doesn't arrive as expected, or you're unsure about a transaction, reach out in our [Discord](https://discord.com/invite/txflow) — the team can help track it down. --- # Export your email wallet If you accidentally send an unsupported asset to your email wallet, or you want to export its private key so you can import it into another self-custody wallet and keep using it, follow the steps in this guide. > **Warning:** **Never share your private key or seed phrase with anyone.** Anyone who has your private key has full access to your account and your assets. TxFlow support will **never** ask you for it. This method only applies if you registered and logged in to TxFlow with an **email account**. If you use a self-custodial wallet such as MetaMask or Rabby, manage your private key in that wallet instead. ## Step-by-step guide 1. Log in to [home.privy.io](https://home.privy.io/). Use the same email address you log in to TxFlow with. 2. On the Home page, under **My apps**, select **TxFlow**. ![The Privy home page, with TxFlow highlighted under My apps](/images/export_email1.avif) 3. Click **Export keys** in the upper right corner. Before you click, check that the email address and the wallet address are the ones you expect. ![The TxFlow app page on Privy, with the Export keys button highlighted in the upper right](/images/export_email2.avif) 4. Click **Export** to continue. ![The Your wallets list, with the Export button highlighted next to the wallet address](/images/export_email3.avif) 5. Confirm your wallet address once more, then click **Copy key** or **Copy Phrase** to finish the export. ![The Export wallet dialog, showing the wallet address with the Copy key and Copy Phrase buttons](/images/export_email4.avif) > **Warning:** **Never share your private key or seed phrase with anyone.** Anyone who has your private key has full access to your account and your assets. TxFlow support will **never** ask you for it. --- # Ecosystem Everything TxFlow does happens on-chain, so anyone can build on top of it: the bridge that brings native assets in, explorers that replay the block stream, and partner [channels](/txcore/channels.md) running their own markets on the shared L1. This page collects what exists today. ## Bridge TxRelay is the official bridge for native assets from other chains. Integrators: [TxRelay API](/api/txrelay.md). - [TxRelay](https://txrelay.com/) — official Bridge native BTC, ETH, SOL, and six more assets onto TxFlow, held 1:1 rather than as wrapped IOUs. txrelay.com > **Info:** **A deposit sent the wrong way can be recovered.** Below the minimum, in an unsupported token, or to an address issued for a different route: it is not credited automatically, but it is not lost. Contact TxRelay support with the source transaction hash, the asset and network, and your destination address. Self-serve recovery is coming. ## Builder tools Both explorers read the same chain. The official one ships with the app; the community one is run independently. - [TxFlow Explorer](https://app.txflow.com/explorer) — official Blocks, transactions, and account activity on TxFlow L1, alongside the trading app itself. app.txflow.com/explorer - [TXFLOW://SCAN](https://www.txfscan.xyz/#/) — community Independent Tx-BFT chain explorer — a second view of the same block stream, run by the community. txfscan.xyz > **Info:** Community tools are built and operated by third parties, not by TxFlow. Verify anything you sign, and check the URL against [Official Links](/resources/official-links.md) before connecting a wallet. ## Channels A [channel](/txcore/channels.md) is an isolated trading venue on TxFlow. The L1 underneath supplies consensus, matching, margin, and settlement. A channel scopes which markets exist and who runs them — not whose funds they are, since a user holds one TxFlow account across every channel. - [PROBLY](https://probly.com) — The first partner channel on TxFlow — prediction markets on politics, sports, crypto, and finance. Probly creates its own outcome markets and provides the liquidity you trade against; on testnet they are channel `exchangeId: 1`. probly.com > **Info:** **Channel or builder code?** Running a channel means listing your own markets and standing behind them with liquidity — [limited availability](/txcore/channels.md), onboarded case by case. Earning a fee on flow you route into markets that already exist needs no channel: that's a [builder code](/builders.md). --- # Trading on TxFlow This section explains how trading on TxFlow works from a **trader's** point of view — the mechanics, formulas, and worked examples behind margin, liquidation, funding, and fees. If you're integrating programmatically instead, see the [Info API](/api/info.md) and [WebSocket API](/api/websocket.md). TxFlow offers three kinds of markets: | Market | What you trade | Settles in | |---|---|---| | **Spot** | one asset against another (e.g. BTC/USDC) | a direct swap of balances | | **Perpetual** | a leveraged contract tracking an oracle price | mark-to-market PnL + funding | | **Outcome** | shares of a yes/no prediction market | redeemed at 0 or 1 on resolution | Everything settles on-chain and is computed by the same deterministic rules for every trader — there is no hidden matching or off-book treatment. ## In this section - [Margin & Leverage](/trade/margin-and-leverage.md) — cross vs. isolated, position modes, tiered margin, and the formulas that decide how much you can open. - [Entry Price & PnL](/trade/entry-price-and-pnl.md) — how entry price is averaged, and how unrealized and realized PnL follow from it. - [Liquidation](/trade/liquidation.md) — when it triggers, what happens to your position, and how to read the liquidation price. - [Auto-deleveraging (ADL)](/trade/adl.md) — the backstop when the book can't absorb a liquidation, and how to read your ADL light. - [Funding](/trade/funding.md) — who pays whom, how much, and when — with a worked example. - [Mark vs. Oracle Price](/trade/mark-and-index-price.md) — which price drives PnL, liquidation, and funding. - [Orders & Time-in-Force](/trade/orders.md) — order types, GTC/IOC/ALO/GTD, reduce-only, TP/SL, TWAP. - [Fees & Discounts](/trade/fees.md) — maker/taker, volume tiers, and the referral discount. - [Referral](/trade/referral.md) — how to unlock and use a referral code, invite slots, and what each side earns. > **Tip:** Numbers like fee rates, maintenance-margin tiers, and funding intervals are protocol parameters that can change. The formulas here are stable; always read live values from the app or the [Info API](/api/info.md). --- # Margin & Leverage Margin is the collateral that backs your perpetual positions. This page covers how TxFlow margins a position, how leverage and position size interact, and how to compute your numbers before you open. > **Key takeaway:** Your position is safe as long as your **margin ratio stays at or above 1.0**. It drops below 1.0 — and liquidation begins — when your equity no longer covers the position's **maintenance margin plus the fee reserved to close it**. Bigger positions require a higher maintenance margin rate, so size and leverage are linked. ## Margin modes - **Cross** — all the USDC in your perp account backs all of your cross positions together. Profit on one position can cover loss on another, and your liquidation depends on your whole account. - **Isolated** — each position locks its own margin. Your loss on that position is capped at the margin you assigned to it; it can't pull down your other positions. Margin mode is **per market**, set with [`updateMarginMode`](/api/exchange/updateMarginMode.md), and defaults to cross for a market you have never configured. ## Position modes - **One-Way** — a single net position per market *within one margin mode* (long *or* short). - **Hedge** — hold a long *and* a short in the same market at once. Position mode is **account-wide** for perpetuals, set with [`updatePositionMode`](/api/exchange/updatePositionMode.md). > **One-Way nets per margin mode, not per market:** A position is keyed by account + market + **margin mode** + position mode + side. So "one net position per market" holds inside a single margin mode: a cross long and an isolated short in the same market are two separate positions, each with its own margin, leverage, and liquidation price, and they do not net against each other. If you need a single net exposure per market, keep that market on one margin mode. ## Choosing the margin mode of an order The margin mode an order lands on is part of the position it opens into or reduces: - Send `m` (`marginMode`) on the [`order`](/api/exchange/order.md) body to state it explicitly. - **Omit `m`** and the node routes the order to your current margin mode for that market — the saved per-market setting, or cross if you never set one. The same body can therefore land in a different mode after an [`updateMarginMode`](/api/exchange/updateMarginMode.md), and an order can open a position you did not intend to touch. API clients should always send `m` explicitly; read the effective mode back from [`activeAssetData`](/api/info/activeAssetData.md). ## Tiered margin The **maintenance margin rate (MMR)** and the **maximum leverage** depend on your position size. Larger positions sit in a higher tier: higher MMR, lower max leverage. Each market publishes its own tier table — check it before sizing up, because increasing size can bump you into a stricter tier. ## The formulas ``` notional = size × mark price initial margin = notional / leverage maintenance margin = notional × MMR (MMR from your size tier) liquidation fee = notional × market taker fee rate (reserved to close the position) margin ratio = margin + unrealized PnL ───────────────────────────────── maintenance margin + liquidation fee ``` You are liquidated when **margin ratio < 1.0**. Two reference prices matter — both include the fee charged to close the position: ``` liquidation price (long) = size × entry − margin ─────────────────────────── size × (1 − MMR − feeRate) bankruptcy price (long) = size × entry − margin ─────────────────────── size × (1 − feeRate) ``` For shorts, both denominators become `size × (1 + MMR + feeRate)` and `size × (1 + feeRate)`, with `size × entry + margin` in the numerator. The liquidation price is always reached *before* the bankruptcy price — that buffer is what the liquidation engine works with. > **Note:** `margin` here is the collateral backing the position: the isolated margin for an isolated position, or your account balance for a cross position. In cross mode with several open positions, the liquidation and bankruptcy prices of any one position also depend on the others, so treat the single-position formula above as intuition only. Use `getLiquidationPrices` to estimate the liquidation price impact of a proposed order, and read your current position state for the live liquidation price. ## Worked example You open a **long 0.1 BTC** perpetual at an entry/mark price of **$64,000**, **isolated**, at **10× leverage**. Assume this size sits in a tier with **MMR = 0.5%** and that the market's taker fee rate is **0.045%**. | | Calculation | Result | |---|---|---| | Notional | 0.1 × 64,000 | **$6,400** | | Initial margin (locked) | 6,400 / 10 | **$640** | | Maintenance margin | 6,400 × 0.005 | **$32** | | Liquidation fee reserve | 6,400 × 0.00045 | **$2.88** | | Liquidation price | (6,400 − 640) / (0.1 × 0.99455) | **≈ $57,915.64** | | Bankruptcy price | (6,400 − 640) / (0.1 × 0.99955) | **≈ $57,625.93** | So with $640 of collateral you control $6,400 of BTC. If BTC falls ~9.5% to about **$57,916**, your equity no longer covers the maintenance margin plus the closing fee, and liquidation begins; by roughly **$57,626** the position is fully underwater (bankruptcy). Raising leverage shrinks the initial margin and pushes the liquidation price closer to entry; lowering it does the opposite. > **Warning:** In **cross** mode the liquidation price isn't fixed per position — it depends on your whole account's equity and every other cross position. The isolated example above is the simplest case to reason about. ## Where the live numbers come from - **Mark price** drives valuation and liquidation (see Mark vs. oracle price). - **MMR and max leverage** come from the market's tier table — query `perpMetaDetail` via the [Info API](/api/info.md). - The protocol exposes calculators such as `getMaxPositions` and `getLiquidationPrices` so you can pre-check a proposed order without computing by hand. --- # Entry Price & PnL Your **entry price** is the reference point for every profit-and-loss number you see. This page explains how it's calculated, how it changes as you trade, and how unrealized and realized PnL follow from it. ## Entry price The entry price is the **volume-weighted average price** at which your current position was opened or added to: ``` Entry Price = ∑(Fill Price × Fill Size) ÷ Total Position Size ``` How it changes depends on what your trade does to the position: | Action | Effect on entry price | Example | |---|---|---| | **Open** | Set to the fill price | Buy 1 BTC @ 50,000 → entry = 50,000 | | **Add** (same direction) | Recalculated as the weighted average | Hold 1 BTC @ 50,000, buy 1 BTC @ 51,000 → entry = 50,500 | | **Reduce** (partial close) | Unchanged — only realized PnL is booked | Hold 2 BTC @ 50,500, sell 1 BTC → entry stays 50,500 | | **Flip** (reverse direction) | Old side closes; the remainder opens fresh at the crossing fill price | Hold 1 BTC long @ 50,000, sell 3 BTC → close long, open 2 BTC short at the new fill price | Entry price resets to zero once the position is fully closed. ## Unrealized PnL While a position is open, it's marked continuously against the [mark price](/trade/mark-and-index-price.md) — so your unrealized PnL updates in real time: - **Long** — `Unrealized PnL = (Mark Price − Entry Price) × Position Size` - **Short** — `Unrealized PnL = (Entry Price − Mark Price) × Position Size` This is the number that feeds your margin and [liquidation](/trade/liquidation.md) calculations — not the last traded price. **Example.** You hold 2 BTC long at an entry of 50,500. Mark price is 52,000: ``` Unrealized PnL = (52,000 − 50,500) × 2 = +3,000 USDC ``` ## Realized PnL Realized PnL is locked in whenever you **reduce or close** a position, priced at the exit fill: - **Long** — `Realized PnL = (Exit Price − Entry Price) × Closed Size` - **Short** — `Realized PnL = (Entry Price − Exit Price) × Closed Size` Costs are then applied to get what actually hits your balance: ``` Net PnL = Realized PnL − Trading Fees − Funding Paid ``` See [Fees & Discounts](/trade/fees.md) for trading fees and [Funding](/trade/funding.md) for funding payments. **Example.** From the position above, you sell 1 BTC at 52,000: ``` Realized PnL = (52,000 − 50,500) × 1 = +1,500 USDC ``` Your remaining position is 1 BTC long, still at an entry price of 50,500. > **Note:** The formulas above are for **linear (USD-margined)** perpetuals, where PnL is denominated in the quote asset. Check a market's `currencyType` and `quoteCurrency` in [`perpMeta`](/api/info/perpMeta.md) before applying them — see [TIP 2: Perpetuals](/tip/perps.md#contract-types). --- # Liquidation Liquidation closes a position that can no longer meet its [maintenance margin](/trade/margin-and-leverage.md). This page explains when it triggers, what happens to your position, and how to read the numbers. > **Key takeaway:** Liquidation is triggered by the **mark price**, not the last traded price — so a brief wick on low liquidity won't liquidate you if the mark holds. It begins the moment your **margin ratio drops below 1.0**, meaning your equity no longer covers the maintenance margin **plus the liquidation fee reserve**. ## When it triggers Your account is re-evaluated every block. Liquidation starts when the margin ratio falls below `1.0`, computed per margin mode: ``` Cross (account-wide): balance + cross unrealized PnL margin ratio = ─────────────────────────────────────────────── < 1.0 cross maintenance margin + cross liquidation fee Isolated (per position): position margin + unrealized PnL margin ratio = ───────────────────────────────────── < 1.0 maintenance margin + liquidation fee ``` where: ``` balance = available balance + balance frozen by open orders maintenance margin = position notional × MMR (MMR from your size tier) liquidation fee = position notional × market taker fee rate position notional = position size × mark price ``` Two details are easy to miss: - The denominator includes a **liquidation fee reserve**, so liquidation starts slightly *earlier* than a plain "equity = maintenance margin" comparison would suggest. - In cross mode the numerator is your **balance plus cross unrealized PnL**. Margin locked in isolated positions is not part of it, so isolated collateral cannot rescue a cross account. `MMR` is the **maintenance margin rate**, which varies by tier — see [Margin & leverage](/trade/margin-and-leverage.md). The **bankruptcy price** is the mark price at which the position's margin is fully consumed, including the fee to close it — the point where your margin balance reaches zero. ## What happens to your position Once triggered, the protocol runs a fixed, on-chain sequence: 1. **Decision.** The engine computes how much of the position must be taken over and picks the liquidator account. Positions in a low margin tier are always closed in full; from **tier 3 upward** the engine may run a **partial liquidation** instead, reducing you to the size cap of the tier below. 2. **Cancel orders.** In **cross** mode, all of your open perpetual orders are cancelled (spot and outcome orders are untouched). In **isolated** mode, only orders on the liquidated instrument are cancelled. 3. **Takeover.** A **liquidator account** takes over the position at the **bankruptcy price**. 4. **Market disposal.** The liquidator submits one reduce-only `IOC` order, priced past the bankruptcy price — below it when selling, above it when buying — so that resting liquidity can absorb the position instead of leaving the order unfilled. How far past is configured per market. If the order fills completely, the liquidation ends here. 5. **ADL.** Whatever is left after market disposal is closed against opposing traders through [auto-deleveraging](/trade/adl.md). Every step emits an on-chain event, so a liquidation can be fully reconstructed and audited. ## What you keep - **Isolated**: only the liquidated position and the margin assigned to it are handed to the liquidator account. Your cross margin and other positions are untouched. - **Cross**: all cross positions share one equity pool, so liquidation is assessed account-wide — losses on one position can trigger liquidation while another is still healthy. - Because takeover happens at the **bankruptcy price**, the maintenance-margin buffer is **not returned** to you. - After a **partial** liquidation you keep the remaining position; only the amount needed to get back inside a safer tier is taken over. ## Reducing your risk - Add margin (isolated) or reduce size to push the liquidation point further from the mark. - Lower leverage → smaller position for the same margin → more buffer. - Watch the **mark price**, not the last price — see [Mark vs. oracle price](/trade/mark-and-index-price.md). - Use `getLiquidationPrices` via the [Info API](/api/info.md) to estimate how a proposed order would change liquidation prices; for the live liquidation price, read your current position state. --- # Auto-deleveraging (ADL) When a liquidation can't be cleared on the order book, the protocol falls back to **auto-deleveraging (ADL)**: the position is closed against traders holding the opposite side. > **Key takeaway:** If you're holding a **profitable, high-leverage** position on the opposite side of a liquidation that the book can't absorb, you may be auto-deleveraged. Your **ADL light (1–5)** shows how close you are to the front of the queue. ## How ADL works A liquidated position is first taken over by a liquidator account at the **bankruptcy price** and offered to the book as a single reduce-only `IOC` order. That order is priced past the bankruptcy price — below it when selling, above it when buying — so the book has room to absorb the position; how far past is configured per market. Whatever the order cannot fill is force-closed against **counterparties with opposing positions**, until the position is cleared. Widening that price buffer sends more of the position to the book and less to ADL, at the cost of a worse disposal price; narrowing it does the reverse. ADL is the fallback for the remainder, not the first resort. ADL fills are priced from the **bankruptcy price adjusted by the taker fee rate** — not the mark price: ``` ADL price (long position) = bankruptcy price × (1 + taker fee rate) ADL price (short position) = bankruptcy price × (1 − taker fee rate) ``` Counterparties are ranked by **leverage-PnL ratio** — roughly, *how profitable* the position is *combined with how much leverage* it uses. The most profitable, highest-leverage positions are deleveraged first. ## Reading your ADL light Every position carries an **ADL indicator from 1 to 5**, reported as `adlQuantile` in your position state and derived from your leverage-PnL ratio relative to the highest ratio currently held on your side of that market: | Light | Meaning | |---|---| | 1–2 | low priority — unlikely to be deleveraged | | 3 | mid | | 4 | elevated | | 5 | **first in line** — highest profit + leverage | Positions that are flat or losing always sit at 1. The light updates in real time. If you're at 5 and want to avoid being deleveraged, reduce leverage or take some profit to move down the queue. --- # Funding Perpetuals have no expiry, so a periodic **funding payment** between longs and shorts keeps the contract price anchored to the oracle price. This page covers who pays whom, how much, and when. > **Key takeaway:** Funding is paid **between traders**, not to the exchange. When funding is **positive, longs pay shorts**; when **negative, shorts pay longs**. You only pay or receive on positions you hold when the funding cycle settles. ## How it works - Funding settles on a fixed cycle (default **every hour**). - The rate is published on-chain by the oracle and reflects how far the contract trades above or below its [oracle price](/trade/mark-and-index-price.md): trading above the oracle price → positive rate (longs pay), below → negative (shorts pay). - The payment is proportional to your position's notional, valued at the **mark price**: ``` funding payment = position notional × funding rate = (size × mark price) × rate ``` - **Isolated** positions: funding adjusts the position's margin directly. - **Cross** positions: funding adjusts your account equity. Funding accrues per position since you opened (or last changed) it, and is booked when the settlement cycle is reached. ## Worked example You hold a **long 0.1 BTC** at a mark price of **$64,000**, and the funding rate for this interval is **+0.01%**. | | Calculation | Result | |---|---|---| | Notional | 0.1 × 64,000 | $6,400 | | Funding rate | +0.01% | 0.0001 | | Payment | 6,400 × 0.0001 | **−$0.64** | Because the rate is positive and you're **long**, you **pay $0.64** to the shorts this interval. If you were short, you'd receive it. Over a day at the same rate that's ~$15.36 — funding is small per interval but compounds, so it matters for held positions. ## Where to read it - The current rate and next funding time come from `activeAssetCtx` (`funding`, `nextFundingTime`, `prevFundingTime`) — available both as an [Info API](/api/info.md) query and as a [WebSocket channel](/api/websocket/activeAssetCtx.md). - The `funding` field is expressed **in percent**: a value of `0.01` means 0.01%, i.e. a rate of `0.0001` in the formula above. - A persistent positive rate means the market is paying you to be short (and vice versa) — some traders trade the funding itself. --- # Mark vs. Oracle Price A perpetual has more than one "price." Knowing which one drives what protects you from surprises around liquidation and funding. > **Key takeaway:** Your **PnL and liquidation use the mark price** — a smoothed, manipulation-resistant price — not the last trade. The **oracle price** is the external reference the contract is anchored to via funding. ## The three prices | Price | What it is | What it drives | |---|---|---| | **Last** | the most recent trade on TxFlow | display, your fills | | **Mark** | a smoothed fair price | position valuation, unrealized PnL, **liquidation** | | **Oracle** | aggregated price of the underlying on external markets | the funding anchor | ## Why liquidation uses mark, not last On thin liquidity, a single aggressive order can spike the last price for an instant. If liquidation used the last price, that wick could liquidate healthy positions unfairly. The **mark price is deliberately smoothed**, so a momentary spike doesn't trigger liquidations — your position is valued against a price that reflects the broader market, not one print. ## How the oracle price ties in [Funding](/trade/funding.md) pulls the contract back toward the oracle price: - contract trading **above** the oracle price → positive funding → longs pay → discourages longs → price drifts back down - contract trading **below** the oracle price → negative funding → shorts pay → drifts back up So the oracle price keeps the perpetual honest to the underlying over time, while the mark keeps valuation and liquidation fair in the moment. ## Where they come from Both are published on-chain by validators each block, identical for every trader — there's no per-user price. Read live values (`markPx`, `oraclePx`, `lastPrice`) from `activeAssetCtx`, available both as an [Info API](/api/info.md) query and as a [WebSocket channel](/api/websocket/activeAssetCtx.md). For the protocol-level detail, see the [Oracle](/txcore/oracle.md) page. --- # Orders & Time-in-Force The order types and time-in-force options available across spot, perpetual, and outcome markets, from a trader's standpoint. ## Order types | Type | Use it to | |---|---| | **Market** | fill immediately at the best available prices | | **Limit** | rest at a price you choose and wait to be filled | | **Trigger (TP/SL)** | arm a take-profit or stop-loss that activates when the **mark price** crosses your trigger, then enters the book | ### Limit orders A limit order executes at your limit price or better, never at a worse price. Whatever does not fill on arrival rests in the book until it is matched or you cancel it. | | | |---|---| | Price control | you set the exact price | | Execution | fills when the market reaches your limit price | | Fees | **maker** rate if it rests and is filled later; **taker** rate if it matches immediately on arrival | | Best for | precise entries and exits; avoiding slippage | **Example:** you want to buy 1 BTC at $50,000 or lower while the market trades at $50,200. Your limit buy rests in the book until BTC trades down to $50,000, or until you cancel it. ### Market orders A market order executes immediately against the best available prices in the book. You trade price certainty for speed: the execution price is not guaranteed. | | | |---|---| | Execution | fills immediately against available liquidity | | Price | takes the best available prices in the book, within a fixed **5% slippage tolerance** — see [Position & Order Limits](/trade/position-and-order-limits.md) | | Fees | always the **taker** rate | | Best for | entering or exiting quickly in liquid markets | ## Time-in-force (TIF) | TIF | Behavior | When to use | |---|---|---| | **GTC** | good-til-canceled — rests on the order book until it is filled or canceled | default resting orders | | **IOC** | fills what it can now, cancels the rest | take liquidity without leaving a resting order | | **ALO** | post-only — rejected if it would fill immediately | guarantee you're a **maker** (earn maker rates) | | **GTD** | valid until a time you set | time-boxed orders | An `ALO` order only ever adds liquidity, so it always pays the **maker** rate, never the taker rate. That makes it the usual choice for capturing maker rates and for algorithmic strategies. In a fast market an `ALO` order can be rejected: if the price moves so that the order would cross the spread on arrival, it never enters the book. ## What each market supports Not every option is available on every market: | | Perps | Spot | Outcome | |---|---|---|---| | Market / limit orders | ✓ | ✓ | ✓ | | GTC · ALO | ✓ | ✓ | ✓ | | IOC | ✓ | ✓ | market orders only | | GTD | — | — | ✓ | | Trigger (TP/SL) | ✓ | — | — | | Reduce-only | ✓ | — | ✓ | A GTD order must carry an expiry time, and expiry is measured by the consensus timestamp rather than your local clock. ## Perpetual-specific - **Reduce-only** — an order that can only *shrink* your position, never flip or grow it. Use it to close safely without accidentally opening the other side. - **Take-profit / stop-loss** — trigger orders evaluated against the **mark price** (so a wick on last price won't fire them prematurely). - **TWAP** — slices a large order into smaller child orders over time to reduce market impact. ## Things the engine enforces - **Price–time priority** — at a given price, earlier orders fill first. - **Self-trade prevention** — if your order would trade against your own resting order, the resting order is **cancelled** and matching continues; your incoming order is not cancelled by this check. - **Minimum size & price tick** — each market has a smallest tradable size and price increment; orders must conform. Check them via `perpMeta` / `spotMeta` on the [Info API](/api/info.md). - **Price limits** — orders too far from the mark are rejected to protect the book during abnormal moves. For the exact request format and how orders are signed, see the [Exchange API](/api/exchange.md) and [Signing](/api/signing.md). --- # Contract Specifications TxFlow perpetuals are linear derivatives with **no expiration date**. They track the underlying spot asset through hourly [funding payments](/trade/funding.md) rather than a settlement date. > **Key takeaway:** The specification model is deliberately thin: minimal contract-specific restrictions and **no address-level limitations**. What varies per market is the leverage tier and the position/order limit, not the contract shape. ## Specification | Parameter | Specification | |---|---| | Instrument type | Linear perpetual | | Contract size | 1 unit of the underlying spot asset | | Underlying asset | TxFlow oracle index of the spot asset | | Initial margin fraction | `1 / (user-selected leverage)` | | Mark price | see [Mark vs. Oracle Price](/trade/mark-and-index-price.md) | | Delivery / expiration | none — hourly [funding](/trade/funding.md) instead | | Account type | cross-margin or isolated margin, per wallet | | Position and order limits | see [Position & Order Limits](/trade/position-and-order-limits.md) | Live per-market values — tick size, lot size, maximum leverage, and the current margin tiers — come from the `perpMeta` and `perpMetaDetail` endpoints rather than this page. See [perpMeta](/api/info/perpMeta.md) and [perpMetaDetail](/api/info/perpMetaDetail.md); treat the API as authoritative, since governance can retune these parameters. --- # Position & Order Limits The ceilings an order has to clear before it reaches the book: a per-market position cap, order size bounds, price bands relative to the oracle, and slippage protection on market orders. > **Key takeaway:** Reducing or closing a position is **always** allowed — the position limit only gates orders that increase or reverse your exposure. Limit prices are bounded relative to the [oracle price](/trade/mark-and-index-price.md), and market orders carry a fixed **5% slippage tolerance** that is not user-configurable. ## Position limit The maximum position you can hold in a market, in base asset units (BTC, ETH, …). On submission TxFlow checks: ``` |resulting position size| ≤ position limit ``` where `resulting position size = current position ± order size`, depending on direction. | Scenario | Check | |---|---| | Adding to an existing position (same direction) | `|resulting position size| ≤ position limit` | | Reducing or closing (opposite direction) | always allowed — it reduces exposure | | Reversing (flipping long ↔ short) | `|resulting position size| ≤ position limit` | ## Order size limits | Parameter | Description | Example | |---|---|---| | Minimum order size | smallest quantity allowed | 0.001 BTC | | Maximum market order size | largest single market order | 50 BTC | | Maximum limit order size | largest single limit order | 100 BTC | > **Info:** Orders below the minimum size are rejected immediately. The values above are examples — the live per-market numbers come from [perpMetaDetail](/api/info/perpMetaDetail.md). ## Price limits on limit orders Limit prices are bounded relative to the oracle price, so a fat-fingered or manipulative order cannot rest far away from fair value. **Buy (long) limit orders** | Limit | Formula | Purpose | |---|---|---| | Price cap | `oracle price × (1 + max deviation %)` | prevent buying far above fair value | | Price floor | `oracle price × 0.2` | prevent unrealistic lowball bids | **Sell (short) limit orders** | Limit | Formula | Purpose | |---|---|---| | Price cap | `oracle price × 5` | prevent unrealistic high asks | | Price floor | `oracle price × (1 − max deviation %)` | prevent selling far below fair value | ### Dynamic price bands In actively traded markets TxFlow tightens the bands using recent premium: ``` price cap (buy) = min[ max(oracle, oracle × (1 + Y) + average premium), oracle × (1 + Z) ] price floor (sell) = min[ max(oracle, oracle × (1 − Y) + average premium), oracle × (1 + Z) ] ``` - `Y` — short-term deviation allowance (e.g. 2%) - `Z` — maximum deviation cap (e.g. 5%) - `average premium` — 2-minute moving average of `mid price − oracle price` ## Slippage protection on market orders Market orders carry a fixed **5%** tolerance, not user-configurable: | Order type | Accepted while | If exceeded | |---|---|---| | Market buy | `execution price ≤ oracle price × 1.05` | fill the valid portion, cancel the rest | | Market sell | `execution price ≥ oracle price × 0.95` | fill the valid portion, cancel the rest | This is why a large market order in a thin book can come back partially filled rather than walking the book — the remainder is cancelled, not queued. ## Related | Page | Covers | |---|---| | [Margin & Leverage](/trade/margin-and-leverage.md) | maximum leverage by asset and position size | | [Orders & Time-in-Force](/trade/orders.md) | order types and matching mechanics | | [Liquidation](/trade/liquidation.md) | liquidation mechanics | | [Fees & Discounts](/trade/fees.md) | trading and funding fees | --- # Fees & Discounts What you pay to trade, how the maker/taker split works, and how volume lowers your rate. > **Key takeaway:** Your fee rate is set by your **14-day rolling trading volume** — trade more, pay less. **Makers** (resting liquidity) pay a lower rate than **takers** (those who cross the book), so posting with `ALO` can cut your cost. ## Maker vs. taker | Role | You are… | Rate | |---|---|---| | **Maker** | the resting order that gets filled | lower | | **Taker** | the order that crosses and removes liquidity | higher | Use a post-only (`ALO`) [limit order](/trade/orders.md) to ensure you're a maker. ## Volume tiers Your tier is set by your **14-day rolling weighted volume**, assessed daily in UTC (excluding the current day). Perps and spot volume count **together** toward a single tier level; the tier you land in then supplies separate maker/taker rates for perps and for spot: ``` 14d weighted volume = 14d perps volume × perps weight + 14d spot volume × spot weight ``` Both weights default to `1`, so in practice the two volumes simply add up. They are governance parameters and can be retuned per exchange. | Level | Weighted volume | Perps maker | Perps taker | Spot maker | Spot taker | |---|---|---|---|---|---| | VIP 0 | < $5M | 0.0150% | 0.0450% | 0.0400% | 0.0700% | | VIP 1 | ≥ $5M | 0.0120% | 0.0400% | 0.0300% | 0.0600% | | VIP 2 | ≥ $25M | 0.0080% | 0.0350% | 0.0200% | 0.0500% | | VIP 3 | ≥ $100M | 0.0040% | 0.0300% | 0.0100% | 0.0400% | | VIP 4 | ≥ $500M | 0.0000% | 0.0280% | 0.0000% | 0.0350% | | VIP 5 | ≥ $1B | 0.0000% | 0.0260% | 0.0000% | 0.0300% | | VIP 6 | ≥ $2B | 0.0000% | 0.0240% | 0.0000% | 0.0250% | The ladder above is the schedule published for the platform exchange. Thresholds and rates are governance parameters and can change — read the configured ladder via [`feeTierInfo`](/api/info/feeTierInfo.md), or query effective rates via `userFees` / [`feeTierRates`](/api/info/feeTierRates.md). ## How a fee is charged A fee applies to the **notional value** of each fill: ``` fee = fill notional × your rate (maker or taker) fill notional = fill size × fill price ``` Fees are charged **per side** — opening and closing a position each incur their own fee — and settle atomically in the same block as the fill, with no separate settlement step. On **spot**, the fee is taken from the asset you receive: a buy pays `fill size × rate` in the **base** asset, a sell pays `fill notional × rate` in the **quote** asset. The value is the same either way; only the asset it is deducted in differs. Perps always charge in the quote asset (USDC). ## Referral discount If you sign up under a referral, your fees are billed at a **discount** — you pay **95%** of the standard rate — up to a per-address volume cap. Your referrer separately earns a **rebate** on the **perp** fees you pay (default **10%**). Both rates are governance parameters. The discount is applied automatically once a referrer is set. Query your referral state with the `referral` request on the [Info API](/api/info.md). See [Referral](/trade/referral.md) for how to create and use a code, the volume caps on both sides, and how rewards are paid out. --- # Deposit & Withdrawal Fees What you pay to move USDC in and out of TxFlow. These are separate from [trading fees](/trade/fees.md) — they cover moving funds, not the trade. > **Key takeaway:** **Deposits are free.** A **withdrawal costs a flat 1 USDC**, whichever chain you withdraw to. Transferring to another TxFlow account is free, except for a one-time **1 USDC activation fee** the first time you send to a given account. ## Deposit fees Deposits are free on every supported route — Arbitrum, Base, Polygon PoS, Ethereum, and Solana. You still pay the ordinary network fee on the source chain, and bridged routes may show a separate bridge cost in the app before you confirm. ## Withdrawal fees | | | |---|---| | Withdrawal fee | **1 USDC**, flat | | Depends on destination chain? | **No** — the same fee applies to every supported chain | | Minimum withdrawal | **2 USDC** | The fee is deducted from the withdrawal amount: request `100 USDC` and `99 USDC` is released on the destination chain. ## Transfers to another TxFlow account Sending funds to another TxFlow account is free, with one exception: the **first** transfer you make to a given recipient carries a one-time **1 USDC activation fee**. Later transfers to that same recipient are free, and the fee never applies when you send to yourself. --- These fees are protocol parameters and can change. See [Deposits & Withdrawals](/getting-started/deposits-and-withdrawals.md) for how to move funds. --- # Fee Credits Platform-issued vouchers that reimburse your perp trading fees. **1 Fee Credit = $1** of fee reimbursement, applied automatically. > **Key takeaway:** Fee Credits apply to **perpetual contract fees only**. Nothing to claim and nothing to activate — credits are consumed as you trade, and the equivalent amount is paid back to your perp account in **USDC once per day**. ## What they cover | Fee type | Covered | |---|---| | Perpetual contract trading fees | Yes | | Spot trading fees | No | | Deposit / withdrawal and other fees | No | ## How they work 1. **Distribution** — credits from a campaign land in your balance, visible in the Fee Credits Center. Nothing to claim. 2. **Consumption** — every eligible perp fill consumes credits against the fee charged. If your balance does not cover the whole fee, the remainder is charged to your account as normal. 3. **Charge** — the trading fee is still charged in full at fill time and still shows in your trade history. Credits are tracked against it rather than waiving it at the point of trade. 4. **Daily settlement** — the day's consumed credits are totalled and paid back as **USDC** to your perp account. The USDC you receive is ordinary collateral and can be used as margin. 5. **History** — the Fee Credits Center keeps an Earn record, a per-trade Spend record, and a daily breakdown. ## Interaction with a referral discount If you joined with a referral code, the **5% referee discount** is applied *first*; credits then offset what is left: ``` original fee → −5% referee discount → −Fee Credits → amount actually paid ``` The amount actually paid is also the basis for your referrer's commission, so if your credits cover a fee in full, that trade generates **no** commission for the referrer. | Step | Credits cover it fully | Credits cover part of it | |---|---|---| | Original fee | $10.00 | $10.00 | | After 5% referee discount | $9.50 | $9.50 | | Fee Credits applied | −$9.50 | −$4.00 (balance was $4.00) | | You pay | $0.00 | $5.50 | | Referrer's commission basis | $0.00 | $5.50 | The 5% referee discount itself runs out after your first **$25M** of combined volume — see [Referral](/trade/referral.md). Fee Credits keep working past that point. ## Expiry and restrictions - Credits **expire** on their own schedule. Unused credits are forfeited at expiry and are **not reissued**. - **Non-transferable** — valid only for the account they were issued to. - **Non-withdrawable** — they cannot be sent to an external wallet or converted to another asset. - **No cash value** — usable only as a fee offset. Your balance, earn history, spend history, and daily breakdown are all in the Fee Credits Center in the app. --- # Referral How to unlock your referral code, how many people you can invite, and what both sides earn. > **Key takeaway:** Trade **$10,000** in total volume to unlock your referral code. You can invite **3 people at a time**, and you earn **10% of each referred user's net perpetual trading fees** on their first **$1B** in trading volume. Each referred user gets **5% off** both spot and perpetual fees for their first **$25M** in combined volume. ## Getting your referral code To unlock your referral code, you must trade **$10,000 or more in total volume** across spot, perpetuals, or both. Once you meet this requirement, visit the [Referral Page](https://app.txflow.com/referral) to find your unique Referral Link and Referral Code. You can share either one with friends through Telegram, X (Twitter), Discord, or any other channel you prefer. ## Invite slot system Invites are rationed. You hold a number of **invite slots**, and each signup consumes one. - Every account starts with **3 invite slots**. - Each time someone signs up using your referral code, **1 invite slot is used**. - Referral codes cannot be bound between accounts that already have an existing referral relationship. - If you use all your invite slots, you can unlock **3 new invite slots** by trading **$10,000 or more in total volume** across spot, perpetuals, or both within a single day. - New invite slots are added at the next daily reset, which occurs at **00:00 UTC**. - You can unlock a maximum of **3 new invite slots per day**, regardless of how much volume you trade. The Referral Page shows your remaining slots, your progress toward the next unlock, and a countdown to the daily reset. ## Referral rewards - You earn **10% of each referred user's net perpetual trading fees**, calculated after any fee discount they receive. - Rewards apply to each referred user's **first $1B in trading volume**. - Rewards are settled automatically each day and credited directly to your **Perp account**. ## How to use a referral code Go to the [Referral Page](https://app.txflow.com/referral), enter the code in the **Bind a Referral Code** section, and click **Bind**. Once you bind a referral code, you receive **5% off fees on both spot and perpetual trades** for your first **$25M in combined trading volume**. See [Fees & Discounts](/trade/fees.md) for how that discount sits on top of your volume tier. > **Warning:** A referral code cannot be changed once it is bound. Make sure you enter the correct code before confirming. ## At a glance | | Referrer | Referee | |---|---|---| | Requirement | $10,000 total volume, spot or perpetuals or both | Bind a code on the Referral Page | | Limit | 3 invite slots at a time, up to 3 more per day | One code, permanent once bound | | Benefit | 10% of the referee's net perpetual trading fees | 5% off both spot and perpetual fees | | Cap | Referee's first $1B in trading volume | Your first $25M in combined volume | | Payout | Settled daily to your Perp account | Applied automatically once the code is bound | ## FAQ **Can I create multiple referral codes?** No. Each account can only have one referral code. **Can I change my referral code after it is created?** No. Your referral code is permanent once generated and cannot be modified. **Can I use my own referral code?** No. The referrer and referee must be separate accounts. **What happens when I run out of invite slots?** Once all of your invite slots are used, you can unlock **3 more invite slots** by trading **$10,000 or more in total volume** within a single day. The new slots are added at the next **daily reset, 00:00 UTC**. You can track your progress and view the countdown timer on the Referral Page. **What happens after a referred user reaches $1B in trading volume?** You will stop earning rewards from that referred user once they reach $1B in trading volume. Rewards from your other referred users will continue as normal. **Can I withdraw my referral rewards?** Yes. Referral rewards cannot be claimed manually, but pending commissions are settled automatically during the next settlement cycle and credited directly to your **Perp account**. Once the rewards are credited, you can withdraw them from your Perp account at any time. ## Reading your referral state programmatically Query your referral code, your referrer, and accrued rewards with the [`referral`](/api/info/referral.md) request on the [Info API](/api/info.md). --- # Affiliate A multi-tier version of the [Referral](/trade/referral.md) program for KOLs, agencies, and professional marketers. Affiliates build a hierarchy up to three levels deep and earn the **spread** between their own rate and their sub-affiliate's. ## Affiliate vs. Referral | | Referral | Affiliate | |---|---|---| | Target | individual traders | KOLs, agencies, communities | | Depth | 1 level (direct referrals only) | **3 levels** (Affiliate → Sub → Sub-Sub) | | Commission model | flat 10% of referee fees | **differential** — you earn the spread between levels | | Sub-affiliates | not supported | promote invitees to sub-affiliates | | Rates | fixed 10% | customizable per level, within limits | | Tooling | basic dashboard | advanced analytics | ## How the differential works Each level has a commission rate, and each party earns the difference between their rate and the rate of the level below. For a user who generated **100 USDC** of trading fees: | Tier | Rate | Earns | |---|---|---| | Master Affiliate | 20% | `100 × (20% − 12%)` = **8 USDC** | | Sub-Affiliate | 12% | `100 × (12% − 10%)` = **2 USDC** | | Sub-Sub-Affiliate | 10% | `100 × 10%` = **10 USDC** | | User (invitee) | — | generated the 100 USDC of fees | The rates above are an illustration, not the published schedule. Total payout across the chain equals the top-level rate — the hierarchy splits one commission pool rather than stacking additional cost onto the trader. ## Applying Affiliate status is granted by application. Typical criteria: - 5,000+ social media followers or subscribers - an active trading or alpha group - a distinct ability to drive TxFlow growth > **Info:** A plain referral code needs no application — create one on the Referral page once you have traded **$10,000** in volume. See [Referral](/trade/referral.md). --- # Vaults A vault is a pooled `USDC` account that trades a strategy and shares the result with its depositors. TxFlow has two kinds: the **Protocol Vault**, run by the protocol, and **User Vaults**, run by individual traders. ## Protocol Vault The Protocol Vault is run by the TxFlow protocol. It provides liquidity on the TxFlow order book and takes over liquidated positions. Returns come from market making and liquidations. Positions the vault holds, including liquidated positions it takes over, move with the market. The vault's value can fall during volatile markets. Its risk is relatively lower than most user vaults, but not zero. - Profit share and fees: **none**. - Lock-up: **4 days** from your most recent deposit. Each new deposit restarts the lock-up for your entire balance. *Example:* you deposit on Oct 10 at 10:00 UTC, so your balance is withdrawable from Oct 14 at 10:00 UTC. If you deposit again on Oct 12 at 08:00 UTC, your entire balance becomes withdrawable from Oct 16 at 08:00 UTC. > **Not principal-protected:** Unlike CEX flexible savings, vault returns are not guaranteed and can be negative. ## User Vaults User Vaults are created and managed by individual traders (leaders). Depositors share in the vault's trading profits and losses: - Profit share: the leader receives **10%** of your profits. - Lock-up: **1 day** from your most recent deposit. Each new deposit restarts it for your entire balance. > **Info:** During the early mainnet phase, vault creation is limited to a selected pilot group. It will open to all users in a later phase. ## Protocol Vault vs. User Vaults | | Protocol Vault | User Vaults | |---|---|---| | Operator | TxFlow protocol | individual traders (leaders) | | Strategy | market making and taking over liquidated positions | set by the leader | | Returns from | market making and liquidations | the vault's trading profits and losses | | Profit share | none | 10% of your profits to the leader | | Lock-up | 4 days from your most recent deposit | 1 day from your most recent deposit | | Risk | lower than most user vaults, but not zero | varies with the leader's strategy | ## Deposits and withdrawals Deposits move `USDC` from your Perp account. You can deposit up to your **transferable balance**, which is the amount you can move without affecting the initial margin of open positions. Funds in a vault no longer count as margin. Withdrawals go back to your Perp account. The amount you receive is set when the withdrawal completes, based on the vault's value at that time. For User Vaults, it is after the leader's profit share. Large withdrawals may be processed in part. The rest stays in the vault, and you can withdraw it again. ## APR APR is annualized from the vault's performance over the past month. Past performance does not guarantee future results. --- # Prediction Markets Trade the outcome of a future event. Each market asks one question — *Will BTC be above $100,000 at 08:00 UTC on September 30?* — and you take a position on **YES**, **NO**, or one of several listed outcomes. > **Key takeaway:** Price **is** probability: YES at $0.65 means the market prices the event at roughly 65%. A winning share settles at **$1**, a losing share at **$0**, so your maximum loss is capped at what you paid. Always 1x — no leverage, no funding, no liquidation. ## Price represents probability | YES price | Implied probability | |---|---| | $0.20 | ~20% | | $0.50 | ~50% | | $0.80 | ~80% | Prices move continuously as traders update their views. In a binary market, `YES price + NO price ≈ $1.00` — small gaps come from the spread and available liquidity. Buy **YES** when you think the event is more likely than the YES price implies; buy **NO** when you think it is less likely. ## How payouts work Shares settle after the market resolves: `1 winning share = $1.00`, `1 losing share = $0`. Buy 100 YES at $0.60 → cost `100 × $0.60 = $60`. - YES wins: `100 × $1.00 = $100`, profit **$40**. - YES loses: `$0`, loss **$60**. Fees are excluded from the example. > **Warning:** Prices reflect market expectations and guarantee nothing. If your position settles at $0 you lose the full amount you paid for it. ## You can exit before resolution You do not have to hold to resolution. If there is liquidity, sell at any time: buy YES at $0.40, sell at $0.70, realize the difference. Execution depends on liquidity and the fill price you get. ## Versus perpetuals | | Perps | Predictions | |---|---|---| | You trade | the price of an asset | the outcome of an event | | Direction | long or short | YES or NO | | Leverage | up to 60x | none — always 1x | | Expiry | none; hold while margin holds | settles when the event resolves | | Price means | market price of the underlying | implied probability (63¢ ≈ 63%) | | Payoff | scales with the move and your leverage | $1 if right, $0 if not | | Max loss | liquidation risk with leverage | capped at what you paid | | Fees | [VIP tier rates](/trade/fees.md) | market-specific | | Funding | paid or received while open | none | ## Events provider Prediction markets on TxFlow are provided by [**Probly**](https://www.probly.com/en); TxFlow provides the trading interface. See the [Probly documentation](https://docs.probly.com/) for provider-side detail. > **Warning:** These markets carry elevated risk beyond ordinary market risk — including thin liquidity, incomplete documentation, imperfect settlement, and incorrect outcome resolution. Prediction markets are **excluded from the points program**. ## In this section | Page | Covers | |---|---| | [Market Types](/prediction/market-types.md) | Binary and category markets, crypto up/down markets | | [Trading](/prediction/trading.md) | Choosing a side, order types, the book, position value, fees | | [Resolution & Settlement](/prediction/resolution-and-settlement.md) | Resolution rules, settlement, cancelled and invalid markets | --- # Market Types TxFlow lists prediction markets with two outcome structures: **binary** (two outcomes) and **category** (several outcomes, one winner). ## Binary markets Two outcomes, **YES** or **NO**: > Will BTC be above $100,000 at 08:00 UTC on September 30? Buy YES if you think the condition will be met, NO if you think it will not. At resolution one side settles at $1 and the other at $0. ### Crypto up / down markets Some crypto markets ask whether an asset finishes above or below a reference price. The reference price, observation time, price source, and resolution conditions are all shown in that market's **Resolution Rules** — read them before trading, since they differ per market. ## Category markets Several possible outcomes, exactly one of which wins: > Which team will win the tournament? | Outcome | YES price | |---|---| | Team A | $0.40 | | Team B | $0.30 | | Team C | $0.20 | | Team D | $0.10 | YES prices across all outcomes sum to roughly $1 because only one can win. If Team A wins, Team A YES settles at $1 and every other YES settles at $0. ### Trading a single outcome Each outcome in a category market can be traded from either side: - **Buy YES on Team A** — you win if Team A is the winning outcome. - **Buy NO on Team A** — you win if *any* other valid outcome wins. ## Categories TxFlow may list markets across crypto, sports, and other event categories. The available set changes over time. Evaluate each market on its own [Resolution Rules](/prediction/resolution-and-settlement.md) rather than on its category. --- # Trading Prediction Markets Prediction markets trade through an order book, the same as any other market on TxFlow. ## Choosing a side Pick the outcome first, then the side. **Buy YES** when you think the event is more likely than the price implies. YES at $0.40 implies ~40%; if you think it is closer to 60%, YES is the buy. **Buy NO** when you think it is less likely. YES at $0.75 means NO is around $0.25; if you think the true probability is well below 75%, NO is the buy. ## Order types **Market order** — executes against the best available orders in the book. The fill price can differ from what was displayed when you submitted, especially in a thin market. **Limit order** — you set the price you are willing to trade at. It rests until filled, cancelled, or the market stops accepting orders, and it may fill partially. See [Orders & Time-in-Force](/trade/orders.md) for the shared order semantics. ## Reading the book - **Bid** — the highest price buyers will currently pay. - **Ask** — the lowest price sellers will currently accept. - **Spread** — the gap between them. Thin liquidity and a wide spread both raise the cost of entering and exiting, which matters more here than in a deep perp book. ## Position value Buy `100 YES @ $0.40` → cost `$40`. If the position can be sold at $0.65: ``` market value = 100 × $0.65 = $65 unrealized PnL = $65 − $40 = +$25 ``` Selling at $0.65 realizes the difference. Actual PnL depends on your fill prices and fees. ### PnL at resolution ``` win: shares × ($1 − average entry price) − fees lose: shares × (0 − average entry price) − fees ``` > **Info:** Winning shares settle at $1 per share, losing shares at $0. ## Fees Prediction market trades may carry a trading fee that depends on the market and how the order executed. The applicable fee is shown before you place the order. Three things that make these fees different from the rest of the platform: 1. Prediction volume does **not** count toward [volume-based fee tiers](/trade/fees.md). 2. Prediction fees are **not** eligible for [referral](/trade/referral.md) rebates. 3. Prediction fees **cannot** be offset with [Fee Credits](/trade/fee-credits.md). --- # Resolution & Settlement Every market carries predefined rules that determine its final result. Read a market's **Resolution Rules** before trading it — they, not the market title, decide the outcome. ## Resolution rules A market's rules specify the conditions used to pick the winning outcome. Depending on the market that can include: - the market question - resolution date and time - resolution source - the exact conditions for YES and NO - the reference price or data source - how delays, cancellations, and exceptional events are treated - any additional market-specific conditions For *Will BTC be above $100,000 at 08:00 UTC on September 30?* the rules might specify: | Field | Value | |---|---| | Observation time | September 30, 08:00 UTC | | Resolution source | Binance BTC/USDT 1-hour candle close | | YES condition | reference price **above** $100,000 at the observation time | | NO condition | reference price **at or below** $100,000 at the observation time | ## Market closing Trading close and event resolution are two different moments. A market can stop accepting orders well before the result is known. ## Resolution Once the event has occurred and the result is confirmed against the published rules, the market is resolved. > **Info:** A market may not resolve immediately after trading closes. Automated price-based markets can resolve shortly after the observation time; markets needing external or manual verification take longer. ## Settlement For a binary market: | Your position | Resolves YES | Resolves NO | |---|---|---| | YES | $1 per share | $0 | | NO | $0 | $1 per share | In a category market, the winning outcome's shares settle at $1 and every losing outcome settles at $0. Settlement runs **automatically** once the market resolves, and proceeds are credited in **USDC**. There is nothing to claim. ## Cancelled, invalid, or unresolvable markets Some events cannot be resolved under the original rules — for example: - the event is cancelled - the event is postponed - required data is missing or unavailable - the final result is ambiguous - the specified resolution source fails - the market was configured incorrectly A market that cannot resolve normally may be cancelled or declared invalid under its own rules. How affected positions are settled or refunded follows the rules published for that market. ## Resolution sources Markets may depend on external data. Price-based crypto markets may use a specified price index, exchange price, or oracle; sports markets may use an official competition or an approved results provider. The source in force is shown in each market's Resolution Rules. --- # Product Notices Trading hours, product-specific terms, and risk disclosures for the markets on TxFlow that reference off-chain assets. Crypto perpetuals trade continuously. Markets that reference equities, private companies, or currencies do not: their underlying venues close, and the contracts carry risks that a crypto perpetual does not. The notices in this section set out when those markets trade and what you are taking on when you trade them. > **Warning:** These notices are part of the terms you accept when you trade the products they describe. Read the one that applies to your market before you open a position. ## In this section | Notice | Covers | |---|---| | [Stock Perpetuals](/notices/stock-perpetuals.md) | Regular trading hours, daylight saving time, the US market holiday calendar, and the equity risk disclaimer | | [FX Perpetuals](/notices/fx-perpetuals.md) | 24/5 trading hours and the foreign exchange risk disclaimer | | [Pre-IPO Perpetuals](/notices/pre-ipo-perpetuals.md) | What a Pre-IPO contract is, eligibility, and the risk disclosure | ## Reduce-only outside trading hours Stock and Pre-IPO perpetuals reference assets whose primary venues close on weekends, public holidays, and outside regular session hours. While a market's underlying venue is closed, trading on that market may be limited to **reduce-only** orders: you can close or shrink an existing position, but you cannot open a new one or add to what you hold. Plan your positions around the session schedule. A position left open across a closed session is still marked, still funded, and still liquidatable while you cannot add margin by opening an offsetting position. ## Changes to these notices At its sole discretion, TxFlow may revise or withdraw any notice in this section at any point, for any reason, and without giving advance notice. Holiday calendars and session hours in particular are republished as the relevant exchanges publish theirs. For the general terms that govern your use of TxFlow, see the [Terms of Use](https://txflow.com/terms) and [Privacy Policy](https://txflow.com/privacy). --- # Stock Perpetuals Trading Trading hours and risk disclosure for perpetuals that reference listed equities. > **Key takeaway:** Stock perpetuals track equity prices, so they follow the US equity session. Outside regular trading hours on weekdays, and on weekends and public holidays when the relevant financial markets are closed, trading may be limited to **reduce-only** orders. Plan your positions accordingly. ## Regular trading hours Weekdays, Monday through Friday: | | Standard Time | Daylight Saving Time | |---|---|---| | **Open** | 14:30 UTC | 13:30 UTC | | **Close** | 21:00 UTC | 20:00 UTC | Daylight Saving Time begins on the second Sunday in March and ends on the first Sunday in November, with the time change occurring at 2:00 a.m. local time. Standard Time begins on the first Sunday in November and ends on the second Sunday in March of the following year, with the time change occurring at 2:00 a.m. local time. ## Public holidays The following public holidays may affect trading hours: | Date | Holiday | Market status | |---|---|---| | Jan 1 | New Year's Day | Closed all day | | Jan 19 | Martin Luther King, Jr. Day | Closed all day | | Feb 16 | Presidents' Day | Closed all day | | Apr 3 | Good Friday | Closed all day | | May 25 | Memorial Day | Closed all day | | Jun 19 | Juneteenth National | Closed all day | | Jul 3 | Independence Day | Closed all day | | Sep 7 | Labor Day | Closed all day | | Nov 26 | Thanksgiving Day | Closed all day | | Nov 27 | Day after Thanksgiving Day | Early close (18:00 UTC) | | Dec 24 | Christmas Eve | Early close (18:00 UTC) | | Dec 25 | Christmas Day | Closed all day | At its sole discretion, TxFlow may revise or withdraw this announcement at any point, for any reason, and without giving advance notice. ## Important notice *Stock perpetuals and/or Pre-IPO perpetuals are derivative products that mirror the price movements of the referenced equities, yet they grant no ownership stake in those underlying assets. Because market conditions can shift quickly, trading these instruments carries considerable risk, up to and including the loss of your entire investment. In some regions, access to this product may be limited. The responsibility rests entirely with you to grasp the risks at stake and to judge whether this product suits your financial situation before entering into any transaction.* ## Disclaimer and risk warning The contracts listed above bear no association, sponsorship, endorsement, or affiliation with the issuer of the relevant underlying shares or with the exchange on which those shares trade. Stock perpetuals and/or Pre-IPO perpetuals are derived from a basket of tokenized stock indices that already circulate in the market. That said, Stock perpetuals and/or Pre-IPO perpetuals do not constitute a security and confer no actual or beneficial ownership of the underlying stocks, their corresponding stock tokens/Pre-IPO tokens, or the indices. Nor do Stock perpetuals and/or Pre-IPO perpetuals grant you any entitlement to dividends, interest, voting rights, shareholder rights, or rights offerings (including, without limitation, share splits, spin-offs, or subscription rights) connected to the underlying stocks, their corresponding stock tokens/Pre-IPO tokens, or the indices. In addition, Stock perpetuals and/or Pre-IPO perpetuals are not backed, insured, approved, or guaranteed by any government or institution. Trading these instruments carries risks, among them market risk, liquidity risk, and risks stemming from pricing mechanisms or issuer adjustments. We encourage you to do your own research and evaluate your risk tolerance carefully before trading. Past performance offers no assurance of future results. Stock perpetuals and/or Pre-IPO perpetuals are unavailable in certain jurisdictions, and each user is solely responsible for confirming that their use of this product complies with local laws and regulations. Digital assets are highly speculative and prone to considerable volatility; they can become illiquid at any moment, and investors may lose the full value of their investment. Before trading any digital asset, you should carry out your own research and gauge your risk tolerance. TxFlow accepts no liability for any losses you may sustain from trading digital assets. Nothing in this material should be taken as financial advice. For further details, please refer to our [Terms of Use](https://txflow.com/terms) and [Privacy Policy](https://txflow.com/privacy). --- Trading a Pre-IPO contract as well? See the [Pre-IPO Perpetuals](/notices/pre-ipo-perpetuals.md) disclaimer, which sets out terms specific to contracts whose underlying company has not yet listed. --- # FX Perpetuals Trading Trading hours and risk disclosure for perpetuals that reference foreign exchange rates. > **Key takeaway:** FX perpetuals trade **24/5**, Monday through Friday. Outside those hours, and on weekends and public holidays when the relevant financial markets are closed, trading may be limited to **reduce-only** orders. Plan your positions accordingly. ## Regular trading hours Regular trading hours are **24/5**: Monday through Friday, 00:00 to 24:00 UTC each trading day. Public holidays may affect trading hours. Trading hours for FX perpetuals follow the global foreign exchange market sessions and may be affected by public holidays, daylight saving time adjustments, or other market closures in the relevant jurisdictions. Check the trading hours schedule for each contract before placing orders. At its sole discretion, TxFlow may revise or withdraw this announcement at any point, for any reason, and without giving advance notice. ## Important notice *FX Perpetuals are derivative products that mirror the price movements, yet they grant no ownership stake in those underlying assets. Because market conditions can shift quickly, trading these instruments carries considerable risk, up to and including the loss of your entire investment. In some regions, access to this product may be limited. The responsibility rests entirely with you to grasp the risks at stake and to judge whether this product suits your financial situation before entering into any transaction.* ## Disclaimer and risk warning The FX Perpetuals listed above bear no association, sponsorship, endorsement, or affiliation with any central bank, government authority, or financial institution responsible for issuing or managing the underlying currencies. FX Perpetuals are derivatives whose value is derived from the exchange rates of the relevant currency pairs. As such, FX Perpetuals do not constitute legal tender and confer no actual or beneficial ownership of any underlying currency. Nor do FX Perpetuals grant you any entitlement to interest, monetary policy rights, or any other rights connected to the underlying currencies or their issuing authorities. In addition, FX Perpetuals are not backed, insured, approved, or guaranteed by any government or institution. Trading these instruments carries risks, among them exchange rate risk, liquidity risk, interest rate risk, geopolitical risk, and risks stemming from central bank policy changes or macroeconomic events. We encourage you to do your own research and evaluate your risk tolerance carefully before trading. Past performance offers no assurance of future results. FX Perpetuals may be unavailable in certain jurisdictions, and each user is solely responsible for confirming that their use of this product complies with local laws and regulations. Digital assets are highly speculative and prone to considerable volatility; they can become illiquid at any moment, and investors may lose the full value of their investment. Before trading any digital asset, you should carry out your own research and gauge your risk tolerance. TxFlow accepts no liability for any losses you may sustain from trading FX Perpetuals. Nothing in this material should be taken as financial advice. For further details, please refer to our [Terms of Use](https://txflow.com/terms) and [Privacy Policy](https://txflow.com/privacy). --- # Pre-IPO Perpetuals Disclaimer Terms and risk disclosure for perpetual contracts whose underlying company has not yet completed an IPO. > **Warning:** A Pre-IPO perpetual references a private company's valuation, not its shares. You hold no equity, you receive no shares at any point, and the contract price is set by TxFlow market participants alone. It is not the company's IPO price and should not be relied on as an indicator of it. ## Nature of the product Pre-IPO Perpetuals are pre-market perpetual contracts whose underlying company has not yet completed an IPO. The contract gives you price exposure to changes in the underlying company's overall valuation (market cap); you do not hold any equity in the company, and you trade its valuation changes solely through the contract's price movements. The contract is settled exclusively in USDC. The contract's price is determined solely by market participants trading on TxFlow and does not represent an official valuation of the company, its actual IPO offering price, or its post-listing stock price. These contracts are not equity, securities entitlements, ownership interests, IPO allocations, tokenized shares, physical commodity claims, warehouse receipts, or any other form of claim against the referenced issuer, asset, or its custodian. Holders of a position receive no voting rights, dividend rights, information rights, registration rights, allocation rights, delivery rights, or other rights attaching to the underlying or its issuer, and will not receive any shares or securities at any point, including upon any conversion described above. ## No affiliation; not a regulated entity You understand and acknowledge that TxFlow is not registered as, nor purports to act as, a broker-dealer, exchange operator, transfer agent, custodian, or similar regulated entity in any jurisdiction. ## Eligibility, acknowledgments, representations and warranties By using any services, you acknowledge the relevant risks, agree to bear the trading risks on your own, and further acknowledge, represent and warrant that the following are true and correct as of the date hereof: 1. You are not domiciled in a Prohibited Jurisdiction under TxFlow's Terms of Use; 2. You are not purchasing the Pre-IPO Perpetual for the account or benefit of a person in a Prohibited Jurisdiction; 3. You, and any authorized director, employee, signatory or representative who placed the buy order, are not in any Prohibited Jurisdiction at the time the buy order was originated; 4. You are eligible to participate in Pre-IPO trading, whether as a professional, sophisticated or accredited investor, qualified purchaser, or otherwise, as may be required under the laws of your jurisdiction or other laws applicable to you; 5. You possess the requisite experience, knowledge and expertise in financial and business matters to evaluate the merits, risks and suitability of trading Digital Assets; 6. You have not entered into, and are not trading the Pre-IPO Perpetual with a view to entering into, any agreement or arrangement for the resale or transfer of the Pre-IPO Perpetual under which the economic benefits and risks would pass to another person; and 7. You have conducted your own research and understand that the Pre-IPO Perpetual confer no ownership, voting, dividend, or information rights, are not affiliated with, endorsed by, or issued by the referenced companies, and that administrative controls are in place as needed for legal, security, or operational purposes. ## Risks Trading perpetuals on TxFlow involves substantial risk and may result in a total loss of your assets. Specific risks include but are not limited to: **a) No-IPO and IPO-cancellation risk.** If the IPO is instead cancelled or postponed indefinitely, TxFlow reserves the right to delist the contract and settle it at a fair value price determined by TxFlow in its sole discretion. The settlement price may differ materially from any external valuation reference, your expected IPO valuation, or your entry price. **b) Price does not imply IPO pricing.** The contract price is set exclusively by TxFlow market participants and may not reflect, and should not be relied upon as an indicator of, the company's actual IPO offering price or post-listing stock price. ## No investment advice All information provided or referenced on TxFlow is for informational purposes only and shall not be construed or relied upon as legal, financial, tax, accounting, investment, or other professional advice; is not a recommendation or solicitation to buy or sell securities or to engage with the Pre-IPO Perpetual; is not guaranteed to be accurate, complete, or useful; is used at your own risk; and may change without notice. You are responsible for obtaining current information and performing your own due diligence before each use of any services. Past performance of any reference issuer, asset, or market does not predict future results. Users should consult qualified professionals before entering any position. ## Jurisdictional restrictions TxFlow is not available to residents of restricted jurisdictions provided in the Terms of Use. Users are responsible for verifying that their access to TxFlow complies with all applicable laws in their jurisdiction. Use of VPNs or other circumvention measures to access restricted services is prohibited. ## Modifications Specifications, parameters, and procedures described in this documentation are subject to change. **Where a material change is reasonably likely to adversely affect an open position, holders of that position as at the date of publication may close the position without penalty at any time before the change takes effect; this right does not apply to urgent changes made in response to market events, nor does it entitle a position holder to any specific price or outcome on closure.** ## Acknowledgment I have fully read, understand and accept the above and TxFlow's Terms of Use. I confirm that I am eligible to participate. --- Pre-IPO contracts follow the equity session schedule. See [Stock Perpetuals](/notices/stock-perpetuals.md) for regular trading hours and the holiday calendar. --- # How Points Work How the TxFlow Points Program measures trading, how each weekly pool is shared out, and what does not earn Points. > **Key takeaway:** Points are a **share of a weekly pool**, not a fixed reward per trade. **1,250,000 trader Points** are distributed every week among eligible traders, based on the overall quality of their **perpetual** trading. Activity that exists only to farm Points earns fewer Points or none. ## Why Points exist The TxFlow Points Program exists to reward real traders and support the healthy, long-term growth of the platform. Traders always come first. The trader pool is the largest pool in the program, and activity that only exists to farm Points earns fewer Points or none, which leaves more for genuine traders. ## A fair game for every trader Points are a share of a weekly pool, so your reward reflects how your trading compares with others that week. Every trader can earn a fair share of the pool: - **Everyone plays by the same rules.** All trading is measured against the same standards of genuine activity. - **No one can take the whole pool.** The scoring limits how much the very largest traders can dominate, so the pool stays open to everyone. - **Every eligible trader counts.** Our scoring is designed to recognize genuine trading across different trading volumes, giving smaller traders a real opportunity to take part. - **Farming doesn't pay off.** Activity that only exists to collect Points earns fewer Points or none, and those Points stay in the pool for genuine traders. ## How Points are distributed Season 1 starts on **October 12, 2026**, and weekly distribution will conclude no later than **Q1 2027**. Each week is one epoch, running Monday to Monday (UTC). Points are credited on **Tuesday** after each epoch ends and displayed on your Points dashboard. The first Season 1 drop will happen on **Tuesday, October 20, 2026**. Early activity before Season 1 has been accounted for in the Season 0 points distribution. More details found [here](/points/season-0-points.md). This weekly Points epoch is unrelated to the consensus epochs reported by [`epochHistory`](/api/info/epochHistory.md). | Pool | Share | Basis | Amount | |---|---|---|---| | Trader | 82% | Weekly | 1,250,000 | | Referral | 10% | Weekly | 152,000/Cap | | Growth Reserve | 8% | Monthly | 488,000/Month | - **1,250,000** trader Points are distributed every week among eligible traders. Your share reflects the overall quality of your trading across multiple factors, with volume as one part of the picture. - **152,000** Points is the weekly cap for total referral rewards. Referral rewards are 10% of the trader points earned by referred users. Total referral rewards will never exceed this weekly cap, including any future referral campaigns or incentives - **Growth Reserve** is a monthly budget of **488,000** Points for growth activities. Unused Points roll over to the next month, and any Points still unused at the end of Season 1 are burned. See [Growth Reserve](#growth-reserve) below. ## How to earn Points Trade perpetual futures on TxFlow. Both maker and taker trades count. Points are based on the overall quality of your trading, including: - Trading volume - Open interest (holding real positions) - Trading activity over time - Fees paid - TVL contribution (capital you keep on the platform) - Referrals The exact weights and formulas are not published, so the program rewards genuine trading rather than trading aimed at a formula. You need a minimum amount of genuine trading in an epoch to qualify. ### Do Vault deposits earn Points? Depositing into a Vault alone does not earn Points. To have your Vault contribution factored into your Points for an epoch, you must have eligible trading activity during that epoch. ## Activity that earns no Points - Spot trading and Prediction Markets (Season 1) - Orders that are placed but canceled before filling. - Depositing, withdrawing or holding a position on its own. Your balance and open positions still count toward your score when you're trading. ## Referrals - Anyone can share a referral code. No application is needed. - Referrers earn trader Points from their own trading like everyone else, and on top of that earn referral rewards based on their referred users' trading. - Referral rewards come from a **separate pool of up to 152,000 Points per week**, so they never reduce anyone's trader Points, including the referred user's own. - Any referral Points not used within a given week are burned at the end of that week. They do not carry over. - A referral link is set when someone signs up and cannot be changed later. To get a referral code to share, see [Referral](/trade/referral.md). Referral Points are separate from the fee rebate and fee discount described there. ## Growth Reserve The Growth Reserve is a monthly budget of 488,000 Points, drawn from the 8% share of the Season 1 budget. It funds growth activities such as community trading campaigns and builder programs. Points not used in a month roll forward to the next month, and any Points still unused at the end of Season 1 are burned. - **Why monthly, not weekly:** growth activity doesn't follow a weekly cycle, and market conditions can change. We adjust the budget accordingly. A monthly budget gives room to respond without locking Points into weeks where they wouldn't be used. - **Rollover:** if the monthly budget is not fully used, the remainder rolls forward to the next month. - **Burn:** any Points still remaining in this pool at the end of the season are burned. - **Use:** Growth Reserve Points may fund growth activities, including future community trading campaigns and builder programs. Growth Reserve Points will never be used by the TxFlow team for its own benefit. ## Keeping the game fair Our checks exist to protect genuine traders. We use automated and manual systems to detect Sybil accounts and farming, such as wash trading, deliberately creating multiple accounts (manually or in an automated way) to farm Points systematically, and artificial volume. Activity identified as farming earns fewer Points or none, and those Points go to genuine traders instead. Our detection is designed to identify abusive activity while minimizing the impact on legitimate traders. The signals we use are not disclosed, so they cannot be worked around. Full details are in the [Points Program Terms](/terms/points-program-terms.md). ## Your Points - Points stay with the wallet that earned them and cannot be transferred, bought or sold. - TxFlow never sells Points and does not allow anyone to sell them. Anyone offering to sell Points is running a scam. - Keep your wallet secure. Points cannot be moved to a new wallet if access to the original wallet is lost. > **Info:** Points are a measure of your trading contribution to TxFlow. Read the full [Points Program Terms](/terms/points-program-terms.md) for details. --- # Season 0 Points To recognize users who supported TxFlow in its early stages, **10,769,544** Season 0 Points were distributed on **October 27, 2026**. Eligible users who were active on TxFlow before Season 1 began on **October 12, 2026**, do not need to take any action. Points have been credited to their accounts and are visible on the Points Dashboard. Season 0 used the same base point criteria as Season 1. Referrers received 10% of the trader points earned by their referred users. Spot and prediction markets, market makers, and vault addresses were excluded from the Season 0 allocation. --- # Builder Program **One integration. All markets. Earn on every trade.** The TxFlow Builder Program connects your product to all TxFlow markets with one integration. Your users access shared liquidity, and you earn trading fees at your own fee rate. Ready to start? See [Getting Started](/builders/getting-started.md) or [apply as a builder](https://txflow.com/apply). ## Why build on TxFlow | Advantage | What it means for you | |---|---| | Real-time, on-chain revenue attribution | Revenue is attributed to each individual fill and verifiable on-chain. Earnings accrue in real time, with no lockup and flexible claiming. | | Dual rewards: fees + points | Earn builder fees in real time and access a separate Builder Points pool. Both reward mechanisms operate independently. | | Non-custodial by design | Builder codes track trade attribution without accessing user funds. Users retain full control over their funds and withdrawals. | | Simple integration, faster launch | Signed API actions cover authorization, order placement and claiming in one flow, with step-by-step integration guides and a browser-based [testnet quickstart](/try-testnet/quickstart.md). | ## Who it's for | Builder type | Typical product | |---|---| | [Wallets](/builders/types-and-integration.md#wallets) | In-app trading inside a self-custody wallet | | [Trading apps and terminals](/builders/types-and-integration.md#trading-apps-and-terminals) | Pro terminals, mobile apps, aggregators | | [Bots, Telegram apps and AI agents](/builders/types-and-integration.md#bots-telegram-apps-and-ai-agents) | Trading bots, copy trading, Telegram apps, AI agents | | [Institutions and brokers](/builders/types-and-integration.md#institutions-and-brokers) | Brokers, exchanges, large wallets, distribution partners | ## How it works 1. **Approve your fee.** Your user signs an approval from their main wallet, naming your builder address and the maximum fee you can charge. 2. **Attach your builder code.** Attach your builder code to user orders. The code is your address and fee rate, and it is covered by the order signature. 3. **Earn fees on-chain.** When an order fills on TxFlow, your fee is calculated from the fill and credited to you on-chain. 4. **Claim your earnings.** Claim your fees at any time. Claimed fees are credited to your Perps account, where you can withdraw them. The protocol-level detail of each step is in [Technical Documentation](/builders/technical.md). ## Key numbers | Item | Value | |---|---| | Builder fee (perpetuals) | 0–0.1% | | Builder fee (spot) | 0–1%, charged on sell fills only | | Builder fee (prediction markets) | 0–1% | | Builders a user can approve at once | 20 | | Minimum balance to earn | 100 USDC in your builder Perps account | | Typical integration time | 1–2 weeks | ## Next steps - [Builder Types & Integration](/builders/types-and-integration.md) — what to build for your kind of product. - [Fees & Benefits](/builders/fees-and-benefits.md) — builder fees, referral commission and growth incentives. - [Getting Started](/builders/getting-started.md) — requirements, onboarding and the launch checklist. --- # Builder Types & Integration Every builder uses the same builder code. What differs is how users are onboarded and who places their orders. ## Builder types | Builder type | Use case | Who places orders | Typical integration | What you'll use | |---|---|---|---|---| | [Wallets](#wallets) | Trade every TxFlow market without leaving the wallet | The user, from your wallet UI | ~2 weeks, full | REST API | | [Trading apps and terminals](#trading-apps-and-terminals) | Pro terminal, mobile app or aggregator on TxFlow's order book | The user, from your interface | ~1 week, light | REST + WebSocket | | [Bots, Telegram apps and AI agents](#bots-telegram-apps-and-ai-agents) | Strategies, copy trading, chat or agent-driven trading | Your agent, on the user's behalf | ~1 week, light | REST + WebSocket | | [Institutions and brokers](#institutions-and-brokers) | Route client flow at scale | Your platform | Case by case | Custom integration | ## Integration depth | Depth | What you build | Typical effort | |---|---|---| | Light | Add your builder code to an existing trading flow | ~1 week | | Full | Approval UI, deposits, withdrawals and fee display | ~2 weeks | ## Wallets Enable users to trade across all TxFlow markets without leaving your app, while they keep control of their keys. ### User flow 1. **Connect wallet.** The user's wallet address is their TxFlow account. No separate sign-up is required. 2. **Deposit funds.** Transfer USDC to the user's TxFlow Perps account. See [Gas-Free Deposit](/getting-started/gas-free-deposit) for details. 3. **Approve builder fees.** The user signs [`approveBuilderFee`](/api/exchange/approveBuilderFee.md) once from their main wallet to authorize your builder fee. 4. **Authorize trading.** The user signs a one-time, gasless authorization ([`approveAgent`](/api/exchange/approveAgent.md)) so your app can place orders. 5. **Place orders.** Your app submits orders with your builder object attached. 6. **Withdraw funds.** The user withdraws funds from their Perps account back to their wallet. Steps 2–4 must happen in this order — see [Set up an account](/api/flows.md#set-up-an-account). ### Approval screen The approval happens in your wallet's own signing UI. Before the user signs, show: | Field | What to show | |---|---| | Builder | Your app or platform's name and builder address | | Max fee rate | The highest rate you may charge, for example 0.05% | | Trading permission | What your app can do on the user's behalf | | Duration | Permanent until the user revokes it | ### Fee display Show the TxFlow trading fee and your builder fee as separate lines in your trade ticket. On spot, the builder fee is charged on sells only, so a buy ticket should show no builder fee — see [Spot: sell fills only](/builders/fees-and-benefits.md#spot-sell-fills-only). ## Trading apps and terminals Build a trading terminal, mobile app or aggregator on TxFlow's order book — without running a matching engine or holding user funds. | You build | TxFlow provides | |---|---| | Interface, charts, order entry | Order book, matching, margin and liquidation | | User onboarding and fee display | Settlement and custody on TxFlow L1 | | Your own features (alerts, analytics, strategies) | Market data across all TxFlow markets | ### Integration steps 1. Read market data from the TxFlow API — the [Info API](/api/info.md) and [WebSocket API](/api/websocket.md). 2. Onboard users: connect wallet, deposit, enable trading. See [Wallets](#wallets) for the full flow. 3. Ask each user to approve your builder fee once. 4. Add your builder object to every order. 5. If a user hasn't approved you, prompt them to approve before routing, or route without a fee. **Aggregators:** attach your builder code to every order you send to TxFlow. Each user approves you once, regardless of how many TxFlow markets they trade. ## Bots, Telegram apps and AI agents Build trading bots, copy-trading tools, Telegram trading apps or AI agents on TxFlow. Users authorize once, your service places orders on their behalf, and you earn builder fees on every fill. | Signature | What it allows | Who can grant it | |---|---|---| | [`approveBuilderFee`](/api/exchange/approveBuilderFee.md) | You charge a builder fee up to `maxFeeRate` | The user's main wallet only. Agents cannot grant it | | Enable trading ([`approveAgent`](/api/exchange/approveAgent.md)) | Your agent places and cancels orders for the user | The user's main wallet | ### Integration steps 1. Direct users to a signing page to connect their wallet. 2. Collect both signatures — builder fee approval, then trading authorization — clearly displaying the fee rate and the agent's permissions. 3. Attach your builder object to every order your agent submits. 4. Display builder fees on each trade confirmation in your chat or dashboard. ## Institutions and brokers For brokers, large wallets, exchanges and distribution partners routing client flow at scale. | | Standard builders | Institutions and brokers | |---|---|---| | Onboarding | Self-serve testnet, then mainnet application | Direct with the TxFlow BD team | | Commercial terms | Program tiers | Bespoke terms available | | Support | Docs and community channel | Dedicated BD contact | | Verification | Not required at entry level | Entity verification required | **Custodial products:** if your platform holds client funds, a separate integration path is available. Contact the TxFlow BD team before you start building — [apply as a builder](https://txflow.com/apply). **Treasury:** builder addresses must be EVM externally owned accounts. Multi-sig and contract addresses are not supported, so claim to the builder address and then transfer to your treasury. --- # Builder Fees & Benefits Builders earn from two sources on the same trader: the **builder fee**, paid by the user, and **referral commission**, paid by TxFlow out of the trading fee. Growth incentives come on top. ## Builder fees - You set the fee per order with `f`, up to the user's approved rate and the market cap. - The user pays it on top of the TxFlow trading fee. It never changes TxFlow's own fee. - Fees accrue on every fill. Claim any time with [`claimBuilderRewards`](/api/exchange/claimBuilderRewards.md), free of charge. - No builder fee is charged on liquidation or ADL fills. | Market | Max builder fee | |---|---| | Perpetuals | 0.1% (10 bps) | | Spot | 1% (100 bps), sell fills only | | Prediction markets | 1% (100 bps) | ``` Total fee paid by the user = TxFlow trading fee + builder fee ``` ### Spot: sell fills only On spot, the builder fee is **one-sided**: it is charged only when your user's order **sells** the base asset, never when it buys. | Your user's spot fill | Builder fee | Paid in | |---|---|---| | Sell | `fill notional × your rate` | USDC, deducted from the quote the user receives | | Buy | None | — | - A buy order still accepts your builder object and still fills — the fee rate on it is simply never charged, so there is nothing to reject or retry. - It applies whether the sell is the maker or the taker side of the fill. - The TxFlow spot trading fee is separate and charged on **both** sides: in the base asset on a buy, in the quote asset on a sell (see [Fees & Discounts](/trade/fees.md)). So spot earnings depend on how much of your users' spot volume is selling, not on total spot volume — a user who buys $10,000 of a token and later sells it pays the builder fee once, on the sell. Price your spot rate and show it in your ticket with that in mind. ## Referral commission Traders who bind your [referral code](/trade/referral.md) earn you a share of their net perpetual trading fees, after their discount. | Item | Rule | |---|---| | Commission rate | 10% of the trader's net perpetual trading fees | | Trader discount | 5% off spot and perpetual fees on the trader's first $25M combined volume | | Markets covered | Perpetuals only. On spot and prediction markets you earn the builder fee only | | Settlement | Daily, credited to your Perps account | | Traders referred by someone else | You still earn your full builder fee. The commission stays with their original referrer | | Fee stacking | Each fill pays one TxFlow commission (referral or [affiliate](/trade/affiliate.md)); your builder fee is charged separately on top | ## Growth incentives Eligible builders can unlock commission boosts, builder points, grants and co-marketing opportunities as they grow. | Tier | Referral commission | Main benefits | |---|---|---| | Builder | 10% | Builder points, ecosystem listing, docs and community channel | | Partner | Commission boost (subject to review) | Integration grants, co-funded competitions, joint launch, customized partnership | | Strategic | Commission boost (subject to review) | Ecosystem fund, co-branded campaigns, early access to new markets, customized partnership, 24/7 support | Partner and Strategic tiers are by review. **Contact the TxFlow BD team for a customized partnership.** **Builder points:** builders earn points on volume routed with their builder code. Volume flagged by risk controls is excluded. Points follow the [Points Program Terms](/terms/points-program-terms.md). ## Earnings examples Illustrative only. Actual earnings depend on your users' volume, your fee rate and which traders are bound to your referral code. ### Example 1: perpetuals A trader bound to your referral code generates $1,000,000 in perpetual taker volume at VIP 0, with a 0.045% [trading fee](/trade/fees.md) and a 0.05% builder fee. | Item | Amount | |---|---| | Builder fee (0.05%) | $500.00 | | TxFlow perpetual fee after the trader's 5% discount | $427.50 | | Your referral commission (10%) | $42.75 | | **You earn** | **$542.75** | ### Example 2: prediction markets Your users trade $1,000,000 on prediction markets, with a 0.5% builder fee. | Item | Amount | |---|---| | Builder fee (0.5%) | $5,000.00 | | Referral commission | Not applicable | | **You earn** | **$5,000.00** | ### Example 3: spot Your users trade $1,000,000 on spot — $600,000 of buys and $400,000 of sells — with a 0.5% builder fee. | Item | Amount | |---|---| | Builder fee on buys ($600,000) | $0.00 | | Builder fee on sells ($400,000 × 0.5%) | $2,000.00 | | Referral commission | Not applicable | | **You earn** | **$2,000.00** | --- # Getting Started Testnet is open to everyone. Mainnet builder codes are available through **limited access**: apply once your testnet integration is complete. Most teams go live within 1–2 weeks. ## Requirements | Requirement | Detail | |---|---| | Builder address | A new EVM externally owned account. No contract or multi-sig addresses, and not an address you trade from | | Balance | At least 100 USDC in the builder address's Perps account while you earn | | Mainnet access | An approved application and a whitelisted mainnet builder address | | Cost to join | None | ## Onboarding process 1. **Submit an application.** Fill out the [application form](https://txflow.com/apply). A dedicated BD contact will reach out to discuss partnership details. 2. **Build on testnet.** Open to everyone. Run the full flow in your browser with [Try Testnet](/try-testnet/quickstart.md), and claim 1,000 test USDC per hour via **Claim Test Fund** in the [testnet app](https://txflow-testnet.xyz/trade). 3. **Apply for mainnet.** Submit your product details, target users and planned order flow. 4. **Join the integration group.** Your BD contact creates a Telegram group with a technical contact. 5. **Get whitelisted.** Send your mainnet builder address to your BD contact. 6. **Fund and verify.** Deposit at least 100 USDC and complete one live order. 7. **Go live.** Builder fees accrue from your first fill. ## Onboarding checklist ### Testnet - [ ] Create a dedicated testnet builder address (EVM EOA) - [ ] Claim test USDC and deposit at least 100 USDC to the builder Perps account - [ ] Confirm `eligible` is `true` in [`builderState`](/api/info/builderState.md) - [ ] Approve your builder fee from a test user wallet - [ ] Place orders with your builder object and confirm fees accrue - [ ] Call [`claimBuilderRewards`](/api/exchange/claimBuilderRewards.md) and confirm the balance moves to your Perps account ### Mainnet - [ ] Submit the [builder application form](https://txflow.com/apply) - [ ] Join the Telegram integration group - [ ] Send your mainnet builder address to BD for whitelisting - [ ] Deposit at least 100 USDC to your mainnet builder Perps account - [ ] Run one live order end to end and confirm your fee accrues ### Before you launch - [ ] Your approval screen shows builder name, address, max fee rate, trading permission and duration - [ ] Your trade ticket shows the builder fee separately from the TxFlow fee - [ ] You check each user's approval before routing - [ ] You have read the [Program Rules](/builders/program-rules.md) --- # Program Rules These rules keep rewards flowing to builders who bring real traders. Breaking them can lead to clawback of rewards and removal from the program. ## Partnership standards - **Real user activity.** Trading volume from self-owned or linked wallets, or self-matched trades, does not qualify for commissions, points or tier credit. - **One entity, one account.** All builder addresses controlled by the same entity are treated as a single builder. - **Entity verification.** Required for the Partner and Strategic tiers. - **Ecosystem listing.** Official partner listings on txflow.com require BD approval, based on brand recognition and user volume. ## Fee rules - Builder fees must not exceed the market cap or the user's approved rate. If either limit is exceeded, the order still fills, but no builder fee is charged. - No builder fees apply to liquidation or ADL fills. - On spot, builder fees are charged on sell fills only. Buy fills carry no builder fee, whatever `f` the order sets. - [Fee credits](/trade/fee-credits.md) cover TxFlow trading fees only and do not cover builder fees. - Fee caps are set by the platform. Changes are announced in advance with a transition period. Builder fees already accrued are unaffected. ## Reward conditions - Referral commissions are calculated on TxFlow fees after trader discounts and fee credits. Builder fees are excluded from commission and points calculations. - Volume flagged by risk controls is not eligible for points. - Points and pool rewards are subject to the [TxFlow Points Program Terms](/terms/points-program-terms.md), including anti-farming and anti-sybil reviews. ## User consent Users must explicitly approve builder fees. Before the user signs [`approveBuilderFee`](/api/exchange/approveBuilderFee.md), your interface must display your app name, builder address, maximum fee rate, trading permissions and authorization duration. Silent approvals, pre-selected referral binding, and approval requests above the fee rate you charge are prohibited. ## Risk control - TxFlow monitors builder activity and may restrict addresses that show abnormal behavior. - Builder addresses cannot be used as trading addresses, which prevents self-approval at the protocol level. - Front-running, or trading ahead of routed order flow, is prohibited. - Report suspected abuse to your BD contact. --- # FAQ ## Overview **What is a builder code?** A builder code attributes orders to your platform, so you earn builder fees on each eligible fill. **Who can become a builder?** Trading terminals, wallets, aggregators, bots, Telegram apps, AI agents and other platforms routing orders through TxFlow can apply. **How do I get started?** Submit an application, complete your testnet integration, and obtain mainnet approval and whitelisting. Once verified, you can go live. See [Getting Started](/builders/getting-started.md). ## Fees & earnings **How much can I charge?** Up to the user's approved rate and the market cap: 0.1% on perpetuals, 1% on spot and 1% on prediction markets. If either limit is exceeded, the order still fills, but no builder fee is charged. **Why don't I earn on spot buys?** The spot builder fee is one-sided: it is charged only on sell fills, in USDC, out of the quote the user receives. Buy orders can carry your builder object and fill normally, but no builder fee is charged on them. See [Spot: sell fills only](/builders/fees-and-benefits.md#spot-sell-fills-only). **Do users pay additional fees?** Yes. Builder fees are charged on top of TxFlow trading fees. Users bound to your referral code receive a 5% discount on TxFlow trading fees. **When and how can I claim my earnings?** Builder fees accrue on-chain with each fill. Claim your available balance at any time with [`claimBuilderRewards`](/api/exchange/claimBuilderRewards.md), free of charge. Claimed funds are credited to your Perps account and can then be withdrawn. **Can builder fees stack with referral or affiliate commissions?** Yes. Builder fees are separate from TxFlow commissions. Each fill generates only one TxFlow commission, either referral or affiliate, while your builder fee is earned in addition. **What happens if a user has not approved my builder fee?** The order can still fill, but no builder fee is charged. Make sure the user's approval is in place before routing orders — you can check it with [`maxBuilderFee`](/api/info/maxBuilderFee.md). ## Integration & authorization **How long does integration take?** Most teams go live within 1–2 weeks, depending on integration scope and testing. **Which programming languages and environments are supported?** Any language that can sign EIP-712 messages. TxFlow provides REST and WebSocket APIs with no API keys required. Testnet and mainnet APIs are aligned, so you can test before going live. **What must users authorize?** Users approve the builder fee and sign a trading authorization. Before they sign, your interface must clearly display your app name, builder address, maximum fee rate, trading permissions and authorization duration. **Can I change my builder fee rate?** Yes, within the user's approved rate and the market cap. Raising the approved rate or changing other authorization parameters requires the user to sign a new approval. ## Risk & compliance **Are all trades eligible for builder fees and rewards?** No. Liquidation and ADL fills are not eligible for builder fees. Volume flagged by risk controls is excluded from points and other applicable rewards. **Can I route trades from my own wallets?** Self-trading, self-matched trades and volume generated through your own or linked wallets do not qualify for commissions, points or tier credit. **What happens if my builder address is flagged?** TxFlow monitors builder activity and may restrict addresses that show abnormal behavior. Contact your BD representative if you suspect an issue. --- # Technical Documentation How builder codes work at the protocol level, plus the API and actions you integrate with. Builder codes apply to **perpetual, spot and prediction (outcome) markets** — on spot, to sell fills only — and everything below runs on testnet today — no application needed. ## Builder address - An EVM externally owned account (EOA). Contract and multi-sig addresses are not supported. - It cannot be the same address you or your users trade from, and cannot be the zero address or a vault. - Use separate addresses for testnet and mainnet. ## Eligibility Your builder address earns fees only while its **Perps account holds at least 100 USDC**. Below that, orders still fill, but the builder fee on those orders is `0`. This is a check on your own perpetual sub-account, not on the market being traded. Check the `eligible` flag in [`builderState`](#check-your-builder-state). ## User approval - The user signs [`approveBuilderFee`](/api/exchange/approveBuilderFee.md) from their **main wallet** (EIP-712), naming your builder address and a `maxFeeRate`. - Agent wallets and API keys cannot grant this approval — it is the user's own decision about their money. - A user can approve up to **20** builders at once. Submitting the action again for the same builder **updates** the rate without using another slot. - The approval is live once the action lands in a block. The user can remove it at any time with [`revokeBuilderFee`](/api/exchange/revokeBuilderFee.md), which sets the rate to `0` and marks the approval `REVOKED`. ```json { "type": "approveBuilderFee", "txflowNetwork": "TxFlow-Testnet", "chainId": 2511, "apiVersion": 1, "builder": "0x2e4D75dF2eE2653Ea7A113Cc1Fa36d45e923061F", "maxFeeRate": "0.05%", "nonce": 1757992743994 } ``` `maxFeeRate` is a **percentage string** (`"0.05%"`), greater than zero and at most `1%`. In a new account's setup, the approval comes after the deposit and before the user authorizes your agent key — see [Set up an account](/api/flows.md#set-up-an-account). Before routing, confirm the approval and its cap with [`maxBuilderFee`](/api/info/maxBuilderFee.md), or list every builder the user has approved with [`approvedBuilders`](/api/info/approvedBuilders.md). ## Attaching your code to orders Add a `builder` object at the action level of each [`order`](/api/exchange/order.md). It sits beside `orders`, not inside an individual order, and **the order signature covers it**. ```json { "type": "order", "grouping": "na", "orders": [ { "a": 1, "b": true, "p": "50000.0", "s": "0.01", "r": false, "t": { "limit": { "tif": "Gtc" } } } ], "builder": { "b": "0x2e4D75dF2eE2653Ea7A113Cc1Fa36d45e923061F", "f": 50 } } ``` - `b` is your builder address. - `f` is your fee in **tenths of a basis point**: `f: 50` is `0.05%`. This unit is the easiest thing to get wrong: | `f` | Basis points | Percent | |---|---|---| | `1` | 0.1 bp | 0.001% | | `10` | 1 bp | 0.01% | | `100` | 10 bp | 0.1% | | `1000` | 100 bp | 1% | `f` must satisfy **both** the user's approval and the market cap: | Market | Max builder fee | Max `f` | |---|---|---| | Perpetuals | 0.1% | `100` | | Spot | 1% | `1000` | | Prediction (outcome) markets | 1% | `1000` | ``` Builder fee = notional value × f / 100,000 ``` ### Spot: sell fills only On spot, the protocol charges the builder fee on the **sell** leg only: - **Sell fill** — the fee is deducted in USDC from the quote the user has just received, and accrues to you like any other builder fee. - **Buy fill** — no builder fee. The order is still valid with a `builder` object attached, and still fills; the rate is ignored. This is by design: a spot order reserves only its principal when it rests, so a buy has no USDC set aside to pay a builder fee from, while a sell receives USDC on the fill. Because the builder object is harmless on buys, you can attach it to every spot order and let the protocol charge only where it applies. Show no builder fee on buy tickets. > **Warning:** If the builder object is absent, the user has not approved you, or `f` exceeds either cap, **the builder fee is dropped and the order still fills** — there is no rejection to debug. Watch `accruedRewards` in [`builderState`](#check-your-builder-state) to catch it. Builder fees are **additional** to TxFlow trading fees: the user pays their own [fee tier](/trade/fees.md) plus your builder fee. ## Claiming - Fees accrue to `claimableRewards` in real time. - [`claimBuilderRewards`](/api/exchange/claimBuilderRewards.md) sweeps the **full** balance to your builder Perps account. Partial claims are not supported. It is signed by an **agent key** authorized by your builder address, not by the builder address itself. - The claim moves `claimableRewards` into `claimedRewards`; `accruedRewards` is the lifetime figure and does not change. With nothing to claim, the action fails with `No unclaimed builder rewards`. ```json { "type": "claimBuilderRewards" } ``` ## Check your builder state [`builderState`](/api/info/builderState.md) shows your lifetime accrued, claimable and claimed amounts, your eligibility, and your active user count. Pass your **builder** address as `user`. ```json { "type": "builderState", "user": "0x4916d34209A2E010912dE8b412A90ECcEc77D8aC" } ``` ```json { "accruedRewards": "0.000000", "claimableRewards": "0.000000", "claimedRewards": "0.000000", "eligible": true, "perpsAccountValue": "200.012971", "isStandardAccount": true, "activeUserCount": 0 } ``` | Field | Meaning | |---|---| | `accruedRewards` | Lifetime total accrued to you; unaffected by claiming | | `claimableRewards` | Unclaimed balance — what the next claim pays out | | `claimedRewards` | Total already claimed | | `eligible` | Whether you currently qualify to accrue builder fees | | `perpsAccountValue` | Your available Perps account balance in USDC, checked against the `100` minimum | | `isStandardAccount` | Whether your address is a standard account | | `activeUserCount` | Users currently approving you as a builder | If `accruedRewards` is not moving on flow you are tagging, check `eligible` first. ## Builder actions | Action | Signed by | Purpose | |---|---|---| | [`approveBuilderFee`](/api/exchange/approveBuilderFee.md) | User's main wallet | Approve a builder and a max fee rate | | [`revokeBuilderFee`](/api/exchange/revokeBuilderFee.md) | User's main wallet | Remove a builder approval | | [`builder` object on orders](/api/exchange.md#attach-a-builder-to-an-order) | Covered by the order signature | Attribute an order and set its fee | | [`claimBuilderRewards`](/api/exchange/claimBuilderRewards.md) | Agent key | Sweep claimable fees to the Perps account | | [`builderState`](/api/info/builderState.md) | Read only | Eligibility, accrued, claimable and claimed fees, active users | | [`maxBuilderFee`](/api/info/maxBuilderFee.md), [`approvedBuilders`](/api/info/approvedBuilders.md) | Read only | A user's approval of you, and all their approved builders | Mainnet and testnet APIs are the same. A working testnet integration needs no code changes beyond the network endpoint and your mainnet builder address. ## Development guide 1. **Set up addresses.** Create separate builder EOAs for testnet and mainnet. Endpoints, signing and every action are documented in the [Builder API Reference](/api.md). 2. **Fund your builder account.** Deposit at least 100 USDC and verify eligibility with `builderState`. 3. **Implement user onboarding.** Guide users through wallet connection, deposit, `approveBuilderFee`, and trading authorization (`approveAgent`). 4. **Verify authorization.** Check the user's approved fee rate with `maxBuilderFee` before routing orders, and make sure `f` does not exceed it. 5. **Attach the builder object.** Include the builder object in every order. 6. **Display fees.** Show builder fees separately in trade tickets and confirmations. 7. **Monitor and claim.** Track earnings with `builderState` and call `claimBuilderRewards` to claim available fees. 8. **Test and deploy.** Complete end-to-end testing on testnet with [Try Testnet](/try-testnet/quickstart.md), then switch to the mainnet endpoint and builder address. --- # Consensus & Execution TxCore is the trading core of TxFlow: the consensus layer that orders transactions and the deterministic state machine that executes them. This page covers how transactions become ordered, finalized blocks and how those blocks are replayed identically by every node. ## Tx-BFT consensus TxFlow orders transactions with **Tx-BFT**, a **DAG-based (directed acyclic graph) Byzantine Fault Tolerant protocol**. - Validators organize transaction batches as **vertices in a DAG** in parallel. The protocol deterministically extracts a globally consistent commit sequence from the DAG. There is no single-leader, block-by-block proposal bottleneck, so throughput scales with validator parallelism. - A batch reaches **finality** once a **2f+1** quorum of voting power confirms it. Finality is **non-reversible** — there is no chain reorganization. ### The transaction batcher Between clients and consensus sits a **transaction batcher** that groups transactions into batches to amortize consensus overhead. It exposes three confirmation states to clients: | State | Meaning | |---|---| | `Submitted` | accepted and handed to consensus | | `Included` | placed into a proposed block | | `Committed` | reached quorum and is final | ### Consensus output For each committed batch, consensus emits: - a **globally sequential block number**, - a **commit digest**, and - a **consensus timestamp** — the finality time agreed by consensus for the batch. The consensus timestamp is the **single clock** for all time-dependent logic in the state machine — funding settlement, order expiry (GTD), TWAP slicing — which guarantees identical results across the network. The state machine never reads wall-clock time. ## Deterministic execution Each committed block is executed by a **single-threaded state machine** under fixed rules. Every node that replays the same block produces byte-for-byte identical state. The block executes in five stages: 1. **Signature verification** — each transaction's [EIP-712](https://eips.ethereum.org/EIPS/eip-712) signature is recovered and checked against the claimed sender in a pre-pass; any transaction that fails authentication is rejected, ensuring only validly signed operations reach execution. This is pure computation and does not affect determinism. 2. **Deterministic ordering** — operations are placed in a canonical order derived from the committed sequence, so every node executes the block identically. 3. **Per-transaction execution** — for each transaction: validate the nonce → route to the appropriate handler (match / clear / transfer / …) → collect state-change events and the set of affected users. 4. **Block-tail processing** — funding settlement, margin recomputation, liquidation checks, and ADL. On the ordinary path this runs **incrementally** on accounts touched by the block; an **oracle update** in the same block triggers a full margin recalculation so every position is marked to the new prices. 5. **Event output** — all state changes are emitted as an event stream, consumed by persistence and real-time push. --- # Matching Engine Every instrument has its own in-memory matching-engine instance. Matching runs inside the deterministic state machine, so fills are reproducible by any node replaying the block. ## Supported markets | Market | Description | |---|---| | **Spot** | direct asset-for-asset matching; no leverage or position tracking | | **Perpetual** | leveraged contracts with position tracking, margin, funding, liquidation, and ADL | | **Outcome** | prediction markets traded as `Yes` / `No` shares; runs on the perpetual matching path and settles once the market resolves | ## Order book structure - Each side (bid / ask) is organized by **price level**, kept in price order. - Within a price level, orders queue by **arrival order**. - Together this gives strict **price–time priority**. ## Order types | Type | Behavior | |---|---| | **Market** | executes immediately against the book at the best available prices | | **Limit** | rests on the book at a specified price | | **Trigger (TP/SL)** | stored separately; activates into the book when the **mark price** crosses the trigger price | ## Time-in-force (TIF) | TIF | Meaning | |---|---| | `GTC` | good-til-canceled — rests until canceled | | `IOC` | immediate-or-cancel — fill what crosses now, cancel the remainder | | `ALO` | post-only — rest only; if it would cross immediately, the order is **rejected** | | `GTD` | good-til-date — valid until a specified time (measured by the consensus timestamp); requires an expiry | Availability differs by instrument type: **perps** reject `GTD`; **spot** rejects `GTD` and trigger orders; **outcome** limit orders accept only `GTC`, `GTD`, and `ALO`, and reject trigger orders. Reduce-only is available on perps and outcome, not on spot. ## Perpetual-specific rules - **Reduce-only** orders may only reduce an existing position; they can never open or increase one. - **Trigger orders** are stored outside the book and activate when the mark price crosses the trigger price, then enter the book as the configured order type. ## TWAP A TWAP order slices a large order into a series of child orders executed across blocks, reducing market impact. Slicing is driven by the consensus timestamp, so the schedule is deterministic across nodes. See the [Exchange API](/api/exchange.md) for the TWAP operation. ## Protections & constraints - **Self-trade prevention** — if a new order would trade against a resting order from the same account, the resting order is **cancelled** and matching continues. The incoming order is not cancelled by this check. - **Minimum order size** — each instrument limits the minimum order size by its size precision, preventing dust orders. See [Instruments](/tip/instrument-config.md) for how size precision maps to the minimum order size. - **Order-size caps** — instruments may cap the size of a single limit order and a single market order. - **Price limits** — the oracle can publish per-instrument buy/sell price bounds; orders outside the bounds are rejected. See [Oracle](/txcore/oracle.md). ## From match to settlement Matching produces **fills**. Fills are handed to the clearing module, which converts them into balance and position changes atomically in the same block — see [Clearing & Settlement](/txcore/clearing-settlement.md). --- # Clearing & Settlement The clearing module turns fills produced by the [matching engine](/txcore/matching-engine.md) into balance and position changes. All of it completes **atomically within the same block** — there is no separate settlement cycle. ## Settlement by instrument type | Type | Settlement | |---|---| | **Spot** | settles on fill — the buyer's and seller's spot balances are swapped directly | | **Perps** | per-fill update of the position (size, average entry price) and realized PnL; funding continues to accrue against the open size and is settled on the funding cycle | | **Outcome** | shares trade as synthetic assets; at market resolution they are redeemed to the margin asset at 0 or 1 | See [TIP — Improvement Proposal](/tip/overview.md) for the full per-type semantics. ## Fees Fees are charged at settlement: - Each user is matched to a **single fee tier** based on their **14-day rolling trading volume**, which combines weighted perpetual and spot volume. The matched tier then supplies four rates: perps maker/taker and spot maker/taker. Outcome instruments use their own type-specific fee schedule instead of the perpetual / spot tier ladder. - When no tier ladder is configured, the instrument's own base maker/taker rates apply. - **Referral system:** - a referred user pays a discounted fee (billed at **95%** of the standard rate); - the referrer earns a rebate on those fees (default **10%**); - the discount has a per-address volume cap. - Remaining fees accrue to the protocol fee account. > **Note:** Exact fee schedules, tier thresholds, and referral caps are governance parameters and can change. Query current values through the [Info API](/api/info.md). ## Realized vs. unrealized PnL (perps) - **Realized PnL** is booked when a position is reduced or closed — the difference between the average entry price and the fill price, times the closed size. - **Unrealized PnL** is marked continuously against the **mark price** and feeds the margin / health calculation (see [Margin System](/txcore/margin-system.md)). - **Funding** is accrued per position and settles into margin (isolated) or account equity (cross) — see [Risk Engine § Funding](/txcore/risk-engine.md#funding-rate). --- # Margin System The margin system governs how perpetual positions are collateralized and when they become eligible for liquidation. ## Margin modes - **Cross** — the collateral held in the perp account jointly backs all cross positions. Profit on one position can cover loss on another. - **Isolated** — each position locks its own margin. Loss is capped at the isolated margin; risk does not spill over to other positions. ## Position modes - **One-Way** — a single net position per instrument **within one margin mode**. - **Hedge** — long and short positions in the same instrument can be held simultaneously. A position is keyed by account + instrument + **margin mode** + position mode + side, so the One-Way net applies inside a single margin mode: a cross long and an isolated short in the same instrument are two independent positions and do not net against each other. See [Margin & Leverage](/trade/margin-and-leverage.md) for the trading-side view. ## Tiered margin The maintenance margin rate (MMR) and maximum leverage are **tiered** by **position size**. Each instrument publishes its own tier table. Larger positions sit in a higher tier: higher MMR, lower max leverage. | Field | Meaning | |---|---| | Tier | tier index, ordered by position size | | Max leverage | maximum leverage allowed in this tier | | Maintenance margin rate | maintenance margin rate (MMR) for this tier | ## Core formulas ``` initial margin = size × mark price / leverage maintenance margin = size × mark price × MMR liquidation fee = size × mark price × taker fee rate balance = available balance + balance frozen by open orders ``` The **margin ratio** is evaluated at two levels. The account-level ratio governs **cross** liquidation; the position-level ratio governs **isolated** liquidation: ``` cross margin ratio = (balance + cross unrealized PnL) ───────────────────────────────────────────── (cross maintenance margin + cross liquidation fee) isolated margin ratio = (position margin + unrealized PnL) ───────────────────────────────────── (maintenance margin + liquidation fee) ``` Liquidation begins when the relevant ratio drops **strictly below 1.0**. The full procedure is described in [Risk Engine](/txcore/risk-engine.md). Note that the cross numerator is **balance plus cross unrealized PnL**, not the account's full equity — equity also includes isolated margin and isolated unrealized PnL, and those do not back the cross check. The **estimated liquidation price** and **bankruptcy price** both account for the fee charged to close the position. For an isolated long: ``` liquidation price = (position margin − size × entry price) ────────────────────────────────────── (size × (MMR + fee rate − 1)) bankruptcy price = (position margin − size × entry price) ────────────────────────────────────── (size × (fee rate − 1)) ``` For shorts, the sign of `size × entry price` flips and the denominators become `size × (MMR + fee rate + 1)` and `size × (fee rate + 1)`. In cross mode, account balance replaces position margin; when several cross positions are open, bankruptcy prices are solved jointly rather than one position at a time — use the Info API calculators for those cases. ## Deterministic margin Margin is computed against immutable snapshots of the oracle and margin metadata. The calculator is therefore a **pure function** of position, price snapshot, and margin config — the same inputs always produce the same result on every node. --- # Risk Engine The risk engine monitors accounts during [block-tail processing](/txcore/consensus-execution.md#deterministic-execution) and handles liquidation, auto-deleveraging, and funding settlement. On the ordinary path this is incremental — limited to accounts touched by the block — but an **oracle update** triggers a full margin recalculation so every position is marked to the new prices. ## Liquidation When a cross account's or isolated position's [margin ratio](/txcore/margin-system.md#core-formulas) falls below 1.0, liquidation runs in a fixed sequence: 1. **Decision** — using the tiered-margin config, compute how much of the position must be taken over, classify the liquidation as full or partial, and select the liquidator account. Partial liquidation is only considered from **tier 3 upward**; below that the position is closed in full. A partial liquidation reduces the position to the size cap of the tier below. 2. **Cancel** — cancel the orders that conflict with the liquidation. In **cross** mode this covers all of the user's perpetual orders (spot and outcome orders are left alone); in **isolated** mode only orders on the liquidated instrument are cancelled. 3. **Takeover** — the liquidator account takes over the position at the **bankruptcy price**. 4. **Market disposal** — the liquidator places a single reduce-only `IOC` order, priced past the bankruptcy price by a per-instrument slippage rate (lower for a sell, higher for a buy) so resting liquidity can absorb it. Instruments without an override use the protocol default. 5. **ADL (auto-deleveraging)** — whatever market disposal could not clear is force-closed against counterparties on the opposite side, highest leverage-PnL ratio first. 6. **Settlement** — a fill better than the bankruptcy price is recorded as a liquidation surplus; a fill worse than it is recorded as a shortfall. Every stage emits on-chain events, so a liquidation can be fully reconstructed from the block stream. ## ADL ranking and price Counterparties on the opposite side are ranked by **leverage-PnL ratio** and exposed as a **1–5 ADL indicator** (`adlQuantile`); 5 is deleveraged first. ADL does not execute at the mark price — the fill price is the **bankruptcy price adjusted by the taker fee rate**, raised for a long being deleveraged and lowered for a short. See [Auto-deleveraging (ADL)](/trade/adl.md) for the trader-facing view of the queue and the fill price. ## Liquidation surplus and shortfall Settlement classifies every liquidation result: a surplus above the bankruptcy price is recorded as a liquidation fee, and a shortfall as a bankruptcy loss. Both are emitted as auditable on-chain events. ## Funding rate - Perpetual contracts settle funding on a fixed cycle (**default every hour**) so the contract price tracks the oracle price. - The oracle publishes the funding rate **in percent**. Divide by 100 to get the decimal rate used in settlement: a published `0.01` means 0.01%, i.e. a rate of `0.0001`. See [Oracle](/txcore/oracle.md). - Payment for a position is `−size × mark price × (published rate / 100)`, so longs pay and shorts receive when the rate is positive. - Funding is **accrued per position** (segmented since open / since last change) and booked when the settlement cycle is reached. - For **isolated** positions, funding adjusts the position margin; for **cross** positions, it adjusts account equity. --- # Oracle The oracle supplies the prices and funding rates that drive valuation, liquidation, and funding settlement. Prices are fed **on-chain** as governance transactions, so every node sees identical values in the same block — there is no off-chain price source that nodes must independently trust. ## What the oracle publishes Each oracle update targets one instrument and carries: | Field | Meaning | |---|---| | Instrument | the market this update applies to | | Mark price | used for valuation, unrealized PnL, and liquidation | | Oracle price | aggregated external reference price for the underlying | | Funding rate | published **in percent** — divide by 100 for the decimal rate used in settlement (so `0.01` means 0.01%) | | Timestamp | source timestamp of the price (milliseconds) | | Buy / sell price bounds | optional max and min order prices per side; orders outside them are rejected by the [matching engine](/txcore/matching-engine.md#protections--constraints) | | Open-interest limit | optional cap on the instrument's open interest | Price bounds and the open-interest limit are optional: when an update omits them, the instrument keeps trading without that constraint. ## Mark price vs. oracle price - **Mark price** is used for position valuation, unrealized PnL, and liquidation triggers. It is smoothed to resist short-term wicks, so a momentary spike on one venue does not cause unwarranted liquidations. - **Oracle price** is the aggregated external reference for the underlying asset and anchors the perpetual's funding rate. ## Who can feed prices Oracle updates are **governance operations** and execute **first** within each block during [deterministic execution](/txcore/consensus-execution.md#deterministic-execution), so all subsequent matching, clearing, and risk logic in that block uses the freshly published prices. Who may publish an update is resolved in priority order — a higher level that is configured takes precedence: 1. a price feeder pinned on the instrument, if set; 2. the channel's authorized oracle feeders, if the instrument belongs to a channel; 3. otherwise, platform-level authorized signers. Every node that accepts the block sees the same prices. --- # Channels A **Channel** is an isolated trading venue on TxFlow. Multiple channels run on the same L1 — sharing its [consensus](/txcore/consensus-execution.md), [clearing](/txcore/clearing-settlement.md), and [risk engine](/txcore/risk-engine.md) — while each channel independently controls its own markets, fees, and authorized operators. This lets brands, partners, or front-ends operate isolated venues on shared infrastructure without standing up their own chain. > **Info:** **Channels are in limited availability.** The first channels are already live — for example, [probly.com](https://probly.com) runs a prediction-market channel on TxFlow. Creating and operating your own channel is still onboarded case by case, and the channel rules are **still unstable and expected to change significantly**. If you're a brand, partner, or front-end interested in running one — or want to help shape the rules — join the discussion in our [Discord](https://discord.com/invite/txflow). ## What a channel owns A channel is identified by a numeric **channel ID** and defines: | Property | Meaning | |---|---| | instruments | the markets that belong to the channel — an [instrument](/tip/instrument-config.md) joins a channel by referencing its channel ID | | fee address | the address that collects the channel's trading fees | | authorized deployers | addresses allowed to list and manage the channel's instruments | | authorized oracle feeders | addresses allowed to push prices for the channel's instruments | Instruments with no channel (channel ID absent or `0`) belong to the **default platform channel**. ## Channel identifiers Public requests and protocol records use `exchangeId` as the channel identifier. An `exchangeId` of `0` refers to the default platform channel; other values identify independently configured channels. For [outcome markets](/tip/outcome.md), an event is identified by `(exchangeId, eventSlug)`. `eventSlug` is 1–256 bytes of ASCII letters, digits, `-`, `_`, `.`, or `:`. The same `eventSlug` may be used in different channels without joining their markets into one event. Each outcome market belongs to exactly one channel, while templates are registered globally and may be reused across channels. ## What is shared vs. isolated Channels are an **organizational and permission boundary**, not a separate execution environment. Everything below the venue layer is shared: | Shared across all channels | Isolated per channel | |---|---| | consensus & block ordering | instrument set | | deterministic state machine | fee collection address | | margin, liquidation, and ADL logic | authorized deployers / oracle feeders | | account model (one account, all channels) | fee tiers & referral config | | the block stream served to full nodes | — | A user has a single TxFlow account; positions and balances are not partitioned by channel. A channel scopes *which markets exist, who operates them, and where their fees go* — not *whose funds they are*. ## Permission scoping Channel membership drives several governance permission checks: - **Instrument deployment** — listing or managing an instrument that belongs to a channel requires the signer to be one of that channel's authorized deployers. Without a channel (channel ID absent or `0`), platform-level permission is required. - **Oracle feeds** — who may push prices is resolved in priority order: a price feeder pinned on the instrument → the channel's authorized oracle feeders → platform-level signers. When a higher level is configured, it takes precedence. See [Oracle](/txcore/oracle.md#who-can-feed-prices). - **Fees** — trading fees for a channel's instruments accrue to that channel's fee address, under its own fee-tier and referral configuration. ## Querying by channel Channel-scoped read queries — positions, open orders, fee tiers — accept a channel filter so an integrator can serve only its own venue's data. See the [Info API](/api/info.md). Outcome event and metadata queries use `exchangeId` explicitly. Event-list and aggregate-metadata responses only include markets from the requested channel. --- # Run a Full Node > **Info:** **Coming soon.** Full-node deployment and operation docs are in progress. A TxFlow full node subscribes to the validators' gRPC block stream, replays the deterministic state machine locally to verify results, and serves queries and real-time push to clients — without participating in consensus. This section will cover hardware requirements, configuration, startup, syncing from genesis or a snapshot, and the query/streaming endpoints it exposes. In the meantime, see [Consensus & Execution](/txcore/consensus-execution.md) for the block stream and deterministic replay model. --- # TIP — Improvement Proposal The **TxFlow Improvement Proposal (TIP)** defines the unified instrument standard for TxFlow — the single abstraction under which every tradable market is built. Under the TIP, spot, perpetual, and outcome markets are all **instruments**: they share one configuration schema and one account model, and they go through the same clearing and risk framework — differing only where their economics genuinely require it. This standard is what lets the trading core ([TxCore](/txcore/consensus-execution.md)) treat a spot pair, a perpetual contract, and a prediction market as variations of one instrument — and lets integrators learn one model instead of three. ## The instrument type An instrument is one of three types: | Type | What trades | Settled in | Position? | |---|---|---|---| | **Spot** | base asset vs. quote asset | balance swap | no — balances only | | **Perps** | perpetual contract | mark-to-market + funding | yes | | **Outcome** | prediction-market shares | redeemed to margin at 0/1 | yes (synthetic) | **Perps** and **Outcome** share most logic — both maintain positions, margin, and PnL. **Spot** is the simplest case: a fill is just a swap of balances with no position to track. ## What every instrument shares 1. **One config schema** — a single instrument config describes every instrument: name, assets, precision, fees, tick sizes, order-size limits, trading-halt flag, and an optional type-specific extension. See [Instruments](/tip/instrument-config.md). 2. **One order vocabulary** — the same order types and time-in-force names are used across markets, with **type-specific restrictions** (for example spot rejects GTD and triggers; perps reject GTD; outcome limit orders accept GTC/GTD/ALO). See [Matching Engine](/txcore/matching-engine.md). 3. **One account model** — every user has a spot, perp, and outcome sub-account, with the same three-bucket balance accounting (total / hold / withdrawal hold). Spot has no positions or margin; perps and outcome do. See [Key Concepts](/introduction/key-concepts.md#accounts). 4. **One oracle interface** — markets draw mark price, oracle price, funding, and price / open-interest limits from the same oracle structure. See [Oracle](/txcore/oracle.md). ## What differs per type | Concern | Spot | Perps | Outcome | |---|---|---|---| | Position tracking | none | yes | yes (YES / NO share legs) | | Funding | n/a | yes (default 1h cycle) | n/a | | Leverage / margin | n/a | tiered margin (cross or isolated) | isolated margin only | | Settlement trigger | on fill | continuous mark-to-market | market resolution | | Contract / share style | — | linear or inverse, set per market | complementary YES + NO shares | | Special operations | — | reduce-only, TP/SL, TWAP | split / merge | The pages that follow document the shared config schema first, then the specifics of each type: - [Instruments](/tip/instrument-config.md) — the shared config schema every instrument builds on: name, assets, precision, fees, tick sizes, order-size limits, and the type-specific extension. - [TIP 1: Spot](/tip/spot.md) — direct base-vs-quote trading, settled as a balance swap with no position to track. **Unstable.** - [TIP 2: Perpetuals](/tip/perps.md) — perpetual contracts with tiered margin, leverage, and funding. **Unstable.** - [TIP 3: Outcome Markets](/tip/outcome.md) — prediction markets trading complementary YES / NO shares that redeem to margin at 0 or 1 on resolution. **Unstable.** > **Info:** TIPs 1–3 are **still unstable** and may change significantly as the standard evolves. We welcome community input — share proposals and suggestions in our [Discord](https://discord.com/invite/txflow). --- # Instruments An **instrument** is a single tradable market — a spot pair, a perpetual contract, or an outcome (prediction) market. Whatever its type, every instrument is described by the same set of settings, which is what lets the trading core treat them all through one model. This page introduces those settings. Instruments are listed and configured through governance operations on [`/exchange`](/api/exchange.md); the current set and their live values can be read through the [Info API](/api/info.md). ## Settings | Setting | Description | |---|---| | Name | instrument name — `BTC-USDC` for perpetuals, `BTC/USDC` for spot | | Instrument type | Spot, Perpetual, or Outcome | | Channel | the [Channel](/txcore/channels.md) this instrument belongs to; absent means the default platform channel | | Base asset | the base asset of the pair | | Quote asset | the quote asset of the pair | | External ID | external/reference identifier for the instrument | | Size precision | the size precision; determines the minimum order size (see below) | | Price tick | minimum price increment (tick size) | | Order-book aggregation ticks | additional aggregation tick sizes offered for L2 order-book views | | Taker fee rate | base taker fee rate | | Maker fee rate | base maker fee rate | | Minimum order size | optional — explicit minimum order size (overrides the value derived from size precision when set) | | Max limit order size | optional — maximum size for a single limit order | | Max market order size | optional — maximum size for a single market order | | Trading halt | optional — when set, trading on this instrument is halted | | Isolated-only | optional — when set, only isolated-margin positions are allowed (perpetuals) | | Perpetual type | optional — Linear or Inverse (perpetual contracts only) | | Liquidation slippage | optional — how far past the bankruptcy price the liquidator's [market-disposal](/trade/liquidation.md) order is priced (perpetual contracts only); unset means the protocol default | | Price feeder | optional — the address authorized to feed prices for this instrument | | Delisted | optional — when set, the perpetual is delisted and no longer tradable | | Type-specific extension | optional — extra configuration that applies to one instrument type (currently outcome) | | Resolution status | optional — outcome markets: Prepared or Resolved | > **Note:** The taker and maker fee rates above are the instrument's **base** rates for spot and perpetuals. The fee a given user actually pays is determined by their volume-based [fee tier](/txcore/clearing-settlement.md#fees) and any referral discount. **Outcome** markets use their own extension fee schedule instead of these base rates and volume tiers. ## Size precision → minimum order size The size precision defines the smallest tradable unit: | Size precision | Minimum order size | |---|---| | `3` | `0.001` | | `2` | `0.01` | | `0` | `1` | | `-1` | `10` | | `-2` | `100` | In general, a precision of zero or above gives a minimum order size of 1 divided by 10 raised to that precision; a negative precision gives a minimum order size of 10 raised to its absolute value. A negative precision is how an instrument enforces whole-lot trading (e.g. a minimum order size of `100`). When an explicit minimum order size is set, it takes precedence over this derived value. ## Type-specific extension The type-specific extension carries configuration that only applies to one instrument type. Today the only variant is for outcome markets; its fields are documented in [Outcome Markets](/tip/outcome.md#configuration). ## Trading lifecycle controls - **Trading halt** pauses all order entry/matching on the instrument without removing its config or state. - **Resolution status** (outcome only) moves from prepared to resolved when the market settles. See [Outcome Markets](/tip/outcome.md#resolution). --- # TIP 1: Spot Spot is the simplest instrument type: trading the base asset directly against the quote asset, with no position, leverage, or funding. ## Model - Trades settle on the **spot sub-account**. - A fill is a **direct swap of balances**: the buyer's quote balance decreases and base balance increases; the seller's the reverse. There is no position object — balances *are* the state. - Settlement is immediate and atomic within the block, like all TxFlow clearing. See [Clearing & Settlement](/txcore/clearing-settlement.md). ## Naming Spot instruments are named `BASE/QUOTE` — for example `BTC/USDC`. Perpetuals use a hyphen (`BTC-USDC`), so the separator distinguishes the two instrument types. ## Settings Spot uses the [common instrument settings](/tip/instrument-config.md) with no type-specific extension. The ones that matter for spot: - **Base & quote asset** — the two assets being swapped. - **Size & price precision** — the size precision and price tick. - **Maker & taker fee rates** — base fees (subject to the user's [fee tier](/txcore/clearing-settlement.md#fees)). - **Order-size bounds** — the minimum order size and the maximum size for a single limit or market order. There is no margin, leverage, funding, or position mode for spot. Perpetual type, margin tiers, and funding settlement do not apply. ## Orders Spot uses the shared [order model](/txcore/matching-engine.md): market / limit orders with `GTC`, `IOC`, and `ALO` time-in-force. `GTD` is rejected on spot, as are trigger (TP/SL) orders and reduce-only — those are perpetual concepts. --- # TIP 2: Perpetuals Perpetual contracts (perps) are the core derivative instrument: a position in a synthetic contract that tracks an underlying via the [mark and oracle prices](/txcore/oracle.md), held with leverage and kept anchored to the oracle price by periodic funding. ## Model - Trades settle on the **perp sub-account** as **positions**, not balance swaps. - A fill updates the position's size and average entry price and books realized PnL on reduction. Funding continues to accrue against the open size and settles on the funding cycle. See [Clearing & Settlement](/txcore/clearing-settlement.md). - Positions are valued continuously against the **mark price**; unrealized PnL feeds the margin/health calculation in the [Margin System](/txcore/margin-system.md). ## Entry price & PnL ### Entry price The **entry price** is the volume-weighted average price at which the current position was opened or added to: ``` Entry Price = ∑(Fill Price × Fill Size) ÷ Total Position Size ``` How it changes per action: | Action | Effect on entry price | |---|---| | Open position | Set to the fill price | | Add in the same direction | Recalculated as the weighted average of old and new fills | | Reduce (partial close) | Unchanged — only realized PnL is booked | | Flip direction | The old side closes; the residual opens a new position at the crossing fill price | Entry price resets to zero once the position is fully closed. ### Unrealized PnL Open positions are marked continuously against the [mark price](/trade/mark-and-index-price.md): - **Long** — `Unrealized PnL = (Mark Price − Entry Price) × Position Size` - **Short** — `Unrealized PnL = (Entry Price − Mark Price) × Position Size` Unrealized PnL feeds the margin/health calculation in the [Margin System](/txcore/margin-system.md). ### Realized PnL Realized PnL is booked when a position is reduced or closed, at the exit fill price: - **Long** — `Realized PnL = (Exit Price − Entry Price) × Closed Size` - **Short** — `Realized PnL = (Entry Price − Exit Price) × Closed Size` Net of costs: `Net PnL = Realized PnL − Trading Fees − Funding Paid`. See [Clearing & Settlement](/txcore/clearing-settlement.md) for how these settle into the account, or [Entry Price & PnL](/trade/entry-price-and-pnl.md) for a trader-facing walkthrough with worked examples. ## Contract types Perpetuals can be configured as: | Type | Name | Margin / PnL denominated in | |---|---|---| | Linear | USD-margined | quote asset (e.g. USDC) | | Inverse | coin-margined | base asset | The contract style is set per market in its [instrument configuration](/tip/instrument-config.md). Read `currencyType` from [`perpMeta`](/api/info/perpMeta.md) to see which currency a market margins and settles in, rather than assuming one style across all markets. ## Margin & leverage Perps are the instrument type that uses the full margin system: - **Margin modes** — Cross or Isolated (a market may be configured as isolated-only). - **Position modes** — One-Way or Hedge. - **Tiered margin** — maintenance margin rate and max leverage scale with position size. All formulas and tier semantics are in [Margin System](/txcore/margin-system.md). ## Funding Perpetuals settle funding on a fixed cycle (protocol default **1 hour**; the interval is a network parameter and can be configured) to anchor the contract price to the oracle price. Funding is accrued per position and books into margin (isolated) or account equity (cross). See [Risk Engine § Funding](/txcore/risk-engine.md#funding-rate). ## Perp-specific order behavior On top of the shared [order model](/txcore/matching-engine.md): - **Reduce-only** — orders that may only reduce an existing position, never open or increase one. - **Trigger orders (TP/SL)** — stored separately and activated into the book when the **mark price** crosses the trigger price. - **TWAP** — large orders sliced across blocks to reduce impact. ## Config Perps use the [common instrument settings](/tip/instrument-config.md), with type set to perpetual and the contract style (linear / inverse) selected. Margin tiers are configured per market through governance. Open interest may be capped via the oracle, and order prices are bounded by the oracle's [price limits](/txcore/matching-engine.md#protections--constraints). --- # TIP 3: Outcome Markets Outcome markets are prediction markets expressed as instruments. A market settles to a **settlement fraction** of `0`, `0.5`, or `1`, and shares of each side redeem to the margin asset at that price (YES) or `1 − fraction` (NO). Internally, outcome reuses the perpetual position machinery — it is treated together with perps — with a fixed-price split/merge mechanism layered on top. ## Market definitions An outcome market separates its human-readable definition from its tradable instrument. Three protocol concepts make up that definition: | Concept | Meaning | |---|---| | **Template** | An optional reusable question format. It defines the title, description, side names, and the parameters that may be substituted into them. | | **Event** | A channel-scoped group of related markets, identified by an `eventSlug`. An event may contain one or more markets. | | **Market** | A tradable outcome instrument with its own `instrumentId`, order book, trading parameters, and settlement state. | Display copy is not required on-chain. A registrar may attach a template so clients can render a title and description from consensus data, or register a **template-free** market and keep that copy in an application metadata service. For example, a template may define the title `{asset} above {price} at {expiry}?`. A market supplies values such as `BTC`, `65000`, and `20260812-0600`; clients combine the template and those values to display the complete question. ### Templates A template contains: - a globally unique `templateId` — 1–64 bytes of lowercase ASCII letters, digits, or `-`; - a title template and description template; - two side names, such as `Above` and `Below`; - a list of named parameters (`keywordSpecs`) and their value types (`dateTime`, `date`, `string`, `instrument`, `uInt`, `uDecimal`); - an enabled status. Templates are maintained by TxFlow governance and can be reused by markets in different [channels](/txcore/channels.md). At most 1,024 templates may be registered. A disabled template remains queryable so that existing markets can still be rendered, but it cannot be used to create new markets. Images, logos, banners, and other presentation assets are not part of a template or market definition. Applications may attach them through their own metadata services. ### Events and markets Every market has an `instrumentId` and belongs to one event in one channel. The event is identified by the pair `(exchangeId, eventSlug)`: - `exchangeId` is the [channel](/txcore/channels.md) identifier (`0` is the default platform channel). - `eventSlug` is a stable value supplied by the market registrar: 1–256 bytes of ASCII letters, digits, `-`, `_`, `.`, or `:`. A registrar may change its own mapping from that slug to an external event ID without changing the TxFlow market definition. The same `eventSlug` may be reused in a different channel without joining those markets into one event. Multiple markets can share the same event. For example, one event may contain separate markets for BTC finishing above 65,000 and above 70,000 at the same expiry. Each market has its own instrument ID and order book while remaining discoverable as part of the same event. A market may be registered with a template instance (`templateId` plus `keywordValues`) or with no template. Template-free markets still carry `eventSlug` and the trading parameters; query services render them with `eventSlug` as the title, empty description, and default side names `Yes` / `No`. The `instrumentId` is also the identifier used by the shared trading and account APIs. ## Shares as a complete set An outcome market has two sides — call them **YES** and **NO** — represented as a **long** and a **short** position leg. They are linked by a fixed relationship: ``` price(YES) + price(NO) = 1 ``` The split price and merge price are both fixed at **0.5**: one unit of margin collateral splits into one YES share and one NO share, each worth 0.5, summing to 1. Split and merge are submitted as a single signed [`POST /exchange`](/api/exchange/outcomeOperate.md) action, `outcomeOperate`, which carries a batch of `split` or `merge` items. A batch may contain at most 50 items. ### Split Each `split` item takes `sz` units of margin and mints a complete set: 1. Freeze `sz` of margin collateral from the outcome account. 2. Open a **long (YES)** leg of size `sz` and a **short (NO)** leg of size `sz`, both at price 0.5. The user now holds both sides and can sell either into the order book. Split is skipped (not failed) for an item if available balance is insufficient or `sz` is invalid — each item in a batch is processed independently. ### Merge `merge` is the inverse: combine a matched YES + NO set of size `sz` back into `sz` units of margin collateral, releasing the frozen collateral. Merge is guarded by a `can_merge` check (the user must hold both legs in sufficient size); failing items are skipped. ## Trading Beyond split/merge, outcome shares trade through the **shared order book and matching path** like any other instrument — market/limit orders, the standard time-in-force set, and price–time priority. A user who wants only one side can split a set and sell the unwanted leg, or simply buy the desired side from the book. ## Resolution When the market's real-world outcome is known, it is **settled** via `SettleOutcomeV2`: - `resolution_status` moves from `prepared` to `resolved` ([config field](/tip/instrument-config.md#trading-lifecycle-controls)). - `settlement_fraction` is `0`, `0.5`, or `1` (trailing zeros are stripped: `"1.000"` → `"1"`). - YES redeems at the settlement fraction; NO redeems at `1 − fraction`, paid out in the margin asset. `0.5` is an even settlement: both sides redeem at 0.5. An optional `settlement_details` string (up to 2 KB) may record the rationale; it is stored in consensus history and is not used in payout math. A resolved market cannot be settled again, and its trading parameters (such as `price_tick`) cannot be updated. ## Configuration Outcome markets carry an `OutcomeExtConfig` in the instrument's `ext_config`: | Field | Description | |---|---| | `fee_rate` | trading fee rate for the market | | `exponent` | scaling exponent used in the market's fee curve | | `maker_rebate_rate` | rebate rate paid to makers | | `min_limit_order_size` | minimum size for a limit order | | `min_market_order_size` | minimum size for a market order | | `min_limit_order_notional` | minimum notional for a limit order | | `min_market_order_notional` | minimum notional for a market order | Fee for a fill is `fee_rate × (p × (1 − p))^exponent`, where `p` is the fill price. All other fields come from the [common `InstrumentConfig`](/tip/instrument-config.md) with `instrument_type = outcome`. ## Account Outcome activity settles on the dedicated **outcome sub-account**, with the same three-bucket balance accounting as the other sub-accounts. Margin for open share legs is sourced from the outcome account. ### Application risk notice The first-party application asks a wallet to acknowledge the Prediction Market risk notice before enabling its trading UI. Checking and recording that acknowledgement uses application-session account endpoints, not `POST /info`, `POST /exchange`, or the consensus protocol. Third-party integrations should present disclosures appropriate to their product and jurisdiction; those application-only endpoints are not part of this public API reference. ## On-chain data and recovery Template registration and status changes, market registration, and market settlement are recorded in consensus-ordered block history. This allows an application to reconstruct the protocol-defined outcome data from blocks rather than relying on a single application database. The recoverable definition includes: - templates and their enabled status; - events and their channel membership; - the markets attached to each event; - market parameters and settlement history. Query services may index this block history to provide application-friendly views. Those views are derived from the same consensus data and may briefly lag the latest block. Live order books, positions, balances, and open orders remain trading-state queries rather than market-definition data. ## Querying outcome markets Outcome markets are queried on [`POST /info`](/api/info.md): | Request type | Returns | |---|---| | [`outcomeMeta`](/api/info/outcomeMeta.md) | Indexed event and market metadata enriched with live trading, pricing, volume, and open-interest data. | | [`outcomeNavTags`](/api/info/outcomeNavTags.md) | The category and tag tree used to filter `outcomeMeta`. | | [`outcomeAssetCtxs`](/api/info/outcomeAssetCtxs.md) | 24-hour volume and price statistics for Outcome instruments. | | [`outcomeSeriesTimeline`](/api/info/outcomeSeriesTimeline.md) | Past, current, and upcoming markets in a recurring series. | | [`cryptoPrice`](/api/info/cryptoPrice.md) | Price-to-beat and settlement price for a crypto Outcome window. | | [`cryptoPriceHistory`](/api/info/cryptoPriceHistory.md) | Crypto price samples used by Outcome charts. | | [`outcomeProbability`](/api/info/outcomeProbability.md) | Historical YES probability samples for one market. | | [`l2Book`](/api/info/l2Book.md) | The order book of one outcome market, with its `instrument_id` as `coin`. | | [`outcomePositions`](/api/info/outcomePositions.md) | A user's outcome positions. | | [`clearinghouseState`](/api/info/clearinghouseState.md) | A user's outcome-account value and withdrawable balance, with `accountType: "outcome"`. | | [`outcomeFrontendOpenOrders`](/api/info/outcomeFrontendOpenOrders.md) | A user's open outcome orders. | For the live product flow, use [`outcomeBbos`](/api/websocket/outcomeBbos.md) for discovery-card quotes, [`l2Book`](/api/websocket/l2Book.md) and [`trades`](/api/websocket/trades.md) for one market, and [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md) for matching lifecycle. The API-service [`outcomeOrderUpdate`](/api/websocket/outcomeOrderUpdate.md) reports asynchronously accepted Outcome orders that fail before reaching the book, while [`candle`](/api/websocket/candle.md) supplies the live probability bar. --- # Builder API Reference Every integration makes the same three moves: **read a snapshot** with `POST /info`, **keep it live** over WebSocket, and **write** with a signed `POST /exchange` action. Reads are unauthenticated. Writes are signed locally — by the user's wallet for account setup and fund movements, and by an [agent key](/api/signing.md) for trading — so there are no API keys to provision. New here? [Set up an account](#set-up-an-account) first, run [Try Testnet](/try-testnet/quickstart.md) to see a full client work in your browser, follow the call order in [Integration flows](/api/flows.md), then read [Signing](/api/signing.md) before writing your own client. ## Set up an account Before an agent key can trade, the account owner completes three one-time steps, in this order: | Step | Call | Signed by | |---|---|---| | 1. Deposit USDC | [Deposit flow](/api/flows.md#deposit-usdc); on testnet, `POST /request-fund` | — | | 2. Approve a builder code | [`approveBuilderFee`](/api/exchange/approveBuilderFee.md) for the builder you trade through | User's wallet | | 3. Approve the agent | [`approveAgent`](/api/exchange/approveAgent.md) for the key that signs trades | User's wallet | From then on the agent key signs every trading action. The user's wallet is needed again only to move funds or change these approvals. See [Set up an account](/api/flows.md#set-up-an-account) for the full call sequence. ## Find the right API Pick what you are building. Each line lists its calls in order; **live** marks a WebSocket subscription. **Trading** - Set up an account to trade: [`approveBuilderFee`](/api/exchange/approveBuilderFee.md), [`approveAgent`](/api/exchange/approveAgent.md), [`maxBuilderFee`](/api/info/maxBuilderFee.md), [`approvedAgents`](/api/info/approvedAgents.md), [Deposit USDC](/api/flows.md#deposit-usdc) — [flow](/api/flows.md#flow-account-setup) - Place an order and know when it fills: [`order`](/api/exchange/order.md), [`orderUpdates`](/api/websocket/orderUpdates.md) (live), [`userFills`](/api/info/userFills.md) — [flow](/api/flows.md#flow-perp-order) - Show, cancel, or amend open orders: [`frontendOpenOrders`](/api/info/frontendOpenOrders.md), [`orderUpdates`](/api/websocket/orderUpdates.md) (live), [`cancel`](/api/exchange/cancel.md), [`modify2`](/api/exchange/modify2.md) - Show positions and margin: [`clearinghouseState`](/api/info/clearinghouseState.md), [`clearinghouseState`](/api/websocket/clearinghouseState.md) (live), [`updateLeverage`](/api/exchange/updateLeverage.md), [`updateMarginMode`](/api/exchange/updateMarginMode.md) - Fill the order ticket (max size, leverage, liq. price): [`activeAssetData`](/api/info/activeAssetData.md), [`activeAssetData`](/api/websocket/activeAssetData.md) (live), [`getMaxPositions`](/api/info/getMaxPositions.md), [`getLiquidationPrices`](/api/info/getLiquidationPrices.md) - Set and show take-profit / stop-loss: [`order`](/api/exchange/order.md), [`tpslOrders`](/api/info/tpslOrders.md), [`positionTpslOrders`](/api/info/positionTpslOrders.md), [`historicalTpslOrders`](/api/info/historicalTpslOrders.md) **Market data** - Build a market page (price, book, trades): [`perpMeta`](/api/info/perpMeta.md), [`ticker`](/api/websocket/ticker.md) (live), [`activeAssetCtx`](/api/info/activeAssetCtx.md), [`activeAssetCtx`](/api/websocket/activeAssetCtx.md) (live), [`l2Book`](/api/info/l2Book.md), [`l2Book`](/api/websocket/l2Book.md) (live), [`trades`](/api/websocket/trades.md) (live) - Draw a candlestick chart: [`candleSnapshot`](/api/info/candleSnapshot.md), [`candle`](/api/websocket/candle.md) (live) - Show a live markets table or ticker: [`perpMeta`](/api/info/perpMeta.md), [`allAssetCtxs`](/api/websocket/allAssetCtxs.md) (live), [`allMids`](/api/websocket/allMids.md) (live), [`ticker`](/api/websocket/ticker.md) (live), [`multiCandle`](/api/websocket/multiCandle.md) (live) **Account & funds** - Deposit USDC: [`settings`](/api/info/settings.md), [`depositWithPermit`](/api/info/depositWithPermit.md), [`userNonFundingLedgerUpdates`](/api/info/userNonFundingLedgerUpdates.md), [TxRelay](/api/txrelay.md) — [flow](/api/flows.md#flow-deposit) - Withdraw funds: [`withdraw3`](/api/exchange/withdraw3.md), [`tokenSend`](/api/exchange/tokenSend.md), [`userNonFundingLedgerUpdates`](/api/info/userNonFundingLedgerUpdates.md), [TxRelay](/api/txrelay.md) — [flow](/api/flows.md#flow-withdraw) - Move funds between perp and Outcome: [`tokenSend`](/api/exchange/tokenSend.md), [`perpClearinghouseState`](/api/info/perpClearinghouseState.md), [`clearinghouseState`](/api/info/clearinghouseState.md) - Show order and trade history: [`historicalOrders`](/api/info/historicalOrders.md), [`userFills`](/api/info/userFills.md), [`orderDetails`](/api/info/orderDetails.md) - Show the funding rate and payments: [`activeAssetCtx`](/api/info/activeAssetCtx.md), [`userFundings`](/api/info/userFundings.md), [`userFundings`](/api/websocket/userFundings.md) (live) - Show a user’s fee rate: [`userFees`](/api/info/userFees.md), [`feeTierRates`](/api/info/feeTierRates.md), [`feeTierInfo`](/api/info/feeTierInfo.md) **Outcome markets** - Browse Outcome markets: [`outcomeMeta`](/api/info/outcomeMeta.md), [`outcomeAssetCtxs`](/api/info/outcomeAssetCtxs.md), [`outcomeBbos`](/api/websocket/outcomeBbos.md) (live) - Trade an Outcome market: [`tokenSend`](/api/exchange/tokenSend.md), [`order`](/api/exchange/order.md), [`outcomeOrderUpdate`](/api/websocket/outcomeOrderUpdate.md) (live), [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md) (live), [`outcomePositions`](/api/info/outcomePositions.md) — [flow](/api/flows.md#flow-outcome-order) - Chart an Outcome probability: [`outcomeProbability`](/api/info/outcomeProbability.md), [`candle`](/api/websocket/candle.md) (live) **Builders & keys** - Earn builder fees on the flow you route: [`approveBuilderFee`](/api/exchange/approveBuilderFee.md), [`order`](/api/exchange/order.md), [`builderState`](/api/info/builderState.md), [`claimBuilderRewards`](/api/exchange/claimBuilderRewards.md) — [flow](/api/flows.md#flow-builder) - Manage agent (API) keys: [`approveAgent`](/api/exchange/approveAgent.md), [`approvedAgents`](/api/info/approvedAgents.md), [`agentOwner`](/api/info/agentOwner.md) ## Endpoints | Endpoint | What it does | Reference | |---|---|---| | `POST /info` | Read-only queries — balances, positions, orders, fills, history, the book, market metadata, deposit settings | [Info API](/api/info.md) | | `POST /exchange` | Every write — orders, cancels, margin, fund movements, builder codes. One signed action per request. | [Exchange API](/api/exchange.md) | | `WS /ws` | Node stream — books, trades, account updates, chain data | [WebSocket API](/api/websocket.md) | | `WS /api-ws` | API-service stream — tickers, trade lists, candles, history snapshots, and async Outcome failures | [WebSocket API](/api/websocket.md#api-service) | | `WS /ws-rtds` | Crypto reference prices for Outcome underlyings — spot, Chainlink, TWAP | [WebSocket API](/api/websocket.md#crypto-prices-ws-rtds) | | TxRelay `/api/v1/bridge/…` | Cross-chain deposit / peg addresses, on a separate host | [TxRelay API](/api/txrelay.md) | The Info, WebSocket, Exchange, and TxRelay references are **Beta**: endpoints are live and stable in shape, but some pages are still being written. Limits are on [Rate limits](/api/rate-limits.md). ## Networks One host per network serves `/info`, `/exchange`, `/ws`, `/api-ws`, `/ws-rtds`, `/account`, and the testnet faucet — it routes each request to the node or the API service for you. TxRelay has its own host. | | Mainnet | Testnet | |---|---|---| | API host | `https://rpc.txflow.com` | `https://rpc.txflow-testnet.xyz` | | Faucet — `POST /request-fund` | — | same as API host | | TxRelay — `/api/v1/bridge` | `https://api-bridge.txflow.com` | `https://devnet-bridge.txflow.net` | | EIP-712 network name | `TxFlow-Mainnet` | `TxFlow-Testnet` | | Chain ID | `869` | `2511` | | Signing `apiVersion` (EIP-712 `version`) | `1` | `1` | The network name and chain ID go into the EIP-712 [domain](/api/signing.md#domain), which keeps a testnet signature from replaying on mainnet. **Every code sample targets testnet**, so nothing copied from these docs can hit mainnet by accident. Swap the host and the domain values above when you go live. > **Warning:** Testnet is for integration only. The keys the docs generate in your browser are throwaway — never reuse one or fund it with real assets. ## Conventions | | | |---|---| | Content type | `application/json` on both `POST` endpoints | | Version header | send `x-api-version: 2026-08-16` on every `/info` and `/exchange` request — every sample here does, and responses are documented in that format | | Responses | `/info`: `{ "success": true, "code": 0, "data": … }`, or `success: false` with `code` and `message` on error. `/exchange`: see [Response](/api/exchange.md#response) | | Field case | camelCase, except the Outcome metadata responses, which are snake_case | | Numbers | quantities and prices are **strings**, to avoid float rounding | | Instrument ids | The `index` from [`perpMeta`](/api/info/perpMeta.md), or an Outcome market's `instrument_id` from [`outcomeMeta`](/api/info/outcomeMeta.md). **In requests**, `coin` and the `/info` `asset` filter take it as a numeric **string** (`"coin": "1"`) — every endpoint accepts that form, and node reads such as `l2Book` accept no other; `instrumentId` / `instrumentIds` and the `/exchange` fields `a` / `asset` take it as an **integer** (`"a": 1`). **In responses**, `coin` is a display name whose form varies by endpoint (`BTC-USDC`, `BTC`, `OUTCOME-4991183`) — match rows on `asset` / `instrumentId` instead, compared as numbers (`asset` is `"1"` in some responses and `1` in others). | | Account types | `accountType` is `perp` or `outcome` (`perps` is accepted as an alias) | | Nonces | millisecond timestamps, unique per signer | | Time | the consensus timestamp is the chain's only clock — see [Consensus & Execution](/txcore/consensus-execution.md) | --- # Try Testnet This page talks to the live TxFlow **testnet** straight from your browser. It generates a throwaway key, claims testnet USDC, sets up the account, and places real orders — every write is signed locally with the same [EIP-712](https://eips.ethereum.org/EIPS/eip-712) + MessagePack flow described in [Signing](/api/signing.md). > **Warning:** Everything here runs against **testnet only**. The wallet is generated in your browser, holds only test funds, and should never be reused or funded with real assets. > **Note:** The page's live ticker uses two market-data sources on purpose: `/api-ws` [`ticker`](/api/websocket/ticker.md) from the API service for a lightweight **last price** stream, and the node's [`activeAssetCtx`](/api/websocket/activeAssetCtx.md) / [Info API](/api/info/activeAssetCtx.md) for **mark** and **oracle** prices. The node WebSocket protocol itself is served at `/ws`. ## Two examples One terminal per instrument type — **perpetuals** and **outcome**. Each is the same three pieces: a **ticker**, a **chart**, and an **order ticket**. Only the outcome terminal differs in how it renders them: its price is a probability, so it plots a probability curve and its ticket picks YES or NO instead of buy or sell. Both share one throwaway account. Setting up the account takes these steps: | Step | Endpoint / action | Reference | |---|---|---| | Generate a key | `ethers.Wallet.createRandom()`, in your browser | [Signing](/api/signing.md) | | Deposit | `POST /request-fund` on the HTTP API host (see Testnet endpoints) → credited to the perp account | [Key Concepts](/introduction/key-concepts.md#accounts) | | Approve a builder code | `approveBuilderFee` — the main key approves the builder the orders go through | [`approveBuilderFee`](/api/exchange/approveBuilderFee.md) | | Approve the agent | `approveAgent` — the main key authorizes an agent key to sign orders | [`approveAgent`](/api/exchange/approveAgent.md) | | Fund the sub-accounts | `tokenSend` — perps and outcome each settle on their own sub-account, and the faucet only credits the perp one | below | The faucet is rate-limited per address, so claim once and trade both. ```ts // every HTTP call sends the version header; /info replies are { success, code, data } const API = 'https://rpc.txflow-testnet.xyz' async function post(path: string, body: unknown) { const res = await fetch(API + path, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-version': '2026-08-16' }, body: JSON.stringify(body), }).then((r) => r.json()) return path === '/info' ? res.data : res } // move USDC between your own sub-accounts — "perp" | "outcome". // Signed directly by the main key; an agent key cannot move funds. const value = { txflowNetwork: 'TxFlow-Testnet', amount: '300', fromAccountType: 'perp', toAccountType: 'outcome', token: 'USDC:0xd04f0d19E8FbA49cfA541E272F9C60a2ad720464', // NAME:address, from the token list nonce: Date.now(), } const types = { TransferToken: [ { name: 'txflowNetwork', type: 'string' }, { name: 'amount', type: 'string' }, { name: 'fromAccountType', type: 'string' }, { name: 'toAccountType', type: 'string' }, { name: 'token', type: 'string' }, { name: 'nonce', type: 'uint64' } ] } const sig = ethers.Signature.from(await main.signTypedData(domain, types, value)) await post('/exchange', { action: { type: 'tokenSend', chainId: 2511, apiVersion: 1, toAddress: null, ...value }, signature: { r: sig.r, s: sig.s, v: sig.v }, nonce: value.nonce, vaultAddress: null, }) ``` Setting `toAddress` sends to another user instead of between your own accounts; that variant signs the `SendToken` struct rather than `TransferToken`. The first send to a new address may charge a one-time 1 USDC activation fee (`0` when the destination is yourself). See [`tokenSend`](/api/exchange/tokenSend.md). Every terminal calls the same endpoints, changing only the instrument id: | Piece | Call | |---|---| | Ticker | `/api-ws` `ticker` subscription | | Candles | `/info` [`candleSnapshot`](/api/info/candleSnapshot.md) for 90 days of history, `/api-ws` `candle` for today's open bar — on an outcome market the closes are probabilities, so the terminal draws them as a curve | | Order book | [`l2Book`](/api/info/l2Book.md) — an outcome market passes its `instrument_id` as `coin` | | Outcome card quotes | `/ws` [`outcomeBbos`](/api/websocket/outcomeBbos.md), up to 50 instruments per subscription | | Order | [`order`](/api/exchange/order.md) → `POST /exchange`, read back with [`frontendOpenOrders`](/api/info/frontendOpenOrders.md) | | Cancel | [`cancelByCloid`](/api/exchange/cancelByCloid.md) → `POST /exchange` | ```ts // the only thing that differs between the three examples const coin = String(instrumentId) // 1 = BTC-USDC, 1284355 = an outcome market // 90 daily candles; the open one keeps streaming on the `candle` channel const history = await post('/info', { type: 'candleSnapshot', req: { coin, interval: '1d', startTime: Date.now() - 90 * 86_400_000, endTime: Date.now() }, }) ws.send(JSON.stringify({ method: 'subscribe', subscription: { type: 'ticker', coin, instrumentId } })) ws.send(JSON.stringify({ method: 'subscribe', subscription: { type: 'candle', coin, interval: '1d' } })) // order book — levels[0] is bids, levels[1] is asks, each best-first const { levels } = await post('/info', { type: 'l2Book', coin }) // signOrder(agent, action) wraps the action in the signed envelope — see Signing. // agent-signed order. `c` is a client order id — tag every order with one so // you can cancel it by cloid. await post('/exchange', await signOrder(agent, { type: 'order', grouping: 'na', orders: [{ a: instrumentId, b: true, p: '75000.0', s: '0.0006', r: false, t: { limit: { tif: 'Gtc' } }, c: 'my-order-001' }], })) await post('/exchange', await signOrder(agent, { type: 'cancelByCloid', cancels: [{ asset: instrumentId, cloid: 'my-order-001' }], })) ``` ### Perpetuals Perpetual instruments are named `BASE-QUOTE` and listed by [`perpMeta`](/api/info/perpMeta.md); the `index` field is the `a` you sign. Margin comes from the perp sub-account. See [TIP 2: Perpetuals](/tip/perps.md). ### Outcome Outcome markets are prediction markets: two sides priced so that `price(YES) + price(NO) = 1`. They trade through the same order book and the same `order` action, keyed by `instrumentId`. See [TIP 3: Outcome Markets](/tip/outcome.md). Discovery is different: markets are grouped under events and scoped to a [channel](/txcore/channels.md), so instead of `perpMeta` you list them with [`outcomeMeta`](/api/info/outcomeMeta.md). Testnet publishes them on channel `1`. That channel belongs to [Probly](https://probly.com), the first partner to run a channel on TxFlow. The questions on this page, their tick sizes and settlement, and the resting orders you trade against are all Probly's — TxFlow provides the matching, margin, and settlement, the channel operator provides the markets and the liquidity. Anything you list with `exchangeId: 1` is their venue; `exchangeId: 0` is the default platform channel. ```ts const { events } = await post('/info', { type: 'outcomeMeta', exchangeId: 1, limit: 50 }) const tradable = events .flatMap((e) => e.markets) .filter((m) => m.trading.accepting_orders && !m.trading.closed) // → { instrument_id: 1284355, question: 'Will Hyperliquid perform an airdrop…', // instrument: { price_tick: '0.001', sz_decimals: 2 }, outcome_prices: ['0.295', '0.705'] } ``` Most of these have an active book but no fills yet, so `ticker` returns an empty `lastPrice` and `candleSnapshot` returns `[]`. The terminal falls back to the book mid for the headline price. There is one book per market and it is priced in YES, between `0` and `1` — that price *is* the market's probability. So the page's terminal quotes both sides of it rather than a raw price: | In the terminal | On the wire | |---|---| | Buy **YES** at `p` | `b: true`, `p` | | Buy **NO** at `q` | `b: false`, `q` | | The **NO** book | the same `l2Book`, mirrored: YES asks become NO bids at `1 − px` | | The chart | the close of each `candleSnapshot` bucket as a probability on a fixed 0–100% axis, not an OHLC candle | ```ts // buy 10 NO shares at 0.93 const yes = false const price = '0.930' await post('/exchange', await signOrder(agent, { type: 'order', grouping: 'na', orders: [{ a: instrumentId, b: yes, p: price, s: '10', r: false, t: { limit: { tif: 'Gtc' } }, c: 'my-outcome-001' }], })) ``` Two more things differ. Margin comes from the outcome sub-account, funded by the [`tokenSend`](/api/exchange/tokenSend.md) above. And the exchange can acknowledge an Outcome order asynchronously: if the queued order fails, `/api-ws` [`outcomeOrderUpdate`](/api/websocket/outcomeOrderUpdate.md) pushes the reason under the returned `traceId` (successes are not pushed). Use `/ws` [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md) for the matching lifecycle, and confirm the resting state with [`outcomeFrontendOpenOrders`](/api/info/outcomeFrontendOpenOrders.md). ## Testnet endpoints Everything above talks to `https://rpc.txflow-testnet.xyz`: HTTP at `/info` and `/exchange`, WebSocket at `wss://…/ws` and `wss://…/api-ws`, and the faucet at `POST /request-fund` with `{ "address": "0x…" }`. The domain values are under [Networks](/api.md#networks). To build your own client, use the [Info API](/api/info.md), [WebSocket API](/api/websocket.md), and [Signing](/api/signing.md) reference. --- # Connect your coding agent Every page of these docs is also published as plain Markdown, with the API schemas fully expanded, so a coding agent can read the reference as precisely as you do. | File | What it holds | |---|---| | [`/llms.txt`](/llms.txt) | Index of every page in reading order, plus the integration rules agents most often get wrong | | [`/llms-full.txt`](/llms-full.txt) | Every page in one file | | `.md` | Any single page — add `.md` to its URL, for example [`/api/exchange/order.md`](/api/exchange/order.md) | ## Claude Code Add the index to your project's `CLAUDE.md`, so Claude reads the reference before it writes TxFlow code: ```md ## TxFlow API docs: /llms.txt — read it, and the `.md` pages it links, before writing or changing TxFlow client code. Follow its integration rules. ``` ## Cursor In chat, type `@Docs`, choose **Add new doc**, and enter [`/llms-full.txt`](/llms-full.txt). Reference it with `@Docs` from then on. ## Other agents Point the agent at [`/llms.txt`](/llms.txt), or paste the Markdown of the pages you are working from. Use `llms-full.txt` when the agent cannot follow links. > **Tip:** Agents should test against **testnet** first — every example in these docs already does. See [Networks](/api.md#networks). --- # Integration flows Each flow is the order in which a client calls the API for one job. Steps run top to bottom. Each step names its transport, and signed actions say which key signs them. See [Signing](/api/signing.md) for the two signing flows. ## Set up an account Every account completes these steps once, in this order, before its agent key can trade: 1. **Deposit USDC** — see [Deposit USDC](#deposit-usdc) below. 2. **Approve a builder code** with the user's wallet — [`approveBuilderFee`](/api/exchange/approveBuilderFee.md). 3. **Approve the agent** with the user's wallet — [`approveAgent`](/api/exchange/approveAgent.md). Three one-time steps, in this order. The user’s wallet signs both approvals; the agent then signs every trade. 1. USDC [Deposit USDC](/api/flows.md#deposit-usdc) — Fund the account. On testnet, `POST /request-fund` credits test USDC to the perp account. 2. POST /exchange [`approveBuilderFee`](/api/exchange/approveBuilderFee.md) (Account-signed) — Signed by the user’s wallet. Approves the builder you trade through, up to `maxFeeRate`. → Builder approved 3. POST /exchange [`approveAgent`](/api/exchange/approveAgent.md) (Account-signed) — Signed by the user’s wallet. Authorizes the agent key that signs trading actions. → Agent authorized 4. Do all of these: - POST /info [`maxBuilderFee`](/api/info/maxBuilderFee.md) — `status: "ACTIVE"` and the approved rate. - POST /info [`approvedAgents`](/api/info/approvedAgents.md) — The authorized agent key. ## Deposit USDC Look up Bridge2, fund it one of three ways, then confirm the balance. 1. POST /info [`settings`](/api/info/settings.md) — Read the Bridge2 contract address. → `bridge2_contract_address` 2. Pick one of 3: - Arbitrum [USDC transfer](/getting-started/deposits-and-withdrawals.md) — User holds ETH for gas: send USDC to Bridge2 directly. - POST /info [`depositWithPermit`](/api/info/depositWithPermit.md) (Account-signed) — User has no ETH: sign an EIP-2612 permit and the API host submits it. - TxRelay [deposit-address](/api/txrelay/deposit-address.md) — Funds on another chain: get a one-time deposit address and send there. 3. POST /info [`clearinghouseState`](/api/info/clearinghouseState.md) — Confirm the credited balance. 4. POST /info [`userNonFundingLedgerUpdates`](/api/info/userNonFundingLedgerUpdates.md) — Show the deposit in the ledger. ## Place and track a perp order With the account set up, the agent signs every order. Watch the order on the stream; reconcile from history. 1. POST /exchange [`order`](/api/exchange/order.md) (Agent-signed) — Agent-signed. One action can carry a batch of orders, applied in order. → One `success` for the whole action — the oid and fills come from the stream below 2. Do all of these: - WebSocket [`orderUpdates`](/api/websocket/orderUpdates.md) — Order status as it changes: open, filled, cancelled, rejected. - WebSocket [`clearinghouseState`](/api/websocket/clearinghouseState.md) — Position and margin after each fill. 3. POST /info [`userFills`](/api/info/userFills.md) — Executions for history and reconciliation. Indexed, so it can lag the stream briefly. ## Trade an Outcome market Fund the Outcome sub-account, place the order, then follow two channels on two endpoints. > A successful `order` response can mean *queued*, not matched. Subscribe to both channels in step 4, or a rejected queue entry looks like an order that never filled. 1. POST /info [`outcomeMeta`](/api/info/outcomeMeta.md) — Find the event and market. → `instrument_id` — every later step needs it 2. POST /exchange [`tokenSend`](/api/exchange/tokenSend.md) (Account-signed) — Move USDC from `perp` to `outcome`. Account-signed; an agent cannot move funds. 3. POST /exchange [`order`](/api/exchange/order.md) (Agent-signed) — `a` is the `instrument_id`; `b`/`r` encode the leg (buy NO = `b: false, r: false`) and `p` is that leg's price. TIF `Gtc`, `Gtd`, or `Alo` — `Ioc` is rejected. → Possibly an async acceptance with a `traceId` 4. Do all of these: - WebSocket [`outcomeOrderUpdate`](/api/websocket/outcomeOrderUpdate.md) — On `/api-ws`. Pushed only if the queued order fails; match on `traceId`. - WebSocket [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md) — On `/ws`. What the book did: filled, cancelled, rejected. 5. POST /info [`outcomePositions`](/api/info/outcomePositions.md) — Positions only — no margin summary. Use `clearinghouseState` with `accountType=outcome` for that. The [Outcome Integration Guide](/api/outcome-integration.md) walks through the same flow with request examples. ## Withdraw funds USDC to Arbitrum has its own action; everything else goes out through a TxRelay peg. 1. Pick one of 2: - POST /exchange [`withdraw3`](/api/exchange/withdraw3.md) (Account-signed) — USDC to an Arbitrum address. Account-signed; minimum 2 USDC. - TxRelay [deposit-address](/api/txrelay/deposit-address.md) — Other chains: request a peg address with `src_chain: "txflow"`, then send to it with `tokenSend`. 2. POST /info [`userNonFundingLedgerUpdates`](/api/info/userNonFundingLedgerUpdates.md) — Show the withdrawal in the ledger. ## Earn builder fees Each user approves a cap once; your orders carry the fee; you claim what accrues. > If the user has not approved you, or the fee exceeds the cap, the builder fee is silently ignored and the order still goes through. 1. POST /exchange [`approveBuilderFee`](/api/exchange/approveBuilderFee.md) (Account-signed) — Signed by the *user’s* account key, once per builder. Sets `maxFeeRate`. 2. POST /exchange [`order`](/api/exchange/order.md) (Agent-signed) — Add the action-level `builder` object (`b` address, `f` fee) beside `orders`. 3. POST /info [`builderState`](/api/info/builderState.md) — Your claimable balance and whether you are eligible to accrue. 4. POST /exchange [`claimBuilderRewards`](/api/exchange/claimBuilderRewards.md) (Agent-signed) — Claim the full balance. Agent-signed. See [Builder Technical Documentation](/builders/technical.md) for fee caps and eligibility. --- # Outcome Integration Guide Every Outcome endpoint has its own reference page. This guide connects them in the order an integration actually calls them, and flags the three places where the Outcome flow differs from perps. The short version of those three differences: - Discovery metadata comes from a **gateway aggregation** in `snake_case`, while the on-chain instrument definition is `camelCase`. They describe the same market and are not interchangeable. - Placing an order is **asynchronous**. A successful HTTP response means queued, not matched, and the terminal result arrives on a different WebSocket endpoint than the matching result. - A position query returns **only positions**. Account balances live behind a separate call. Examples call the API directly. Each step is one `POST /info`, `POST /exchange`, or WebSocket subscription, built from the same pieces as [Try Testnet](/try-testnet/quickstart.md): ```ts const API = 'https://rpc.txflow-testnet.xyz' // every HTTP call sends the version header; /info replies are { success, code, data } async function post(path: string, body: unknown) { const res = await fetch(API + path, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-version': '2026-08-16' }, body: JSON.stringify(body), }).then((r) => r.json()) return path === '/info' ? res.data : res } const ws = new WebSocket('wss://rpc.txflow-testnet.xyz/ws') // node channels const apiWs = new WebSocket('wss://rpc.txflow-testnet.xyz/api-ws') // API-service channels const subscribe = (socket: WebSocket, subscription: object) => socket.send(JSON.stringify({ method: 'subscribe', subscription })) // signAgent(action) and signAccount(action) wrap an action in the signed // { action, nonce, signature, vaultAddress } envelope — agent key or account // key, as described in Signing. ``` ## The flow at a glance Fund the Outcome sub-account, place the order, then follow two channels on two endpoints. > A successful `order` response can mean *queued*, not matched. Subscribe to both channels in step 4, or a rejected queue entry looks like an order that never filled. 1. POST /info [`outcomeMeta`](/api/info/outcomeMeta.md) — Find the event and market. → `instrument_id` — every later step needs it 2. POST /exchange [`tokenSend`](/api/exchange/tokenSend.md) (Account-signed) — Move USDC from `perp` to `outcome`. Account-signed; an agent cannot move funds. 3. POST /exchange [`order`](/api/exchange/order.md) (Agent-signed) — `a` is the `instrument_id`; `b`/`r` encode the leg (buy NO = `b: false, r: false`) and `p` is that leg's price. TIF `Gtc`, `Gtd`, or `Alo` — `Ioc` is rejected. → Possibly an async acceptance with a `traceId` 4. Do all of these: - WebSocket [`outcomeOrderUpdate`](/api/websocket/outcomeOrderUpdate.md) — On `/api-ws`. Pushed only if the queued order fails; match on `traceId`. - WebSocket [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md) — On `/ws`. What the book did: filled, cancelled, rejected. 5. POST /info [`outcomePositions`](/api/info/outcomePositions.md) — Positions only — no margin summary. Use `clearinghouseState` with `accountType=outcome` for that. The sections below take each step in turn, with code. ## 1. Discover markets [`outcomeMeta`](/api/info/outcomeMeta.md) is the discovery feed: events with nested markets, live trading gates, pricing, volume, and settlement state. ```ts const page = await post('/info', { type: 'outcomeMeta', exchangeId: 1, limit: 20, sortBy: 'volume', }) ``` Two response shapes come out of this one query. An `eventSlug` point lookup returns the event object directly; everything else returns a paged envelope. Narrow before reading `events`: ```ts const events = 'events' in page ? page.events : [page] const market = events[0]?.markets[0] ``` Page size caps at 50; walk with `next_cursor` for more. `sortBy: 'endingSoon'` is an ascending semantic order that carries its own direction, so send it without `sortOrder`. Resolving a deep link works the other way round — look the event up by the market slug in your URL: ```ts const result = await post('/info', { type: 'outcomeMeta', exchangeId: 1, marketSlugs: ['btc-up-or-down-5m'], markets: 'matched', }) ``` `markets: 'matched'` returns only the requested markets; `'event'` returns every market in each owning event. > **Field naming:** `outcomeMeta` responses use `snake_case` (`instrument_id`, `best_bid`, > `next_cursor`) because the gateway serves the indexed catalog verbatim. `market.instrument_id` is the identifier every later step needs. ## 2. Stream quotes For a grid or watchlist, [`outcomeBbos`](/api/websocket/outcomeBbos.md) batches best bid and ask across many markets on one subscription: ```ts subscribe(ws, { type: 'outcomeBbos', instrumentIds: [market.instrument_id], exchangeId: 1 }) ws.onmessage = (e) => { const msg = JSON.parse(e.data) if (msg.channel !== 'outcomeBbos') return for (const { instrumentId, bbo } of msg.data.bbos) { const [bid, ask] = bbo console.log(instrumentId, bid, ask) } } ``` One subscription carries 1–50 instrument ids and one connection accepts up to 20 active groups. Beyond that, open more subscriptions on the same connection. Either side of `bbo` is `null` when the book is empty there. For a single market's full depth, the shared [`l2Book`](/api/websocket/l2Book.md) channel applies. Outcome markets are addressed by instrument id as a **decimal string**, not the `OUTCOME-` name: ```ts subscribe(ws, { type: 'l2Book', coin: String(market.instrument_id) }) // each `l2Book` push carries data.levels: [bids, asks], each best-first ``` The same string rule applies to [`trades`](/api/websocket/trades.md) and [`activeAssetCtx`](/api/websocket/activeAssetCtx.md). To follow several markets' tapes at once, use [`outcomeTrades`](/api/websocket/outcomeTrades.md), which takes numeric `instrumentIds` instead. For charts, [`outcomeProbability`](/api/info/outcomeProbability.md) returns the historical YES probability series. To keep the chart live, subscribe to [`candle`](/api/websocket/candle.md) on `/api-ws` with the instrument id as `coin` and append its closes, which are YES probabilities. ## 3. Fund the sub-account Outcome trades settle in a dedicated sub-account. Move USDC into it with an owner-signed [`tokenSend`](/api/exchange/tokenSend.md). With no `toAddress` it signs the `TransferToken` struct: ```ts await post('/exchange', await signAccount({ type: 'tokenSend', txflowNetwork: 'TxFlow-Testnet', chainId: 2511, apiVersion: 1, toAddress: null, token: 'USDC:0xd04f0d19E8FbA49cfA541E272F9C60a2ad720464', amount: '100', nonce: Date.now(), fromAccountType: 'perp', toAccountType: 'outcome', })) ``` ## 4. Place an order, and learn what happened Orders use the shared [`order`](/api/exchange/order.md) action with the instrument id as `a`. This buys 10 YES shares at `0.55`: ```ts const ack = await post('/exchange', await signAgent({ type: 'order', grouping: 'na', orders: [{ a: market.instrument_id, b: true, p: '0.55', s: '10', r: false, t: { limit: { tif: 'Gtc' } } }], })) // ack can be an asynchronous acceptance: { status: 'ok', traceId, … } ``` > **An `ok` response means queued, not matched:** The gateway may park an Outcome order in a delay queue. The HTTP response only > confirms acceptance. Two different channels, on two different endpoints, report > what happened next — subscribe to both. | Channel | Endpoint | Answers | | ------- | -------- | ------- | | [`outcomeOrderUpdate`](/api/websocket/outcomeOrderUpdate.md) (singular) | `/api-ws` | Did the queued order fail before reaching the node? Pushed only on failure. | | [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md) (plural) | `/ws` | What did the book do — filled, cancelled, rejected? | ```ts // Delay-queue result on /api-ws, correlated by traceId. subscribe(apiWs, { type: 'outcomeOrderUpdate', user }) apiWs.onmessage = (e) => { const msg = JSON.parse(e.data) if (msg.channel !== 'outcomeOrderUpdate') return const { traceId, done, success, body } = msg.data if (done && !success) console.error('never reached the node', traceId, body?.response) } // Matching-engine lifecycle on /ws. subscribe(ws, { type: 'outcomeOrderUpdates', user }) ws.onmessage = (e) => { const msg = JSON.parse(e.data) if (msg.channel !== 'outcomeOrderUpdates') return for (const { order, status } of msg.data) console.log(order.oid, order.outcome, status) } ``` Watching only the plural channel makes a rejected queue entry look like an order that silently never filled. The singular channel stays silent when the order goes through, so it never tells you whether it traded. `order.outcome` is the displayed leg (`Yes` or `No`); `order.side` (`B` or `A`) and `reduceOnly` are the wire `b` and `r` (see [Outcome orders](/api/exchange/order.md#outcome-orders)). Cancelling uses the shared [`cancel`](/api/exchange/cancel.md) action. ## 5. Read positions and balances This is the second place the Outcome flow surprises people: [`outcomePositions`](/api/info/outcomePositions.md) returns **only** the `assetPositions` array. There is no margin summary and no `withdrawable`. ```ts const [positions, account] = await Promise.all([ post('/info', { type: 'outcomePositions', user }), post('/info', { type: 'clearinghouseState', user, accountType: 'outcome', exchangeId: 1 }), ]) ``` | Need | Call | | ---- | ---- | | Open positions | `outcomePositions` | | Account value, margin, and withdrawable balance | `clearinghouseState` with `accountType: 'outcome'` | | Active orders | [`outcomeFrontendOpenOrders`](/api/info/outcomeFrontendOpenOrders.md) | The YES and NO legs of one market are two separate records; the sign of `position.szi` distinguishes them. One asymmetry to keep in mind: `outcomePositions` accepts an optional `exchangeId`, `outcomeFrontendOpenOrders` does not. History endpoints need an explicit opt-in. Add `'outcome'` to `accountType` on [`userFills`](/api/info/userFills.md) and [`historicalOrders`](/api/info/historicalOrders.md), or to `accountTypes` on [`userNonFundingLedgerUpdates`](/api/info/userNonFundingLedgerUpdates.md) — note the plural field name and the singular `'perp'` spelling on that last one. ## 6. Split and merge complete sets [`outcomeOperate`](/api/exchange/outcomeOperate.md) converts between collateral and a complete share set, without touching the book: ```ts // Lock collateral, receive symmetric YES and NO positions. await post('/exchange', await signAgent({ type: 'outcomeOperate', split: [{ a: market.instrument_id, s: '10' }], })) // Return a symmetric pair, release the collateral. await post('/exchange', await signAgent({ type: 'outcomeOperate', merge: [{ a: market.instrument_id, s: '10' }], })) ``` Exactly one of `split` or `merge` is present per action. This is useful for market making one leg while hedging the other, and for exiting when one side has no bid. ## 7. Settlement Settlement state travels on the market object from step 1: ```ts const settlement = market.settlement if (settlement?.resolution_status === 'resolved') { console.log('winner:', settlement.winning_outcome) } ``` `resolution_status` is `prepared` or `resolved`. `winning_outcome` is derived from `outcomes` and `settlement_fraction` after resolution — it is the protocol's result, not the metadata provider's `oracle_outcome`. Poll `outcomeMeta` for the market slug to refresh it. ## Recurring series Crypto up/down markets repeat on a fixed cadence. [`outcomeSeriesTimeline`](/api/info/outcomeSeriesTimeline.md) returns the past, current, and upcoming windows around an anchor event so you can build a period navigator, and [`cryptoPrice`](/api/info/cryptoPrice.md) / [`cryptoPriceHistory`](/api/info/cryptoPriceHistory.md) supply the price-to-beat and the chart series. Unlike `outcomeMeta`, the timeline response is `camelCase`. ## What is out of scope - **Creating markets.** `outcomeDeploy` is permissioned — see [Exchange API](/api/exchange.md#market-lifecycle-actions). - **Risk-notice acknowledgement.** The first-party app records this through application-session endpoints that are not part of this API surface, as noted in [TIP 3](/tip/outcome.md). Present disclosures appropriate to your own product and jurisdiction. ## Next steps - [`outcomeMeta` reference](/api/info/outcomeMeta.md) — full discovery parameters - [TIP 3](/tip/outcome.md) — the protocol model behind these endpoints - [WebSocket API](/api/websocket.md) — heartbeats, reconnection, and re-subscribing --- # Signing Every [`/exchange`](/api/exchange.md) action is an [EIP-712](https://eips.ethereum.org/EIPS/eip-712) signature, so any Ethereum wallet or library can produce it; [`/info`](/api/info.md) reads are unsigned. There are **two flows**, and both end in the same envelope and the same `keccak256(0x19 ‖ 0x01 ‖ domainSeparator ‖ structHash)` digest: | Flow | Signed by | Used for | |---|---|---| | [Direct EIP-712](#flow-1--direct-eip-712) | Account key | Fund movements and authorizations — `approveAgent`, `tokenSend`, `withdraw3`, `approveBuilderFee`, `revokeBuilderFee` | | [Agent signing](#flow-2--agent-signing) | Agent key | Trading — orders, cancels, margin, `outcomeOperate`, `claimBuilderRewards` | ## Domain The standard four-field EIP-712 domain, separate per network so a testnet signature can never replay on mainnet. `salt` is unused. ``` EIP712Domain(string name,string version,uint256 chainId,address verifyingContract) ``` | Network | `name` | `version` | `chainId` | `domainSeparator` | |---|---|---|---|---| | Mainnet | `TxFlow-Mainnet` | `"1"` | `869` | `0x878a21f7c464d592bad925771b5ab6f52897c4321b30eb6cfdf4ea55c749ad8d` | | Testnet | `TxFlow-Testnet` | `"1"` | `2511` | `0x839dd579adc05b1545a37213db5dd3dc6928532ddf73dfa1b53f647421060a8f` | `version` is the `apiVersion` as a decimal string, and `verifyingContract` is always the zero address. ## Request envelope Every action, whichever flow signed it, is submitted in the same envelope: ```json { "action": { "type": "", "...": "action fields" }, "signature": { "r": "0x…", "s": "0x…", "v": 27 }, "nonce": 1757992743994, "vaultAddress": null, "signatureChainId": null } ``` | Field | Type | Description | |---|---|---| | `action` | object | The action body. `type` selects the operation; see [Exchange API](/api/exchange.md). | | `signature.r`, `signature.s` | hex string | The 32-byte ECDSA scalars. | | `signature.v` | integer | `27` or `28` (`27 + recoveryId`). | | `nonce` | uint64 | Millisecond timestamp. Whenever the action body carries its own `nonce`, the envelope must repeat the same value. | | `vaultAddress` | address · optional | Trade on behalf of a vault. Covered by the signature. | | `signatureChainId` | uint32 · optional | Overrides the domain `chainId` for wallets that force their connected chain. | `signatureChainId` only changes the EIP-712 domain, never the struct hash, and it applies only to direct EIP-712 actions (`approveAgent`, `approveBuilderFee`, `revokeBuilderFee`, `tokenSend`, `withdraw3`). Agent-signed actions ignore it. ## Flow 1 — Direct EIP-712 Each account-key action has its own EIP-712 primary type, listed on its reference page, and the action fields are the signed message — so the wallet prompt shows the real values. 1. Build the struct hash for the action's type: `structHash = keccak256(abi.encode(typeHash, encodedFields…))`. String fields are hashed with `keccak256`, addresses are left-padded to 32 bytes, integers are ABI-encoded. 2. `signingHash = keccak256(0x19 ‖ 0x01 ‖ domainSeparator ‖ structHash)`. 3. Sign `signingHash` with secp256k1 and emit `{r, s, v}`. The recovered address is the account itself. The nonce comes from `action.nonce`. ### Example — `approveAgent` The last step of [account setup](/api/flows.md#set-up-an-account), after the deposit and [`approveBuilderFee`](/api/exchange/approveBuilderFee.md): the account key authorizes an agent key to sign orders on its behalf. ``` ApproveAgent(string txflowNetwork,uint32 chainId,uint32 apiVersion,address agentAddress,string agentName,uint64 nonce) ``` ```json { "type": "approveAgent", "txflowNetwork": "TxFlow-Testnet", "chainId": 2511, "apiVersion": 1, "agentAddress": "0x0202020202020202020202020202020202020202", "agentName": "my-trading-bot", "nonce": 2000 } ``` A null `agentName` is hashed as the empty string. To revoke an agent, submit the same action with `agentAddress` set to the zero address and the existing `agentName`. ## Flow 2 — Agent signing The trading actions (and `vaultTransfer`) are not typed data themselves. The body is compacted into a 32-byte `connectionId` and wrapped in one fixed EIP-712 type: ``` Agent(string txflowNetwork,uint32 chainId,uint32 apiVersion,bytes32 connectionId) ``` 1. MessagePack-encode the action body **with the `type` field removed**, as a map with named keys. 2. Append the nonce as 8 bytes big-endian. 3. Append the vault flag: `0x00` when `vaultAddress` is null, otherwise `0x01` followed by the 20 address bytes. 4. `connectionId = keccak256(msgpack ‖ nonceBE8 ‖ vaultFlag)`. 5. Hash `Agent{txflowNetwork, chainId, apiVersion, connectionId}` as EIP-712 and sign the result. The recovered address must be an agent already registered through `approveAgent` — the node rejects any other signer with `Agent … is not authorized by any account`. The nonce comes from the envelope's `nonce`. ### MessagePack rules The encoding must be byte-identical to the server's, so the details matter: - Encode as a **map with named keys**, not an array. Keys are the wire names, including the abbreviated order fields (`a`, `b`, `p`, `s`, `r`, `t`, `c`, `m`, `o`). - Keep the node's field order, which is the key order of each action's **request example** (for an order body: `a`, `b`, `p`, `s`, `r`, `t`, `c`, `m`, `expiresAt`). The field tables sort required fields first, so do not copy their order, and do not sort keys. - Omit optional fields that are null rather than encoding them as nil — for example, leave `tpsl` out of a plain [`modify2`](/api/exchange/modify2.md) instead of sending `"tpsl": null`. - Use the most compact integer encoding, native booleans, and native strings. Prices and sizes are strings and are hashed exactly as written, so `"50000.0"` and `"50000"` are different messages. - Addresses inside an action body (such as `vaultTransfer.vaultAddress`) are encoded as **binary**, not as a hex string. ### Example — `order` ```json { "type": "order", "grouping": "na", "orders": [ { "a": 1, "b": true, "p": "50000.0", "s": "0.01", "r": false, "t": { "limit": { "tif": "Gtc" } } } ] } ``` With `nonce = 1700000000000` and no vault, the msgpack bytes are `0x82a867726f7570696e67a26e61a66f72646572739186a16101a162c3a170a735303030302e30a173a4302e3031a172c2a17481a56c696d697481a3746966a3477463`, giving `connectionId = 0x6546d1c3acf430a28a0b2552c66e985f6543646660874734732ae9e9daf4d806`. > **Note:** `vaultTransfer` is the one exception to step 3: it always hashes with a `0x00` vault flag, because its target vault is already inside the action body. ### Builder-fee actions The builder-code actions intentionally use both signing flows. **The approval actions use direct EIP-712.** `approveBuilderFee` and `revokeBuilderFee` are signed by the account key under distinct primary types. Their different typehashes prevent a signature for one action from being replayed as the other. Both support `signatureChainId`, which changes only the EIP-712 domain chain while the business `chainId` remains in the signed struct. **The reward claim uses tagged Agent signing.** `claimBuilderRewards` has no fields. Its MessagePack preimage keeps the `type` tag so it cannot collide with other field-less actions: ```json { "type": "claimBuilderRewards" } ``` | Action | Preimage | Signer | Replay nonce | |---|---|---|---| | [`approveBuilderFee`](/api/exchange/approveBuilderFee.md) | `ApproveBuilderFee` EIP-712 struct | Account key | `action.nonce` | | [`revokeBuilderFee`](/api/exchange/revokeBuilderFee.md) | `RevokeBuilderFee` EIP-712 struct | Account key | `action.nonce` | | [`claimBuilderRewards`](/api/exchange/claimBuilderRewards.md) | Tagged action | Registered agent | envelope `nonce` | The approval signer must be the account itself; an authorized trading agent cannot approve or revoke a builder on the account's behalf. The reward claim follows the Agent flow and therefore requires an agent authorized by the builder account. ## Nonces Nonces are millisecond timestamps, not counters, so concurrent clients can sign in parallel without coordinating. The node applies three checks: - The nonce must fall within **two days before and one day after** the block timestamp. - The nonce must not already be used. Each address keeps its **100 highest** nonces; once that set is full, a new nonce must exceed the smallest one still stored. - Two transactions from the same address in the same block may not share a nonce. In practice `Date.now()` per request is correct. A client that sends more than 100 transactions inside the retention window must simply keep its nonces increasing. Signatures must also be **canonical low-S** (`s <= n/2`). Most libraries normalize this automatically; high-S signatures are rejected as non-canonical. ## Transaction hash Once signed, a transaction's id is derived from the digest and the signature together, so two signers submitting the same action and nonce still get distinct transaction hashes: ``` txHash = keccak256(signingHash[32] ‖ r[32] ‖ s[32] ‖ v[1]) ``` The `v` byte here is the **raw recovery id** (`0` or `1`), not the `27`/`28` used in JSON. ## Verifying a signature To check a signature server-side, or to debug your own client: 1. Recompute `signingHash` with the flow that matches the action. 2. Take `recoveryId = v - 27`. 3. Recover the public key with secp256k1 ECDSA recovery. 4. The signer address is the last 20 bytes of the keccak256 hash of the uncompressed public key without its leading byte. For agent actions, resolve that address through [`agentOwner`](/api/info/agentOwner.md) to get the account it trades for. ## Test vectors All vectors use the throwaway key `0xabab…abab` (address `0xe239cdc5fbe977a8a141b72194d3cf8c41bc5bc6`) and **testnet** domain parameters. If your implementation reproduces both, the two flows are wired correctly. **`order`** — the action and `connectionId` from the example above, `nonce = 1700000000000`, no vault: | | | |---|---| | `signingHash` | `0xeae1ae63709636fb9567accd33bc5dd2717bfac4d97e9a853f91c7fb391e1cc6` | | `r` | `0x4064848dd881985c52dacdc00f7046f1010f7f370e34620d36fcf3b2d1ecb3e0` | | `s` | `0x25c45ad67b9506ac653fc3658f47aa8b6002bfa86a31ae619b334d71aa8b4f6f` | | `v` | `27` | **`approveAgent`** — the action from the example above, `nonce = 2000`: | | | |---|---| | `structHash` | `0xbb5293971660f3252f455b6debbda781841fb23bd45cbbf0199c4de5250573b9` | | `signingHash` | `0x1df22801b48ae1402ad36b244aa245fd6e842fa5fef2c40819b1e6c661cd2212` | | `r` | `0x4431d4afb9fcd0dcd619afd55a7044cc26cb0ee1d436a1b06d836da217842ead` | | `s` | `0x54a0755ee3d8c241235a1828a2be732d72e9c152b5de25fc7c169966de4210f7` | | `v` | `27` | ## Next steps - [Try Testnet](/try-testnet/quickstart.md) — a working browser client that approves an agent and signs a real order. - [Exchange API](/api/exchange.md) — every action type and which flow signs it. --- # Rate limits Limits are applied **per client IP**. | Surface | Limit | |---|---| | `POST /info` | 50 requests per second | | `POST /exchange` | 10 requests per second | | `WS /ws` | 10 live connections | | `WS /api-ws` | 10 live connections | The WebSocket limits are counted separately for each endpoint, by open connections rather than subscriptions. Each connection is multiplexed, so one `/ws` and one `/api-ws` socket can carry every channel you need. ## When you exceed a limit A rejected request is stopped at the gateway and never reaches the node. The HTTP status is **429** with a `Retry-After` header in seconds. | Where | Response | |---|---| | `POST /info` | `{"success":false,"code":10000,"message":"rate limit exceeded"}` | | `POST /exchange` | `{"status":"err","code":,"response":"rate limit exceeded"}` | | New WebSocket connection over the limit | The handshake fails with 429 and the plain-text body `Too many websocket sessions from this IP` | | Frame on an open `/ws` connection | The frame is dropped and the server replies `{"type":"error","code":"RISK_RATE_LIMITED","message":"rate limit exceeded"}`; the connection stays open | | Frame on an open `/api-ws` connection | `{"channel":"error","data":{"code":"RISK_RATE_LIMITED","message":"rate limit exceeded"}}` | Wait for `Retry-After`, then retry with exponential backoff. A rate-limited `/exchange` action did not execute, so it is safe to sign it again with a fresh nonce. Only when a request times out or its response is lost should you confirm with a read before resending. > **Note:** Nonces are per signer and must be unique — a retried write with a reused nonce is rejected, and for outcome actions that rejection is asynchronous and silent. Generate a fresh nonce per attempt and confirm with a read rather than retrying blind. ## Staying inside the limits - **Stream instead of polling.** Anything on a `/ws` channel — trades, positions, order updates — should come from the subscription, not a loop over `/info`. - **Batch where the endpoint allows it.** [`outcomeAssetCtxs`](/api/info/outcomeAssetCtxs.md) takes a list of instruments; [`order`](/api/exchange/order.md) and [`cancel`](/api/exchange/cancel.md) take up to 50 and 200 entries per action. - **Cache metadata.** [`perpMeta`](/api/info/perpMeta.md) and [`feeTierInfo`](/api/info/feeTierInfo.md) change rarely; resolve ids once at startup. - **Reuse connections.** Open one socket per endpoint and subscribe to many channels on it, rather than one socket per channel. Keep each alive with a [10-second ping](/api/websocket.md#keep-alive). --- # Info API (Beta) `POST /info` serves every **read-only query** — market metadata, balances, positions, orders, the book, fees, and history. Reads need no signature. The body's `type` field selects the query, and each type has its own page. ```json { "type": "l2Book", "coin": "4" } ``` `coin` is the instrument's numeric id as a **string** — the `index` from [`perpMeta`](/api/info/perpMeta.md), or an Outcome market's `instrument_id` from [`outcomeMeta`](/api/info/outcomeMeta.md). Outcome reads that take `instrumentId` / `instrumentIds` want it as an **integer**. Send the exact camelCase `type` shown on each page; `accountAccessStatus` requires a case-sensitive match. Every type goes to the same host, even those answered by the API service rather than the node — see [Networks](/api.md#networks). ## Request types ### Shared Reads that are not tied to one instrument type. [`clearinghouseState`](/api/info/clearinghouseState.md) picks the sub-account with `accountType` (`perp` or `outcome`), and [`getMaxPositions`](/api/info/getMaxPositions.md) takes a perp or an Outcome instrument. The history types are served from an index and can briefly lag the latest block; most are paged — see [Paged history](#paged-history). | Type | Use it to | |---|---| | [`l2Book`](/api/info/l2Book.md) | Take a one-off order-book snapshot for one instrument. | | [`trades`](/api/info/trades.md) | Seed the public trade tape for one instrument with its latest 300 trades. | | [`clearinghouseState`](/api/info/clearinghouseState.md) | Load the Positions panel: every position plus the margin summary, for the `perp` or `outcome` sub-account. | | [`frontendOpenOrders`](/api/info/frontendOpenOrders.md) | Render the Open Orders tab, including trigger and TP/SL fields. Outcome orders appear only with `exchangeId: 1` or `"all"`. | | [`userFees`](/api/info/userFees.md) | Show the maker / taker rate a user currently pays. | | [`feeTierInfo`](/api/info/feeTierInfo.md) | Render the static fee-tier ladder (volume thresholds → rates). | | [`feeTierRates`](/api/info/feeTierRates.md) | Show effective rates for an account type, including the user’s tier and referral discount. | | [`builderState`](/api/info/builderState.md) | Read a builder’s on-chain claimable balance and fee eligibility before `claimBuilderRewards`. | | [`referral`](/api/info/referral.md) | Render the Referral page: referrer, rewards, referrer stage. | | [`userRole`](/api/info/userRole.md) | Pick the referral view to render: regular user, referrer, or affiliate. | | [`userClaimRewards`](/api/info/userClaimRewards.md) | Render the referral reward claim history. | | [`agentOwner`](/api/info/agentOwner.md) | Check whether an agent key is still authorized, and for which account. | | [`approvedAgents`](/api/info/approvedAgents.md) | List the agent keys an account has approved (e.g. an API-key management page). | | [`getLatestBlockNumber`](/api/info/getLatestBlockNumber.md) | Check node liveness or read the current block height. | | [`epochHistory`](/api/info/epochHistory.md) | Map block numbers to consensus epochs. | | [`exchangeRates`](/api/info/exchangeRates.md) | Convert USDC amounts to USD or a local fiat currency for display. | | [`tags`](/api/info/tags.md) | Label instruments with tag names; resolves the `tagIds` on `webData2` asset rows. | | [`marketTags`](/api/info/marketTags.md) | Build the markets menu: top-level tabs, their sub-tags, and which instruments sit under each. | | [`userOverview`](/api/info/userOverview.md) | Fill a portfolio or vault performance card: PnL, ROE, volume, max drawdown, equity over a period. | | [`candleSnapshot`](/api/info/candleSnapshot.md) | Seed a candlestick chart; then keep the last bar live with `candle`. | | [`historicalOrders`](/api/info/historicalOrders.md) | Render the Order History tab (limit / market orders). | | [`historicalTpslOrders`](/api/info/historicalTpslOrders.md) | Render the TP/SL tab of Order History. | | [`historicalTriggerOrders`](/api/info/historicalTriggerOrders.md) | Render the Trigger tab of Order History. | | [`twapHistory`](/api/info/twapHistory.md) | Render the TWAP tab of Order History (finished or cancelled TWAPs). | | [`orderDetails`](/api/info/orderDetails.md) | Open an order-detail view from one `orderId`. | | [`userFills`](/api/info/userFills.md) | Render Trade History / reconcile executions (maker or taker fills). | | [`userTwapSliceFills`](/api/info/userTwapSliceFills.md) | List the individual child fills of a user’s TWAP orders. | | [`userFundings`](/api/info/userFundings.md) | Render Funding History (collected and paid settlements). | | [`userNonFundingLedgerUpdates`](/api/info/userNonFundingLedgerUpdates.md) | Render the Deposits & Withdrawals / transfer ledger. | | [`getMaxPositions`](/api/info/getMaxPositions.md) | Compute the max buy / sell size at a price the user typed into the order ticket. | ### Account setup Query a main account's bound referral code and channel trading access with `accountAccessStatus`. This is a public `POST /info` query; it does not require a signature or Token. Trading access does not guarantee that an order passes every validation. | Type | Use it to | |---|---| | [`accountAccessStatus`](/api/info/accountAccessStatus.md) | Query a main account’s bound referral code and trading access on a channel. | | [`approvedBuilders`](/api/info/approvedBuilders.md) | List every builder a user has approved and its fee cap (e.g. a builder-approval settings page). | | [`maxBuilderFee`](/api/info/maxBuilderFee.md) | Check that a user has approved your builder, and at what cap, before sending orders with a builder fee. | | [`approveAgentEligibility`](/api/info/approveAgentEligibility.md) | Decide whether to show “Enable trading” or a deposit prompt before `approveAgent`. | ### Deposits & withdrawals Served by the API service, not the node. | Type | Use it to | |---|---| | [`settings`](/api/info/settings.md) | Look up the Bridge2 contract address before a native Arbitrum USDC deposit. | | [`depositWithPermit`](/api/info/depositWithPermit.md) | Deposit Arbitrum USDC for a user who holds no ETH for gas (signs an EIP-2612 permit). | | [`dayTotalWithdraw`](/api/info/dayTotalWithdraw.md) | Show how much a user has already withdrawn today on the withdraw form. | | [`sendTokenFee`](/api/info/sendTokenFee.md) | Show the activation fee on a send or cross-chain withdraw form before signing `tokenSend`. | ### Builder Analytics for a builder's own dashboard. Pass the **builder wallet address** as `user`. | Type | Use it to | |---|---| | [`builderMeOverview`](/api/info/builderMeOverview.md) | Headline numbers for a builder dashboard: users, volume, fees, rank. | | [`builderMeDailySeries`](/api/info/builderMeDailySeries.md) | Plot a builder’s daily fees or user count. | | [`builderMeShare`](/api/info/builderMeShare.md) | Break a builder’s fees down by trading user or by instrument. | | [`builderMeEarned`](/api/info/builderMeEarned.md) | List a builder’s earnings, one row per order. | | [`builderMeClaimed`](/api/info/builderMeClaimed.md) | List a builder’s past reward claims. | | [`builderMeClaimable`](/api/info/builderMeClaimable.md) | Show claimable vs. lifetime earned / claimed on a builder dashboard. | ### Vaults Vault profiles and a user's vault holdings. Deposit and withdraw with [`vaultTransfer`](/api/exchange/vaultTransfer.md). | Type | Use it to | |---|---| | [`vaultDetails`](/api/info/vaultDetails.md) | Render a vault page: profile, TVL, lock-up, depositors, and one user’s withdrawable amount. | | [`uservaultequities`](/api/info/uservaultequities.md) | List the vaults a user holds, with current equity and lock-up end. | ### Perpetuals Market metadata, positions, margin, and the order types only perpetuals accept (triggers and TP/SL). | Type | Use it to | |---|---| | [`perpMeta`](/api/info/perpMeta.md) | Resolve perp symbols to the numeric `coin` id every other call takes. Cache at startup. | | [`perpMetaDetail`](/api/info/perpMetaDetail.md) | Show margin tiers and max leverage for one base asset (`BTC`, not `BTC-USDC`). | | [`marketTicker`](/api/info/marketTicker.md) | Fill a perp market header on first load: last, mark, oracle, funding, 24h change and volume. | | [`activeAssetCtx`](/api/info/activeAssetCtx.md) | Show a perp market header: mark, oracle, funding rate, open interest. | | [`webData2`](/api/info/webData2.md) | Fill a markets table with 24h stats for every instrument, plus the user’s open orders, in one call. | | [`perpClearinghouseState`](/api/info/perpClearinghouseState.md) | Show token balances held in the perp sub-account. | | [`activeAssetData`](/api/info/activeAssetData.md) | Fill the order ticket: the user’s leverage, max tradeable size, and available margin on one instrument. | | [`triggerOrders`](/api/info/triggerOrders.md) | List a user’s pending trigger (stop) orders, with any TP/SL children. | | [`tpslOrders`](/api/info/tpslOrders.md) | List all of a user’s pending take-profit / stop-loss orders. | | [`positionTpslOrders`](/api/info/positionTpslOrders.md) | Show the TP/SL pairs bound to one open position (position row → its TP/SL). | | [`tpslOrderPosition`](/api/info/tpslOrderPosition.md) | Resolve a TP/SL child order id back to the position it protects (order → position). | | [`getLiquidationPrices`](/api/info/getLiquidationPrices.md) | Preview the estimated liquidation price before the user submits an order. | | [`preCheckIsolatedMarginAdjust`](/api/info/preCheckIsolatedMarginAdjust.md) | Fill the Adjust Margin dialog: max add / remove and the new liquidation price before `updateIsolatedMargin`. | ### Outcome Event and market metadata, market context, positions, and open orders, plus the timeline and price-window data of recurring crypto markets. The metadata, context, timeline, price, and probability types are API-service aggregations. For one market's book, use the shared [`l2Book`](/api/info/l2Book.md); for account value and margin, [`clearinghouseState`](/api/info/clearinghouseState.md) with `accountType: "outcome"`. | Type | Use it to | |---|---| | [`outcomeMeta`](/api/info/outcomeMeta.md) | Build the Outcome discovery feed or an event page: events, nested markets, live prices, settlement state. | | [`outcomeNavTags`](/api/info/outcomeNavTags.md) | Build the Outcome category bar and tag menu; each node carries the `category` / `tag` to pass to `outcomeMeta`. | | [`outcomeAssetCtxs`](/api/info/outcomeAssetCtxs.md) | Fill 24h volume / price-change columns in an Outcome market grid. | | [`outcomeSeriesTimeline`](/api/info/outcomeSeriesTimeline.md) | Render the past / current / upcoming window strip of a recurring crypto Outcome series. | | [`cryptoPrice`](/api/info/cryptoPrice.md) | Show the price-to-beat and settlement price of a crypto Outcome window. | | [`cryptoPriceHistory`](/api/info/cryptoPriceHistory.md) | Seed the underlying-asset price chart on a crypto Outcome market. | | [`outcomeProbability`](/api/info/outcomeProbability.md) | Backfill a YES-probability chart; append live closes from `candle`. | | [`outcomeMetaByInstrument`](/api/info/outcomeMetaByInstrument.md) | Read on-chain trading params (tick, fee curve, resolution status) for one Outcome instrument. | | [`outcomeL2Book`](/api/info/outcomeL2Book.md) | Snapshot the books of several Outcome instruments at once (e.g. every market of an event). | | [`outcomePositions`](/api/info/outcomePositions.md) | List a user’s Outcome positions with event title and outcome names attached. | | [`outcomeBalances`](/api/info/outcomeBalances.md) | Show token balances held in the Outcome sub-account. | | [`outcomeFrontendOpenOrders`](/api/info/outcomeFrontendOpenOrders.md) | Render resting Outcome orders with event title, slug, and outcome names attached. | ## Paged history The order, fill, funding, and ledger history types share one paging scheme. `candleSnapshot`, `orderDetails`, and `userTwapSliceFills` do not use it. | | | |---|---| | Response | `data` is `{ data, hasMore, total? }`: the rows, whether more exist, and the total when known | | Paging | `offset` / `limit` — default `300`, max `2000`, newest first | | Filters | `user`, `startTimeMs` / `endTimeMs`, `accountType`, `contract` (`all` / `active`), `coin`, `side` | | Side | `Buy` / `Sell` on the request; responses use `B` / `A` | --- # `l2Book` L2 order-book snapshot for one instrument. For an Outcome market, pass its `instrument_id` from [`outcomeMeta`](/api/info/outcomeMeta.md) as the numeric string `coin`. `POST /info` · type `l2Book` Take a one-off order-book snapshot for one instrument. - Access: Public - Live stream: [`l2Book`](/api/websocket/l2Book.md) - Not this? [`outcomeL2Book`](/api/info/outcomeL2Book.md) — when you need books for several Outcome instruments in one call ### Request body - `coin` (string, required) — Instrument external id (numeric string) — the `index` value from the `perpMeta` response. - `type` (string, required) - `mantissa` (integer · int32, nullable, optional) - `nSigFigs` (integer · int32, nullable, optional) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"coin":"4","mantissa":1,"nSigFigs":5,"type":"l2Book"}' ``` ### Response · L2BookSnapshotResponse (200) - `code` (integer) — `0` on success. - `data` (L2BookSnapshotResponse) - `coin` (string) - `levels` (BookLevel[][]) - `n` (integer · int64) - `px` (string) - `sz` (string) - `time` (integer · int64) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "coin": "4", "levels": [ [ { "n": 0, "px": "0", "sz": "0" } ] ], "time": 1700000000000 } } ``` --- # `trades` Returns the latest 300 public trades for one instrument, newest first. Use it to seed a trade tape, then append live trades from the [`trades`](/api/websocket/trades.md) stream. `POST /info` · type `trades` Seed the public trade tape for one instrument with its latest 300 trades. - Access: Public - Live stream: [`trades`](/api/websocket/trades.md) - Not this? [`trades`](/api/websocket/apiTrades.md) — when you want the same 300-trade list re-sent every few seconds ### Request body - `coin` (string, required) — Numeric instrument id as a string. For an Outcome market, its instrument id. - `type` (string, required) — Must be `trades`. Values: `"trades"`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"trades","coin":"1"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object[]) — Up to 300 trades, newest first. - `coin` (string) — Base name. - `hash` (string, nullable) — Transaction hash. - `instrumentId` (integer) - `instrumentType` (string) — `perps` or `outcome`. - `marketType` (string) — `perps` or `outcome`. - `px` (string) — Fill price. - `side` (string) — Taker side: "B" = buy, "A" = sell. Values: `"B"`, `"A"`. - `sz` (string) — Fill size in base units. - `tid` (integer) — Trade id. - `time` (integer) — Unix ms. - `users` (string · address[]) — The two counterparties. - `symbol` (string) — Instrument name. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": [ { "coin": "BTC", "symbol": "BTC-USDC", "side": "A", "px": "83360.3", "sz": "0.0055", "time": 1792575622796, "hash": "0xd9ba89f9d68bff4d4c01d95523497476ec02037c0e4e3561ad0ef346aba79e42", "tid": 336565592000002, "users": [ "0x34136bc0647cd22c1ce37a2d32306004897d317c", "0x079e74e124151e4a411df17ea7e3ef97f4d049e3" ], "instrumentId": 1, "instrumentType": "perps", "marketType": "perps" } ] } ``` `coin` is the numeric instrument id as a string (`"1"` for BTC-USDC; see [`perpMeta`](/api/info/perpMeta.md)). Outcome markets use the same field with their instrument id. `side` is the taker's side. De-duplicate against the stream by `tid`. --- # `clearinghouseState` Full clearinghouse state (positions + margin summary) for one sub-account. `POST /info` · type `clearinghouseState` Load the Positions panel: every position plus the margin summary, for the `perp` or `outcome` sub-account. - Access: By user address - Live stream: [`clearinghouseState`](/api/websocket/clearinghouseState.md) - Not this? [`perpClearinghouseState`](/api/info/perpClearinghouseState.md) — when you only need token balances, not positions - Not this? [`outcomePositions`](/api/info/outcomePositions.md) — when you need Outcome positions with event titles and no margin summary ### Request body - `type` (string, required) - `user` (string, required) - `accountType` (string, nullable, optional) — Optional account type: `perp` or `outcome`. - `exchangeId` (integer · int32, nullable, optional) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"accountType":"perp","type":"clearinghouseState","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · UserStateResponse (200) - `code` (integer) — `0` on success. - `data` (UserStateResponse) - `accountValuePlusWithdrawHold` (string) — Total account equity plus pending withdrawals = accountValue + withdrawHold. - `assetPositions` (AssetPosition[]) - `position` (PositionData) - `asset` (string) - `coin` (string) - `cumFunding` (CumulativeFunding) - `allTime` (string) - `sinceChange` (string) - `sinceOpen` (string) - `leverage` (Leverage) - `type` (string) - `value` (integer · int32) - `rawUsd` (string, nullable) - `marginRatio` (string) — Margin ratio, precise to 4 decimal places. - `marginUsed` (string) - `maxLeverage` (integer · int32) - `positionValue` (string) - `returnOnEquity` (string) - `szi` (string) - `unrealizedPnl` (string) - `adlQuantile` (integer · int32, nullable) — ADL bucket (1-5). 5 = highest priority (auto-deleveraged first); 1 = safest. - `entryPx` (string, nullable) - `liquidationPx` (string, nullable) - `markPx` (string, nullable) - `updateTime` (integer · int64, nullable) — Timestamp of the most recent size change on this position, in milliseconds. Only refreshed on open / add / reduce / close; funding accrual, margin adjustments, and leverage changes do not touch it. `None` when the position has had no size change yet, or for early / unmigrated snapshots. Matches WebSocket `WsPosition.updateTime`. Wire name: `updateTime`. - `type` (string) - `tpsl` (array, nullable) - `createTime` (integer · int64) — Creation timestamp in milliseconds. - `quantity` (string) — Order quantity. `0` means close the entire position; non-zero means partial close. - `slOid` (string) — Stop-loss order id. - `slOrderPrice` (string) — Stop-loss limit price. - `slOrderType` (string) — Stop-loss order type, e.g. `"Limit"` or `"Market"`. - `slTriggerPrice` (string) — Stop-loss trigger price. - `tpOid` (string) — Take-profit order id. - `tpOrderPrice` (string) — Take-profit limit price. - `tpOrderType` (string) — Take-profit order type, e.g. `"Limit"` or `"Market"`. - `tpTriggerPrice` (string) — Take-profit trigger price. - `crossMaintenanceMarginUsed` (string) - `crossMarginRatio` (string) — Cross-margin ratio = (balance + crossUnRealPnl) / (crossMaintenanceMargin + crossLiqFee). OKX-style: higher is safer; liquidation triggers when the ratio drops to `<= 1.0`. - `crossMarginSummary` (MarginSummary) - `accountValue` (string) - `totalMarginUsed` (string) - `totalNtlPos` (string) - `totalRawUsd` (string) - `marginSummary` (MarginSummary) - `accountValue` (string) - `totalMarginUsed` (string) - `totalNtlPos` (string) - `totalRawUsd` (string) - `time` (integer · int64) - `totalRawUsdPlusWithdrawHold` (string) — Total raw USD balance plus pending withdrawals = totalRawUsd + withdrawHold. - `vaultEquity` (string) - `withdrawable` (string) - `activeReverseJobs` (ActiveReverseJob[]) - `asset` (string) - `coin` (string) - `filledCloseQty` (string) - `marginMode` (string) - `phase` (string) - `positionSzAtStart` (string) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "accountValuePlusWithdrawHold": "", "activeReverseJobs": [ { "asset": "4", "coin": "4", "filledCloseQty": "", "marginMode": "", "phase": "", "positionSzAtStart": "" } ], "assetPositions": [ { "position": { "adlQuantile": 0, "asset": "4", "coin": "4", "cumFunding": { "allTime": "", "sinceChange": "", "sinceOpen": "" }, "entryPx": "0", "leverage": { "rawUsd": "", "type": "", "value": 0 }, "liquidationPx": "0", "marginRatio": "", "marginUsed": "", "markPx": "0", "maxLeverage": 0, "positionValue": "", "returnOnEquity": "", "szi": "", "unrealizedPnl": "", "updateTime": 1700000000000 }, "tpsl": [ { "createTime": 1700000000000, "quantity": "0", "slOid": "", "slOrderPrice": "0", "slOrderType": "", "slTriggerPrice": "0", "tpOid": "", "tpOrderPrice": "0", "tpOrderType": "", "tpTriggerPrice": "0" } ], "type": "" } ], "crossMaintenanceMarginUsed": "", "crossMarginRatio": "", "crossMarginSummary": { "accountValue": "", "totalMarginUsed": "", "totalNtlPos": "", "totalRawUsd": "" }, "marginSummary": { "accountValue": "", "totalMarginUsed": "", "totalNtlPos": "", "totalRawUsd": "" }, "time": 1700000000000, "totalRawUsdPlusWithdrawHold": "", "vaultEquity": "", "withdrawable": "" } } ``` `accountType` selects the sub-account: `perp` or `outcome`. For Outcome, `exchangeId` optionally limits the result to one channel. For the token balances of the perp sub-account, use [`perpClearinghouseState`](/api/info/perpClearinghouseState.md); for Outcome positions alone, use [`outcomePositions`](/api/info/outcomePositions.md). --- # `frontendOpenOrders` User open orders with trigger / TP-SL fields The default scope covers only instruments with no configured `exchangeId`, so it omits Outcome orders. Pass `exchangeId: 1` for the Outcome exchange, or `exchangeId: "all"` for every order. For Outcome orders with event titles and outcome names attached, use [`outcomeFrontendOpenOrders`](/api/info/outcomeFrontendOpenOrders.md). `POST /info` · type `frontendOpenOrders` Render the Open Orders tab, including trigger and TP/SL fields. Outcome orders appear only with `exchangeId: 1` or `"all"`. - Access: By user address - Live stream: [`orderUpdates`](/api/websocket/orderUpdates.md) - Not this? [`outcomeFrontendOpenOrders`](/api/info/outcomeFrontendOpenOrders.md) — when you list Outcome orders — it needs no `exchangeId` and attaches event titles - Not this? [`historicalOrders`](/api/info/historicalOrders.md) — when you need filled or cancelled orders, not resting ones ### Request body - `type` (string, required) - `user` (string, required) - `asset` (string, nullable, optional) — Optional: instrument external id (numeric string) — the `index` value from the `perpMeta` response. - `exchangeId` (integer | string · int32, nullable, optional) — Channel to read: `0` (default platform), `1`, … or `"all"`. Outcome orders appear only with `1` or `"all"`. - `limit` (integer · int32, nullable, optional) — Maximum number of orders returned per call. - `offset` (integer · int32, nullable, optional) — Pagination offset. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"asset":"4","limit":50,"offset":0,"type":"frontendOpenOrders","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (FrontendOpenOrdersResponse[]) - `asset` (integer · int64) - `coin` (string) — Instrument name. - `dir` (string) - `instrumentType` (string) - `isPositionTpsl` (boolean) - `isTrigger` (boolean) - `limitPx` (string) - `oid` (integer · int64) - `orderType` (string) - `origSz` (string) - `reduceOnly` (boolean) - `side` (string) — "B" = buy, "A" = sell. - `state` (string) — Order state, e.g. `Open`. - `sz` (string) - `tif` (string) - `timestamp` (integer · int64) - `triggerCondition` (string) - `triggerPx` (string) - `avgFillPx` (string, nullable) - `children` (array, nullable) - `cloid` (string, nullable) - `expiresAt` (integer · int64, nullable) — GTD order expiry timestamp in milliseconds. `None` for non-GTD or unset orders; skipped on serialization. Wire name: `expiresAt`. - `fee` (string, nullable) - `filledAmount` (string, nullable) - `leverage` (integer · int32, nullable) - `marginMode` (string, nullable) - `positionMode` (string, nullable) - `triggerType` (string, nullable) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": [ { "asset": 1, "cloid": "my-order-001", "coin": "BTC-USDC", "dir": "Buy", "filledAmount": "0", "instrumentType": "Perps", "isPositionTpsl": false, "isTrigger": false, "leverage": 10, "limitPx": "66539.0", "marginMode": "Cross", "oid": 211558907950, "orderType": "Limit", "origSz": "0.0010", "positionMode": "OneWay", "reduceOnly": false, "side": "B", "state": "Open", "sz": "0.0010", "tif": "GTC", "timestamp": 1791893561993, "triggerCondition": "", "triggerPx": "" } ] } ``` --- # `userFees` User fee rates; returns default rates when `user` is omitted `POST /info` · type `userFees` Show the maker / taker rate a user currently pays. - Access: By user address - Not this? [`feeTierRates`](/api/info/feeTierRates.md) — when you need the full schedule for an account type ### Request body - `asset` (string, required) — Instrument external id (numeric string) — the `index` value from the `perpMeta` response. - `type` (string, required) - `user` (string, nullable, optional) — Optional: when omitted, returns the default fee rates without any discount. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"asset":"4","type":"userFees","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · UserFeesResponse (200) - `code` (integer) — `0` on success. - `data` (UserFeesResponse) - `activeReferralDiscount` (string) - `dailyUserVlm` (DailyUserVlm[]) - `date` (string) - `exchange` (string) - `userAdd` (string) - `userCross` (string) - `feeSchedule` (FeeSchedule) - `add` (string) - `cross` (string) - `referralDiscount` (string) - `tiers` (Tiers) - `mm` (Mm[]) - `add` (string) - `makerFractionCutoff` (string) - `vip` (Vip[]) - `add` (string) - `cross` (string) - `ntlCutoff` (string) - `userAdd` (string) - `userAddRate` (string) - `userCross` (string) - `userCrossRate` (string) - `volumeToday` (string) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "activeReferralDiscount": "", "dailyUserVlm": [ { "date": "", "exchange": "", "userAdd": "", "userCross": "" } ], "feeSchedule": { "add": "", "cross": "", "referralDiscount": "", "tiers": { "mm": [ { "add": "", "makerFractionCutoff": "" } ], "vip": [ { "add": "", "cross": "", "ntlCutoff": "" } ] } }, "userAdd": "", "userAddRate": "", "userCross": "", "userCrossRate": "", "volumeToday": "" } } ``` --- # `feeTierInfo` Returns the static fee-tier ladder configured for an exchange. The optional HTTP field is named `exchange_id`; omit it to query the platform exchange (`0`). This is separate from [`feeTierRates`](/api/info/feeTierRates.md), which returns effective rates for an account type and optional user. `POST /info` · type `feeTierInfo` Render the static fee-tier ladder (volume thresholds → rates). - Access: Public - Not this? [`feeTierRates`](/api/info/feeTierRates.md) — when you want rates after the user’s tier and referral discount ### Request body - `type` (string, required) - `exchange_id` (integer · int32, nullable, optional) — Exchange id on the HTTP wire. Defaults to the platform exchange (0). ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"exchange_id":1,"type":"feeTierInfo"}' ``` ### Response · FeeTierInfoResponse (200) - `code` (integer) — `0` on success. - `data` (FeeTierInfoResponse) - `tiers` (FeeTierEntryResponse[]) - `level` (integer · int32) - `min14dTradingVolume` (string) - `perpMakerFeeRate` (string) - `perpTakerFeeRate` (string) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "tiers": [ { "level": 0, "min14dTradingVolume": "", "perpMakerFeeRate": "", "perpTakerFeeRate": "" } ] } } ``` --- # `feeTierRates` Returns the effective fee schedule for an account type and exchange. Pass `user` to include the user's volume tier and referral discount; omit it for the default rates. This is separate from [`feeTierInfo`](/api/info/feeTierInfo.md), which returns the static configured fee-tier ladder. `POST /info` · type `feeTierRates` Show effective rates for an account type, including the user’s tier and referral discount. - Access: By user address - Not this? [`feeTierInfo`](/api/info/feeTierInfo.md) — when you want the configured ladder only ### Request body - `accountType` (string, required) — Account type whose effective fee schedule is requested. Values: `"perp"`, `"perps"`, `"outcome"`. - `type` (string, required) - `exchangeId` (integer · int32, nullable, optional) — Exchange id. Defaults to the platform exchange (0). - `user` (string, nullable, optional) — Optional user address. When omitted, returns the default effective rates. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"accountType":"perp","type":"feeTierRates","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · UserFeesResponse (200) - `code` (integer) — `0` on success. - `data` (UserFeesResponse) - `activeReferralDiscount` (string) - `dailyUserVlm` (DailyUserVlm[]) - `date` (string) - `exchange` (string) - `userAdd` (string) - `userCross` (string) - `feeSchedule` (FeeSchedule) - `add` (string) - `cross` (string) - `referralDiscount` (string) - `tiers` (Tiers) - `mm` (Mm[]) - `add` (string) - `makerFractionCutoff` (string) - `vip` (Vip[]) - `add` (string) - `cross` (string) - `ntlCutoff` (string) - `userAdd` (string) - `userAddRate` (string) - `userCross` (string) - `userCrossRate` (string) - `volumeToday` (string) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "activeReferralDiscount": "", "dailyUserVlm": [ { "date": "", "exchange": "", "userAdd": "", "userCross": "" } ], "feeSchedule": { "add": "", "cross": "", "referralDiscount": "", "tiers": { "mm": [ { "add": "", "makerFractionCutoff": "" } ], "vip": [ { "add": "", "cross": "", "ntlCutoff": "" } ] } }, "userAdd": "", "userAddRate": "", "userCross": "", "userCrossRate": "", "volumeToday": "" } } ``` --- # `builderState` Accrual, payout history, and eligibility for one **builder** — the address that collects a [builder code](/builders.md) fee on the orders it submits. `user` is the builder's own address, not the trader's. `POST /info` · type `builderState` Read a builder’s on-chain claimable balance and fee eligibility before `claimBuilderRewards`. - Access: By user address - Not this? [`builderMeClaimable`](/api/info/builderMeClaimable.md) — when you want the indexed dashboard totals ### Request body - `type` (string, required) - `user` (string, required) — Builder address to read state for. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"builderState","user":"0x4916d34209A2E010912dE8b412A90ECcEc77D8aC"}' ``` ### Response · BuilderStateResponse (200) - `code` (integer) — `0` on success. - `data` (BuilderStateResponse) - `accruedRewards` (string) — Builder fees accrued to this address over its lifetime, in USDC. - `activeUserCount` (integer · int64) — Number of users currently approving this builder. - `claimableRewards` (string) — Currently unclaimed balance, in USDC. This is what `claimBuilderRewards` pays out. - `claimedRewards` (string) — Total already claimed, in USDC. - `eligible` (boolean) — Whether the address currently qualifies to accrue builder fees. - `isStandardAccount` (boolean) — Whether the address is a standard account. LP and vault addresses are never eligible. - `perpsAccountValue` (string) — The address's available perp-account balance, in USDC. Builder fees accrue only while this is at least 100. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "accruedRewards": "0.000000", "claimableRewards": "0.000000", "claimedRewards": "0.000000", "eligible": true, "perpsAccountValue": "200.012971", "isStandardAccount": true, "activeUserCount": 0 } } ``` ## Balances The three USDC figures are the same balances [`claimBuilderRewards`](/api/exchange/claimBuilderRewards.md) acts on: | Field | Meaning | |---|---| | `accruedRewards` | Lifetime total accrued to this builder. A claim does not change it. | | `claimableRewards` | Currently unclaimed. This is what a claim pays out; claiming with `0` here fails with `No unclaimed builder rewards`. | | `claimedRewards` | Total already claimed. A claim moves `claimableRewards` into this. | ## Eligibility Builder fees accrue only while the address qualifies. `eligible` is the answer; the two fields beside it are the inputs: | Field | Meaning | |---|---| | `eligible` | Whether the address currently qualifies to accrue builder fees. | | `perpsAccountValue` | The builder's available perp-account balance in USDC — fees accrue only while this is at least `100`. | | `isStandardAccount` | Whether the address is a standard account. LP and vault addresses are never eligible. | `activeUserCount` is the number of users currently approving this builder. > **Note:** An order carrying an ineligible or unapproved builder is **not** rejected — the fee is dropped and the order still executes. Poll this query rather than inferring accrual from order responses. --- # `referral` `POST /info` · type `referral` Render the Referral page: referrer, rewards, referrer stage. - Access: By user address ### Request body - `type` (string, required) - `user` (string, required) — User address. - `exchangeId` (integer · int32, nullable, optional) — Optional channel filter. Omit or `0` for the default platform channel. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"referral","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · ReferralResponse (200) - `code` (integer) — `0` on success. - `data` (ReferralResponse) - `claimedRewards` (string) - `cumVlm` (string) - `referrerState` (ReferrerState) - `data` (ReferrerData) — Payload that depends on the referrer stage. - One of · variant 1: - `code` (string) - `nReferrals` (integer · int64) - `referralStates` (ReferreeState[]) - `cumFeesRewardedToReferrer` (string) - `cumRewardedFeesSinceReferred` (string) - `cumVlm` (string) - `timeJoined` (integer · int64) - `user` (string) - One of · variant 2: - `required` (string) — Remaining volume required before a referral code can be created. - One of · variant 3: - `stage` (string) — `ready` | `needToTrade` | `needToCreateCode`. Values: `"ready"`, `"needToTrade"`, `"needToCreateCode"`. - `unclaimedRewards` (string) - `referredBy` (anyOf) - Any of · variant 1: - `code` (string) - `referrer` (string) - Any of · variant 2: - null - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "referredBy": { "referrer": "", "code": "" }, "cumVlm": "", "unclaimedRewards": "", "claimedRewards": "", "referrerState": { "stage": "ready", "data": { "code": "", "nReferrals": 0, "referralStates": [ { "cumVlm": "", "cumRewardedFeesSinceReferred": "", "cumFeesRewardedToReferrer": "", "timeJoined": 1700000000000, "user": "0x0000000000000000000000000000000000000000" } ] } } } } ``` --- # `userRole` Returns the referral role of `user`. Clients read `domains.referral.role` to choose which referral view to render. `POST /info` · type `userRole` Pick the referral view to render: regular user, referrer, or affiliate. - Access: By user address - Not this? [`referral`](/api/info/referral.md) — when you need the referral volume and reward balances ### Request body - `type` (string, required) — Must be `userRole`. Values: `"userRole"`. - `user` (string · address, required) — Account to look up. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"userRole","user":"0x08829c9bee1df67748ec568da9c98a952fdeda5e"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `domains` (object) — Roles grouped by product domain. - `referral` (object) - `role` (integer) — `0` regular user, `1` referrer, `2` affiliate. Values: `0`, `1`, `2`. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "domains": { "referral": { "role": 1 } } } } ``` | `role` | Meaning | |---|---| | `0` | Regular user | | `1` | Referrer | | `2` | Affiliate | ## Next steps Read referral volume and reward balances from [`referral`](/api/info/referral.md), and past claims from [`userClaimRewards`](/api/info/userClaimRewards.md). --- # `userClaimRewards` Returns the referral reward claims `user` has made, one row per claim. Use it to render a claim history on the referral page. `POST /info` · type `userClaimRewards` Render the referral reward claim history. - Access: By user address - Not this? [`referral`](/api/info/referral.md) — when you need the current unclaimed and claimed totals ### Request body - `type` (string, required) — Must be `userClaimRewards`. Values: `"userClaimRewards"`. - `user` (string · address, required) — Account that claimed. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"userClaimRewards","user":"0x95761f4d19B906B3B09da3EeE0BF30B337f4dB88"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object[]) — One row per claim. Empty when the user has never claimed. - `claimAmount` (string) — Amount claimed, in USDC. - `claimTime` (integer) — Claim time, Unix milliseconds. - `txHash` (string) — TxFlow transaction of the claim. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": [ { "claimAmount": "12.5", "claimTime": 1792402405897, "txHash": "0xabc123def456abc123def456abc123def456abc123def456abc123def456abc1" } ] } ``` `data` is an array; it is empty for a user who has never claimed. `claimAmount` is a decimal USDC string and `claimTime` is Unix milliseconds. ## Next steps Read the current unclaimed and claimed totals from [`referral`](/api/info/referral.md). --- # `agentOwner` Owner of an agent; `user` is null when the agent is not authorized `POST /info` · type `agentOwner` Check whether an agent key is still authorized, and for which account. - Access: Public ### Request body - `agent` (string, required) — Agent wallet address. - `type` (string, required) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"agent":"0x0000000000000000000000000000000000000000","type":"agentOwner"}' ``` ### Response · AgentOwnerResponse (200) - `code` (integer) — `0` on success. - `data` (AgentOwnerResponse) - `agent` (string) - `user` (string, nullable) — Owner wallet address; `None`/absent when the agent is not an authorized agent of any account. Callers derive authorization from `user.is_some()`. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "agent": "0x0000000000000000000000000000000000000000", "user": "0x0000000000000000000000000000000000000000" } } ``` --- # `approvedAgents` `POST /info` · type `approvedAgents` List the agent keys an account has approved (e.g. an API-key management page). - Access: By user address ### Request body - `type` (string, required) - `user` (string, required) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"approvedAgents","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · ApprovedAgentsResponse (200) - `code` (integer) — `0` on success. - `data` (ApprovedAgentsResponse) - `agents` (ApprovedAgent[]) - `agentAddress` (string) - `agentName` (string) - `user` (string) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "agents": [ { "agentAddress": "0x0000000000000000000000000000000000000000", "agentName": "" } ], "user": "0x0000000000000000000000000000000000000000" } } ``` --- # `getMaxPositions` Returns the maximum buy and sell sizes available at the requested price. The query supports both perpetual and Outcome instruments. For Outcome, pass the market's `instrument_id` from [`outcomeMeta`](/api/info/outcomeMeta.md) as the string-valued `asset`. `buyPrice` / `sellPrice` are required according to the side being estimated. `POST /info` · type `getMaxPositions` Compute the max buy / sell size at a price the user typed into the order ticket. - Access: By user address - Not this? [`activeAssetData`](/api/info/activeAssetData.md) — when a price-independent max size is enough ### Request body - `asset` (string, required) — Instrument external id (numeric string) — the `index` value from the `perpMeta` response. - `orderType` (string, required) — Order type: `LIMIT` or `MARKET`. - `type` (string, required) - `user` (string, required) - `buyPrice` (string, nullable, optional) - `reduceOnly` (boolean, nullable, optional) - `sellPrice` (string, nullable, optional) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"asset":"4","buyPrice":"0","orderType":"limit","reduceOnly":false,"sellPrice":"0","type":"getMaxPositions","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · MaxPositionsResponse (200) - `code` (integer) — `0` on success. - `data` (MaxPositionsResponse) — Maximum tradable-quantity response. Reports the maximum long/short size the user can open at the given price. - `availableToTrade` (string) — User available balance. - `buyPrice` (string) — Buy price used in the calculation. - `long` (MaxPositionCalculation) — Long-side calculation. - `leverage` (integer · int32) — Leverage multiplier. - `maxQuantity` (string) — Maximum opening quantity. - `requiredFee` (string) — Taker fee required at the maximum position size. - `requiredMargin` (string) — Margin required at the maximum position size. - `totalCost` (string) — Total cost = margin + fee. - `longCloseQuantity` (string) — Long side closable quantity (`long_position_quantity` minus the already-placed close-long orders). - `longPositionQuantity` (string) — Current long position quantity. - `marginMode` (string) — Margin mode: `cross` or `isolated`. - `positionMode` (string) — Position mode: `oneWay` or `hedge`. - `sellPrice` (string) — Sell price used in the calculation. - `short` (MaxPositionCalculation) — Short-side calculation. - `leverage` (integer · int32) — Leverage multiplier. - `maxQuantity` (string) — Maximum opening quantity. - `requiredFee` (string) — Taker fee required at the maximum position size. - `requiredMargin` (string) — Margin required at the maximum position size. - `totalCost` (string) — Total cost = margin + fee. - `shortCloseQuantity` (string) — Short side closable quantity (`short_position_quantity` minus the already-placed close-short orders). - `shortPositionQuantity` (string) — Current short position quantity. - `symbol` (string) — Trading-pair symbol. - `user` (string) — User address. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "availableToTrade": "", "buyPrice": "0", "long": { "leverage": 0, "maxQuantity": "0", "requiredFee": "", "requiredMargin": "", "totalCost": "" }, "longCloseQuantity": "0", "longPositionQuantity": "0", "marginMode": "", "positionMode": "", "sellPrice": "0", "short": { "leverage": 0, "maxQuantity": "0", "requiredFee": "", "requiredMargin": "", "totalCost": "" }, "shortCloseQuantity": "0", "shortPositionQuantity": "0", "symbol": "", "user": "0x0000000000000000000000000000000000000000" } } ``` --- # `getLatestBlockNumber` `POST /info` · type `getLatestBlockNumber` Check node liveness or read the current block height. - Access: Public - Live stream: [`newBlock`](/api/websocket/newBlock.md) ### Request body - `type` (string, required) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"getLatestBlockNumber"}' ``` ### Response · GetLatestBlockNumberResponse (200) - `code` (integer) — `0` on success. - `data` (GetLatestBlockNumberResponse) - `blockNumber` (integer · int64, nullable) — Latest global block number, or null when no blocks are available yet. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "blockNumber": 0 } } ``` --- # `epochHistory` Recent consensus epochs and the current epoch Each entry records where an epoch starts, as both a global block number and a snapshot version. Entries are ordered oldest first, so the last one is the current epoch. `POST /info` · type `epochHistory` Map block numbers to consensus epochs. - Access: Public ### Request body - `type` (string, required) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"epochHistory"}' ``` ### Response · EpochHistoryResponse (200) - `code` (integer) — `0` on success. - `data` (EpochHistoryResponse) - `currentEpoch` (integer · int64) — Current epoch. Equals the `epoch` of the last entry in `epochs`. - `epochs` (EpochHistoryEntry[]) — Recent epochs in ascending order, oldest first. - `blockNumOffset` (integer · int64) — Global block number at which the epoch starts. - `epoch` (integer · int64) - `previousHash` (string) — Hash of the preceding epoch, hex without a `0x` prefix. - `snapshotVersionOffset` (integer · int64) — Snapshot version at which the epoch starts. - `validatorIndex` (integer · int64) — Index of the validator that served the response. Identical across every entry in one response, and may differ between responses served by different nodes. - `validatorsCount` (integer · int64) — Size of the validator set. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "currentEpoch": 0, "epochs": [ { "blockNumOffset": 0, "epoch": 0, "previousHash": "", "snapshotVersionOffset": 0, "validatorIndex": 0, "validatorsCount": 0 } ] } } ``` --- # `exchangeRates` Returns the USD value of USDC and USDT, and a list of display currencies quoted against 1 USD. Use it to show balances and PnL in USD or a local currency. There is no request body besides `type`. `POST /info` · type `exchangeRates` Convert USDC amounts to USD or a local fiat currency for display. - Access: Public ### Request body - `type` (string, required) — Must be `exchangeRates`. Values: `"exchangeRates"`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"exchangeRates"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `fiat` (object[]) — Display currencies, each quoted against 1 USD. - `fullName` (string) — Display name. - `name` (string) — ISO currency code. - `precision` (string) — Smallest display increment for amounts in this currency. - `rate` (string) — Units of this currency per 1 USD. - `symbol` (string) — Display symbol. - `usdcUsd` (string) — USD value of 1 USDC. Multiply a USDC amount by this to show it in USD. - `usdtUsd` (string) — USD value of 1 USDT. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "usdcUsd": "0.99973725", "usdtUsd": "0.9989355", "fiat": [ { "name": "USD", "rate": "1", "symbol": "$", "fullName": "US Dollar", "precision": "0.01" } ] } } ``` To display a USDC amount in a fiat currency, multiply by `usdcUsd`, then by that currency's `rate`, and round to its `precision`. The rates change slowly; refreshing every few minutes is enough. --- # `tags` Returns every market tag as a flat list of `id`, `name`, and `sort`. There is no request body besides `type`. `POST /info` · type `tags` Label instruments with tag names; resolves the `tagIds` on `webData2` asset rows. - Access: Public - Markets: Perp only - Not this? [`marketTags`](/api/info/marketTags.md) — when you build the two-level markets menu ### Request body - `type` (string, required) — Must be `tags`. Values: `"tags"`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"tags"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object[]) — Ordered by `sort`. - `id` (integer) — Tag id, as used in `tagIds`. - `name` (string) — Display name. - `sort` (integer) — Display order, ascending. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": [ { "id": 1, "name": "Trending", "sort": 1 } ] } ``` Use it to turn the `tagIds` on [`webData2`](/api/info/webData2.md) asset rows into labels. For the full two-level markets menu, use [`marketTags`](/api/info/marketTags.md). --- # `marketTags` Returns the layout of the markets menu in one call: top-level tabs (`l1`), the sub-tags under them (`l2`), and the tags on each instrument (`instrumentTags`). There is no request body besides `type`. `POST /info` · type `marketTags` Build the markets menu: top-level tabs, their sub-tags, and which instruments sit under each. - Access: Public - Markets: Perp only - Not this? [`tags`](/api/info/tags.md) — when a flat list of tag names is enough ### Request body - `type` (string, required) — Must be `marketTags`. Values: `"marketTags"`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"marketTags"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `instrumentTags` (object[]) — One row per tagged instrument. - `businessLine` (string) — Market of the instrument. - `instrumentId` (integer) — Numeric instrument id (`coin`). - `instrumentName` (string) - `tagIds` (integer[]) — `l2` ids this instrument belongs to. - `l1` (object[]) — Top-level tabs, ordered by `sort`. - `businessLine` (string, nullable) — Market the tab covers (`perp`, `spot`); `null` for `favorites`. - `enabled` (boolean) — Hide the tab when `false`. - `id` (integer) - `kind` (string) — `favorites` is the user’s watchlist (filled client-side), `business_line` lists every instrument of one market, `operational` is a curated group. Values: `"favorites"`, `"business_line"`, `"operational"`. - `l2Refs` (integer[]) — Ids of the `l2` sub-tags shown under this tab, in order. - `name` (string) — Display name. - `sort` (integer) — Display order, ascending. - `deletable` (boolean) — `false` for built-in tabs. - `l2` (object[]) — Sub-tags referenced by `l1[].l2Refs` and `instrumentTags[].tagIds`. - `id` (integer) - `name` (string) — Display name. - `sort` (integer) — Display order, ascending. - `enabled` (boolean) — Hide the sub-tag when `false`. - `version` (string) — Layout version. Changes whenever the layout is edited; use it as a cache key. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "version": "1791790787734-204", "l1": [ { "id": 2, "name": "perp", "kind": "business_line", "businessLine": "perp", "sort": 2, "enabled": true, "deletable": false, "l2Refs": [ 1, 2, 3, 4, 5, 6, 7 ] } ], "l2": [ { "id": 1, "name": "Trending", "sort": 1, "enabled": true } ], "instrumentTags": [ { "instrumentId": 1, "instrumentName": "BTC-USDC", "businessLine": "perp", "tagIds": [ 1, 2, 4, 5 ] } ] } } ``` ## Building the menu 1. Render `l1` tabs with `enabled: true`, in `sort` order. 2. Under each tab, render the `l2` entries listed in its `l2Refs`, in that order. 3. A `business_line` tab lists every instrument whose `businessLine` matches. A sub-tag lists the instruments whose `instrumentTags[].tagIds` contain its `id`. 4. A `favorites` tab has no server-side contents; fill it from the user's watchlist. Cache the response and refetch when `version` changes. --- # `candleSnapshot` OHLCV bars for one instrument. The window is `[startTime, endTime)`. A request that would span more than **3000** bars may be rejected as an empty list. `coin` accepts a name (`BTC-USDC`) or the numeric id from [`perpMeta`](/api/info/perpMeta.md), or an outcome `instrumentId`. On an outcome market the closes are probabilities in `[0, 1]`. `data` is a candle array, not a paged `{ data, hasMore, total }` object. `POST /info` · type `candleSnapshot` Seed a candlestick chart; then keep the last bar live with `candle`. - Access: Public - Live stream: [`candle`](/api/websocket/candle.md) - Served from an index — can briefly lag the latest block. ### Request body - `req` (object, required) - `coin` (string, required) — Instrument name (`BTC-USDC`) or numeric id from `perpMeta`, or an outcome `instrumentId`. - `endTime` (integer · uint64, required) — Window end, Unix milliseconds. - `interval` (string, required) Values: `"1m"`, `"3m"`, `"5m"`, `"15m"`, `"30m"`, `"1h"`, `"2h"`, `"4h"`, `"8h"`, `"12h"`, `"1d"`. - `startTime` (integer · uint64, required) — Window start, Unix milliseconds. The returned range is `[startTime, endTime)`. - `type` (string, required) — Must be `candleSnapshot`. Values: `"candleSnapshot"`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"candleSnapshot","req":{"coin":"1","interval":"1d","startTime":1712484000000,"endTime":1720260000000}}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (Candle[]) - `c` (string) - `h` (string) - `i` (string) — Echo of the requested `interval`. - `l` (string) - `n` (integer · uint64) — Trade count in the bar. - `o` (string) - `s` (string) — Echo of the requested `coin`. - `t` (integer · uint64) — Bar open time, Unix milliseconds. - `T` (integer · uint64) — Bar close time, Unix milliseconds. - `v` (string) - `success` (boolean) ### Response example ```json { "success": true, "code": 0, "data": [ { "t": 1720000000000, "T": 1720086400000, "o": "65000.0", "h": "66100.0", "l": "64800.0", "c": "65500.0", "v": "12.4", "n": 842, "s": "1", "i": "1d" } ] } ``` --- # `historicalOrders` Limit and market order history. The Order History **Limit / Market** tab uses this type and omits `orderTypes`. Switch to TP/SL, trigger, or TWAP by changing `type` — see [`historicalTpslOrders`](/api/info/historicalTpslOrders.md), [`historicalTriggerOrders`](/api/info/historicalTriggerOrders.md), and [`twapHistory`](/api/info/twapHistory.md). Look up one row by id with [`orderDetails`](/api/info/orderDetails.md). Include `outcome` in `accountType` to return Outcome orders. For currently resting Outcome orders, prefer [`outcomeFrontendOpenOrders`](/api/info/outcomeFrontendOpenOrders.md), whose rows are enriched with event and market display metadata. `POST /info` · type `historicalOrders` Render the Order History tab (limit / market orders). · paged `{ data, hasMore, total? }` - Access: By user address - Not this? [`frontendOpenOrders`](/api/info/frontendOpenOrders.md) — when you need currently resting orders - Not this? [`orderDetails`](/api/info/orderDetails.md) — when you have one `orderId` - Not this? [`userFills`](/api/info/userFills.md) — when you need executions, not orders - Served from an index — can briefly lag the latest block. ### Request body - `type` (string, required) — Must be `historicalOrders`. Values: `"historicalOrders"`. - `user` (string · address, required) — Account to query. No auth — the body `user` is the subject. - `accountType` (string[], optional) — Instrument tab: `["perp"]`, `["outcome"]`, or both (`perps` is accepted as an alias). - `coin` (string, optional) — Single instrument — a name (`BTC`, `BTC-USDC`) or the numeric id from `perpMeta`. Prefer this over `contract` for one market. - `contract` (string, optional) — `active` = instruments where this user currently has an open perps position (`szi ≠ 0`). Empty list when there is no position. Do not combine with `coin`. Values: `"all"`, `"active"`. - `endTimeMs` (integer · uint64, optional) — Inclusive window end, Unix milliseconds. Alias: `endTime`. - `limit` (integer · int64, optional) — Page size. Default `300`; max `2000` (export). Values above 2000 are clamped. `<= 0` is rejected. - `offset` (integer · int64, optional) — Skip this many newest-first rows. Omit or `0` for the first page; page N is `(N - 1) * limit`. Max `200000`. Alias: `from` on some types. - `orderTypes` (string[], optional) — Omit on the default Order History tab. Pass `["Limit"]` or `["Market"]` only to restrict. - `side` (string, optional) — Request filter uses `Buy` / `Sell`. Response `side` is usually `B` / `A`. Values: `"Buy"`, `"Sell"`. - `startTimeMs` (integer · uint64, optional) — Inclusive window start, Unix milliseconds. Alias: `startTime`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"historicalOrders","user":"0x0000000000000000000000000000000000000001","offset":0,"limit":20}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (HistoricalOrder[]) — Newest-first rows for this page. - `order` (HistoricalOrderInfo) - `coin` (string) - `instrumentId` (integer · uint64) - `instrumentType` (string) Values: `"perps"`, `"outcome"`. - `limitPx` (string) - `marketType` (string) Values: `"perps"`, `"outcome"`. - `oid` (integer · uint64) - `orderType` (string) — `Limit` or `Market`. - `side` (string) — `B` buy / `A` sell. - `status` (string) - `sz` (string) — Remaining size. - `timestamp` (integer · uint64) - `avgFillPrice` (string) - `cancelSource` (string) - `children` (object[]) — Attached TP/SL children when present. - `cloid` (string, nullable) - `dir` (string) — Display label — do not filter on this. - `entryPrice` (string) - `fee` (string) - `filledAmount` (string) - `isPositionTpsl` (boolean) - `isTrigger` (boolean) - `leverage` (integer) - `marginMode` (string) - `origSz` (string) - `pnl` (string) - `positionMode` (string) - `reduceOnly` (boolean) - `roe` (string) - `symbol` (string) — Display symbol when known. - `tif` (string) - `triggerCondition` (string) - `triggerPx` (string) - `status` (string) — Terminal order status. - `statusTimestamp` (integer · uint64) — When this status was recorded, Unix milliseconds. - `hasMore` (boolean) — `true` when more rows exist after this page. Prefer this over guessing from a missing `total`. - `total` (integer · uint64) — Omitted when the count is unknown. - `success` (boolean) ### Response example ```json { "success": true, "code": 0, "data": { "data": [ { "order": { "coin": "BTC", "symbol": "BTC-USDC", "side": "B", "limitPx": "65000.0", "sz": "0.01", "oid": 123456, "timestamp": 1720000000000, "triggerCondition": "", "isTrigger": false, "triggerPx": "0", "children": [ "" ], "isPositionTpsl": false, "reduceOnly": false, "dir": "Open Long", "orderType": "Limit", "origSz": "0.01", "tif": "Gtc", "cloid": "my-order-001", "avgFillPrice": "65010.5", "positionMode": "OneWay", "marginMode": "cross", "leverage": 10, "pnl": "12.5", "entryPrice": "64000.0", "roe": "1.95", "fee": "0.02", "filledAmount": "0.01", "cancelSource": "UserCanceled", "status": "filled", "instrumentId": 1, "instrumentType": "perps", "marketType": "perps" }, "status": "filled", "statusTimestamp": 1720000000000 } ], "hasMore": true, "total": 1337 } } ``` --- # `historicalTpslOrders` Take-profit / stop-loss history. Same shared filters as [`historicalOrders`](/api/info/historicalOrders.md). Live TP/SL rests on [`tpslOrders`](/api/info/tpslOrders.md). `POST /info` · type `historicalTpslOrders` Render the TP/SL tab of Order History. · paged `{ data, hasMore, total? }` - Access: By user address - Markets: Perp only - Not this? [`tpslOrders`](/api/info/tpslOrders.md) — when you need TP/SL that is still pending - Served from an index — can briefly lag the latest block. ### Request body - `type` (string, required) — Must be `historicalTpslOrders`. Values: `"historicalTpslOrders"`. - `user` (string · address, required) — Account to query. No auth — the body `user` is the subject. - `accountType` (string[], optional) — Instrument tab: `["perp"]`, `["outcome"]`, or both (`perps` is accepted as an alias). - `coin` (string, optional) — Single instrument — a name (`BTC`, `BTC-USDC`) or the numeric id from `perpMeta`. Prefer this over `contract` for one market. - `contract` (string, optional) — `active` = instruments where this user currently has an open perps position (`szi ≠ 0`). Empty list when there is no position. Do not combine with `coin`. Values: `"all"`, `"active"`. - `endTimeMs` (integer · uint64, optional) — Inclusive window end, Unix milliseconds. Alias: `endTime`. - `limit` (integer · int64, optional) — Page size. Default `300`; max `2000` (export). Values above 2000 are clamped. `<= 0` is rejected. - `offset` (integer · int64, optional) — Skip this many newest-first rows. Omit or `0` for the first page; page N is `(N - 1) * limit`. Max `200000`. Alias: `from` on some types. - `side` (string, optional) — Request filter uses `Buy` / `Sell`. Response `side` is usually `B` / `A`. Values: `"Buy"`, `"Sell"`. - `startTimeMs` (integer · uint64, optional) — Inclusive window start, Unix milliseconds. Alias: `startTime`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"historicalTpslOrders","user":"0x0000000000000000000000000000000000000001","offset":0,"limit":20}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (HistoricalTpslOrder[]) — Newest-first rows for this page. - `coin` (string) - `createTime` (integer · uint64) - `instrumentId` (integer · uint64) - `marketType` (string) Values: `"perps"`, `"outcome"`. - `quantity` (string) - `side` (string) - `state` (string) - `updateTime` (integer · uint64) - `cancelReason` (string) - `cancelSource` (string) - `leverage` (integer) - `marginMode` (string) - `positionMode` (string) - `reduceOnly` (boolean) - `slOrderId` (string) - `slOrderPrice` (string) - `slOrderType` (string) - `slTriggerPrice` (string) - `tpOrderId` (string) - `tpOrderPrice` (string) - `tpOrderType` (string) - `tpTriggerPrice` (string) - `triggerPrice` (string) - `triggerPriceType` (string) - `triggerType` (string) - `hasMore` (boolean) — `true` when more rows exist after this page. Prefer this over guessing from a missing `total`. - `total` (integer · uint64) — Omitted when the count is unknown. - `success` (boolean) ### Response example ```json { "success": true, "code": 0, "data": { "data": [ { "coin": "BTC", "instrumentId": 1, "side": "A", "triggerPrice": "62000.0", "triggerType": "mark", "triggerPriceType": "mark", "quantity": "0.01", "reduceOnly": true, "marginMode": "cross", "positionMode": "OneWay", "leverage": 10, "slOrderId": "111", "tpOrderId": "112", "slOrderPrice": "61000.0", "tpOrderPrice": "70000.0", "slTriggerPrice": "61500.0", "tpTriggerPrice": "69000.0", "slOrderType": "Market", "tpOrderType": "Market", "cancelSource": "", "cancelReason": "", "state": "triggered", "createTime": 1720000000000, "updateTime": 1720260000000, "marketType": "perps" } ], "hasMore": true, "total": 1337 } } ``` --- # `historicalTriggerOrders` Trigger order history, including optional TP/SL children on the trigger. Same shared filters as [`historicalOrders`](/api/info/historicalOrders.md). Live triggers are [`triggerOrders`](/api/info/triggerOrders.md). `POST /info` · type `historicalTriggerOrders` Render the Trigger tab of Order History. · paged `{ data, hasMore, total? }` - Access: By user address - Markets: Perp only - Not this? [`triggerOrders`](/api/info/triggerOrders.md) — when you need triggers that are still pending - Served from an index — can briefly lag the latest block. ### Request body - `type` (string, required) — Must be `historicalTriggerOrders`. Values: `"historicalTriggerOrders"`. - `user` (string · address, required) — Account to query. No auth — the body `user` is the subject. - `accountType` (string[], optional) — Instrument tab: `["perp"]`, `["outcome"]`, or both (`perps` is accepted as an alias). - `coin` (string, optional) — Single instrument — a name (`BTC`, `BTC-USDC`) or the numeric id from `perpMeta`. Prefer this over `contract` for one market. - `contract` (string, optional) — `active` = instruments where this user currently has an open perps position (`szi ≠ 0`). Empty list when there is no position. Do not combine with `coin`. Values: `"all"`, `"active"`. - `endTimeMs` (integer · uint64, optional) — Inclusive window end, Unix milliseconds. Alias: `endTime`. - `limit` (integer · int64, optional) — Page size. Default `300`; max `2000` (export). Values above 2000 are clamped. `<= 0` is rejected. - `offset` (integer · int64, optional) — Skip this many newest-first rows. Omit or `0` for the first page; page N is `(N - 1) * limit`. Max `200000`. Alias: `from` on some types. - `side` (string, optional) — Request filter uses `Buy` / `Sell`. Response `side` is usually `B` / `A`. Values: `"Buy"`, `"Sell"`. - `startTimeMs` (integer · uint64, optional) — Inclusive window start, Unix milliseconds. Alias: `startTime`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"historicalTriggerOrders","user":"0x0000000000000000000000000000000000000001","offset":0,"limit":20}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (HistoricalTriggerOrder[]) — Newest-first rows for this page. - `coin` (string) - `createTime` (integer · uint64) - `oid` (integer · uint64) - `side` (string) - `sz` (string) - `triggerPx` (string) - `triggerState` (string) - `asset` (string) - `cancelSource` (string) - `cloid` (string) - `leverage` (integer) - `limitPx` (string) - `marginMode` (string) - `orderType` (string) - `positionMode` (string) - `reduceOnly` (boolean) - `tpslChildren` (TriggerTpslChild[]) - `limitPx` (string) - `orderType` (string) - `reduceOnly` (boolean) - `side` (string) - `sz` (string) - `tpslType` (string) - `triggerPx` (string) - `triggerPrice` (string) - `triggerTxHash` (string) - `triggerTxTime` (integer · uint64) - `updateTime` (integer · uint64) - `hasMore` (boolean) — `true` when more rows exist after this page. Prefer this over guessing from a missing `total`. - `total` (integer · uint64) — Omitted when the count is unknown. - `success` (boolean) ### Response example ```json { "success": true, "code": 0, "data": { "data": [ { "oid": 123456, "coin": "BTC", "asset": "BTC", "side": "B", "sz": "0.01", "limitPx": "64000.0", "triggerPx": "63000.0", "orderType": "Stop", "reduceOnly": false, "leverage": 10, "marginMode": "cross", "positionMode": "OneWay", "createTime": 1720000000000, "updateTime": 1720260000000, "cloid": "", "triggerState": "triggered", "triggerTxHash": "", "triggerTxTime": 0, "triggerPrice": "", "cancelSource": "", "tpslChildren": [ { "tpslType": "tp", "triggerPx": "70000.0", "orderType": "Market", "limitPx": "0", "sz": "0.01", "reduceOnly": true, "side": "A" } ] } ], "hasMore": true, "total": 1337 } } ``` --- # `twapHistory` Finished or cancelled TWAP orders. Child fills are [`userTwapSliceFills`](/api/info/userTwapSliceFills.md). The live stream is [`userTwapHistory`](/api/websocket/userTwapHistory.md). `POST /info` · type `twapHistory` Render the TWAP tab of Order History (finished or cancelled TWAPs). · paged `{ data, hasMore, total? }` - Access: By user address - Markets: Perp only - Live stream: [`userTwapHistory`](/api/websocket/userTwapHistory.md) - Not this? [`userTwapSliceFills`](/api/info/userTwapSliceFills.md) — when you need the child fills of a TWAP - Served from an index — can briefly lag the latest block. ### Request body - `type` (string, required) — Must be `twapHistory`. Values: `"twapHistory"`. - `user` (string · address, required) — Account to query. No auth — the body `user` is the subject. - `accountType` (string[], optional) — Instrument tab: `["perp"]`, `["outcome"]`, or both (`perps` is accepted as an alias). - `coin` (string, optional) — Single instrument — a name (`BTC`, `BTC-USDC`) or the numeric id from `perpMeta`. Prefer this over `contract` for one market. - `contract` (string, optional) — `active` = instruments where this user currently has an open perps position (`szi ≠ 0`). Empty list when there is no position. Do not combine with `coin`. Values: `"all"`, `"active"`. - `endTimeMs` (integer · uint64, optional) — Inclusive window end, Unix milliseconds. Alias: `endTime`. - `limit` (integer · int64, optional) — Page size. Default `300`; max `2000` (export). Values above 2000 are clamped. `<= 0` is rejected. - `offset` (integer · int64, optional) — Skip this many newest-first rows. Omit or `0` for the first page; page N is `(N - 1) * limit`. Max `200000`. Alias: `from` on some types. - `side` (string, optional) — Request filter uses `Buy` / `Sell`. Response `side` is usually `B` / `A`. Values: `"Buy"`, `"Sell"`. - `startTimeMs` (integer · uint64, optional) — Inclusive window start, Unix milliseconds. Alias: `startTime`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"twapHistory","user":"0x0000000000000000000000000000000000000001","offset":0,"limit":20}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (TwapHistoryRow[]) — Newest-first rows for this page. - `state` (TwapHistoryState) - `coin` (string) - `minutes` (integer) - `side` (string) - `sz` (number) - `user` (string · address) - `executedNtl` (number) - `executedSz` (number) - `interval` (integer) - `leverage` (integer) - `marginMode` (string) - `positionMode` (string) - `randomize` (boolean) - `reduceOnly` (boolean) - `symbol` (string) - `timestamp` (integer · uint64) - `unfilledSz` (number) - `status` (string) - `time` (integer · uint64) - `twapId` (string) — Serialized as a string. - `failureReason` (string) - `finishedAt` (integer · uint64) - `hasMore` (boolean) — `true` when more rows exist after this page. Prefer this over guessing from a missing `total`. - `total` (integer · uint64) — Omitted when the count is unknown. - `success` (boolean) ### Response example ```json { "success": true, "code": 0, "data": { "data": [ { "time": 1720000000000, "twapId": "9001", "finishedAt": 1720260000000, "status": "finished", "failureReason": "", "state": { "coin": "1", "symbol": "BTC-USDC", "user": "0x0000000000000000000000000000000000000001", "side": "B", "sz": 0.1, "executedSz": 0.1, "executedNtl": 6500, "unfilledSz": 0, "minutes": 30, "reduceOnly": false, "randomize": false, "timestamp": 1720000000000, "interval": 30, "marginMode": "Cross", "positionMode": "OneWay", "leverage": 10 } } ], "hasMore": true, "total": 1337 } } ``` --- # `orderDetails` One order by `orderId`. The inner `orderDetails` object is the same row shape as [`historicalOrders`](/api/info/historicalOrders.md). `data` is not a paged `{ data, hasMore, total }` envelope. `POST /info` · type `orderDetails` Open an order-detail view from one `orderId`. - Access: By user address - Served from an index — can briefly lag the latest block. ### Request body - `orderId` (integer | string, required) — Order id. Number or numeric string. - `type` (string, required) — Must be `orderDetails`. Values: `"orderDetails"`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"orderDetails","orderId":123456}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `orderDetails` (HistoricalOrder) - `order` (HistoricalOrderInfo) - `coin` (string) - `instrumentId` (integer · uint64) - `instrumentType` (string) Values: `"perps"`, `"outcome"`. - `limitPx` (string) - `marketType` (string) Values: `"perps"`, `"outcome"`. - `oid` (integer · uint64) - `orderType` (string) — `Limit` or `Market`. - `side` (string) — `B` buy / `A` sell. - `status` (string) - `sz` (string) — Remaining size. - `timestamp` (integer · uint64) - `avgFillPrice` (string) - `cancelSource` (string) - `children` (object[]) — Attached TP/SL children when present. - `cloid` (string, nullable) - `dir` (string) — Display label — do not filter on this. - `entryPrice` (string) - `fee` (string) - `filledAmount` (string) - `isPositionTpsl` (boolean) - `isTrigger` (boolean) - `leverage` (integer) - `marginMode` (string) - `origSz` (string) - `pnl` (string) - `positionMode` (string) - `reduceOnly` (boolean) - `roe` (string) - `symbol` (string) — Display symbol when known. - `tif` (string) - `triggerCondition` (string) - `triggerPx` (string) - `status` (string) — Terminal order status. - `statusTimestamp` (integer · uint64) — When this status was recorded, Unix milliseconds. - `type` (string) - `success` (boolean) ### Response example ```json { "success": true, "code": 0, "data": { "type": "orderDetails", "orderDetails": { "order": { "coin": "BTC", "symbol": "BTC-USDC", "side": "B", "limitPx": "65000.0", "sz": "0.01", "oid": 123456, "timestamp": 1720000000000, "triggerCondition": "", "isTrigger": false, "triggerPx": "0", "children": [ "" ], "isPositionTpsl": false, "reduceOnly": false, "dir": "Open Long", "orderType": "Limit", "origSz": "0.01", "tif": "Gtc", "cloid": "my-order-001", "avgFillPrice": "65010.5", "positionMode": "OneWay", "marginMode": "cross", "leverage": 10, "pnl": "12.5", "entryPrice": "64000.0", "roe": "1.95", "fee": "0.02", "filledAmount": "0.01", "cancelSource": "UserCanceled", "status": "filled", "instrumentId": 1, "instrumentType": "perps", "marketType": "perps" }, "status": "filled", "statusTimestamp": 1720000000000 } } } ``` --- # `userFills` Fill history. Extra filter: `role` is `Maker` or `Taker`. Include `outcome` in `accountType` to return Outcome book fills and split/merge activity. Outcome rows identify the instrument by its numeric `coin`/instrument id and may carry Outcome-side metadata added by the API service. TWAP child fills can also be read with [`userTwapSliceFills`](/api/info/userTwapSliceFills.md). > **Read maker/taker from `tradeSide`, not `crossed`:** `crossed` is a legacy field and `userFills` returns it as `null` on every row. > The liquidity role is carried by the optional `tradeSide` field (`Maker` or > `Taker`), which matches the value you pass in the `role` filter. Treat `crossed` > as nullable and ignore it; a client that reads it as a boolean will silently see > every fill as a maker fill. `POST /info` · type `userFills` Render Trade History / reconcile executions (maker or taker fills). · paged `{ data, hasMore, total? }` - Access: By user address - Live stream: [`orderUpdates`](/api/websocket/orderUpdates.md) - Not this? [`historicalOrders`](/api/info/historicalOrders.md) — when you want one row per order, not per fill - Served from an index — can briefly lag the latest block. ### Request body - `type` (string, required) — Must be `userFills`. Values: `"userFills"`. - `user` (string · address, required) — Account to query. No auth — the body `user` is the subject. - `accountType` (string[], optional) — Instrument tab: `["perp"]`, `["outcome"]`, or both (`perps` is accepted as an alias). - `coin` (string, optional) — Single instrument — a name (`BTC`, `BTC-USDC`) or the numeric id from `perpMeta`. Prefer this over `contract` for one market. - `contract` (string, optional) — `active` = instruments where this user currently has an open perps position (`szi ≠ 0`). Empty list when there is no position. Do not combine with `coin`. Values: `"all"`, `"active"`. - `endTimeMs` (integer · uint64, optional) — Inclusive window end, Unix milliseconds. Alias: `endTime`. - `limit` (integer · int64, optional) — Page size. Default `300`; max `2000` (export). Values above 2000 are clamped. `<= 0` is rejected. - `offset` (integer · int64, optional) — Skip this many newest-first rows. Omit or `0` for the first page; page N is `(N - 1) * limit`. Max `200000`. Alias: `from` on some types. - `role` (string, optional) — Optional liquidity-role filter. Values: `"Maker"`, `"Taker"`. - `side` (string, optional) — Request filter uses `Buy` / `Sell`. Response `side` is usually `B` / `A`. Values: `"Buy"`, `"Sell"`. - `startTimeMs` (integer · uint64, optional) — Inclusive window start, Unix milliseconds. Alias: `startTime`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"userFills","user":"0x0000000000000000000000000000000000000001","offset":0,"limit":20}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (UserFill[]) — Newest-first rows for this page. - `coin` (string) - `fee` (string) - `instrumentId` (integer · uint64) - `oid` (integer · uint64) - `px` (string) - `side` (string) - `sz` (string) - `time` (integer · uint64) - `closedPnl` (string) - `crossed` (boolean, nullable) — Legacy liquidity-role flag. `userFills` returns `null` — use `tradeSide` to tell maker from taker. - `dir` (string) — Display label. - `entryPrice` (string) - `feeToken` (string) - `hash` (string) - `instrumentType` (string) Values: `"perps"`, `"outcome"`. - `leverage` (integer) - `marginMode` (string) - `marketType` (string) Values: `"perps"`, `"outcome"`. - `roe` (string) - `startPosition` (string) - `symbol` (string) - `tid` (integer · uint64) - `tradeSide` (string) — Liquidity role of this fill leg. Omitted when the engine did not record one; matches the request's `role` filter. Values: `"Maker"`, `"Taker"`. - `twapId` (integer · uint64) - `hasMore` (boolean) — `true` when more rows exist after this page. Prefer this over guessing from a missing `total`. - `total` (integer · uint64) — Omitted when the count is unknown. - `success` (boolean) ### Response example ```json { "success": true, "code": 0, "data": { "data": [ { "coin": "BTC", "symbol": "BTC-USDC", "px": "65010.5", "sz": "0.01", "side": "B", "time": 1720000000000, "startPosition": "0", "dir": "Open Long", "closedPnl": "0", "hash": "", "oid": 123456, "crossed": null, "fee": "0.02", "tid": 987654, "feeToken": "USDC", "twapId": 0, "entryPrice": "", "roe": "", "tradeSide": "Taker", "marginMode": "cross", "leverage": 10, "instrumentId": 1, "instrumentType": "perps", "marketType": "perps" } ], "hasMore": true, "total": 1337 } } ``` --- # `userTwapSliceFills` TWAP slice fills. Page with `from` / `limit` (not `offset`). `data` is `{ isSnapshot, user, twapSliceFills }`, not a paged `{ data, hasMore, total }` object. The live stream is [`userTwapSliceFills`](/api/websocket/userTwapSliceFills.md). `POST /info` · type `userTwapSliceFills` List the individual child fills of a user’s TWAP orders. - Access: By user address - Markets: Perp only - Live stream: [`userTwapSliceFills`](/api/websocket/userTwapSliceFills.md) - Served from an index — can briefly lag the latest block. ### Request body - `type` (string, required) — Must be `userTwapSliceFills`. Values: `"userTwapSliceFills"`. - `user` (string · address, required) — Account to query. No auth — the body `user` is the subject. - `coin` (string, optional) — Single instrument — a name (`BTC`, `BTC-USDC`) or the numeric id from `perpMeta`. Prefer this over `contract` for one market. - `endTimeMs` (integer · uint64, optional) — Inclusive window end, Unix milliseconds. Alias: `endTime`. - `from` (integer · int64, optional) — Cursor for the next page. Do not use `offset`. - `limit` (integer · uint32, optional) — Page size for this cursor read. - `startTimeMs` (integer · uint64, optional) — Inclusive window start, Unix milliseconds. Alias: `startTime`. - `twapId` (integer | string, optional) — Restrict to one TWAP. Number or numeric string. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"userTwapSliceFills","user":"0x0000000000000000000000000000000000000001","from":0,"limit":100}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `isSnapshot` (boolean) - `twapSliceFills` (UserTwapSliceFill[]) - `fill` (UserTwapSliceFillBody) - `coin` (string) - `fee` (string) - `oid` (integer · uint64) - `px` (string) - `side` (string) - `sz` (string) - `time` (integer · uint64) - `closedPnl` (string) - `crossed` (boolean) - `dir` (string) - `feeToken` (string) - `hash` (string) - `startPosition` (string) - `symbol` (string) - `tid` (integer · uint64) - `status` (string) - `twapId` (string) — Serialized as a string. - `failureReason` (string) - `user` (string · address) - `success` (boolean) ### Response example ```json { "success": true, "code": 0, "data": { "isSnapshot": true, "user": "0x0000000000000000000000000000000000000001", "twapSliceFills": [ { "twapId": "9001", "status": "finished", "failureReason": "", "fill": { "closedPnl": "0", "coin": "1", "symbol": "BTC-USDC", "crossed": true, "dir": "Open Long", "hash": "0xabc", "oid": 123456, "px": "65010.5", "side": "B", "startPosition": "0", "sz": "0.002", "time": 1720000000000, "fee": "0.004", "tid": 987654, "feeToken": "USDC" } } ] } } ``` --- # `userFundings` Funding settlements. There is no side filter. `fundingType` is `collected` (`amount > 0`) or `paid` (`amount < 0`); the alias `direction` is accepted and is **not** a sort key. Zero amounts appear only when that filter is omitted. The live stream is [`userFundings`](/api/websocket/userFundings.md). `POST /info` · type `userFundings` Render Funding History (collected and paid settlements). · paged `{ data, hasMore, total? }` - Access: By user address - Markets: Perp only - Live stream: [`userFundings`](/api/websocket/userFundings.md) - Not this? [`activeAssetCtx`](/api/info/activeAssetCtx.md) — when you need the current funding *rate* - Served from an index — can briefly lag the latest block. ### Request body - `type` (string, required) — Must be `userFundings`. Values: `"userFundings"`. - `user` (string · address, required) — Account to query. No auth — the body `user` is the subject. - `accountType` (string[], optional) — Instrument tab: `["perp"]`, `["outcome"]`, or both (`perps` is accepted as an alias). - `coin` (string, optional) — Single instrument — a name (`BTC`, `BTC-USDC`) or the numeric id from `perpMeta`. Prefer this over `contract` for one market. - `contract` (string, optional) — `active` = instruments where this user currently has an open perps position (`szi ≠ 0`). Empty list when there is no position. Do not combine with `coin`. Values: `"all"`, `"active"`. - `endTimeMs` (integer · uint64, optional) — Inclusive window end, Unix milliseconds. Alias: `endTime`. - `fundingType` (string, optional) — `collected` = `amount > 0`, `paid` = `amount < 0`. Alias `direction` is accepted; it is not a sort key. Values: `"collected"`, `"paid"`. - `limit` (integer · int64, optional) — Page size. Default `300`; max `2000` (export). Values above 2000 are clamped. `<= 0` is rejected. - `offset` (integer · int64, optional) — Skip this many newest-first rows. Omit or `0` for the first page; page N is `(N - 1) * limit`. Max `200000`. Alias: `from` on some types. - `startTimeMs` (integer · uint64, optional) — Inclusive window start, Unix milliseconds. Alias: `startTime`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"userFundings","user":"0x0000000000000000000000000000000000000001","offset":0,"limit":20}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (FundingRow[]) — Newest-first rows for this page. - `amount` (string) — Signed. `fundingType=collected` keeps `amount > 0`; `paid` keeps `amount < 0`. Zero amounts only appear when the filter is omitted. - `coin` (string) - `fundingRate` (string) - `szi` (string) - `time` (integer · uint64) - `usdc` (string) - `hasMore` (boolean) — `true` when more rows exist after this page. Prefer this over guessing from a missing `total`. - `total` (integer · uint64) — Omitted when the count is unknown. - `success` (boolean) ### Response example ```json { "success": true, "code": 0, "data": { "data": [ { "time": 1720000000000, "coin": "BTC", "usdc": "0.12", "szi": "0.5", "fundingRate": "0.0001", "amount": "0.12" } ], "hasMore": true, "total": 1337 } } ``` --- # `userNonFundingLedgerUpdates` Non-funding ledger rows. Vault deposits & withdrawals must send `deltaTypes: ["deposit","withdraw"]` so the service uses the deposit/withdraw path and can return `total` as the sum of both counts. Omit `deltaTypes` for the wider ledger (token-send, vault events, multi-chain). That merge has no reliable `total`. There is no `contract` or `side` filter. Include `outcome` in `accountTypes` to return transfers and other non-funding changes involving the Outcome sub-account. `POST /info` · type `userNonFundingLedgerUpdates` Render the Deposits & Withdrawals / transfer ledger. · paged `{ data, hasMore, total? }` - Access: By user address - Not this? [`userFundings`](/api/info/userFundings.md) — when you need funding payments - Served from an index — can briefly lag the latest block. ### Request body - `type` (string, required) — Must be `userNonFundingLedgerUpdates`. Values: `"userNonFundingLedgerUpdates"`. - `user` (string · address, required) — Account to query. No auth — the body `user` is the subject. - `accountTypes` (string[], optional) — Optional post-filter on token-send `from` / `to` account types. Applied to at most one page of token-send rows. - `channel` (integer · uint32, optional) — Optional bridge-history channel. Usually omitted. - `deltaTypes` (string[], optional) — Vault D&W pages must send `["deposit","withdraw"]` — that path skips token-send / vault-event and can return `total` as the sum of both counts. Omit for the wider ledger (no reliable `total`). - `endTimeMs` (integer · uint64, optional) — Inclusive window end, Unix milliseconds. Alias: `endTime`. - `limit` (integer · int64, optional) — Page size. Default `300`; max `2000` (export). Values above 2000 are clamped. `<= 0` is rejected. - `offset` (integer · int64, optional) — Skip this many newest-first rows. Omit or `0` for the first page; page N is `(N - 1) * limit`. Max `200000`. Alias: `from` on some types. - `startTimeMs` (integer · uint64, optional) — Inclusive window start, Unix milliseconds. Alias: `startTime`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"userNonFundingLedgerUpdates","user":"0x0000000000000000000000000000000000000001","deltaTypes":["deposit","withdraw"],"offset":0,"limit":20}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (LedgerUpdate[]) — Newest-first rows for this page. - `delta` (oneOf) — `type` is `deposit`, `withdraw`, `tokenSend`, `vaultEvent`, `multiChainDeposit`, or `multiChainWithdraw`. Vault D&W pages request only deposit + withdraw. - One of · variant 1 — type = "deposit": - `address` (string · address) - `amount` (string) - `chain` (string) - `status` (string) - `txflowBlockNumber` (integer · uint64) - `type` (string) Values: `"deposit"`. - `txflowHash` (string) - One of · variant 2 — type = "withdraw": - `address` (string · address) - `amount` (string) - `chain` (string) - `fee` (string) - `nonce` (integer · uint64) - `status` (string) - `txflowBlockNumber` (integer · uint64) - `txflowHash` (string) - `type` (string) Values: `"withdraw"`. - One of · variant 3 — type = "tokenSend": - `amount` (string) - `destination` (string · address) - `token` (string) - `type` (string) Values: `"tokenSend"`. - `user` (string · address) - `destinationDex` (string) - `fee` (string) - `feeToken` (string) - `nativeTokenFee` (string) - `nonce` (integer · uint64) - `sourceDex` (string) - `usdcValue` (string) - `hash` (string) - `time` (integer · uint64) - `hasMore` (boolean) — `true` when more rows exist after this page. Prefer this over guessing from a missing `total`. - `total` (integer · uint64) — Omitted when the count is unknown. - `success` (boolean) ### Response example ```json { "success": true, "code": 0, "data": { "data": [ { "time": 1720000000000, "hash": "0xabc", "delta": { "type": "deposit", "amount": "100", "status": "completed", "txflowBlockNumber": 12345, "txflowHash": "", "chain": "Arbitrum", "address": "0x0000000000000000000000000000000000000001" } } ], "hasMore": true, "total": 1337 } } ``` --- # `userOverview` Returns performance figures for one account or vault over a period: PnL, return on equity, traded volume, maximum drawdown, and current equity. Use it for a portfolio performance card or a vault's performance panel. `POST /info` · type `userOverview` Fill a portfolio or vault performance card: PnL, ROE, volume, max drawdown, equity over a period. - Access: By user address ### Request body - `period` (string, required) — Lookback window. Values: `"24H"`, `"7D"`, `"30D"`, `"90D"`, `"ALLTIME"`. - `type` (string, required) — Must be `userOverview`. Values: `"userOverview"`. - `user_address` (string · address, required) — Account or vault address. Note the snake_case key. - `account_type` (string[], optional) — Sub-accounts to include. Omit for `perps` only. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"userOverview","user_address":"0x091564806dce41ffeebafbc417e73b6849b1c29e","period":"30D"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `code` (integer) — `0` on success. - `msg` (string) — Empty on success. - `maxDrawdown` (string) — Maximum drawdown over the period, percent. Present when `perps` is included. - `period` (string) — Echo of `period`. - `pnl` (string) — PnL over the period, USDC. - `roe` (string) — Return on equity over the period, percent. - `totalEquity` (string) — Current equity of the included sub-accounts, USDC. - `volume` (string) — Traded notional over the period, USDC. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "code": 0, "msg": "", "period": "30D", "pnl": "0", "roe": "0", "volume": "19752.12", "maxDrawdown": "6.38", "totalEquity": "3831.19" } } ``` The request keys are snake_case: `user_address`, `period`, `account_type`. `account_type` chooses which sub-accounts are combined; omit it for the perp account only. The body is returned at the top level with `code: 0`. `pnl`, `volume`, and `totalEquity` are USDC; `roe` and `maxDrawdown` are percent. --- # `accountAccessStatus` Query the referral code bound to a specified main account and its trading access on a channel. The current implementation is a **public query**: no signature or Token is required. Web clients may query any valid address. `user` is required and must be a valid EVM main account address; Agent addresses are not automatically converted to main accounts. `type` is case-sensitive. ## Request headers ```yaml Content-Type: application/json x-api-version: 2026-08-16 x-channel: 0 ``` `x-channel` selects the channel whose trading access is queried and defaults to `0` when omitted. The examples and Try it below use that default channel. `POST /info` · type `accountAccessStatus` Query a main account’s bound referral code and trading access on a channel. - Access: Public ### Request body - `type` (string, required) — Must be `accountAccessStatus` (case-sensitive). Values: `"accountAccessStatus"`. - `user` (string · address, required) — Required valid EVM main account address. Agent addresses are not automatically resolved to their main accounts. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"accountAccessStatus","user":"0xabcdef0000000000000000000000000000000001"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `allowExchange` (boolean) — Trading access on the requested channel. Does not guarantee that an order passes all validations. - `rc` (string) — Bound referral code; an empty string when unbound. This is not an access code. - `reason` (string, nullable) — `null` when allowed; otherwise `Trading access unavailable`. - `user` (string) — Queried main account address, normalized to lowercase. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "user": "0xabcdef0000000000000000000000000000000001", "rc": "REF123", "allowExchange": true, "reason": null } } ``` `rc` is a **referral code**, not an access code. When no referral code is bound, it is `""`. The returned `user` is lowercase. `allowExchange` describes trading access for the requested channel; it does not guarantee that an order passes all validations. `reason` is `null` when access is allowed and `"Trading access unavailable"` otherwise. ## Errors | HTTP | Scenario | message | | --- | --- | --- | | 400 | Invalid address, missing `user`, or incorrect capitalization of the type | `accountAccessStatus requires a valid user address and exact type` | | 503 | Downstream service failure or unexpected response | `Access status temporarily unavailable` | With the version header above, errors use this envelope (503 example): ```json { "success": false, "code": 10000, "message": "Access status temporarily unavailable" } ``` --- # `approvedBuilders` Lists the builders a user has approved with [`approveBuilderFee`](/api/exchange/approveBuilderFee.md), with the fee cap and timestamps of each approval. By default only `ACTIVE` approvals are returned; pass `status: "REVOKED"` or `"ALL"` to include revoked ones. `POST /info` · type `approvedBuilders` List every builder a user has approved and its fee cap (e.g. a builder-approval settings page). - Access: By user address - Not this? [`maxBuilderFee`](/api/info/maxBuilderFee.md) — when you only need the approval for one builder ### Request body - `type` (string, required) — Must be `approvedBuilders`. Values: `"approvedBuilders"`. - `user` (string · address, required) — Account that granted the approvals. - `status` (string, optional) — Filter by approval status. Case-insensitive. Defaults to `ACTIVE`. Values: `"ACTIVE"`, `"REVOKED"`, `"ALL"`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"approvedBuilders","user":"0x95761f4d19B906B3B09da3EeE0BF30B337f4dB88"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `approvals` (object[]) — One row per approved builder. - `approvedAt` (integer) — Time of the first approval, Unix milliseconds. - `builder` (string · address) — Builder address, lowercase. - `maxFeeRate` (integer) — Approved maximum in tenths of a basis point: `10` is `0.01%`. `approveBuilderFee` takes the same cap as a percentage string. - `status` (string) — Approval status. Values: `"ACTIVE"`, `"REVOKED"`. - `updatedAt` (integer) — Time of the last approve or revoke, Unix milliseconds. - `limit` (integer) — Maximum number of builders the user may keep approved at once. - `total` (integer) — Number of rows in `approvals`. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "approvals": [ { "builder": "0x2e4d75df2ee2653ea7a113cc1fa36d45e923061f", "maxFeeRate": 10, "status": "ACTIVE", "approvedAt": 1792575363976, "updatedAt": 1792575363976 } ], "total": 1, "limit": 20 } } ``` `maxFeeRate` is an integer in **tenths of a basis point**: `10` is `0.01%`, `50` is `0.05%`. This is the same cap the user signed as a percentage string in `approveBuilderFee`. `limit` is the number of builders the user may keep approved at once; compare it with `total` before asking the user to approve another builder. ## Next steps - Check a single builder: [`maxBuilderFee`](/api/info/maxBuilderFee.md). - Approve or update a builder: [`approveBuilderFee`](/api/exchange/approveBuilderFee.md); remove one: [`revokeBuilderFee`](/api/exchange/revokeBuilderFee.md). --- # `maxBuilderFee` Returns the approval a user has granted to one builder: its status and the maximum fee rate the builder may charge. A builder calls this during [account setup](/api/flows.md#set-up-an-account) to confirm that [`approveBuilderFee`](/api/exchange/approveBuilderFee.md) took effect, and before sending orders that carry a builder fee. `POST /info` · type `maxBuilderFee` Check that a user has approved your builder, and at what cap, before sending orders with a builder fee. - Access: By user address - Not this? [`approvedBuilders`](/api/info/approvedBuilders.md) — when you need all of the user’s approved builders ### Request body - `builder` (string · address, required) — Builder address to look up. - `type` (string, required) — Must be `maxBuilderFee`. Values: `"maxBuilderFee"`. - `user` (string · address, required) — Account that granted the approval. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"maxBuilderFee","user":"0x95761f4d19B906B3B09da3EeE0BF30B337f4dB88","builder":"0x2e4d75df2ee2653ea7a113cc1fa36d45e923061f"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `maxFeeRate` (integer) — Approved maximum in tenths of a basis point: `10` is `0.01%`. `0` unless `status` is `ACTIVE`. - `status` (string) — `ACTIVE` while the approval is in force; `NONE` when the user never approved this builder. Values: `"ACTIVE"`, `"REVOKED"`, `"NONE"`. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "maxFeeRate": 10, "status": "ACTIVE" } } ``` `maxFeeRate` is an integer in **tenths of a basis point**: `10` is `0.01%`. Keep the builder fee on each order at or below this cap. | `status` | Meaning | |---|---| | `ACTIVE` | The approval is in force; `maxFeeRate` is the cap. | | `REVOKED` | The user revoked the approval; `maxFeeRate` is `0`. | | `NONE` | The user has never approved this builder; `maxFeeRate` is `0`. | ## Next steps - After `ACTIVE`, approve the agent with [`approveAgent`](/api/exchange/approveAgent.md). - List all of the user's builders: [`approvedBuilders`](/api/info/approvedBuilders.md). --- # `approveAgentEligibility` Tells a client whether `user` can sign [`approveAgent`](/api/exchange/approveAgent.md) now. An account becomes eligible once it is activated by its first deposit, so call this before showing an "Enable trading" button in [account setup](/api/flows.md#set-up-an-account). `POST /info` · type `approveAgentEligibility` Decide whether to show “Enable trading” or a deposit prompt before `approveAgent`. - Access: By user address - Not this? [`accountAccessStatus`](/api/info/accountAccessStatus.md) — when you also need the bound referral code and channel trading access - Not this? [`approvedAgents`](/api/info/approvedAgents.md) — when the account already has agents and you want to list them ### Request body - `type` (string, required) — Must be `approveAgentEligibility`. Values: `"approveAgentEligibility"`. - `user` (string · address, required) — Account that will sign `approveAgent`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"approveAgentEligibility","user":"0x95761f4d19B906B3B09da3EeE0BF30B337f4dB88"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `eligible` (boolean) — `true` once the account is activated by its first deposit. - `reason` (string) — Machine-readable reason, present on some `eligible: false` responses. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "eligible": true } } ``` When `eligible` is `false`, prompt the user to deposit and poll this type every few seconds until it returns `true`, then request the `approveAgent` signature. ## Next steps - Approve the agent: [`approveAgent`](/api/exchange/approveAgent.md). - List existing agents: [`approvedAgents`](/api/info/approvedAgents.md). --- # `settings` Returns the Arbitrum **Bridge2** contract used for native USDC deposits. There is no request body besides `type`. `POST /info` · type `settings` Look up the Bridge2 contract address before a native Arbitrum USDC deposit. - Access: Public ### Request body - `type` (string, required) — Must be `settings`. Values: `"settings"`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"settings"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `bridge2_contract_address` (string · address) — Arbitrum Bridge2 contract that holds USDC for native deposits. Testnet sample; mainnet returns a different address. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "bridge2_contract_address": "0x638956edf1890159C03A4F3cf0DB052967b86145" } } ``` Keys inside `data` are **snake_case**. Use `bridge2_contract_address` as the USDC `transfer` destination and as the EIP-2612 permit spender for [`depositWithPermit`](/api/info/depositWithPermit.md). ## Next steps - Native Arbitrum deposit: transfer USDC to Bridge2, or call [`depositWithPermit`](/api/info/depositWithPermit.md). - Cross-chain deposit or withdraw: [TxRelay API](/api/txrelay.md). - On-chain USDC withdraw to Arbitrum: [`withdraw3`](/api/exchange/withdraw3.md). --- # `depositWithPermit` Credits USDC to the signing user's TxFlow account by asking the API host to submit `batchedDepositWithPermit` on **Bridge2**. Use this when the user has USDC on Arbitrum but **no ETH** to pay gas (typical for email / social login). Wallet users who already hold ETH should `USDC.transfer` to Bridge2 themselves instead of calling this type. This is an `/info` type that **writes on Arbitrum**. It is answered by the API service behind the same [API host](/api.md#networks) as every other type. `POST /info` · type `depositWithPermit` Deposit Arbitrum USDC for a user who holds no ETH for gas (signs an EIP-2612 permit). - Access: Account-signed - Submits a transaction, although it is an `/info` type. ### Request body - `deadline` (string, required) — Unix seconds. Must be at least 120 seconds in the future and at most 7 days ahead. - `signature` (object, required) - `r` (string, required) — EIP-2612 permit `r`. - `s` (string, required) — EIP-2612 permit `s`. - `v` (integer, required) — EIP-2612 permit `v` (`27` or `28`). - `type` (string, required) — Must be `depositWithPermit`. Values: `"depositWithPermit"`. - `usd` (string, required) — Amount in USDC integer units (6 decimals). `5000000` is 5 USDC, the current minimum. - `user` (string · address, required) — Depositor. Must match the permit signer. - `waitForReceipt` (boolean, optional) — `false` returns after broadcast (poll the chain for the hash). Omitted or `true` waits for a receipt on the server (legacy). ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"depositWithPermit","user":"0x0000000000000000000000000000000000000001","usd":"5000000","deadline":"1735689600","signature":{"r":"0x","s":"0x","v":27}}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `txHash` (string) — Arbitrum transaction that submitted `batchedDepositWithPermit`. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "txHash": "0xabc123def456abc123def456abc123def456abc123def456abc123def456abc1" } } ``` `usd` is **integer 6-decimal units**, not a decimal string: `5000000` is 5 USDC (the current minimum). Amounts below that return `Below minimum deposit amount`. The permit is standard **EIP-2612** on the Arbitrum USDC contract: - `owner` = `user` - `spender` = `bridge2_contract_address` from [`settings`](/api/info/settings.md) - `value` = the same integer as `usd` - `deadline` / `nonce` = USDC permit nonce and Unix-second deadline Pass `waitForReceipt: false` and confirm `txHash` on Arbitrum yourself. Omitted or `true` makes the server wait for a receipt before responding. Bridge2 does not revert on a failed permit; it may emit `FailedPermitDeposit`. Treat a mined hash that contains that event for `user` as a failed deposit. ## Next steps Read balances from [`clearinghouseState`](/api/info/clearinghouseState.md) / [`perpClearinghouseState`](/api/info/perpClearinghouseState.md). Cross-chain deposits use [TxRelay](/api/txrelay.md) instead of this type. --- # `dayTotalWithdraw` Returns the total amount and number of withdrawals `user` has made in the current day. Withdraw forms use it to show what the user has already withdrawn today. `POST /info` · type `dayTotalWithdraw` Show how much a user has already withdrawn today on the withdraw form. - Access: By user address - Not this? [`userNonFundingLedgerUpdates`](/api/info/userNonFundingLedgerUpdates.md) — when you need the individual withdrawal records ### Request body - `type` (string, required) — Must be `dayTotalWithdraw`. Values: `"dayTotalWithdraw"`. - `user` (string · address, required) — Account to look up. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"dayTotalWithdraw","user":"0x95761f4d19B906B3B09da3EeE0BF30B337f4dB88"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `count` (integer) — Number of withdrawals in the current day. - `total` (string) — Total withdrawn in the current day, in USDC. - `user` (string · address) — The requested account, lowercase. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "user": "0x95761f4d19b906b3b09da3eee0bf30b337f4db88", "total": "0", "count": 0 } } ``` `total` is a decimal USDC string. `user` is echoed back in lowercase. ## Next steps - Withdraw to Arbitrum: [`withdraw3`](/api/exchange/withdraw3.md). - Individual withdrawal records: [`userNonFundingLedgerUpdates`](/api/info/userNonFundingLedgerUpdates.md). --- # `sendTokenFee` Returns the one-time activation fee a [`tokenSend`](/api/exchange/tokenSend.md) from `user` to `destination` will charge. Call it while the user fills a send form, or before a cross-chain withdraw with `destination` set to the [TxRelay](/api/txrelay.md) peg address, and show the fee next to the amount. `POST /info` · type `sendTokenFee` Show the activation fee on a send or cross-chain withdraw form before signing `tokenSend`. - Access: By user address ### Request body - `destination` (string · address, required) — `toAddress` of the planned `tokenSend`: another user or a TxRelay peg. - `type` (string, required) — Must be `sendTokenFee`. Values: `"sendTokenFee"`. - `user` (string · address, required) — Sender. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"sendTokenFee","user":"0x95761f4d19B906B3B09da3EeE0BF30B337f4dB88","destination":"0x2e4d75df2ee2653ea7a113cc1fa36d45e923061f"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `fee` (string) — One-time activation fee in USDC. `"1"` for the first send to a new destination; `"0"` once the destination is activated or when it is `user` itself. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "fee": "1" } } ``` `fee` is a decimal USDC string. It is `"1"` for the first send to a destination that has not been activated, and `"0"` afterwards or when `destination` is `user`. ## Next steps Send the tokens with [`tokenSend`](/api/exchange/tokenSend.md). --- # `builderMeOverview` Returns aggregated analytics for one Builder account: lifetime users, volume, earned fees, claim totals, platform rank, and the current week's activity. Pass the **builder's own wallet address** in `user`. This is a public read and does not require a signature. For on-chain reward balances, see [`builderState`](/api/info/builderState.md). `POST /info` · type `builderMeOverview` Headline numbers for a builder dashboard: users, volume, fees, rank. - Access: By user address - Served from an index — can briefly lag the latest block. ### Request body - `type` (string, required) - `user` (string, required) — Builder wallet address. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"builderMeOverview","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · BuilderMeOverviewResponse (200) - `code` (integer) — `0` on success. - `data` (BuilderMeOverviewResponse) - `builder` (string) - `claimable` (string) - `hasAggregate` (boolean) - `totalClaimed` (string) - `totalEarned` (string) - `totalUsers` (integer · int64) - `totalVolume` (string) - `week` (BuilderMeWeekStats) - `fee` (string) - `newUsers` (integer · int64) - `volume` (string) - `weekRange` (BuilderMeWeekRange) - `endDate` (string) — UTC date in YYYY-MM-DD format. - `startDate` (string) — UTC date in YYYY-MM-DD format. - `asOf` (integer · int64) — Aggregate update time in milliseconds; omitted before the first aggregation. - `earnedRank` (integer · int64, nullable) — Platform-wide earned-fee rank; null when unranked. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "asOf": 0, "builder": "", "claimable": "", "earnedRank": 0, "hasAggregate": false, "totalClaimed": "", "totalEarned": "", "totalUsers": 0, "totalVolume": "", "week": { "fee": "", "newUsers": 0, "volume": "" }, "weekRange": { "endDate": "", "startDate": "" } } } ``` --- # `builderMeDailySeries` Returns a daily time series for one Builder account. Each point includes the day value and the cumulative total inside the requested date range. `metric` selects whether the series tracks builder fees (`fee`, default) or distinct trading users (`users`). Dates are UTC `YYYY-MM-DD` strings. Pass the **builder's own wallet address** in `user`. `POST /info` · type `builderMeDailySeries` Plot a builder’s daily fees or user count. - Access: By user address - Served from an index — can briefly lag the latest block. ### Request body - `type` (string, required) - `user` (string, required) — Builder wallet address. - `endDate` (string, optional) — UTC end date in YYYY-MM-DD format. Defaults to today. - `metric` (string, optional) — Defaults to `fee`. Values: `"fee"`, `"users"`. - `startDate` (string, optional) — UTC start date in YYYY-MM-DD format. Defaults to 90 days ago. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"endDate":"","metric":"fee","startDate":"","type":"builderMeDailySeries","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · BuilderMeDailySeriesResponse (200) - `code` (integer) — `0` on success. - `data` (BuilderMeDailySeriesResponse) - `metric` (string) Values: `"fee"`, `"users"`. - `points` (BuilderMeDailySeriesPoint[]) - `cumulative` (string) - `daily` (string) - `date` (string) — UTC date in YYYY-MM-DD format. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "metric": "fee", "points": [ { "cumulative": "", "daily": "", "date": "" } ] } } ``` --- # `builderMeShare` Returns how a Builder's fees break down over a time window. Use `dimension=user` for trading-user share or `dimension=instrument` for instrument share. Trading-user addresses are masked in `key` and `label`. Instrument rows include `instrumentName` when the gateway can resolve the instrument id. Pass the **builder's own wallet address** in `user`. `POST /info` · type `builderMeShare` Break a builder’s fees down by trading user or by instrument. - Access: By user address - Served from an index — can briefly lag the latest block. ### Request body - `type` (string, required) - `user` (string, required) — Builder wallet address. - `dimension` (string, optional) — Defaults to `user`. Values: `"user"`, `"instrument"`. - `window` (string, optional) — Defaults to `7d`. Values: `"7d"`, `"30d"`, `"90d"`, `"all"`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"dimension":"user","type":"builderMeShare","user":"0x0000000000000000000000000000000000000000","window":"7d"}' ``` ### Response · BuilderMeShareResponse (200) - `code` (integer) — `0` on success. - `data` (BuilderMeShareResponse) - `dimension` (string) Values: `"user"`, `"instrument"`. - `items` (BuilderMeShareItem[]) - `key` (string) — Masked user address, instrument id, or `__other__`. - `label` (string) - `ratio` (number) — Share of total fee as a decimal. - `value` (string) - `instrumentName` (string, nullable) — Present for instrument shares; null when the instrument cannot be resolved. - `metric` (string) Values: `"fee"`. - `window` (string) Values: `"7d"`, `"30d"`, `"90d"`, `"all"`. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "dimension": "user", "items": [ { "instrumentName": "", "key": "", "label": "", "ratio": 0, "value": "" } ], "metric": "fee", "window": "7d" } } ``` --- # `builderMeEarned` Returns paginated builder-fee earnings aggregated **by order**. Multiple fills for the same order are merged into one row; amounts and quantities are summed, and price is the order VWAP. Pass the **builder's own wallet address** in `user`. Each row's `user` field is the **masked trading-user address**, not the builder. `total` counts matching orders across all pages, not individual fills. Deep paging is capped at `page × pageSize + pageSize ≤ 10000`; narrow the time window when you need older pages. `POST /info` · type `builderMeEarned` List a builder’s earnings, one row per order. - Access: By user address - Served from an index — can briefly lag the latest block. ### Request body - `type` (string, required) - `user` (string, required) — Builder wallet address. - `endTime` (integer · int64, optional) — Inclusive upper bound in milliseconds. - `page` (integer · int32, optional) — Zero-based page index. Defaults to 0. - `pageSize` (integer · int32, optional) — Defaults to 50 and is capped at 100. - `startTime` (integer · int64, optional) — Inclusive lower bound in milliseconds. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"endTime":1700000000000,"page":0,"pageSize":0,"startTime":1700000000000,"type":"builderMeEarned","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · BuilderMeEarnedResponse (200) - `code` (integer) — `0` on success. - `data` (BuilderMeEarnedResponse) - `items` (BuilderMeEarnedItem[]) - `builderFee` (string) — Total builder fee for the aggregated order. - `builderFeeRate` (string) — Fee rate from the latest fill, as a decimal string. - `builderFeeRateF` (integer · int32) — Same rate as integer f units. - `instrumentId` (integer · int64) - `marketType` (string) Values: `"perps"`, `"outcome"`. - `notional` (string) - `orderId` (integer · int64) - `price` (string) — Order VWAP. - `qty` (string) - `role` (string) Values: `"taker"`, `"maker"`. - `time` (integer · int64) — Latest fill time for the aggregated order, in milliseconds. - `tradeId` (integer · int64) - `txHash` (string) — Transaction hash of the latest fill. - `user` (string) — Masked trading-user address. - `instrumentName` (string, nullable) — Resolved instrument name, such as `BTC-USDC`. - `page` (integer · int32) - `pageSize` (integer · int32) - `total` (integer · int64) — Total matching orders across all pages. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "items": [ { "builderFee": "", "builderFeeRate": "", "builderFeeRateF": 0, "instrumentId": 9001, "instrumentName": "", "marketType": "perps", "notional": "", "orderId": 0, "price": "0", "qty": "", "role": "taker", "time": 1700000000000, "tradeId": 0, "txHash": "", "user": "0x0000000000000000000000000000000000000000" } ], "page": 0, "pageSize": 0, "total": 0 } } ``` --- # `builderMeClaimed` Returns paginated reward-claim history for one Builder account. Only successful claims are indexed. Pass the **builder's own wallet address** in `user`. Optional `startTime` / `endTime` filter by claim time in milliseconds. `POST /info` · type `builderMeClaimed` List a builder’s past reward claims. - Access: By user address - Served from an index — can briefly lag the latest block. ### Request body - `type` (string, required) - `user` (string, required) — Builder wallet address. - `endTime` (integer · int64, optional) — Inclusive upper bound in milliseconds. - `page` (integer · int32, optional) — Zero-based page index. Defaults to 0. - `pageSize` (integer · int32, optional) — Defaults to 50 and is capped at 100. - `startTime` (integer · int64, optional) — Inclusive lower bound in milliseconds. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"endTime":1700000000000,"page":0,"pageSize":0,"startTime":1700000000000,"type":"builderMeClaimed","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · BuilderMeClaimedResponse (200) - `code` (integer) — `0` on success. - `data` (BuilderMeClaimedResponse) - `items` (BuilderMeClaimedItem[]) - `amount` (string) - `asset` (string) Values: `"USDC"`. - `status` (string) Values: `"Success"`. - `time` (integer · int64) — Claim time in milliseconds. - `to` (string) — Destination address, typically the builder wallet. - `txHash` (string) - `page` (integer · int32) - `pageSize` (integer · int32) - `total` (integer · int64) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "items": [ { "amount": "", "asset": "USDC", "status": "Success", "time": 1700000000000, "to": "", "txHash": "" } ], "page": 0, "pageSize": 0, "total": 0 } } ``` --- # `builderMeClaimable` Returns the Builder's current claimable balance plus lifetime earned and claimed totals from the aggregate table. Pass the **builder's own wallet address** in `user`. `claimable = max(totalEarned - totalClaimed, 0)`. For the on-chain live balance, compare with [`builderState`](/api/info/builderState.md). `POST /info` · type `builderMeClaimable` Show claimable vs. lifetime earned / claimed on a builder dashboard. - Access: By user address - Not this? [`builderState`](/api/info/builderState.md) — when you need the live on-chain balance a claim will pay - Served from an index — can briefly lag the latest block. ### Request body - `type` (string, required) - `user` (string, required) — Builder wallet address. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"builderMeClaimable","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · BuilderMeClaimableResponse (200) - `code` (integer) — `0` on success. - `data` (BuilderMeClaimableResponse) - `claimable` (string) — Currently claimable balance. - `hasAggregate` (boolean) — Whether an aggregate row exists yet. - `source` (string) Values: `"aggregate"`. - `totalClaimed` (string) - `totalEarned` (string) - `asOf` (integer · int64) — Aggregate update time in milliseconds; omitted before the first aggregation. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "asOf": 0, "claimable": "", "hasAggregate": false, "source": "aggregate", "totalClaimed": "", "totalEarned": "" } } ``` --- # `vaultDetails` Returns one vault's profile and state: name, leader, TVL, total shares, profit share, deposit settings, its parent or child vaults, and its depositors. Use it to render a vault page. `POST /info` · type `vaultDetails` Render a vault page: profile, TVL, lock-up, depositors, and one user’s withdrawable amount. - Access: Public - Markets: Perp only - Not this? [`uservaultequities`](/api/info/uservaultequities.md) — when you list every vault one user holds ### Request body - `type` (string, required) — Must be `vaultDetails`. Values: `"vaultDetails"`. - `vaultAddress` (string · address, required) — Vault to read. The sample is the testnet Liquidity Provider vault. - `user` (string · address, optional) — Depositor to report on. When set, `followerState` replaces `followers` and `maxWithdrawable` is this user’s. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"vaultDetails","vaultAddress":"0x664989a76069c2b153720745bc1c3c8eeee504c1"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `allowDeposits` (boolean) - `alwaysCloseOnWithdraw` (boolean) - `createTimeMillis` (integer) — Creation time, Unix ms. - `description` (string) - `isClosed` (boolean) - `leader` (string · address) — Vault operator. For a child vault, its parent vault. - `leaderCommission` (string) — Leader’s profit share as a fraction (`0.1` = 10%). - `leaderFraction` (number) — Fraction of `totalShares` held by the leader. - `maxDistributable` (string) - `maxWithdrawable` (string) — USDC the `user` can withdraw now; `0.000000` when `user` is omitted. - `name` (string) - `relationship` (object) - `type` (string) — `parent` vaults run their strategies through child vaults. Values: `"normal"`, `"parent"`, `"child"`. - `data` (object) — Present on a `parent` vault. - `childAddresses` (string · address[]) — Child vault addresses. - `totalShares` (string) — Shares outstanding. - `tvl` (string) — Total value locked, USDC. - `vaultAddress` (string · address) - `followers` (object[]) — Depositors. Omitted when `user` is set. - `allTimePnl` (string) — Lifetime PnL in this vault, USDC. - `costBasis` (string) — Net USDC deposited. - `lockupUntil` (integer) — End of the lock-up, Unix ms. - `pnl` (string) — PnL on the current position, USDC. - `shares` (string) - `user` (string · address) - `vaultEntryTime` (integer) — First deposit, Unix ms. - `vaultEquity` (string) — Value of the shares, USDC. - `followerState` (object) — The `user`’s position. Present only when `user` is set. - `allTimePnl` (string) — Lifetime PnL in this vault, USDC. - `daysFollowing` (integer) — Days since the first deposit. - `lockupUntil` (integer) — End of the lock-up, Unix ms. - `pnl` (string) — PnL on the current position, USDC. - `user` (string · address) - `vaultEntryTime` (integer) — First deposit, Unix ms. - `vaultEquity` (string) — Value of the shares, USDC. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "name": "Liquidity Provider", "vaultAddress": "0x664989a76069c2b153720745bc1c3c8eeee504c1", "leader": "0xa84f1e04f1d66e4f326b55eb045ae3b931d31b54", "tvl": "104084637.190682", "isClosed": false, "relationship": { "type": "parent", "data": { "childAddresses": [ "0x299d36d36a217abef8a61081bdc724b4f7057848", "0x89a9e22d834ac85688589c342bde69bacb24f41d" ] } }, "createTimeMillis": 1762760933000, "description": "This vault provides liquidity to TxFlow through multiple market making strategies, performs liquidations, and accrues platform fees.", "totalShares": "104694308.658889", "leaderCommission": "0.000000", "allowDeposits": true, "alwaysCloseOnWithdraw": true, "followers": [ { "user": "0x0032a9aceea6afb7f2a17b2759e7ab3c8f1dc31b", "vaultEquity": "29.997435", "shares": "30.173143", "costBasis": "30.000000", "vaultEntryTime": 1792564787350, "lockupUntil": 1792910487446, "pnl": "-0.002564", "allTimePnl": "-0.002564" } ], "leaderFraction": 0.039738133114642415, "maxDistributable": "104084637.190682", "maxWithdrawable": "0.000000" } } ``` ## One depositor's view Add `user` to read one depositor's position. The response then carries `followerState` (that user's equity, PnL, and lock-up) instead of the `followers` list, and `maxWithdrawable` is the USDC that user can withdraw now. ```json { "type": "vaultDetails", "vaultAddress": "0x664989a76069c2b153720745bc1c3c8eeee504c1", "user": "0x091564806dce41ffeebafbc417e73b6849b1c29e" } ``` ## Parent and child vaults `relationship.type` is `normal`, `parent`, or `child`. A `parent` vault lists its children in `relationship.data.childAddresses`; a child vault's `leader` is its parent. A parent's positions and orders live on its children; read each child with [`clearinghouseState`](/api/info/clearinghouseState.md) and [`frontendOpenOrders`](/api/info/frontendOpenOrders.md). For every vault one user holds, use [`uservaultequities`](/api/info/uservaultequities.md). --- # `uservaultequities` Returns every vault one user holds shares in, with the current USDC value of those shares and the end of the deposit lock-up. Use it for a "Your vaults" list. The type name is all lowercase. `POST /info` · type `uservaultequities` List the vaults a user holds, with current equity and lock-up end. - Access: By user address - Markets: Perp only - Live stream: [`vaultUpdates`](/api/websocket/vaultUpdates.md) - Not this? [`vaultDetails`](/api/info/vaultDetails.md) — when you need one vault’s full profile ### Request body - `type` (string, required) — Must be `uservaultequities`. Values: `"uservaultequities"`. - `user` (string · address, required) — Depositor address. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"uservaultequities","user":"0x091564806dce41ffeebafbc417e73b6849b1c29e"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object[]) — One row per vault the user holds shares in; empty when none. - `equity` (string) — Current value of the user’s shares, USDC. - `lockedUntilTimestamp` (integer) — End of the deposit lock-up, Unix ms. Withdrawals open after this time. - `vaultAddress` (string · address) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": [ { "vaultAddress": "0x664989a76069c2b153720745bc1c3c8eeee504c1", "equity": "1914.936497", "lockedUntilTimestamp": 1792830521530 } ] } ``` The list is empty when the user holds no vault shares. Refresh it when [`vaultUpdates`](/api/websocket/vaultUpdates.md) reports a deposit or withdrawal. For one vault's full profile, use [`vaultDetails`](/api/info/vaultDetails.md). --- # `perpMeta` `POST /info` · type `perpMeta` Resolve perp symbols to the numeric `coin` id every other call takes. Cache at startup. - Access: Public - Markets: Perp only - Not this? [`perpMetaDetail`](/api/info/perpMetaDetail.md) — when you need margin tiers for one base asset ### Request body - `type` (string, required) - `dex` (string, nullable, optional) — Exchange name to read. Omit for the system instruments. - `includeDelisted` (boolean, nullable, optional) — Default `true`: include delisted instruments. Pass `false` to return only tradable instruments. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"includeDelisted":false,"type":"perpMeta"}' ``` ### Response · PerpMeta (200) - `code` (integer) — `0` on success. - `data` (PerpMeta) - `universe` (PerpInstrument[]) - `baseCurrency` (string) - `basePrecision` (string) — Smallest size step, `10^-szDecimals`. - `bookTicks` (string[]) — Aggregation steps accepted by the book. - `currencyType` (string) — The currency the contract is margined and settled in, e.g. `usdc`. - `haltTrading` (boolean) - `index` (integer · int64) — Instrument id — the `coin` / `asset` used by every other call. - `maxLeverage` (integer · int32) - `name` (string) - `onlyIsolated` (boolean) - `pricePrecision` (string) — Same value as `priceTick`. - `priceTick` (string) — Price increment. An order price must be a multiple of it, with no more decimals. - `quoteCurrency` (string) - `quotePrecision` (string) - `szDecimals` (integer · int64) — Decimals allowed in an order size; negative means sizes must be a multiple of `10^-szDecimals`. - `tokens` (integer · int64[]) - `delisted` (boolean) — Whether the instrument is delisted on-chain (`delisted=true`). Every entry in `universe` carries this flag; clients filter by it. - `maxLimitOrderSize` (integer · int64, nullable) — Largest limit-order size, in base units. Omitted when uncapped. - `maxMarketOrderSize` (integer · int64, nullable) — Largest market-order size, in base units. Omitted when uncapped. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "universe": [ { "baseCurrency": "BTC", "basePrecision": "0.0001", "bookTicks": [ "0.1", "1", "10" ], "currencyType": "usdc", "delisted": false, "haltTrading": false, "index": 1, "maxLeverage": 50, "maxLimitOrderSize": 1000, "maxMarketOrderSize": 100, "name": "BTC-USDC", "onlyIsolated": false, "pricePrecision": "0.1", "priceTick": "0.1", "quoteCurrency": "USDC", "quotePrecision": "0.01", "szDecimals": 4, "tokens": [ 2, 1 ] } ] } } ``` --- # `perpMetaDetail` Detailed perpetual metadata and margin tiers for one base asset. Query by **base asset name** (for example `BTC`), not the full perp symbol (`BTC-USDC`). `POST /info` · type `perpMetaDetail` Show margin tiers and max leverage for one base asset (`BTC`, not `BTC-USDC`). - Access: Public - Markets: Perp only - Not this? [`perpMeta`](/api/info/perpMeta.md) — when you need the full instrument list ### Request body - `assetName` (string, required) — Base asset name, e.g. `BTC`. - `type` (string, required) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"perpMetaDetail","assetName":"BTC"}' ``` ### Response · PerpMetaDetailResponse (200) - `code` (integer) — `0` on success. - `data` (PerpMetaDetailResponse) - `asset` (AssetDetail) - `archorTokenWei` (string) - `assetId` (integer · int64) - `initialWei` (string) - `maxSupply` (string) - `name` (string) - `szDecimals` (integer · int64) - `weiDecimals` (integer · int64) - `instruments` (InstrumentDetail[]) - `perp` (PerpDetail) - `assets` (integer · int64[]) - `bookTicks` (string[]) - `delisted` (boolean) - `haltTrading` (boolean) - `instrumentId` (integer · int64) - `makerFeeRate` (string) - `name` (string) - `onlyIsolated` (boolean) - `perpType` (string) — `linear` or `inverse`. - `priceTick` (string) - `szDecimals` (integer · int64) - `takerFeeRate` (string) - `maxLimitOrderSize` (integer · int64, nullable) - `maxMarketOrderSize` (integer · int64, nullable) - `marginTable` (anyOf) - Any of · variant 1: - `instrumentId` (integer · int64) - `tiers` (MarginTierDetail[]) - `maxLeverage` (integer · int32) - `maxSize` (string) - `minSize` (string) - `mmr` (string) - `tier` (integer · int32) - Any of · variant 2: - null - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "asset": { "name": "", "assetId": 0, "szDecimals": 0, "weiDecimals": 0, "maxSupply": "", "initialWei": "", "archorTokenWei": "" }, "instruments": [ { "perp": { "name": "", "instrumentId": 9001, "assets": [ 0 ], "szDecimals": 0, "perpType": "", "takerFeeRate": "", "makerFeeRate": "", "priceTick": "0", "bookTicks": [ "" ], "maxLimitOrderSize": 0, "maxMarketOrderSize": 0, "haltTrading": false, "onlyIsolated": false, "delisted": false }, "marginTable": { "instrumentId": 9001, "tiers": [ { "tier": 0, "minSize": "", "maxSize": "", "maxLeverage": 0, "mmr": "" } ] } } ] } } ``` --- # `marketTicker` Returns the ticker for one perp instrument: last, mark, and oracle price, open interest, the current funding rate and its bounds, and 24-hour change and volume. Use it to fill a market header on first load, then keep it live with the [`ticker`](/api/websocket/ticker.md) stream. `POST /info` · type `marketTicker` Fill a perp market header on first load: last, mark, oracle, funding, 24h change and volume. - Access: Public - Markets: Perp only - Live stream: [`ticker`](/api/websocket/ticker.md) - Not this? [`activeAssetCtx`](/api/info/activeAssetCtx.md) — when you need impact prices or the previous funding time ### Request body - `instrumentId` (integer, required) — Numeric instrument id from `perpMeta` (`index`), sent as a JSON number. - `type` (string, required) — Must be `marketTicker`. Values: `"marketTicker"`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"marketTicker","instrumentId":1}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `blockTime` (integer) — Block time of this snapshot, Unix ms. - `change24h` (string) — `lastPrice` minus `prevDayPx`. - `change24hPct` (string) — 24h change in percent. - `coin` (string) — Base name. - `dayNtlVlm` (string) — 24h notional volume in USDC. - `fundingRate` (string) — Funding rate for the current interval, in percent (`0.01` means 0.01%). - `instrumentId` (integer) - `lastPrice` (string) — Last traded price. - `markPx` (string) — Mark price. - `nextFundingTime` (integer) — Next funding settlement, Unix ms. - `openInterest` (string) — Open interest in base units. - `oraclePx` (string) — Oracle price. - `prevDayPx` (string) — Price 24 hours ago. - `rateCap` (string) — Upper bound of one funding settlement, in percent. - `rateFloor` (string) — Lower bound of one funding settlement, in percent. - `settlementIntervalMs` (integer) — Funding settlement interval in ms. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "coin": "BTC", "instrumentId": 1, "lastPrice": "83387.4", "markPx": "83388.4", "oraclePx": "83420.1", "openInterest": "776.5054", "fundingRate": "0.00125000", "nextFundingTime": 1792576800000, "blockTime": 1792575637530, "dayNtlVlm": "22527876.25983", "prevDayPx": "81309.9", "change24h": "2077.5", "change24hPct": "2.56", "settlementIntervalMs": 3600000, "rateCap": "0.20000000", "rateFloor": "-0.20000000" } } ``` `instrumentId` is the numeric `index` from [`perpMeta`](/api/info/perpMeta.md), sent as a JSON number. `fundingRate`, `rateCap`, and `rateFloor` are percent per settlement interval: `0.00125` means 0.00125% every `settlementIntervalMs`. See [Funding](/trade/funding.md). --- # `activeAssetCtx` `POST /info` · type `activeAssetCtx` Show a perp market header: mark, oracle, funding rate, open interest. - Access: Public - Markets: Perp only - Live stream: [`activeAssetCtx`](/api/websocket/activeAssetCtx.md) - Not this? [`activeAssetData`](/api/info/activeAssetData.md) — when you need the *user’s* leverage and max size, not market stats ### Request body - `coin` (string, required) — Instrument external id (numeric string) — the `index` value from the `perpMeta` response. - `type` (string, required) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"activeAssetCtx","coin":"4"}' ``` ### Response · ActiveAssetCtxResponse (200) - `code` (integer) — `0` on success. - `data` (ActiveAssetCtxResponse) - `blockTime` (integer · int64) — Latest block time, milliseconds. - `coin` (string) - `coinVlm` (string) — 24h base-asset volume. - `dayHighPx` (string) - `dayLowPx` (string) - `dayNtlVlm` (string) — 24h notional volume. - `instrumentId` (integer · int64) - `lastPrice` (string) - `markPx` (string) - `openInterest` (string) - `oraclePx` (string) - `prevDayPx` (string) - `nodeCtx` (ActiveAssetCtxData) — Node-native perps market context. Omitted on instruments the node has no context for. - `blockTime` (integer, nullable) — Latest block time, milliseconds. - `funding` (string) — Current funding rate, published in percent (e.g. `0.01` means 0.01%). - `impactPxs` (string[]) — [impactBidPx, impactAskPx]. - `markPx` (string) - `midPx` (string, nullable) - `nextFundingTime` (integer, nullable) — Next funding settlement time, milliseconds. Omitted on old payloads for backward compatibility. - `openInterest` (string) - `oraclePx` (string) - `prevFundingTime` (integer, nullable) — Previous funding settlement time, milliseconds. - `lastPrice` (string, nullable) — Omitted on instruments that have never traded. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "coin": "4", "instrumentId": 9001, "dayNtlVlm": "", "coinVlm": "", "prevDayPx": "0", "lastPrice": "0", "dayHighPx": "0", "dayLowPx": "0", "openInterest": "", "oraclePx": "0", "markPx": "0", "blockTime": 1700000000000, "nodeCtx": { "funding": "", "openInterest": "", "oraclePx": "0", "markPx": "0", "midPx": "0", "impactPxs": [ "" ], "lastPrice": "0", "nextFundingTime": 1700000000000, "prevFundingTime": 1700000000000, "blockTime": 1700000000000 } } } ``` --- # `webData2` Returns two lists in one call: the user's open orders, and a 24-hour stats row for every listed instrument. Use it to fill a markets table and an order list on page load. `POST /info` · type `webData2` Fill a markets table with 24h stats for every instrument, plus the user’s open orders, in one call. - Access: By user address - Markets: Perp only - Not this? [`allAssetCtxs`](/api/websocket/allAssetCtxs.md) — when you need mark, funding, and OI kept live - Not this? [`webData2`](/api/websocket/webData2.md) — when you need positions and margin streamed with open orders ### Request body - `type` (string, required) — Must be `webData2`. Values: `"webData2"`. - `user` (string · address, required) — Account whose open orders to return. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"webData2","user":"0x0000000000000000000000000000000000000001"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `assetCtxs` (object[]) — One row per listed instrument. - `blockTime` (integer) - `coin` (string) — Base name. - `coinVlm` (string) — 24h volume in base units. - `dayHighPx` (string) — 24h high. - `dayLowPx` (string) — 24h low. - `dayNtlVlm` (string) — 24h notional volume in USDC. - `instrumentId` (integer) - `lastPrice` (string) — Last traded price. - `markPx` (string) — Empty here; read it from `marketTicker`. - `openInterest` (string) — Empty here; read it from `marketTicker`. - `oraclePx` (string) — Empty here; read it from `marketTicker`. - `prevDayPx` (string) — Price 24 hours ago. - `tagIds` (integer[]) — Ids from `tags`. - `openOrders` (object[]) — The user’s open orders, newest first. - `avgFillPrice` (string, nullable) — Average fill price so far. - `children` (object[]) — Attached TP/SL orders. - `cloid` (string, nullable) — Client order id. - `coin` (string) — Base name. - `dir` (string) — Direction label, e.g. `Buy` / `Sell`. - `fee` (string) — Fees paid so far, USDC. - `filledAmount` (string) — Filled notional, USDC. - `isPositionTpsl` (boolean) - `isTrigger` (boolean) - `leverage` (integer) - `limitPx` (string) — Limit price. - `marginMode` (string) - `marketType` (string) - `oid` (integer) — Order id. - `orderType` (string) - `origSz` (string) — Original size. - `pnl` (string) — Realized PnL of the filled part, USDC. - `positionMode` (string) - `reduceOnly` (boolean) - `roe` (string) — Return on equity of the filled part, percent. - `side` (string) — "B" = buy, "A" = sell. - `status` (string) — Order status. - `sz` (string) — Remaining size in base units. - `tif` (string) — Time in force: `GTC`, `ALO`, `IOC`. - `timestamp` (integer) — Placement time, Unix ms. - `triggerCondition` (string) — `N/A` for a non-trigger order. - `triggerPx` (string) — Trigger price; `0.0` when not a trigger. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "openOrders": [ { "coin": "BTC", "side": "A", "limitPx": "63348.2", "sz": "0.0674", "oid": 160904123628, "timestamp": 1784032333182, "triggerCondition": "N/A", "isTrigger": false, "triggerPx": "0.0", "children": [], "isPositionTpsl": false, "reduceOnly": false, "dir": "Sell", "orderType": "Limit", "origSz": "0.0709", "tif": "GTC", "cloid": "0x08c1a869b51e0669d42b", "avgFillPrice": "63348.2", "positionMode": "OneWay", "marginMode": "Cross", "leverage": 8, "pnl": "2.2266", "roe": "8.1155", "fee": "0.099773415", "filledAmount": "221.7187", "status": "PartialFilled", "marketType": "perps" } ], "assetCtxs": [ { "coin": "BTC", "instrumentId": 1, "dayNtlVlm": "22528397.36778", "coinVlm": "275.59", "prevDayPx": "81309.6", "lastPrice": "83394.6", "dayHighPx": "83590.2", "dayLowPx": "80925", "openInterest": "", "oraclePx": "", "markPx": "", "blockTime": 0, "tagIds": [ 1 ] } ] } } ``` `assetCtxs` carries last price, 24h high, low, previous price, and volume. Read mark price, oracle price, and open interest from [`marketTicker`](/api/info/marketTicker.md) or [`activeAssetCtx`](/api/info/activeAssetCtx.md). Resolve `tagIds` with [`tags`](/api/info/tags.md). This is the `/info` type of the same name as the node [`webData2`](/api/websocket/webData2.md) stream, which pushes positions and margin with open orders. For an Open Orders tab with trigger and TP/SL detail, use [`frontendOpenOrders`](/api/info/frontendOpenOrders.md). --- # `perpClearinghouseState` Perp sub-account token balances Returns the tokens held in the perp account. For positions and the margin summary use [`clearinghouseState`](/api/info/clearinghouseState.md) with `accountType=perp`. `POST /info` · type `perpClearinghouseState` Show token balances held in the perp sub-account. - Access: By user address - Markets: Perp only - Not this? [`clearinghouseState`](/api/info/clearinghouseState.md) — when you need positions and margin, not balances ### Request body - `type` (string, required) - `user` (string, required) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"perpClearinghouseState","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · UserTokenBalanceResponse (200) - `code` (integer) — `0` on success. - `data` (UserTokenBalanceResponse) - `balances` (UserTokenBalance[]) - `coin` (string) - `displayName` (string) - `entryNtl` (string) - `hold` (string) - `token` (integer · int64) - `total` (string) - `lastPrice` (string, nullable) - `midPrice` (string, nullable) - `withdrawable` (string, nullable) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "balances": [ { "coin": "4", "displayName": "", "entryNtl": "", "hold": "", "lastPrice": "0", "midPrice": "0", "token": 0, "total": "" } ], "withdrawable": "" } } ``` --- # `activeAssetData` Private per-user trading parameters for a single instrument: the user's current leverage setting, maximum tradeable size, available margin, plus the instrument's mark price. Requires both `user` and `coin`. `POST /info` · type `activeAssetData` Fill the order ticket: the user’s leverage, max tradeable size, and available margin on one instrument. - Access: By user address - Markets: Perp only - Live stream: [`activeAssetData`](/api/websocket/activeAssetData.md) - Not this? [`activeAssetCtx`](/api/info/activeAssetCtx.md) — when you need market stats (mark, funding, OI), not user limits - Not this? [`getMaxPositions`](/api/info/getMaxPositions.md) — when you need the max size at a specific price ### Request body - `coin` (string, required) — Instrument external id (numeric string) — the `index` value from the `perpMeta` response. - `type` (string, required) - `user` (string, required) - `mode` (string, nullable, optional) — Optional: `isolated` or `cross`. Falls back to the account default when omitted. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"coin":"4","mode":"cross","type":"activeAssetData","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · ActiveAssetDataResponse (200) - `code` (integer) — `0` on success. - `data` (ActiveAssetDataResponse) - `availableToTrade` (string[]) - `coin` (string) - `leverage` (LeverageSetting) - One of · variant 1 — mode = "oneWay": - `mode` (string) Values: `"oneWay"`. - `type` (string) - `value` (integer · int32) - `raw_usd` (string, nullable) - One of · variant 2 — mode = "hedge": - `long_value` (integer · int32) - `mode` (string) Values: `"hedge"`. - `short_value` (integer · int32) - `type` (string) - `markPx` (string) - `maxTradeSzs` (string[]) - `onlyIsolated` (boolean) - `user` (string) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "availableToTrade": [ "" ], "coin": "4", "leverage": { "mode": "oneWay", "raw_usd": "", "type": "", "value": 0 }, "markPx": "0", "maxTradeSzs": [ "" ], "onlyIsolated": false, "user": "0x0000000000000000000000000000000000000000" } } ``` --- # `triggerOrders` `POST /info` · type `triggerOrders` List a user’s pending trigger (stop) orders, with any TP/SL children. - Access: By user address - Markets: Perp only - Not this? [`tpslOrders`](/api/info/tpslOrders.md) — when you want standalone TP/SL orders - Not this? [`historicalTriggerOrders`](/api/info/historicalTriggerOrders.md) — when you need triggered or cancelled ones ### Request body - `type` (string, required) - `user` (string, required) - `asset` (string, nullable, optional) — Optional: instrument external id (numeric string) — the `index` value from the `perpMeta` response. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"asset":"4","type":"triggerOrders","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (TriggerOrderResponse[]) - `asset` (string) - `coin` (string) - `createTime` (integer · int64) - `leverage` (integer · int32) - `limitPx` (string) - `marginMode` (string) - `oid` (integer · int64) - `orderType` (string) - `positionMode` (string) - `reduceOnly` (boolean) - `side` (string) - `sz` (string) - `triggerPx` (string) - `triggerState` (string) - `cloid` (string, nullable) - `tpslChildren` (array, nullable) - `limitPx` (string) - `orderType` (string) - `reduceOnly` (boolean) - `side` (string) - `sz` (string) - `triggerPx` (string) - `triggerType` (string) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": [ { "asset": "4", "cloid": "", "coin": "4", "createTime": 1700000000000, "leverage": 0, "limitPx": "0", "marginMode": "", "oid": 123456, "orderType": "limit", "positionMode": "", "reduceOnly": false, "side": "", "sz": "0", "tpslChildren": [ { "limitPx": "0", "orderType": "limit", "reduceOnly": false, "side": "", "sz": "0", "triggerPx": "0", "triggerType": "" } ], "triggerPx": "0", "triggerState": "" } ] } ``` --- # `tpslOrders` `POST /info` · type `tpslOrders` List all of a user’s pending take-profit / stop-loss orders. - Access: By user address - Markets: Perp only - Not this? [`positionTpslOrders`](/api/info/positionTpslOrders.md) — when you only want the TP/SL attached to one position - Not this? [`triggerOrders`](/api/info/triggerOrders.md) — when you want trigger (stop-entry) orders - Not this? [`historicalTpslOrders`](/api/info/historicalTpslOrders.md) — when you need executed or cancelled TP/SL ### Request body - `type` (string, required) - `user` (string, required) - `asset` (string, nullable, optional) — Optional: instrument external id (numeric string) — the `index` value from the `perpMeta` response. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"asset":"4","type":"tpslOrders","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (TpslOrdersResponse[]) - `asset` (string) - `coin` (string) - `createTime` (integer · int64) - `leverage` (integer · int32) - `marginMode` (string) - `positionMode` (string) - `quantity` (string) - `reduceOnly` (boolean) - `side` (string) - `slOid` (string) - `slOrderPrice` (string) - `slOrderType` (string) - `slTriggerPrice` (string) - `tpOid` (string) - `tpOrderPrice` (string) - `tpOrderType` (string) - `tpTriggerPrice` (string) - `cloid` (string, nullable) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": [ { "asset": "4", "cloid": "", "coin": "4", "createTime": 1700000000000, "leverage": 0, "marginMode": "", "positionMode": "", "quantity": "0", "reduceOnly": false, "side": "", "slOid": "", "slOrderPrice": "0", "slOrderType": "", "slTriggerPrice": "0", "tpOid": "", "tpOrderPrice": "0", "tpOrderType": "", "tpTriggerPrice": "0" } ] } ``` --- # `positionTpslOrders` Lists the pending take-profit / stop-loss order pairs bound to one position, optionally filtered by close mode. This is separate from [`tpslOrderPosition`](/api/info/tpslOrderPosition.md), which resolves one TP/SL child order id back to its bound position. `POST /info` · type `positionTpslOrders` Show the TP/SL pairs bound to one open position (position row → its TP/SL). - Access: By user address - Markets: Perp only - Not this? [`tpslOrderPosition`](/api/info/tpslOrderPosition.md) — when you have a TP/SL order id and need its position (the reverse lookup) - Not this? [`tpslOrders`](/api/info/tpslOrders.md) — when you want every TP/SL across all positions ### Request body - `asset` (string, required) — Instrument external id (numeric string) — the `index` value from `perpMeta`. - `marginMode` (string, required) Values: `"cross"`, `"isolated"`. - `side` (string, required) Values: `"long"`, `"short"`. - `type` (string, required) - `user` (string, required) — User address. - `closeMode` (string, optional) — Optional close-mode filter. Empty or omitted returns both modes. Values: `"entire"`, `"partial"`, `""`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"asset":"4","closeMode":"entire","marginMode":"cross","side":"long","type":"positionTpslOrders","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (TpslOrdersResponse[]) - `asset` (string) - `coin` (string) - `createTime` (integer · int64) - `leverage` (integer · int32) - `marginMode` (string) - `positionMode` (string) - `quantity` (string) - `reduceOnly` (boolean) - `side` (string) - `slOid` (string) - `slOrderPrice` (string) - `slOrderType` (string) - `slTriggerPrice` (string) - `tpOid` (string) - `tpOrderPrice` (string) - `tpOrderType` (string) - `tpTriggerPrice` (string) - `cloid` (string, nullable) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": [ { "asset": "4", "cloid": "", "coin": "4", "createTime": 1700000000000, "leverage": 0, "marginMode": "", "positionMode": "", "quantity": "0", "reduceOnly": false, "side": "", "slOid": "", "slOrderPrice": "0", "slOrderType": "", "slTriggerPrice": "0", "tpOid": "", "tpOrderPrice": "0", "tpOrderType": "", "tpTriggerPrice": "0" } ] } ``` --- # `tpslOrderPosition` Resolves one TP/SL child order id to the position it is bound to. This is separate from [`positionTpslOrders`](/api/info/positionTpslOrders.md), which lists the TP/SL order pairs bound to a position. `POST /info` · type `tpslOrderPosition` Resolve a TP/SL child order id back to the position it protects (order → position). - Access: By user address - Markets: Perp only - Not this? [`positionTpslOrders`](/api/info/positionTpslOrders.md) — when you start from the position instead ### Request body - `oid` (integer · int64, required) — TP/SL child order id. - `type` (string, required) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"oid":123456,"type":"tpslOrderPosition"}' ``` ### Response · TpslOrderPositionResponse (200) - `code` (integer) — `0` on success. - `data` (TpslOrderPositionResponse) - `asset` (string) - `coin` (string) - `entryPx` (string) - `marginMode` (string) - `positionMode` (string) - `side` (string) - `sz` (string) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "asset": "4", "coin": "4", "entryPx": "0", "marginMode": "", "positionMode": "", "side": "", "sz": "0" } } ``` --- # `getLiquidationPrices` `POST /info` · type `getLiquidationPrices` Preview the estimated liquidation price before the user submits an order. - Access: By user address - Markets: Perp only ### Request body - `asset` (string, required) — Instrument external id as a numeric string. Perpetual ids come from `perpMeta`; Outcome ids come from `outcomeMeta` (`instrument_id`). - `buyQuantity` (string, required) — Buy quantity. - `orderType` (string, required) - `sellQuantity` (string, required) — Sell quantity. - `type` (string, required) - `user` (string, required) - `buyPrice` (string, nullable, optional) - `reduceOnly` (boolean, nullable, optional) - `sellPrice` (string, nullable, optional) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"asset":"4","buyPrice":"0","buyQuantity":"0","orderType":"limit","reduceOnly":false,"sellPrice":"0","sellQuantity":"0","type":"getLiquidationPrices","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · LiquidationPricesResponse (200) - `code` (integer) — `0` on success. - `data` (LiquidationPricesResponse) — Estimated liquidation-price response. Computes the estimated liquidation price for the requested buy/sell quantities. - `buyPrice` (string) — Buy price used in the calculation. - `long` (LiquidationPriceCalculation) — Long-side calculation. - `leverage` (integer · int32) — Leverage multiplier. - `liquidationPrice` (string) — Estimated liquidation price. Empty string when the position cannot be liquidated, or when the requested quantity exceeds the maximum tradable size. - `quantity` (string) — Trade quantity. - `requiredFee` (string) — Required taker fee. - `requiredMargin` (string) — Required margin. - `totalCost` (string) — Total cost = margin + fee. - `error` (string, nullable) — Error message returned when the quantity exceeds the maximum tradable size. - `maxQuantity` (string, nullable) — Reference maximum tradable quantity. - `marginMode` (string) — Margin mode: `cross` or `isolated`. - `positionMode` (string) — Position mode: `oneWay` or `hedge`. - `sellPrice` (string) — Sell price used in the calculation. - `short` (LiquidationPriceCalculation) — Short-side calculation. - `leverage` (integer · int32) — Leverage multiplier. - `liquidationPrice` (string) — Estimated liquidation price. Empty string when the position cannot be liquidated, or when the requested quantity exceeds the maximum tradable size. - `quantity` (string) — Trade quantity. - `requiredFee` (string) — Required taker fee. - `requiredMargin` (string) — Required margin. - `totalCost` (string) — Total cost = margin + fee. - `error` (string, nullable) — Error message returned when the quantity exceeds the maximum tradable size. - `maxQuantity` (string, nullable) — Reference maximum tradable quantity. - `symbol` (string) — Trading-pair symbol. - `user` (string) — User address. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "buyPrice": "0", "long": { "error": "", "leverage": 0, "liquidationPrice": "0", "maxQuantity": "0", "quantity": "0", "requiredFee": "", "requiredMargin": "", "totalCost": "" }, "marginMode": "", "positionMode": "", "sellPrice": "0", "short": { "error": "", "leverage": 0, "liquidationPrice": "0", "maxQuantity": "0", "quantity": "0", "requiredFee": "", "requiredMargin": "", "totalCost": "" }, "symbol": "", "user": "0x0000000000000000000000000000000000000000" } } ``` --- # `preCheckIsolatedMarginAdjust` Returns one isolated position with the most margin that can be added or removed, and, when `ntli` is set, a preview of the position after that adjustment. Use it to fill an Adjust Margin dialog before signing [`updateIsolatedMargin`](/api/exchange/updateIsolatedMargin.md). Nothing is written. `POST /info` · type `preCheckIsolatedMarginAdjust` Fill the Adjust Margin dialog: max add / remove and the new liquidation price before `updateIsolatedMargin`. - Access: By user address - Markets: Perp only - Not this? [`clearinghouseState`](/api/info/clearinghouseState.md) — when you need every position, not one isolated position ### Request body - `asset` (string, required) — Perp instrument id (`index` from `perpMeta`). - `side` (string, required) — Side of the isolated position. Values: `"long"`, `"short"`. - `type` (string, required) — Must be `preCheckIsolatedMarginAdjust`. Values: `"preCheckIsolatedMarginAdjust"`. - `user` (string · address, required) — Position owner. - `ntli` (string, optional) — Amount to preview, in decimal USDC: positive adds margin, negative removes it. Omit to read the limits only. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"preCheckIsolatedMarginAdjust","user":"0x06361e568c6375a376e4244b1435b4dd8ea1e247","asset":"30","side":"long","ntli":"1"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `position` (object) - `coin` (string) — Instrument symbol. - `effectiveLeverage` (string) — Position value divided by margin plus unrealized PnL. - `entryPrice` (string) — Average entry price. - `leverage` (integer) — Configured leverage. - `liquidationPrice` (string, nullable) — Current liquidation price. - `margin` (string) — Isolated margin currently on the position, in USDC. - `markPrice` (string) — Current mark price. - `maxDecrease` (string) — Most USDC that can be removed now. - `maxIncrease` (string) — Most USDC that can be added now (bounded by the available balance). - `side` (string) — Position side. Values: `"Long"`, `"Short"`. - `szi` (string) — Signed size in base units; negative for a short. - `unrealizedPnl` (string) — Unrealized PnL in USDC. - `preview` (object) — Present when the request includes `ntli`. - `adjustAmount` (string) — The requested `ntli`, in USDC. - `newEffectiveLeverage` (string) — Effective leverage after the adjustment. - `newLiquidationPrice` (string, nullable) — Liquidation price after the adjustment. - `newMargin` (string) — Margin after the adjustment, in USDC. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "position": { "coin": "HBAR-USDC", "side": "Long", "szi": "65800", "entryPrice": "0.08000", "markPrice": "0.08465", "margin": "126.76", "leverage": 50, "effectiveLeverage": "12.87", "unrealizedPnl": "306.00", "liquidationPrice": "0.07891", "maxIncrease": "1135.01", "maxDecrease": "21.48", "preview": { "adjustAmount": "1.00", "newMargin": "127.76", "newLiquidationPrice": "0.07889", "newEffectiveLeverage": "12.84" } } } } ``` `ntli` here is a **decimal USDC** string: `"1"` previews adding 1 USDC and `"-1"` previews removing 1 USDC. [`updateIsolatedMargin`](/api/exchange/updateIsolatedMargin.md) takes the same amount as a 6-decimal integer (`"1000000"`). Without `ntli`, the response has no `preview`. A typical dialog calls once without `ntli` to show `maxIncrease` / `maxDecrease`, then again as the user types an amount. ## Next steps Apply the change with [`updateIsolatedMargin`](/api/exchange/updateIsolatedMargin.md), then read the position from [`clearinghouseState`](/api/info/clearinghouseState.md). --- # `outcomeMeta` Returns Outcome events with nested markets, live trading gates, pricing, volume, open interest, settlement state, and instrument parameters. Use `cursor` / `limit` for the discovery feed. Use `eventSlug` or `marketSlugs` for targeted lookups; with `marketSlugs`, `markets=matched` returns only the requested markets, while `markets=event` returns every market in each owning event. An `eventSlug` point lookup returns that event object directly; discovery and market-slug queries return the paged envelope. `sortBy=volume` and `sortBy=oi` default to descending order. `sortBy=endingSoon` is an ascending semantic order and should be sent without `sortOrder`. Response metadata fields use `snake_case`. This is an API-host aggregation, not a direct fullnode query. It combines the indexed event/market catalog with live trading gates and book-derived pricing, so it can briefly lag consensus or omit live prices when the book is empty. The nested `instrument` object contains precision, fee-curve, tick, and size limits. `settlement.winning_outcome` is derived from `outcomes` and `settlement_fraction` after resolution. It is not the metadata provider's oracle result. `trading` and `pricing` are nullable on responses that have not been live-enriched. `POST /info` · type `outcomeMeta` Build the Outcome discovery feed or an event page: events, nested markets, live prices, settlement state. - Access: Public - Markets: Outcome only - Not this? [`outcomeNavTags`](/api/info/outcomeNavTags.md) — when you need the category and tag menu to filter by - Not this? [`outcomeMetaByInstrument`](/api/info/outcomeMetaByInstrument.md) — when you already have one `instrumentId` and only need its trading params ### Request body - `exchangeId` (integer · uint32, required) - `type` (string, required) — Must be `outcomeMeta`. Values: `"outcomeMeta"`. - `category` (string, nullable, optional) - `cursor` (string, nullable, optional) - `eventSlug` (string, nullable, optional) - `includeDelisted` (boolean, optional) - `limit` (integer, nullable, optional) - `markets` (string, nullable, optional) Values: `"event"`, `"matched"`, `null`. - `marketSlugs` (string[], optional) - `q` (string, nullable, optional) - `sortBy` (string, nullable, optional) Values: `"volume"`, `"oi"`, `"endingSoon"`, `null`. - `sortOrder` (string, nullable, optional) Values: `"asc"`, `"desc"`, `null`. - `status` (string, nullable, optional) - `tag` (string, nullable, optional) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"outcomeMeta","exchangeId":1,"limit":20,"includeDelisted":false}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (oneOf) - One of · variant 1: - `events` (OutcomeMetaEvent[]) - `exchange_id` (integer · uint32) - `markets` (OutcomeMetaMarket[]) - `event_slug` (string) - `exchange_id` (integer · uint32) - `instrument_id` (integer · uint64) - `outcomes` (string[]) - `question` (string) - `title` (string) - `description` (string, nullable) - `end_date` (integer · int64, nullable) - `event_start_time` (integer · int64, nullable) - `group_item_threshold` (string, nullable) - `group_item_title` (string, nullable) - `icon` (string, nullable) - `image` (string, nullable) - `instrument` (OutcomeMetaInstrument) - `assets` (array, nullable) — [base asset id, quote asset id]. - `exponent` (string, nullable) - `fee_rate` (string, nullable) - `maker_rebate_rate` (string, nullable) - `max_limit_order_size` (integer, nullable) - `max_market_order_size` (integer, nullable) - `min_limit_order_notional` (string, nullable) - `min_limit_order_size` (string, nullable) - `min_market_order_notional` (string, nullable) - `min_market_order_size` (string, nullable) - `price_tick` (string, nullable) - `sz_decimals` (integer, nullable) - `keyword_values` (string[][]) - `liquidity` (string, nullable) - `metadata_pending` (boolean) - `open_interest` (string) - `open_price` (string, nullable) — Crypto price-to-beat when available. - `pricing` (oneOf) - One of · variant 1: - `best_ask` (string, nullable) - `best_bid` (string, nullable) - `last_trade_price` (string, nullable) - `spread` (string, nullable) - One of · variant 2: - null - `question_id` (string, nullable) - `resolution_mechanism` (string, nullable) - `resolution_source` (string, nullable) - `resolution_status` (string) Values: `"prepared"`, `"resolved"`. - `settlement` (OutcomeMetaSettlement) - `resolution_status` (string) Values: `"prepared"`, `"resolved"`. - `resolved_at` (integer · int64, nullable) - `settlement_details` (string, nullable) - `settlement_fraction` (string, nullable) - `winning_outcome` (string, nullable) — Winner label derived from outcomes and settlement_fraction when resolved; not the provider oracle_outcome. - `slug` (string, nullable) - `start_date` (integer · int64, nullable) - `template_id` (string, nullable) - `trading` (oneOf) - One of · variant 1: - `accepting_orders` (boolean) - `closed` (boolean) - One of · variant 2: - null - `volume` (string) - `slug` (string) - `title` (string) - `category` (string, nullable) - `description` (string, nullable) - `end_date` (integer · int64, nullable) - `event_date` (integer · int64, nullable) - `first_registered_at` (integer · int64, nullable) - `icon` (string, nullable) - `image` (string, nullable) - `liquidity` (string, nullable) - `market_count` (integer · uint64) - `metadata_pending` (boolean) - `open_interest` (string) - `parent_event_slug` (string, nullable) - `resolution_source` (string, nullable) - `series_slug` (string, nullable) - `source_of_truth` (string, nullable) - `start_date` (integer · int64, nullable) - `start_time` (integer · int64, nullable) - `status` (string) - `subcategory` (string, nullable) - `tag_slugs` (string[]) - `volume` (string) - `exchange_id` (integer · uint32) - `has_more` (boolean) - `next_cursor` (string, nullable) - One of · variant 2: - `exchange_id` (integer · uint32) - `markets` (OutcomeMetaMarket[]) - `event_slug` (string) - `exchange_id` (integer · uint32) - `instrument_id` (integer · uint64) - `outcomes` (string[]) - `question` (string) - `title` (string) - `description` (string, nullable) - `end_date` (integer · int64, nullable) - `event_start_time` (integer · int64, nullable) - `group_item_threshold` (string, nullable) - `group_item_title` (string, nullable) - `icon` (string, nullable) - `image` (string, nullable) - `instrument` (OutcomeMetaInstrument) - `assets` (array, nullable) — [base asset id, quote asset id]. - `exponent` (string, nullable) - `fee_rate` (string, nullable) - `maker_rebate_rate` (string, nullable) - `max_limit_order_size` (integer, nullable) - `max_market_order_size` (integer, nullable) - `min_limit_order_notional` (string, nullable) - `min_limit_order_size` (string, nullable) - `min_market_order_notional` (string, nullable) - `min_market_order_size` (string, nullable) - `price_tick` (string, nullable) - `sz_decimals` (integer, nullable) - `keyword_values` (string[][]) - `liquidity` (string, nullable) - `metadata_pending` (boolean) - `open_interest` (string) - `open_price` (string, nullable) — Crypto price-to-beat when available. - `pricing` (oneOf) - One of · variant 1: - `best_ask` (string, nullable) - `best_bid` (string, nullable) - `last_trade_price` (string, nullable) - `spread` (string, nullable) - One of · variant 2: - null - `question_id` (string, nullable) - `resolution_mechanism` (string, nullable) - `resolution_source` (string, nullable) - `resolution_status` (string) Values: `"prepared"`, `"resolved"`. - `settlement` (OutcomeMetaSettlement) - `resolution_status` (string) Values: `"prepared"`, `"resolved"`. - `resolved_at` (integer · int64, nullable) - `settlement_details` (string, nullable) - `settlement_fraction` (string, nullable) - `winning_outcome` (string, nullable) — Winner label derived from outcomes and settlement_fraction when resolved; not the provider oracle_outcome. - `slug` (string, nullable) - `start_date` (integer · int64, nullable) - `template_id` (string, nullable) - `trading` (oneOf) - One of · variant 1: - `accepting_orders` (boolean) - `closed` (boolean) - One of · variant 2: - null - `volume` (string) - `slug` (string) - `title` (string) - `category` (string, nullable) - `description` (string, nullable) - `end_date` (integer · int64, nullable) - `event_date` (integer · int64, nullable) - `first_registered_at` (integer · int64, nullable) - `icon` (string, nullable) - `image` (string, nullable) - `liquidity` (string, nullable) - `market_count` (integer · uint64) - `metadata_pending` (boolean) - `open_interest` (string) - `parent_event_slug` (string, nullable) - `resolution_source` (string, nullable) - `series_slug` (string, nullable) - `source_of_truth` (string, nullable) - `start_date` (integer · int64, nullable) - `start_time` (integer · int64, nullable) - `status` (string) - `subcategory` (string, nullable) - `tag_slugs` (string[]) - `volume` (string) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "exchange_id": 1, "events": [ { "slug": "btc-updown-5m-1787567400", "exchange_id": 1, "title": "Bitcoin Up or Down?", "description": "", "image": "", "icon": "", "category": "", "subcategory": "", "tag_slugs": [ "" ], "series_slug": "", "parent_event_slug": "", "resolution_source": "", "source_of_truth": "", "end_date": 0, "event_date": 0, "start_date": 0, "start_time": 1700000000000, "market_count": 1, "first_registered_at": 0, "status": "active", "metadata_pending": false, "markets": [ { "instrument_id": 19127212, "exchange_id": 1, "event_slug": "btc-updown-5m-1787567400", "slug": "btc-up-or-down-5m", "question": "Bitcoin Up or Down?", "title": "Bitcoin Up or Down?", "description": "", "image": "", "icon": "", "outcomes": [ "Up", "Down" ], "resolution_mechanism": "", "resolution_source": "", "question_id": "", "group_item_title": "", "group_item_threshold": "", "start_date": 0, "event_start_time": 1787567400000, "end_date": 1787567700000, "template_id": "", "keyword_values": [ [ null ] ], "instrument": { "assets": [ null ], "sz_decimals": 0, "fee_rate": "", "exponent": "", "maker_rebate_rate": "", "price_tick": "0", "max_limit_order_size": 0, "max_market_order_size": 0, "min_limit_order_size": "", "min_market_order_size": "", "min_limit_order_notional": "", "min_market_order_notional": "" }, "settlement": { "resolution_status": "prepared", "settlement_fraction": "", "settlement_details": "", "resolved_at": 0, "winning_outcome": "" }, "resolution_status": "prepared", "metadata_pending": false, "trading": { "accepting_orders": null, "closed": null }, "pricing": { "best_bid": null, "best_ask": null, "last_trade_price": null, "spread": null }, "volume": "12500.25", "open_interest": "8000", "liquidity": "", "open_price": "0" } ], "volume": "12500.25", "open_interest": "8000", "liquidity": "" } ], "next_cursor": "", "has_more": false } } ``` --- # `outcomeNavTags` Returns the whole Outcome navigation tree in one call: top-level categories, their tag groups, and optional sub-tags. Each node says how to filter [`outcomeMeta`](/api/info/outcomeMeta.md) when a user picks it. The request takes no filters. `POST /info` · type `outcomeNavTags` Build the Outcome category bar and tag menu; each node carries the `category` / `tag` to pass to `outcomeMeta`. - Access: Public - Markets: Outcome only - Not this? [`outcomeMeta`](/api/info/outcomeMeta.md) — when you need the events and markets under a node ### Request body - `type` (string, required) — Must be `outcomeNavTags`. Values: `"outcomeNavTags"`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"outcomeNavTags"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (OutcomeNavTagsResponse) - `categories` (OutcomeNavCategory[]) — In display order. - `children` (OutcomeNavGroup[]) - `label` (string) — Display text. Render as-is. - `queryTags` (string[]) — Send as the `outcomeMeta` `tag` when this node is picked. - `slug` (string) — Node id. Case-sensitive. - `category` (string) — Taxonomy branch of the tag; send as the `outcomeMeta` `category`. It can differ from the top-level category the node is listed under. - `children` (OutcomeNavLeaf[]) — Third level. Empty when the group has no sub-tags. - `label` (string) — Display text. Render as-is. - `queryTags` (string[]) — Parent slug then own slug. Send comma-joined as the `outcomeMeta` `tag`. - `slug` (string) — Node id. Case-sensitive. - `category` (string) — Send as the `outcomeMeta` `category` when this node is picked. - `count` (integer) — Markets under this node. - `count` (integer) — Markets under this node. - `position` (string, nullable) — Side-menu group: `leftDown` renders below a divider. `null` or absent means `leftUp`. Values: `"leftUp"`, `"leftDown"`, `null`. - `label` (string) — Display text. Render as-is. - `slug` (string) — Top-level category; send as the `outcomeMeta` `category`. - `category` (string) - `count` (integer) — Markets under this category. - `queryTags` (string[]) — Present only on a top-level entry that is itself a tag (for example `iran`); send it as the `outcomeMeta` `tag`. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "categories": [ { "slug": "sports", "label": "Sports", "category": "sports", "count": 153, "children": [ { "slug": "soccer", "label": "Football", "queryTags": [ "soccer" ], "category": "sports", "position": null, "count": 153, "children": [ { "slug": "EPL", "label": "EPL", "queryTags": [ "soccer", "EPL" ], "category": "sports", "count": 26 } ] } ] } ] } } ``` ## Filtering `outcomeMeta` by a node | Level | Example | `outcomeMeta` filter | |---|---|---| | Category | `sports` | `category: "sports"` | | Tag group | `soccer` | `category: "sports"`, `tag: "soccer"` | | Sub-tag | `EPL` | `category: "sports"`, `tag: "soccer,EPL"` | - Send the node's `queryTags` comma-joined as `tag`. Multiple tags are AND-filtered. - Send the node's own `category`, not the slug of the entry it is listed under. They can differ: the top-level `iran` entry is a tag (it carries `queryTags: ["iran"]`) whose `category` is `geopolitics`. - `slug` and `queryTags` are case-sensitive (`EPL`, `5M`). Render `label` as-is. ```json { "type": "outcomeMeta", "exchangeId": 1, "category": "sports", "tag": "soccer,EPL", "limit": 20 } ``` The tree changes rarely. Fetch it once per session and cache it. --- # `outcomeAssetCtxs` Returns 24-hour volume and price statistics for Outcome instruments. Omit `instrumentIds` for a full-market scan, or pass at most 2,000 ids for a targeted query. Unknown and non-Outcome ids are ignored. Explicit ids with no trades are returned with empty price and volume values. `exchangeId` is reserved for scoped queries and is currently accepted only when `instrumentIds` is non-empty. `POST /info` · type `outcomeAssetCtxs` Fill 24h volume / price-change columns in an Outcome market grid. - Access: Public - Markets: Outcome only ### Request body - `type` (string, required) — Must be `outcomeAssetCtxs`. Values: `"outcomeAssetCtxs"`. - `exchangeId` (integer · uint32, nullable, optional) - `instrumentIds` (integer · uint64[], optional) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"outcomeAssetCtxs","instrumentIds":[19127212]}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `assetCtxs` (OutcomeAssetCtx[]) - `coin` (string) — Instrument id as a string. - `coinVlm` (string) - `dayHighPx` (string) - `dayLowPx` (string) - `dayNtlVlm` (string) - `instrumentId` (integer · uint64) - `lastPrice` (string) - `prevDayPx` (string) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "assetCtxs": [ { "coin": "19127212", "instrumentId": 19127212, "dayNtlVlm": "12500.25", "coinVlm": "18000", "prevDayPx": "0.48", "lastPrice": "0.55", "dayHighPx": "0.61", "dayLowPx": "0.43" } ] } } ``` --- # `outcomeSeriesTimeline` Returns the previous, past, current, upcoming, and overflow windows around an event in the same recurring Outcome series. For open markets, `marketOutcomePrices` is enriched from the live order book. If live-book enrichment is unavailable, the request still succeeds and that field remains `null`. Settled windows retain their indexed settlement prices. `POST /info` · type `outcomeSeriesTimeline` Render the past / current / upcoming window strip of a recurring crypto Outcome series. - Access: Public - Markets: Outcome only ### Request body - `eventSlug` (string, required) - `exchangeId` (integer · uint32, required) - `type` (string, required) — Must be `outcomeSeriesTimeline`. Values: `"outcomeSeriesTimeline"`. - `futureHours` (integer, nullable, optional) - `futurePreviewLimit` (integer, nullable, optional) - `now` (integer · int64, nullable, optional) - `pastHours` (integer, nullable, optional) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"outcomeSeriesTimeline","exchangeId":1,"eventSlug":"btc-updown-5m-1787567400","pastHours":24,"futureHours":4,"futurePreviewLimit":3}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (OutcomeSeriesTimelineResponse) - `anchorEventSlug` (string) - `counts` (object) - `current` (OutcomeSeriesTimelineItem) - `selected` (boolean) - `slug` (string) - `status` (string) - `title` (string) - `endDate` (integer · int64, nullable) - `id` (integer, nullable) - `instrumentId` (integer, nullable) - `marketOutcomePrices` (array, nullable) — Live YES/NO prices or indexed settlement prices. - `marketOutcomes` (array, nullable) - `marketQuestion` (string, nullable) - `marketSlug` (string, nullable) - `startTime` (integer · int64, nullable) - `winningOutcome` (string, nullable) - `more` (OutcomeSeriesTimelineItem[]) - `selected` (boolean) - `slug` (string) - `status` (string) - `title` (string) - `endDate` (integer · int64, nullable) - `id` (integer, nullable) - `instrumentId` (integer, nullable) - `marketOutcomePrices` (array, nullable) — Live YES/NO prices or indexed settlement prices. - `marketOutcomes` (array, nullable) - `marketQuestion` (string, nullable) - `marketSlug` (string, nullable) - `startTime` (integer · int64, nullable) - `winningOutcome` (string, nullable) - `past` (OutcomeSeriesTimelineItem[]) - `selected` (boolean) - `slug` (string) - `status` (string) - `title` (string) - `endDate` (integer · int64, nullable) - `id` (integer, nullable) - `instrumentId` (integer, nullable) - `marketOutcomePrices` (array, nullable) — Live YES/NO prices or indexed settlement prices. - `marketOutcomes` (array, nullable) - `marketQuestion` (string, nullable) - `marketSlug` (string, nullable) - `startTime` (integer · int64, nullable) - `winningOutcome` (string, nullable) - `seriesSlug` (string) - `upcoming` (OutcomeSeriesTimelineItem[]) - `selected` (boolean) - `slug` (string) - `status` (string) - `title` (string) - `endDate` (integer · int64, nullable) - `id` (integer, nullable) - `instrumentId` (integer, nullable) - `marketOutcomePrices` (array, nullable) — Live YES/NO prices or indexed settlement prices. - `marketOutcomes` (array, nullable) - `marketQuestion` (string, nullable) - `marketSlug` (string, nullable) - `startTime` (integer · int64, nullable) - `winningOutcome` (string, nullable) - `window` (object) - `previous` (oneOf) - One of · variant 1: - `selected` (boolean) - `slug` (string) - `status` (string) - `title` (string) - `endDate` (integer · int64, nullable) - `id` (integer, nullable) - `instrumentId` (integer, nullable) - `marketOutcomePrices` (array, nullable) — Live YES/NO prices or indexed settlement prices. - `marketOutcomes` (array, nullable) - `marketQuestion` (string, nullable) - `marketSlug` (string, nullable) - `startTime` (integer · int64, nullable) - `winningOutcome` (string, nullable) - One of · variant 2: - null - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "anchorEventSlug": "", "seriesSlug": "", "window": {}, "previous": { "id": 0, "slug": "", "title": "", "startTime": 1700000000000, "endDate": 0, "status": "", "selected": false, "instrumentId": 9001, "marketSlug": "", "marketQuestion": "", "marketOutcomes": [ "" ], "marketOutcomePrices": [ "0" ], "winningOutcome": "" }, "past": [ { "id": 0, "slug": "", "title": "", "startTime": 1700000000000, "endDate": 0, "status": "", "selected": false, "instrumentId": 9001, "marketSlug": "", "marketQuestion": "", "marketOutcomes": [ "" ], "marketOutcomePrices": [ "0" ], "winningOutcome": "" } ], "current": { "id": 0, "slug": "", "title": "", "startTime": 1700000000000, "endDate": 0, "status": "", "selected": false, "instrumentId": 9001, "marketSlug": "", "marketQuestion": "", "marketOutcomes": [ "" ], "marketOutcomePrices": [ "0" ], "winningOutcome": "" }, "upcoming": [ { "id": 0, "slug": "", "title": "", "startTime": 1700000000000, "endDate": 0, "status": "", "selected": false, "instrumentId": 9001, "marketSlug": "", "marketQuestion": "", "marketOutcomes": [ "" ], "marketOutcomePrices": [ "0" ], "winningOutcome": "" } ], "more": [ { "id": 0, "slug": "", "title": "", "startTime": 1700000000000, "endDate": 0, "status": "", "selected": false, "instrumentId": 9001, "marketSlug": "", "marketQuestion": "", "marketOutcomes": [ "" ], "marketOutcomePrices": [ "0" ], "winningOutcome": "" } ], "counts": {} } } ``` --- # `cryptoPrice` Returns the price-to-beat at the start of a crypto Outcome window and its settlement price after the window closes. Before the window starts, both prices are `null`. During the window, `openPrice` is present and `closePrice` remains `null`. After the window ends, both are present and `completed` is `true`. `eventStartTime` must be earlier than `eventEndTime`. Supported variants are `fivemin`, `fifteenmin`, and `onehour`. `POST /info` · type `cryptoPrice` Show the price-to-beat and settlement price of a crypto Outcome window. - Access: Public - Markets: Outcome only - Not this? [`cryptoPriceHistory`](/api/info/cryptoPriceHistory.md) — when you need the price line for a chart ### Request body - `eventEndTime` (integer · int64, required) - `eventStartTime` (integer · int64, required) - `symbol` (string, required) - `type` (string, required) — Must be `cryptoPrice`. Values: `"cryptoPrice"`. - `variant` (string, required) Values: `"fivemin"`, `"fifteenmin"`, `"onehour"`. - `topic` (string, nullable, optional) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"cryptoPrice","symbol":"btc","eventStartTime":1787567400000,"eventEndTime":1787567700000,"variant":"fivemin"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (CryptoPriceResponse) - `cached` (boolean) - `closePrice` (string, nullable) — Null until the window has completed. - `completed` (boolean) - `incomplete` (boolean) - `openPrice` (string, nullable) - `timestamp` (integer · int64) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "openPrice": "65120.5", "closePrice": "65234.12", "timestamp": 1787567700000, "completed": true, "incomplete": false, "cached": false } } ``` --- # `cryptoPriceHistory` Returns ascending timestamp/value samples used to seed crypto Outcome charts. Timestamps are Unix milliseconds and prices are decimal strings. `to` is required. `from` may be supplied to narrow the range; otherwise the server derives the lookback from `variant`. Supported variants are `fivemin`, `fifteenmin`, and `onehour`. `POST /info` · type `cryptoPriceHistory` Seed the underlying-asset price chart on a crypto Outcome market. - Access: Public - Markets: Outcome only - Not this? [`outcomeProbability`](/api/info/outcomeProbability.md) — when you want the YES-probability line instead ### Request body - `symbol` (string, required) - `to` (integer · int64, required) - `type` (string, required) — Must be `cryptoPriceHistory`. Values: `"cryptoPriceHistory"`. - `variant` (string, required) Values: `"fivemin"`, `"fifteenmin"`, `"onehour"`. - `from` (integer · int64, nullable, optional) - `topic` (string, nullable, optional) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"cryptoPriceHistory","symbol":"btc","to":1787567718116,"variant":"fivemin"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (CryptoPriceHistoryPoint[]) - `timestamp` (integer · int64) - `value` (string) — USD price as a decimal string. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": [ { "timestamp": 1787567718116, "value": "65234.12" } ] } ``` --- # `outcomeProbability` Returns the historical YES probability for one Outcome instrument. Use it to backfill a probability chart, then append live closes from the [`candle`](/api/websocket/candle.md) channel. `range` selects both the lookback window and the server-side sampling cadence. Valid values are `1H`, `6H`, `1D`, `1W`, `1M`, and `ALL`. Each `t` is Unix seconds and each `p` is a probability in `[0, 1]`. This query is served by the API host and may briefly lag the latest trade. `POST /info` · type `outcomeProbability` Backfill a YES-probability chart; append live closes from `candle`. - Access: Public - Markets: Outcome only - Live stream: [`candle`](/api/websocket/candle.md) - Served from an index — can briefly lag the latest block. ### Request body - `instrumentId` (integer · uint64, required) - `range` (string, required) Values: `"1H"`, `"6H"`, `"1D"`, `"1W"`, `"1M"`, `"ALL"`. - `type` (string, required) — Must be `outcomeProbability`. Values: `"outcomeProbability"`. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"outcomeProbability","instrumentId":19127212,"range":"1D"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (OutcomeProbabilityResponse) - `history` (OutcomeProbabilityPoint[]) - `p` (number · double) — YES probability in the inclusive range 0–1. - `t` (integer · int64) — Sample time as Unix seconds. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "history": [ { "t": 1787567400, "p": 0.55 } ] } } ``` --- # `outcomeMetaByInstrument` On-chain trading parameters for one Outcome instrument: name, assets, fee curve, price tick, resolution status, and channel. Fields are `camelCase`. For event titles, slugs, and live prices, use [`outcomeMeta`](/api/info/outcomeMeta.md). `POST /info` · type `outcomeMetaByInstrument` Read on-chain trading params (tick, fee curve, resolution status) for one Outcome instrument. - Access: Public - Markets: Outcome only - Not this? [`outcomeMeta`](/api/info/outcomeMeta.md) — when you need event titles, slugs, or a list of markets ### Request body - `instrumentId` (integer · int64, required) — Outcome instrument id. - `type` (string, required) ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"outcomeMetaByInstrument","instrumentId":9001}' ``` ### Response · OutcomeInstrument (200) - `code` (integer) — `0` on success. - `data` (OutcomeInstrument) - `exponent` (string) — Fee-curve exponent. Wire value is a decimal string (for example `"1"`). - `feeRate` (string) — Outcome fee-curve base rate. - `index` (integer · int64) — Instrument external id (same as `instrumentId`). - `makerRebateRate` (string) - `name` (string) - `priceTick` (string) - `szDecimals` (integer · int64) - `tokens` (integer · int64[]) — Base and quote asset ids. - `exchangeId` (integer · int32, nullable) — Channel id. `0` or omitted is the default platform channel. - `resolutionStatus` (string, nullable) — `Prepared` or `Resolved`. Defaults to `Prepared` when unset on the instrument. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "name": "", "szDecimals": 0, "tokens": [ 0 ], "index": 0, "priceTick": "0", "feeRate": "", "exponent": "", "makerRebateRate": "", "resolutionStatus": "" } } ``` --- # `outcomeL2Book` Order-book snapshots for one or more Outcome instruments, returned in request order — for example every market of one event. Each book is the YES book; derive NO prices as `1 − price`. For a single instrument, use [`l2Book`](/api/info/l2Book.md) with the instrument id as `coin`. `POST /info` · type `outcomeL2Book` Snapshot the books of several Outcome instruments at once (e.g. every market of an event). - Access: Public - Markets: Outcome only - Not this? [`l2Book`](/api/info/l2Book.md) — when you only need one instrument ### Request body - `type` (string, required) - `instrumentIds` (integer · int64[], optional) — Instrument id list. Empty list returns an empty array. Numeric strings are also accepted. Unknown, non-outcome, or failed ids are skipped. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"outcomeL2Book","instrumentIds":[9001,9002]}' ``` ### Response · OutcomeL2BookResponse (200) - `code` (integer) — `0` on success. - `data` (OutcomeL2BookResponse) - `coin` (string) — Instrument name. - `instrumentId` (integer · int64) — Instrument id, matching the request list. - `levels` (BookLevel[][]) — `levels[0]` is bids, `levels[1]` is asks. - `n` (integer · int64) - `px` (string) - `sz` (string) - `time` (integer · int64) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": [ { "instrumentId": 9001, "coin": "4", "time": 1700000000000, "levels": [ [ { "n": 0, "px": "0", "sz": "0" } ] ] } ] } ``` --- # `outcomePositions` A user's outcome positions. Parallel to [`clearinghouseState`](/api/info/clearinghouseState.md) with `accountType=outcome`, but the response is only the `assetPositions` array — it does not include margin summaries or `withdrawable`. Through the API gateway, each position is enriched with `title`, `slug`, `outcomeNames`, `priceDecimals`, `sizeDecimals`, and `resolutionStatus` from indexed Outcome metadata. These display fields are absent when calling the fullnode directly. `POST /info` · type `outcomePositions` List a user’s Outcome positions with event title and outcome names attached. - Access: By user address - Markets: Outcome only - Not this? [`clearinghouseState`](/api/info/clearinghouseState.md) — when you also need the margin summary ### Request body - `type` (string, required) - `user` (string, required) — User address. - `exchangeId` (integer · int32, nullable, optional) — Optional channel filter, same semantics as `clearinghouseState`. - `instrumentIds` (array, nullable, optional) — Optional instrument id list. Empty or omitted returns every outcome position for the user. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"outcomePositions","user":"0x0000000000000000000000000000000000000000","instrumentIds":[9001]}' ``` ### Response · OutcomePositionsResponse (200) - `code` (integer) — `0` on success. - `data` (OutcomePositionsResponse) — Outcome positions only. Unlike `clearinghouseState`, this response is the `assetPositions` array — it does not include margin summaries or `withdrawable`. - `position` (PositionData) - `asset` (string) - `coin` (string) - `cumFunding` (CumulativeFunding) - `allTime` (string) - `sinceChange` (string) - `sinceOpen` (string) - `leverage` (Leverage) - `type` (string) - `value` (integer · int32) - `rawUsd` (string, nullable) - `marginRatio` (string) — Margin ratio, precise to 4 decimal places. - `marginUsed` (string) - `maxLeverage` (integer · int32) - `positionValue` (string) - `returnOnEquity` (string) - `szi` (string) - `unrealizedPnl` (string) - `adlQuantile` (integer · int32, nullable) — ADL bucket (1-5). 5 = highest priority (auto-deleveraged first); 1 = safest. - `entryPx` (string, nullable) - `liquidationPx` (string, nullable) - `markPx` (string, nullable) - `updateTime` (integer · int64, nullable) — Timestamp of the most recent size change on this position, in milliseconds. Only refreshed on open / add / reduce / close; funding accrual, margin adjustments, and leverage changes do not touch it. `None` when the position has had no size change yet, or for early / unmigrated snapshots. Matches WebSocket `WsPosition.updateTime`. Wire name: `updateTime`. - `type` (string) - `tpsl` (array, nullable) - `createTime` (integer · int64) — Creation timestamp in milliseconds. - `quantity` (string) — Order quantity. `0` means close the entire position; non-zero means partial close. - `slOid` (string) — Stop-loss order id. - `slOrderPrice` (string) — Stop-loss limit price. - `slOrderType` (string) — Stop-loss order type, e.g. `"Limit"` or `"Market"`. - `slTriggerPrice` (string) — Stop-loss trigger price. - `tpOid` (string) — Take-profit order id. - `tpOrderPrice` (string) — Take-profit limit price. - `tpOrderType` (string) — Take-profit order type, e.g. `"Limit"` or `"Market"`. - `tpTriggerPrice` (string) — Take-profit trigger price. - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": [ { "position": { "adlQuantile": 0, "asset": "4", "coin": "4", "cumFunding": { "allTime": "", "sinceChange": "", "sinceOpen": "" }, "entryPx": "0", "leverage": { "rawUsd": "", "type": "", "value": 0 }, "liquidationPx": "0", "marginRatio": "", "marginUsed": "", "markPx": "0", "maxLeverage": 0, "positionValue": "", "returnOnEquity": "", "szi": "", "unrealizedPnl": "", "updateTime": 1700000000000 }, "tpsl": [ { "createTime": 1700000000000, "quantity": "0", "slOid": "", "slOrderPrice": "0", "slOrderType": "", "slTriggerPrice": "0", "tpOid": "", "tpOrderPrice": "0", "tpOrderType": "", "tpTriggerPrice": "0" } ], "type": "" } ] } ``` --- # `outcomeBalances` Token balances held in one user's Outcome sub-account — the Outcome counterpart of [`perpClearinghouseState`](/api/info/perpClearinghouseState.md). For account value, margin, and withdrawable balance, use [`clearinghouseState`](/api/info/clearinghouseState.md) with `accountType: "outcome"`. `POST /info` · type `outcomeBalances` Show token balances held in the Outcome sub-account. - Access: By user address - Markets: Outcome only ### Request body - `type` (string, required) - `user` (string, required) — User address. ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"outcomeBalances","user":"0x0000000000000000000000000000000000000000"}' ``` ### Response · UserTokenBalanceResponse (200) - `code` (integer) — `0` on success. - `data` (UserTokenBalanceResponse) - `balances` (UserTokenBalance[]) - `coin` (string) - `displayName` (string) - `entryNtl` (string) - `hold` (string) - `token` (integer · int64) - `total` (string) - `lastPrice` (string, nullable) - `midPrice` (string, nullable) - `withdrawable` (string, nullable) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": { "balances": [ { "coin": "4", "displayName": "", "entryNtl": "", "hold": "", "lastPrice": "0", "midPrice": "0", "token": 0, "total": "" } ], "withdrawable": "" } } ``` --- # `outcomeFrontendOpenOrders` Open orders on outcome instruments for one user. The item shape matches [`frontendOpenOrders`](/api/info/frontendOpenOrders.md). Filter by `instrumentIds` and page with `direction` / `from` / `limit` (capped at 1000). Through the API gateway, each order is enriched with `title`, `slug`, `outcomeNames`, `priceDecimals`, `sizeDecimals`, and `resolutionStatus`. These display fields are absent when calling the fullnode directly. `POST /info` · type `outcomeFrontendOpenOrders` Render resting Outcome orders with event title, slug, and outcome names attached. - Access: By user address - Markets: Outcome only - Live stream: [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md) - Not this? [`frontendOpenOrders`](/api/info/frontendOpenOrders.md) — when you list perp orders ### Request body - `type` (string, required) - `user` (string, required) — User address. - `direction` (string, nullable, optional) — `prev` (default) returns newest-first (`oid` descending). `next` returns oldest-first. Values: `"prev"`, `"next"`. - `from` (integer · int64, nullable, optional) — Cursor oid from the previous page. `prev` returns oid < from; `next` returns oid > from. - `instrumentIds` (array, nullable, optional) — Optional instrument id list. Empty or omitted returns every outcome open order for the user. Numeric strings are also accepted. Unknown or non-outcome ids are skipped. - `limit` (integer · int32, nullable, optional) — Maximum number of orders returned. Omitted, `0`, or values above 1000 are capped at 1000. - `state` (oneOf, optional) — Optional order-state filter. Accepts a comma-separated string or an array of strings. Matching is case-insensitive against OrderState display values (`Pending`, `Open`, `PartialFilled`, `Filled`, `Canceled`, `PartialCanceled`, `Rejected`). Unknown values are skipped. - One of · variant 1: - string - One of · variant 2: - string[] - One of · variant 3: - null ### Request example ```bash curl -X POST https://rpc.txflow-testnet.xyz/info \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"type":"outcomeFrontendOpenOrders","user":"0x0000000000000000000000000000000000000000","instrumentIds":[9001],"direction":"prev","from":0,"state":"","limit":50}' ``` ### Response · OutcomeFrontendOpenOrdersResponse (200) - `code` (integer) — `0` on success. - `data` (OutcomeFrontendOpenOrdersResponse) - `asset` (integer · int64) - `coin` (string) — Instrument name. - `dir` (string) - `instrumentType` (string) - `isPositionTpsl` (boolean) - `isTrigger` (boolean) - `limitPx` (string) - `oid` (integer · int64) - `orderType` (string) - `origSz` (string) - `reduceOnly` (boolean) - `side` (string) — "B" = buy, "A" = sell. - `state` (string) — Order state, e.g. `Open`. - `sz` (string) - `tif` (string) - `timestamp` (integer · int64) - `triggerCondition` (string) - `triggerPx` (string) - `avgFillPx` (string, nullable) - `children` (array, nullable) - `cloid` (string, nullable) - `expiresAt` (integer · int64, nullable) — GTD order expiry timestamp in milliseconds. `None` for non-GTD or unset orders; skipped on serialization. Wire name: `expiresAt`. - `fee` (string, nullable) - `filledAmount` (string, nullable) - `leverage` (integer · int32, nullable) - `marginMode` (string, nullable) - `positionMode` (string, nullable) - `triggerType` (string, nullable) - `success` (boolean) — `false` on error, with `code` set and the text in `message` instead of `data`. ### Response example ```json { "success": true, "code": 0, "data": [ { "asset": 1, "cloid": "my-order-001", "coin": "BTC-USDC", "dir": "Buy", "filledAmount": "0", "instrumentType": "Perps", "isPositionTpsl": false, "isTrigger": false, "leverage": 10, "limitPx": "66539.0", "marginMode": "Cross", "oid": 211558907950, "orderType": "Limit", "origSz": "0.0010", "positionMode": "OneWay", "reduceOnly": false, "side": "B", "state": "Open", "sz": "0.0010", "tif": "GTC", "timestamp": 1791893561993, "triggerCondition": "", "triggerPx": "" } ] } ``` --- # WebSocket API (Beta) Three multiplexed endpoints, all JSON text frames. Open one connection to each you need and subscribe to any number of channels on it. `/ws` and `/api-ws` use camelCase fields. `/ws-rtds` relays an external price feed in its own format; see [Crypto prices](#crypto-prices-ws-rtds). | | Carries | Mainnet | Testnet | |---|---|---|---| | `/ws` | Node stream: books, trades, account state, chain data | `wss://rpc.txflow.com/ws` | `wss://rpc.txflow-testnet.xyz/ws` | | `/api-ws` | API-service stream: tickers, trade lists, candles, history snapshots, async Outcome failures | `wss://rpc.txflow.com/api-ws` | `wss://rpc.txflow-testnet.xyz/api-ws` | | `/ws-rtds` | Crypto reference prices for Outcome underlyings: spot, Chainlink, TWAP | `wss://rpc.txflow.com/ws-rtds` | `wss://rpc.txflow-testnet.xyz/ws-rtds` | ## Connection & control plane A connection carries `subscribe`, `unsubscribe`, and `ping` frames from the client. On `/ws`, put the channel's subscribe type at the top level as well as inside `subscription` — the node routes on the top-level `type` and silently drops a frame without it: ```json { "method": "subscribe", "type": "orderUpdates", "subscription": { "type": "orderUpdates", "user": "0x…" } } ``` `/api-ws` takes the `subscription` object alone. ### Keep-alive **Send a ping every 10 seconds** on every endpoint, starting when the socket opens. The gateway closes a `/ws` or `/api-ws` connection that has sent nothing for about 16 seconds (close code `1006`); `/ws-rtds` allows 60 seconds. Server pushes do not count as activity. | Endpoint | Client sends | Server replies | |---|---|---| | `/ws` | `{"method":"ping"}` | `{"method":"PONG"}` | | `/api-ws` | `{"method":"ping"}` | `{"channel":"pong"}` | | `/ws-rtds` | `PING` (plain text, not JSON) | `PONG` (plain text) | Drop the pong before dispatching messages by `channel`. Any client frame counts as activity, but use a dedicated timer so a quiet connection stays up. ```ts const ws = new WebSocket('wss://rpc.txflow-testnet.xyz/ws') let heartbeat: ReturnType | undefined ws.onopen = () => { ws.send(JSON.stringify({ method: 'subscribe', type: 'trades', subscription: { type: 'trades', coin: '1' } })) heartbeat = setInterval(() => ws.send(JSON.stringify({ method: 'ping' })), 10_000) } ws.onmessage = (event) => { const msg = JSON.parse(event.data) if (msg.method === 'PONG' || msg.channel === 'pong') return // heartbeat reply // handle msg.channel / msg.data } ws.onclose = () => clearInterval(heartbeat) // then reconnect and re-subscribe ``` WebSocket `wss://rpc.txflow-testnet.xyz/ws` · JSON text frames A single connection multiplexes every channel. On `/ws`, every subscribe and unsubscribe frame repeats the channel type at the top level (`"type"`) — the node dispatches on it and ignores a frame without it. The client sends `subscribe` / `unsubscribe` / `ping` frames; the server replies to `ping` with `pong`. On `/ws` there is no subscribe acknowledgement — invalid subscriptions are silently ignored. `/api-ws` acknowledges each subscribe with a `subscriptionResponse` frame and answers an invalid one with `{"channel":"error","message":…}`. ### Subscribe ```json { "method": "subscribe", "type": "l2Book", "subscription": { "type": "l2Book", "coin": "4" } } ``` ### Unsubscribe ```json { "method": "unsubscribe", "type": "l2Book", "subscription": { "type": "l2Book", "coin": "4" } } ``` ### Ping (every 10 s) ```json { "method": "ping" } ``` ### Client request envelope `method` is `subscribe`, `unsubscribe`, or `ping`; `subscription` is the channel's subscription object, documented on each channel page. ### Server pong - `method` (string) Values: `"PONG"`. ## Channels Grouped like the [Info API](/api/info.md#request-types). Each page lists the subscription parameters and the pushed payload. ### Shared Books, trades, and order updates take a `coin`. The explorer channels stream the chain itself and take no parameters. | Channel | Use it to | |---|---| | [`trades`](/api/websocket/trades.md) | Drive the public trade tape for one instrument. | | [`l2Book`](/api/websocket/l2Book.md) | Drive an order-book widget for one instrument; each push is a full snapshot. | | [`l2BookDepth`](/api/websocket/l2BookDepth.md) | Maintain a local fixed-depth book from one snapshot plus deltas (lower bandwidth, you apply updates). | | [`bbo`](/api/websocket/bbo.md) | Stream best bid / best ask for one instrument (spread display, quoting bots). | | [`allMids`](/api/websocket/allMids.md) | Stream the mid price of every instrument — the lightest way to keep a ticker live. | | [`orderUpdates`](/api/websocket/orderUpdates.md) | Track a user’s orders live: placed, filled, cancelled, rejected. | | [`block`](/api/websocket/block.md) | Feed an explorer’s latest-blocks list. | | [`tx`](/api/websocket/tx.md) | Feed an explorer’s latest-transactions list. | | [`newBlock`](/api/websocket/newBlock.md) | Tick the current block height once per second. | ### Perpetuals Mark, oracle and funding context, position and margin state, and the TWAP streams. | Channel | Use it to | |---|---| | [`activeAssetCtx`](/api/websocket/activeAssetCtx.md) | Keep one market header live: mark, oracle, funding, OI (Outcome: price, volume, OI). | | [`allAssetCtxs`](/api/websocket/allAssetCtxs.md) | Keep a perp markets table live — every instrument’s context in one subscription. | | [`activeAssetData`](/api/websocket/activeAssetData.md) | Keep the order ticket’s leverage / max size / available margin live for one instrument. | | [`clearinghouseState`](/api/websocket/clearinghouseState.md) | Keep the Positions panel and margin summary live. | | [`position`](/api/websocket/position.md) | React to individual position changes (opened, resized, closed). | | [`webData2`](/api/websocket/webData2.md) | One-subscription account view: positions + margin + open orders (Hyperliquid-compatible). | | [`userFundings`](/api/websocket/userFundings.md) | Append funding settlements to Funding History as they happen. | | [`twapStates`](/api/websocket/twapStates.md) | Show progress of a user’s running TWAP orders. | | [`userTwapHistory`](/api/websocket/userTwapHistory.md) | Append TWAPs to the history list as they finish or are cancelled. | | [`userTwapSliceFills`](/api/websocket/userTwapSliceFills.md) | Stream TWAP child fills as they execute. | | [`vaultUpdates`](/api/websocket/vaultUpdates.md) | Confirm vault deposits and withdrawals as they land, and refresh vault equity. | ### Outcome Dedicated batch-BBO, trade, and order-lifecycle channels, plus the shared single-market streams with the numeric `instrument_id` as `coin`. `outcomeTrades` follows several instruments in one subscription; `trades` follows one market. For Outcome balances, poll [`outcomeBalances`](/api/info/outcomeBalances.md) or [`clearinghouseState`](/api/info/clearinghouseState.md) with `accountType: "outcome"`. | Channel | Use it to | |---|---| | [`outcomeBbos`](/api/websocket/outcomeBbos.md) | Keep prices live in an Outcome discovery grid or watchlist (up to 50 instruments per subscription). | | [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md) | Track the matching lifecycle of a user’s Outcome orders: filled, rejected, cancelled. | | [`outcomeTrades`](/api/websocket/outcomeTrades.md) | One public tape across several Outcome instruments (every leg of an event). | | [`l2Book`](/api/websocket/l2Book.md) | Drive an order-book widget for one instrument; each push is a full snapshot. | | [`trades`](/api/websocket/trades.md) | Drive the public trade tape for one instrument. | | [`activeAssetCtx`](/api/websocket/activeAssetCtx.md) | Keep one market header live: mark, oracle, funding, OI (Outcome: price, volume, OI). | ### API service Subscribe on `/api-ws`, not `/ws`. | Channel | Use it to | |---|---| | [`ticker`](/api/websocket/ticker.md) | Show last price, 24-hour change, high/low, and volume for one instrument. | | [`trades`](/api/websocket/apiTrades.md) | Show the latest 300 trades for one instrument from `/api-ws`, refreshed every few seconds. | | [`candle`](/api/websocket/candle.md) | Keep the last bar of a chart live after seeding it with `candleSnapshot`. | | [`multiCandle`](/api/websocket/multiCandle.md) | Keep the last bar of up to 8 coins live on one subscription, e.g. a markets list with sparklines. | | [`outcomeOrderUpdate`](/api/websocket/outcomeOrderUpdate.md) | Catch async Outcome order submissions that fail (by `traceId`). Only failures are pushed; not a fill signal. | | [`userHistoricalOrders`](/api/websocket/userHistoricalOrders.md) | Load a user’s order history over `/api-ws` — one snapshot, no updates. | | [`userFills`](/api/websocket/userFills.md) | Load a user’s fill history over `/api-ws` — one snapshot, no updates. | | [`webData2`](/api/websocket/apiWebData2.md) | Load a user’s open orders over `/api-ws` — one snapshot, no updates. | `userHistoricalOrders`, `userFills`, and `webData2` on `/api-ws` send one snapshot on subscribe and nothing after. ## Crypto prices (`/ws-rtds`) Reference prices for crypto Outcome underlyings, relayed from Polymarket's real-time data service. Frames use snake_case fields and have no `channel` envelope. ```json { "action": "subscribe", "subscriptions": [ { "topic": "crypto_prices", "type": "update", "filters": "btcusdt,ethusdt" }, { "topic": "crypto_prices_chainlink", "type": "update", "filters": "{\"symbol\":\"btc/usd\"}" } ] } ``` `unsubscribe` takes the same shape. | Topic | Price | `filters` | Symbols | |---|---|---|---| | `crypto_prices` | Binance spot | Comma-separated: `"btcusdt,ethusdt"` | `btc`, `eth`, `sol`, `xrp`, `doge`, `bnb`, `hype` + `usdt` | | `crypto_prices_chainlink` | Chainlink | JSON: `"{\"symbol\":\"btc/usd\"}"` | The above plus `zec`, as `/usd` | | `crypto_prices_twap_thirty` | 30s TWAP of Chainlink | Same as Chainlink | Same as Chainlink | | `crypto_prices_twap_sixty` | 60s TWAP of Chainlink | Same as Chainlink | Same as Chainlink | Each symbol pushes about once a second: ```json { "topic": "crypto_prices_chainlink", "type": "update", "timestamp": 1792047647886, "payload": { "symbol": "btc/usd", "timestamp": 1792047647000, "value": 80856.15721525438, "full_accuracy_value": "80856157215254380000000" } } ``` Limits: 20 connections per IP, 128 symbol subscriptions per connection, 64 subscriptions per frame. --- # `trades` Public real-time trade stream. Outcome markets reuse this channel: pass the numeric `instrument_id` as `coin`. This is the public tape for one market. The node also supports the Outcome-specific `outcomeTrades` batch/user subscription, but the current public web client uses this single-instrument stream. WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `trades` · type `trades` Drive the public trade tape for one instrument. - Access: Public - Not this? [`outcomeTrades`](/api/websocket/outcomeTrades.md) — when one tape must span several Outcome markets - Not this? [`trades`](/api/websocket/apiTrades.md) — when you want the latest 300 trades re-sent as a list from `/api-ws` ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "trades", "subscription": { "type": "trades", "coin": "4" } } ``` ### Subscription parameters - `coin` (string, required) - `type` (string, required) Values: `"trades"`. - `user` (string, nullable, optional) — Optional filter: only trades where this address is taker or maker are pushed. Omit or "" for all trades. Use the account address, not an agent's. ### Server message - `channel` (string) Values: `"trades"`. - `data` (WsTrade[]) - `coin` (string) — Trading pair symbol. - `hash` (string) — Transaction hash, hex, 0x-prefixed. - `px` (DecimalString) — Trade price. - `side` (string) — Taker side: "B" = buy, "A" = sell. Values: `"A"`, `"B"`. - `symbol` (string) — Trading pair symbol (duplicate of coin, kept for backward compatibility). - `sz` (DecimalString) — Trade size. - `tid` (integer) — Trade id. - `time` (integer) — Trade timestamp, milliseconds. - `users` (string[]) — [takerAddress, makerAddress]. - `instrumentId` (integer, nullable) — Instrument's external id. Omitted on legacy payloads. - `makerClosedPnl` (DecimalString) — Realized PnL closed by the maker in this trade. - `makerFee` (DecimalString) — Fee charged to the maker. - `makerReduceOnly` (boolean) — Whether the maker's order was reduce-only. - `takerClosedPnl` (DecimalString) — Realized PnL closed by the taker in this trade. - `takerFee` (DecimalString) — Fee charged to the taker. - `takerReduceOnly` (boolean) — Whether the taker's order was reduce-only. ### Message example ```json { "channel": "trades", "data": [ { "coin": "4", "symbol": "", "instrumentId": 9001, "side": "B", "px": "0", "sz": "0", "time": 1700000000000, "hash": "", "tid": 0, "users": [ "" ], "takerFee": "", "makerFee": "", "takerClosedPnl": "", "makerClosedPnl": "", "takerReduceOnly": false, "makerReduceOnly": false } ] } ``` --- # `l2Book` Public order-book snapshot, pushed on a fixed server-side tick. For an Outcome market, set `coin` to its numeric `instrument_id`. The book is the YES book; derive displayed NO prices by taking `1 - price` and reversing the side. Set `tick` to the market's `instrument.price_tick` when a specific aggregation is required. WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `l2Book` · type `l2Book` Drive an order-book widget for one instrument; each push is a full snapshot. - Access: Public - Snapshot: [`l2Book`](/api/info/l2Book.md) - Not this? [`l2BookDepth`](/api/websocket/l2BookDepth.md) — when you want snapshot-then-deltas at a fixed depth - Not this? [`bbo`](/api/websocket/bbo.md) — when top of book is enough - Not this? [`outcomeBbos`](/api/websocket/outcomeBbos.md) — when you need top of book for many Outcome markets ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "l2Book", "subscription": { "type": "l2Book", "coin": "4", "nSigFigs": 5, "mantissa": 1, "tick": "" } } ``` ### Subscription parameters - `coin` (string, required) - `type` (string, required) Values: `"l2Book"`. - `mantissa` (integer, nullable, optional) - `nSigFigs` (integer, nullable, optional) — Optional significant-figure aggregation. - `tick` (string, nullable, optional) — Optional explicit price tick. ### Server message - `channel` (string) Values: `"l2Book"`. - `data` (WsL2BookData) - `coin` (string) — Trading pair symbol. - `id` (integer, nullable) — Instrument id. - `levels` (WsBookLevel[][]) — [bidLevels, askLevels]. Each is a list of WsBookLevel ordered from best to worst price. - `n` (integer) — Number of resting orders at this level. - `px` (DecimalString) — Price at this level. - `sz` (DecimalString) — Aggregate size resting at this level. - `time` (integer) — Snapshot timestamp, milliseconds. - `tick` (DecimalString, nullable) — Effective price tick used for aggregation. Omitted when the default tick is used. ### Message example ```json { "channel": "l2Book", "data": { "coin": "4", "time": 1700000000000, "levels": [ [ { "px": "0", "sz": "0", "n": 0 } ] ], "tick": "", "id": 0 } } ``` --- # `l2BookDepth` WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `l2BookDepth` · type `l2BookDepth` Maintain a local fixed-depth book from one snapshot plus deltas (lower bandwidth, you apply updates). - Access: Public - Not this? [`l2Book`](/api/websocket/l2Book.md) — when you prefer full snapshots and no delta bookkeeping ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "l2BookDepth", "subscription": { "type": "l2BookDepth", "coin": "4" } } ``` ### Subscription parameters - `coin` (string, required) — Depth and price granularity are fixed server-side. - `type` (string, required) Values: `"l2BookDepth"`. ### Server message - `channel` (string) Values: `"l2BookDepth"`. - `data` (WsL2BookDepthData) — Fixed-depth order book frame, snapshot or delta. prevSeq is always present: null for snapshot frames, the previous frame's seq for delta frames. levels[0] = bids, levels[1] = asks, following the same shape as WsL2BookData.levels. Bids are strictly descending, asks strictly ascending. For Snapshot frames, levels contain the full top-N book (levels with sz == "0" are never included). For Delta frames, levels contain only the price levels that changed this period; sz == "0" means that price level was removed. - `coin` (string) — Trading pair symbol. - `id` (integer) — Instrument id. - `levels` (WsBookLevel[][]) - `n` (integer) — Number of resting orders at this level. - `px` (DecimalString) — Price at this level. - `sz` (DecimalString) — Aggregate size resting at this level. - `prevSeq` (integer, nullable) — Sequence number of the previous frame. null on snapshot frames. - `seq` (integer) — Monotonically increasing sequence number of this frame. - `time` (integer) — Frame timestamp, milliseconds. - `type` (string) — Frame type. Values: `"snapshot"`, `"delta"`. ### Message example ```json { "channel": "l2BookDepth", "data": { "coin": "4", "id": 0, "time": 1700000000000, "seq": 0, "prevSeq": 0, "type": "snapshot", "levels": [ [ { "px": "0", "sz": "0", "n": 0 } ] ] } } ``` --- # `bbo` WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `bbo` · type `bbo` Stream best bid / best ask for one instrument (spread display, quoting bots). - Access: Public - Not this? [`l2Book`](/api/websocket/l2Book.md) — when you need depth beyond the top level ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "bbo", "subscription": { "type": "bbo", "coin": "4" } } ``` ### Subscription parameters - `coin` (string, required) - `type` (string, required) Values: `"bbo"`. ### Server message - `channel` (string) Values: `"bbo"`. - `data` (WsBbo) - `bbo` (object[]) — [bestBid, bestAsk]. Either side is null when there is no resting liquidity on that side. - `coin` (string) - `time` (integer) — Timestamp, milliseconds. - `impactBuyPrice` (DecimalString, nullable) — Decimal number encoded as a string to avoid floating-point precision loss. - `impactSellPrice` (DecimalString, nullable) ### Message example ```json { "channel": "bbo", "data": { "coin": "4", "time": 1700000000000, "bbo": [ null ], "impactBuyPrice": "0", "impactSellPrice": "0" } } ``` --- # `allMids` Public mid price for every coin (Hyperliquid-compatible). WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `allMids` · type `allMids` Stream the mid price of every instrument — the lightest way to keep a ticker live. - Access: Public - Markets: Perp only - Not this? [`allAssetCtxs`](/api/websocket/allAssetCtxs.md) — when you also need funding, OI, or mark ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "allMids", "subscription": { "type": "allMids" } } ``` ### Subscription parameters - `type` (string, required) Values: `"allMids"`. - `exchangeId` (ExchangeScope, optional) — Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id). - One of · variant 1: - null - One of · variant 2: - integer - One of · variant 3: - string ### Server message - `channel` (string) Values: `"allMids"`. - `data` (WsAllMids) - `mids` (object) — Map of coin symbol (e.g. "BTC") to mid price string (midpoint of best bid/ask). ### Message example ```json { "channel": "allMids", "data": { "mids": {} } } ``` --- # `orderUpdates` WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `orderUpdates` · type `orderUpdates` Track a user’s orders live: placed, filled, cancelled, rejected. - Access: By user address - Snapshot: [`frontendOpenOrders`](/api/info/frontendOpenOrders.md) - Not this? [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md) — when the orders are on Outcome markets ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "orderUpdates", "subscription": { "type": "orderUpdates", "user": "0x0000000000000000000000000000000000000000", "coin": "4" } } ``` ### Subscription parameters - `type` (string, required) Values: `"orderUpdates"`. - `user` (EthAddress, required) — User whose orders to track. Use the account address, not an agent's. - `coin` (string, optional) — Optional trading-pair filter. Omitted or "" means all coins. - `exchangeId` (ExchangeScope, optional) — Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id). - One of · variant 1: - null - One of · variant 2: - integer - One of · variant 3: - string ### Server message - `channel` (string) Values: `"orderUpdates"`. - `data` (WsOrderUpdate[]) - `order` (WsBasicOrder) - `asset` (integer) — Instrument id. - `avgFillPrice` (DecimalString, nullable) — Average fill price across all fills of this order. - `coin` (string) — Instrument name, e.g. `BTC-USDC`. - `isTrigger` (boolean) — Whether this is a trigger (conditional) order. - `limitPx` (DecimalString) — Limit price. - `oid` (integer) — Order id. - `orderType` (string) — Order type: `"Limit"` or `"Market"`. - `origSz` (DecimalString) — Original order size. - `side` (string) — "B" = buy, "A" = sell. Values: `"A"`, `"B"`. - `sz` (DecimalString) — Remaining size. - `timestamp` (integer) — Order creation time, milliseconds. - `cloid` (string, nullable) — Client order id. Omitted when not set. - `fee` (DecimalString, nullable) — Fee charged so far. Omitted when not applicable. - `outcome` (string, nullable) — Only meaningful for outcome-market instruments, derived from (side, reduceOnly): (Buy && !reduceOnly) or (Sell && reduceOnly) -> "Yes"; (Buy && reduceOnly) or (Sell && !reduceOnly) -> "No". Omitted for perps orders. Values: `"Yes"`, `"No"`. - `pnl` (DecimalString, nullable) — Realized PnL so far. Omitted when not applicable. - `reduceOnly` (boolean, nullable) — Whether the order is reduce-only. Present for perps and outcome orders. - `triggerPx` (DecimalString, nullable) — Trigger price. Omitted for non-trigger orders. - `status` (string) — Order lifecycle status. Values: `"Pending"`, `"Open"`, `"PartialFilled"`, `"Filled"`, `"PartialCanceled"`, `"Canceled"`, `"Rejected"`. - `statusTimestamp` (integer) — Timestamp of this status change, milliseconds. ### Message example ```json { "channel": "orderUpdates", "data": [ { "order": { "asset": 1, "coin": "BTC-USDC", "side": "B", "orderType": "Limit", "limitPx": "83000.0", "sz": "0.001", "oid": 123456, "timestamp": 1700000000000, "origSz": "0.001", "avgFillPrice": null, "reduceOnly": false, "isTrigger": false }, "status": "Pending", "statusTimestamp": 1700000000000 } ] } ``` --- # `block` WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `block` · type `explorerBlock` Feed an explorer’s latest-blocks list. - Access: Public - Not this? [`newBlock`](/api/websocket/newBlock.md) — when you only need the height ### Subscribe · parameter-less action Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "explorerBlock" } ``` ### Subscription parameters No parameters. Subscribe with the top-level `type` / `action` shown above — there is no `subscription` object. ### Server message - `blockTime` (integer) — Milliseconds. - `channel` (string) Values: `"block"`. - `hash` (string) - `height` (integer) - `numTxs` (integer) - `proposer` (string) ### Message example ```json { "channel": "block", "height": 0, "hash": "", "blockTime": 1700000000000, "proposer": "", "numTxs": 0 } ``` --- # `tx` WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `tx` · type `explorerTxs` Feed an explorer’s latest-transactions list. - Access: Public ### Subscribe · parameter-less action Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "explorerTxs" } ``` ### Subscription parameters No parameters. Subscribe with the top-level `type` / `action` shown above — there is no `subscription` object. ### Server message - `channel` (string) Values: `"tx"`. - `data` (Transaction[]) - `block` (integer) — Block height containing this transaction. - `hash` (string) - `name` (string) — Transaction/action type name. - `sender` (string) - `timestamp` (integer) — Milliseconds. ### Message example ```json { "channel": "tx", "data": [ { "hash": "", "timestamp": 1700000000000, "sender": "", "block": 0, "name": "" } ] } ``` --- # `newBlock` WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `newBlock` · type `newBlock` Tick the current block height once per second. - Access: Public - Snapshot: [`getLatestBlockNumber`](/api/info/getLatestBlockNumber.md) - Not this? [`block`](/api/websocket/block.md) — when you need the block summary ### Subscribe · parameter-less action Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "newBlock" } ``` ### Subscription parameters No parameters. Subscribe with the top-level `type` / `action` shown above — there is no `subscription` object. ### Server message - `channel` (string) Values: `"newBlock"`. - `data` (NewBlockPayload) - `blockNum` (integer) ### Message example ```json { "channel": "newBlock", "data": { "blockNum": 0 } } ``` --- # `activeAssetCtx` Public market context for one instrument. Perpetuals expose funding, oracle, mark price, and open interest. Outcome markets reuse the same channel with `coin` set to the numeric `instrument_id`; Outcome consumers use its live price, 24-hour volume, and open-interest fields. WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `activeAssetCtx` · type `activeAssetCtx` Keep one market header live: mark, oracle, funding, OI (Outcome: price, volume, OI). - Access: Public - Snapshot: [`activeAssetCtx`](/api/info/activeAssetCtx.md) - Not this? [`allAssetCtxs`](/api/websocket/allAssetCtxs.md) — when you show every perp market at once ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "activeAssetCtx", "subscription": { "type": "activeAssetCtx", "coin": "4" } } ``` ### Subscription parameters - `coin` (string, required) - `type` (string, required) Values: `"activeAssetCtx"`. ### Server message - `channel` (string) Values: `"activeAssetCtx"`. - `data` (WsActiveAssetCtx) - `coin` (string) - `ctx` (WsActiveAssetCtxData) - `funding` (DecimalString) — Current funding rate. - `markPx` (DecimalString) — Decimal number encoded as a string to avoid floating-point precision loss. - `openInterest` (DecimalString) - `oraclePx` (DecimalString) - `blockTime` (integer, nullable) — Latest block time, milliseconds. - `dayBaseVlm` (DecimalString, nullable) — 24h base-asset volume. - `dayNtlVlm` (DecimalString, nullable) — 24h notional volume. - `impactPxs` (string[], nullable) — [impactBidPx, impactAskPx]. - `lastPrice` (DecimalString, nullable) - `midPx` (DecimalString, nullable) - `nextFundingTime` (integer, nullable) — Milliseconds. Omitted on old payloads for backward compatibility. - `premium` (DecimalString, nullable) - `prevDayPx` (DecimalString, nullable) - `prevFundingTime` (integer, nullable) — Milliseconds. ### Message example ```json { "channel": "activeAssetCtx", "data": { "coin": "4", "ctx": { "funding": "", "openInterest": "", "prevDayPx": "0", "dayNtlVlm": "", "premium": "", "oraclePx": "0", "markPx": "0", "midPx": "0", "impactPxs": [ "" ], "dayBaseVlm": "", "lastPrice": "0", "nextFundingTime": 1700000000000, "prevFundingTime": 1700000000000, "blockTime": 1700000000000 } } } ``` --- # `allAssetCtxs` WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `allAssetCtxs` · type `allAssetCtxs` Keep a perp markets table live — every instrument’s context in one subscription. - Access: Public - Markets: Perp only - Not this? [`activeAssetCtx`](/api/websocket/activeAssetCtx.md) — when you only show one market - Not this? [`allMids`](/api/websocket/allMids.md) — when mid prices are all you need ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "allAssetCtxs", "subscription": { "type": "allAssetCtxs" } } ``` ### Subscription parameters - `type` (string, required) Values: `"allAssetCtxs"`. - `exchangeId` (ExchangeScope, optional) — Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id). - One of · variant 1: - null - One of · variant 2: - integer - One of · variant 3: - string ### Server message - `channel` (string) Values: `"allAssetCtxs"`. - `data` (WsAllAssetCtxs) - `ctxs` (WsActiveAssetCtx[]) - `coin` (string) - `ctx` (WsActiveAssetCtxData) - `funding` (DecimalString) — Current funding rate. - `markPx` (DecimalString) — Decimal number encoded as a string to avoid floating-point precision loss. - `openInterest` (DecimalString) - `oraclePx` (DecimalString) - `blockTime` (integer, nullable) — Latest block time, milliseconds. - `dayBaseVlm` (DecimalString, nullable) — 24h base-asset volume. - `dayNtlVlm` (DecimalString, nullable) — 24h notional volume. - `impactPxs` (string[], nullable) — [impactBidPx, impactAskPx]. - `lastPrice` (DecimalString, nullable) - `midPx` (DecimalString, nullable) - `nextFundingTime` (integer, nullable) — Milliseconds. Omitted on old payloads for backward compatibility. - `premium` (DecimalString, nullable) - `prevDayPx` (DecimalString, nullable) - `prevFundingTime` (integer, nullable) — Milliseconds. ### Message example ```json { "channel": "allAssetCtxs", "data": { "ctxs": [ { "coin": "4", "ctx": { "funding": "", "openInterest": "", "prevDayPx": "0", "dayNtlVlm": "", "premium": "", "oraclePx": "0", "markPx": "0", "midPx": "0", "impactPxs": [ "" ], "dayBaseVlm": "", "lastPrice": "0", "nextFundingTime": 1700000000000, "prevFundingTime": 1700000000000, "blockTime": 1700000000000 } } ] } } ``` --- # `activeAssetData` WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `activeAssetData` · type `activeAssetData` Keep the order ticket’s leverage / max size / available margin live for one instrument. - Access: By user address - Markets: Perp only - Snapshot: [`activeAssetData`](/api/info/activeAssetData.md) - Not this? [`activeAssetCtx`](/api/websocket/activeAssetCtx.md) — when you need market stats, not user limits ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "activeAssetData", "subscription": { "type": "activeAssetData", "user": "0x0000000000000000000000000000000000000000", "coin": "4", "mode": "cross" } } ``` ### Subscription parameters - `coin` (string, required) - `type` (string, required) Values: `"activeAssetData"`. - `user` (EthAddress, required) — User to follow. Use the account address, not an agent's. - `mode` (string, nullable, optional) ### Server message - `channel` (string) Values: `"activeAssetData"`. - `data` (WsActiveAssetData) - `availableToTrade` (string[]) — Available notional to trade per side, [buy, sell]. - `coin` (string) - `leverage` (WsLeverage) - `type` (string) — Margin mode. Values: `"Cross"`, `"Isolated"`. - `value` (integer) — Leverage multiple. - `rawUsd` (DecimalString) — Isolated margin amount (only present when `type` is `Isolated`). - `markPx` (DecimalString) — Decimal number encoded as a string to avoid floating-point precision loss. - `maxTradeSzs` (string[]) — Max tradeable size per side, [buy, sell]. - `onlyIsolated` (boolean) — Whether this instrument is restricted to isolated margin only. - `user` (string) ### Message example ```json { "channel": "activeAssetData", "data": { "user": "0x0000000000000000000000000000000000000000", "coin": "4", "leverage": { "type": "Cross", "value": 0, "rawUsd": "" }, "maxTradeSzs": [ "" ], "availableToTrade": [ "" ], "markPx": "0", "onlyIsolated": false } } ``` --- # `clearinghouseState` WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `clearinghouseState` · type `clearinghouseState` Keep the Positions panel and margin summary live. - Access: By user address - Markets: Perp only - Snapshot: [`clearinghouseState`](/api/info/clearinghouseState.md) - Not this? [`position`](/api/websocket/position.md) — when you only care about per-position deltas - Not this? [`webData2`](/api/websocket/webData2.md) — when you also want open orders in the same push ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "clearinghouseState", "subscription": { "type": "clearinghouseState", "user": "0x0000000000000000000000000000000000000000", "dex": "txflow" } } ``` ### Subscription parameters - `type` (string, required) Values: `"clearinghouseState"`. - `user` (EthAddress, required) — User to follow. Use the account address, not an agent's. - `dex` (string, nullable, optional) — Optional perp dex name. Defaults to the first dex. - `exchangeId` (ExchangeScope, optional) — Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id). - One of · variant 1: - null - One of · variant 2: - integer - One of · variant 3: - string ### Server message - `channel` (string) Values: `"clearinghouseState"`. - `data` (WsClearinghouseState) — Aggregate margin and position state for one user. - `accountValuePlusWithdrawHold` (DecimalString) — accountValue + withdrawHold. - `assetPositions` (WsAssetPosition[]) - `position` (WsPositionData) - `asset` (string) — Instrument id (as a string). - `coin` (string) — Trading pair symbol. - `cumFunding` (WsCumulativeFunding) - `allTime` (DecimalString) — Cumulative funding paid/received since account creation. - `sinceChange` (DecimalString) — Cumulative funding since the last position size change. - `sinceOpen` (DecimalString) — Cumulative funding since the position was opened. - `leverage` (WsLeverage) - `type` (string) — Margin mode. Values: `"Cross"`, `"Isolated"`. - `value` (integer) — Leverage multiple. - `rawUsd` (DecimalString) — Isolated margin amount (only present when `type` is `Isolated`). - `marginRatio` (DecimalString) — Margin ratio, 4 decimal places. - `marginUsed` (DecimalString) — Margin currently used by this position. - `maxLeverage` (integer) — Maximum leverage allowed for this instrument. - `positionValue` (DecimalString) — Notional value of the position. - `returnOnEquity` (DecimalString) — Return on equity. - `szi` (DecimalString) — Signed position size. Positive = long, negative = short. - `unrealizedPnl` (DecimalString) — Unrealized PnL. - `adlQuantile` (integer) — Auto-deleverage priority bucket. 5 = highest priority (deleveraged first), 1 = safest. Omitted when not applicable. - `entryPx` (DecimalString, nullable) — Average entry price. - `liquidationPx` (DecimalString, nullable) — Estimated liquidation price. - `markPx` (DecimalString) — Mark price used for PnL/liquidation calculation. Omitted when not applicable. - `type` (string) — Position mode. Values: `"oneWay"`, `"hedge"`. - `tpsl` (object[]) — Attached take-profit/stop-loss orders for this position. Omitted when there are none. - `crossMaintenanceMarginUsed` (DecimalString) — Cross-margin maintenance margin requirement. - `crossMarginRatio` (DecimalString) — Cross margin ratio = (balance + crossUnrealizedPnl) / (crossMaintenanceMargin + crossLiquidationFee). Higher is safer; liquidation triggers at <= 1.0. - `crossMarginSummary` (WsMarginSummary) - `accountValue` (DecimalString) — Account equity. - `totalMarginUsed` (DecimalString) — Total margin used. - `totalNtlPos` (DecimalString) — Total notional position value. - `totalRawUsd` (DecimalString) — Raw balance excluding unrealized PnL. - `marginSummary` (WsMarginSummary) - `accountValue` (DecimalString) — Account equity. - `totalMarginUsed` (DecimalString) — Total margin used. - `totalNtlPos` (DecimalString) — Total notional position value. - `totalRawUsd` (DecimalString) — Raw balance excluding unrealized PnL. - `totalRawUsdPlusWithdrawHold` (DecimalString) — totalRawUsd + withdrawHold. - `user` (EthAddress) — Ethereum-style account or token address, 0x-prefixed, 40 hex characters. - `vaultEquity` (DecimalString) — User's total equity across all vaults. - `withdrawable` (DecimalString) — Amount currently withdrawable. - `activeReverseJobs` (object[]) — In-flight reverse-position jobs for this user. Omitted when empty. ### Message example ```json { "channel": "clearinghouseState", "data": { "user": "0x0000000000000000000000000000000000000000", "marginSummary": { "accountValue": "", "totalMarginUsed": "", "totalNtlPos": "", "totalRawUsd": "" }, "crossMarginSummary": { "accountValue": "", "totalMarginUsed": "", "totalNtlPos": "", "totalRawUsd": "" }, "crossMaintenanceMarginUsed": "", "crossMarginRatio": "", "withdrawable": "", "totalRawUsdPlusWithdrawHold": "", "accountValuePlusWithdrawHold": "", "vaultEquity": "", "assetPositions": [ { "type": "oneWay", "position": { "coin": "4", "asset": "4", "szi": "", "leverage": { "type": "Cross", "value": 0, "rawUsd": "" }, "entryPx": "0", "positionValue": "", "unrealizedPnl": "", "returnOnEquity": "", "liquidationPx": "0", "marginUsed": "", "maxLeverage": 0, "cumFunding": { "allTime": "", "sinceOpen": "", "sinceChange": "" }, "marginRatio": "", "adlQuantile": 0, "markPx": "0" }, "tpsl": [ {} ] } ], "activeReverseJobs": [ {} ] } } ``` --- # `position` WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `position` · type `positionUpdates` React to individual position changes (opened, resized, closed). - Access: By user address - Markets: Perp only - Not this? [`clearinghouseState`](/api/websocket/clearinghouseState.md) — when you need margin totals too ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "positionUpdates", "subscription": { "type": "positionUpdates", "user": "0x0000000000000000000000000000000000000000" } } ``` ### Subscription parameters - `type` (string, required) Values: `"positionUpdates"`. - `user` (EthAddress, required) — User to follow. Use the account address, not an agent's. - `exchangeId` (ExchangeScope, optional) — Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id). - One of · variant 1: - null - One of · variant 2: - integer - One of · variant 3: - string ### Server message - `action` (string) — Event that produced this update, e.g. `"Create"`. - `channel` (string) Values: `"position"`. - `closedTime` (integer) — Close time, milliseconds. 0 if still open. - `createTime` (integer) — Position open time, milliseconds. - `entryPrice` (DecimalString) — Decimal number encoded as a string to avoid floating-point precision loss. - `instrumentId` (integer) - `leverage` (integer) - `margin` (DecimalString) - `marginMode` (string) — Margin mode: `"Cross"` or `"Isolated"`. - `mode` (string) — Position mode: `"OneWay"` or `"Hedge"`. - `positionSide` (string) — "Long" or "Short". - `size` (DecimalString) - `updateTime` (integer) — Last update time, milliseconds. - `user` (EthAddress) — Ethereum-style account or token address, 0x-prefixed, 40 hex characters. - `fundingSinceChange` (DecimalString, nullable) - `fundingSinceOpen` (DecimalString, nullable) - `markPrice` (DecimalString, nullable) ### Message example ```json { "channel": "position", "user": "0x0000000000000000000000000000000000000000", "instrumentId": 1, "mode": "OneWay", "marginMode": "Cross", "positionSide": "Long", "action": "Create", "createTime": 1791895804242, "updateTime": 1791895804242, "closedTime": 0, "size": "0.0010", "leverage": 10, "margin": "0.000000", "entryPrice": "83227.1", "markPrice": "83241.4" } ``` --- # `webData2` WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `webData2` · type `webData2` One-subscription account view: positions + margin + open orders (Hyperliquid-compatible). - Access: By user address - Markets: Perp only - Not this? [`clearinghouseState`](/api/websocket/clearinghouseState.md) — when you don’t need open orders - Not this? [`orderUpdates`](/api/websocket/orderUpdates.md) — when you need per-order events rather than snapshots ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "webData2", "subscription": { "type": "webData2", "user": "0x0000000000000000000000000000000000000000" } } ``` ### Subscription parameters - `type` (string, required) Values: `"webData2"`. - `user` (EthAddress, required) — User to follow. Use the account address, not an agent's. - `exchangeId` (ExchangeScope, optional) — Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id). - One of · variant 1: - null - One of · variant 2: - integer - One of · variant 3: - string ### Server message - `channel` (string) Values: `"webData2"`. - `data` (WsWebData2) — Hyperliquid-compatible aggregate account snapshot: margin summary, positions and open orders in one push. - `clearinghouseState` (WsClearinghouseState) — Aggregate margin and position state for one user. - `accountValuePlusWithdrawHold` (DecimalString) — accountValue + withdrawHold. - `assetPositions` (WsAssetPosition[]) - `position` (WsPositionData) - `asset` (string) — Instrument id (as a string). - `coin` (string) — Trading pair symbol. - `cumFunding` (WsCumulativeFunding) - `allTime` (DecimalString) — Cumulative funding paid/received since account creation. - `sinceChange` (DecimalString) — Cumulative funding since the last position size change. - `sinceOpen` (DecimalString) — Cumulative funding since the position was opened. - `leverage` (WsLeverage) - `type` (string) — Margin mode. Values: `"Cross"`, `"Isolated"`. - `value` (integer) — Leverage multiple. - `rawUsd` (DecimalString) — Isolated margin amount (only present when `type` is `Isolated`). - `marginRatio` (DecimalString) — Margin ratio, 4 decimal places. - `marginUsed` (DecimalString) — Margin currently used by this position. - `maxLeverage` (integer) — Maximum leverage allowed for this instrument. - `positionValue` (DecimalString) — Notional value of the position. - `returnOnEquity` (DecimalString) — Return on equity. - `szi` (DecimalString) — Signed position size. Positive = long, negative = short. - `unrealizedPnl` (DecimalString) — Unrealized PnL. - `adlQuantile` (integer) — Auto-deleverage priority bucket. 5 = highest priority (deleveraged first), 1 = safest. Omitted when not applicable. - `entryPx` (DecimalString, nullable) — Average entry price. - `liquidationPx` (DecimalString, nullable) — Estimated liquidation price. - `markPx` (DecimalString) — Mark price used for PnL/liquidation calculation. Omitted when not applicable. - `type` (string) — Position mode. Values: `"oneWay"`, `"hedge"`. - `tpsl` (object[]) — Attached take-profit/stop-loss orders for this position. Omitted when there are none. - `crossMaintenanceMarginUsed` (DecimalString) — Cross-margin maintenance margin requirement. - `crossMarginRatio` (DecimalString) — Cross margin ratio = (balance + crossUnrealizedPnl) / (crossMaintenanceMargin + crossLiquidationFee). Higher is safer; liquidation triggers at <= 1.0. - `crossMarginSummary` (WsMarginSummary) - `accountValue` (DecimalString) — Account equity. - `totalMarginUsed` (DecimalString) — Total margin used. - `totalNtlPos` (DecimalString) — Total notional position value. - `totalRawUsd` (DecimalString) — Raw balance excluding unrealized PnL. - `marginSummary` (WsMarginSummary) - `accountValue` (DecimalString) — Account equity. - `totalMarginUsed` (DecimalString) — Total margin used. - `totalNtlPos` (DecimalString) — Total notional position value. - `totalRawUsd` (DecimalString) — Raw balance excluding unrealized PnL. - `totalRawUsdPlusWithdrawHold` (DecimalString) — totalRawUsd + withdrawHold. - `user` (EthAddress) — Ethereum-style account or token address, 0x-prefixed, 40 hex characters. - `vaultEquity` (DecimalString) — User's total equity across all vaults. - `withdrawable` (DecimalString) — Amount currently withdrawable. - `activeReverseJobs` (object[]) — In-flight reverse-position jobs for this user. Omitted when empty. - `openOrders` (WsOpenOrder[]) - `coin` (string) — Instrument name, e.g. `BTC-USDC`. - `limitPx` (DecimalString) — Limit price. - `oid` (integer) — Order id. - `origSz` (DecimalString) — Original order size. - `side` (string) — "B" = buy, "A" = sell. Values: `"A"`, `"B"`. - `sz` (DecimalString) — Remaining size. - `timestamp` (integer) — Order creation time, milliseconds. - `cloid` (string, nullable) — Client order id. Omitted when not set. - `serverTime` (integer) — Server timestamp, milliseconds. - `user` (string) ### Message example ```json { "channel": "webData2", "data": { "user": "0x0000000000000000000000000000000000000000", "clearinghouseState": { "user": "0x0000000000000000000000000000000000000000", "marginSummary": { "accountValue": "", "totalMarginUsed": "", "totalNtlPos": "", "totalRawUsd": "" }, "crossMarginSummary": { "accountValue": "", "totalMarginUsed": "", "totalNtlPos": "", "totalRawUsd": "" }, "crossMaintenanceMarginUsed": "", "crossMarginRatio": "", "withdrawable": "", "totalRawUsdPlusWithdrawHold": "", "accountValuePlusWithdrawHold": "", "vaultEquity": "", "assetPositions": [ { "type": "oneWay", "position": { "coin": "4", "asset": "4", "szi": "", "leverage": { "type": "Cross", "value": 0, "rawUsd": "" }, "entryPx": "0", "positionValue": "", "unrealizedPnl": "", "returnOnEquity": "", "liquidationPx": "0", "marginUsed": "", "maxLeverage": 0, "cumFunding": { "allTime": "", "sinceOpen": "", "sinceChange": "" }, "marginRatio": "", "adlQuantile": 0, "markPx": "0" }, "tpsl": [ {} ] } ], "activeReverseJobs": [ {} ] }, "openOrders": [ { "coin": "BTC-USDC", "side": "B", "limitPx": "83000.0", "sz": "0.001", "oid": 123456, "timestamp": 1700000000000, "origSz": "0.001" } ], "serverTime": 1700000000000 } } ``` --- # `userFundings` Private funding settlement update (Hyperliquid-compatible). Historical settlements are [`userFundings`](/api/info/userFundings.md). WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `userFundings` · type `userFundings` Append funding settlements to Funding History as they happen. - Access: By user address - Markets: Perp only - Snapshot: [`userFundings`](/api/info/userFundings.md) ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "userFundings", "subscription": { "type": "userFundings", "user": "0x0000000000000000000000000000000000000000" } } ``` ### Subscription parameters - `type` (string, required) Values: `"userFundings"`. - `user` (string, required) — User to follow. Use the account address, not an agent's. - `exchangeId` (ExchangeScope, optional) — Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id). - One of · variant 1: - null - One of · variant 2: - integer - One of · variant 3: - string ### Server message - `channel` (string) Values: `"userFundings"`. - `data` (WsFundingUpdate) — Hyperliquid-compatible funding settlement update. - `coin` (string) - `fundingRate` (DecimalString) — Funding rate, percentage form (e.g. "0.01" means 0.01%). - `szi` (DecimalString) — Signed position size at settlement time. - `time` (integer) — Settlement timestamp, milliseconds. - `usdc` (DecimalString) — Funding amount in USDC. Negative = paid, positive = received. ### Message example ```json { "channel": "userFundings", "data": { "time": 1700000000000, "coin": "4", "usdc": "", "szi": "", "fundingRate": "" } } ``` --- # `twapStates` Private running-state snapshot of every active TWAP order for one user on one dex. WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `twapStates` · type `twapStates` Show progress of a user’s running TWAP orders. - Access: By user address - Markets: Perp only - Not this? [`userTwapHistory`](/api/websocket/userTwapHistory.md) — when you need finished TWAPs ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "twapStates", "subscription": { "type": "twapStates", "user": "0x0000000000000000000000000000000000000000", "dex": "txflow" } } ``` ### Subscription parameters - `type` (string, required) Values: `"twapStates"`. - `user` (string, required) — User to follow. Use the account address, not an agent's. - `dex` (string, optional) — Defaults to "" (first/default dex). - `exchangeId` (ExchangeScope, optional) — Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id). - One of · variant 1: - null - One of · variant 2: - integer - One of · variant 3: - string ### Server message - `channel` (string) Values: `"twapStates"`. - `data` (WsTwapStates) - `dex` (string) - `states` (TwapIdCoinPair[]) - `user` (string) ### Message example ```json { "channel": "twapStates", "data": { "dex": "txflow", "user": "0x0000000000000000000000000000000000000000", "states": [ [ null ] ] } } ``` --- # `userTwapHistory` Private history of finished/cancelled TWAP orders for one user. The REST list is [`twapHistory`](/api/info/twapHistory.md). WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `userTwapHistory` · type `userTwapHistory` Append TWAPs to the history list as they finish or are cancelled. - Access: By user address - Markets: Perp only - Snapshot: [`twapHistory`](/api/info/twapHistory.md) ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "userTwapHistory", "subscription": { "type": "userTwapHistory", "user": "0x0000000000000000000000000000000000000000" } } ``` ### Subscription parameters - `type` (string, required) Values: `"userTwapHistory"`. - `user` (string, required) — User to follow. Use the account address, not an agent's. - `exchangeId` (ExchangeScope, optional) — Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id). - One of · variant 1: - null - One of · variant 2: - integer - One of · variant 3: - string ### Server message - `channel` (string) Values: `"userTwapHistory"`. - `data` (WsUserTwapHistory) - `history` (WsTwapHistoryRow[]) - `state` (WsTwapState) — Hyperliquid-aligned running TWAP order state. - `coin` (string) - `executedNtl` (number) - `executedSz` (number) - `interval` (integer) — TxFlow extension: slice interval, seconds. - `minutes` (integer) — Total TWAP duration, minutes. - `randomize` (boolean) - `reduceOnly` (boolean) - `side` (string) Values: `"B"`, `"A"`. - `sz` (number) - `timestamp` (integer) - `unfilledSz` (number) — max(0, sz - executedSz); non-zero on dust-style completion within half of the min-size tolerance. - `user` (string) - `leverage` (integer, nullable) — Leverage for the next child slice. - `marginMode` (string, nullable) — Snapshotted perps margin mode. Values: `"Cross"`, `"Isolated"`. - `positionMode` (string, nullable) — Snapshotted perps position mode. Values: `"OneWay"`, `"Hedge"`. - `status` (string) — Job lifecycle, e.g. "finished" / "cancelled". - `time` (integer) - `twapId` (string) — TWAP job id, encoded as a string. - `failureReason` (string, nullable) - `finishedAt` (integer, nullable) - `isSnapshot` (boolean) - `user` (string) ### Message example ```json { "channel": "userTwapHistory", "data": { "isSnapshot": false, "user": "0x0000000000000000000000000000000000000000", "history": [ { "time": 1700000000000, "twapId": "", "finishedAt": 0, "state": { "coin": "4", "user": "0x0000000000000000000000000000000000000000", "side": "B", "sz": 0, "executedSz": 0, "executedNtl": 0, "unfilledSz": 0, "minutes": 0, "reduceOnly": false, "randomize": false, "timestamp": 1700000000000, "interval": 0, "marginMode": "Cross", "positionMode": "OneWay", "leverage": 0 }, "status": "", "failureReason": "" } ] } } ``` --- # `userTwapSliceFills` Private stream of individual TWAP child-order fills for one user. Historical slice fills are [`userTwapSliceFills`](/api/info/userTwapSliceFills.md). WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `userTwapSliceFills` · type `userTwapSliceFills` Stream TWAP child fills as they execute. - Access: By user address - Markets: Perp only - Snapshot: [`userTwapSliceFills`](/api/info/userTwapSliceFills.md) ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "userTwapSliceFills", "subscription": { "type": "userTwapSliceFills", "user": "0x0000000000000000000000000000000000000000" } } ``` ### Subscription parameters - `type` (string, required) Values: `"userTwapSliceFills"`. - `user` (string, required) — User to follow. Use the account address, not an agent's. - `exchangeId` (ExchangeScope, optional) — Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id). - One of · variant 1: - null - One of · variant 2: - integer - One of · variant 3: - string ### Server message - `channel` (string) Values: `"userTwapSliceFills"`. - `data` (WsUserTwapSliceFills) - `isSnapshot` (boolean) - `twapSliceFills` (WsTwapSliceFill[]) - `fill` (WsTwapSliceFillBody) - `closedPnl` (DecimalString) — Decimal number encoded as a string to avoid floating-point precision loss. - `coin` (string) - `crossed` (boolean) — Whether this fill crossed the spread (taker fill). - `dir` (string) — Human-readable fill direction, e.g. "Open Long". - `fee` (DecimalString) - `feeToken` (string) - `hash` (string) - `oid` (integer) - `px` (DecimalString) - `side` (string) Values: `"A"`, `"B"`. - `startPosition` (DecimalString) — Position size before this fill. - `sz` (DecimalString) - `tid` (integer) - `time` (integer) - `status` (string) - `twapId` (string) - `failureReason` (string, nullable) - `user` (string) ### Message example ```json { "channel": "userTwapSliceFills", "data": { "isSnapshot": false, "user": "0x0000000000000000000000000000000000000000", "twapSliceFills": [ { "twapId": "", "status": "", "failureReason": "", "fill": { "closedPnl": "", "coin": "4", "crossed": false, "dir": "", "hash": "", "oid": 123456, "px": "0", "side": "A", "startPosition": "", "sz": "0", "time": 1700000000000, "fee": "", "tid": 0, "feeToken": "" } } ] } } ``` --- # `vaultUpdates` Streams one user's vault activity over the node `/ws` connection: deposits and withdrawals made with [`vaultTransfer`](/api/exchange/vaultTransfer.md), vault creation, and leader commission payouts. Subscribe with type `vaultUpdates`; pushes arrive on channel `vault` as flat frames with no `data` wrapper. Read current vault equity with [`uservaultequities`](/api/info/uservaultequities.md). WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `vault` · type `vaultUpdates` Confirm vault deposits and withdrawals as they land, and refresh vault equity. - Access: By user address - Markets: Perp only ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "vaultUpdates", "subscription": { "type": "vaultUpdates", "user": "0xabc123abc123abc123abc123abc123abc123abc1" } } ``` ### Subscription parameters - `type` (string, required) Values: `"vaultUpdates"`. - `user` (string · address, required) — Account address (the owner, not an agent). ### Server message - `action` (string) — `create` when the vault is created, `transfer` for a deposit or withdrawal, `leaderCommission` for a leader commission payout. Values: `"create"`, `"transfer"`, `"leaderCommission"`. - `amount` (string) — Amount moved, in token units. - `channel` (string) Values: `"vault"`. - `isDeposit` (boolean) — `true` for a deposit, `false` for a withdrawal. - `leader` (string · address) — Vault leader address. - `status` (string) Values: `"success"`, `"failed"`. - `token` (string) - `txHash` (string) - `user` (string · address) - `vaultAddress` (string · address) - `commission` (string) — Leader commission amount. - `error` (string) — Reason, present when `status` is `failed`. - `requestedUsd` (string) — Requested USD amount. ### Message example ```json { "channel": "vault", "user": "0xabc123abc123abc123abc123abc123abc123abc1", "vaultAddress": "0x664989a76069c2b153720745bc1c3c8eeee504c1", "leader": "0x0000000000000000000000000000000000000000", "action": "transfer", "isDeposit": true, "amount": "10.000000", "token": "USDC:0xd04f0d19E8FbA49cfA541E272F9C60a2ad720464", "txHash": "0x6a661ee8ee3056b1cb301338754fb6e24a89cb1fb8aac4775c1d24c1c24797b4", "status": "success" } ``` --- # `outcomeBbos` Streams the best bid and best ask for a batch of Outcome instruments over the node `/ws` connection. A subscription contains 1–50 unique `instrumentIds` and an `exchangeId`. WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `outcomeBbos` · type `outcomeBbos` Keep prices live in an Outcome discovery grid or watchlist (up to 50 instruments per subscription). - Access: Public - Markets: Outcome only - Not this? [`l2Book`](/api/websocket/l2Book.md) — when you need full depth for one market ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "outcomeBbos", "subscription": { "type": "outcomeBbos", "instrumentIds": [ 20165500, 20165501 ], "exchangeId": 1 } } ``` ### Subscription parameters - `exchangeId` (integer · uint32, required) - `instrumentIds` (integer · uint64[], required) - `type` (string, required) Values: `"outcomeBbos"`. ### Server message - `channel` (string) Values: `"outcomeBbos"`. - `data` (object) - `bbos` (object[]) - `bbo` (string[]) - `instrumentId` (integer · uint64) - `time` (integer · int64) ### Message example ```json { "channel": "outcomeBbos", "data": { "time": 1788935653280, "bbos": [ { "instrumentId": 20165500, "bbo": [ "0.40", "0.99" ] } ] } } ``` Each `bbo` tuple is `[bestBid, bestAsk]`. Either side is `null` when the book has no order there. To watch more than 50 instruments, create multiple subscriptions on the same connection; one connection accepts up to 20 active Outcome BBO groups. This channel is intended for discovery grids and watchlists. For full depth, subscribe to [`l2Book`](/api/websocket/l2Book.md) with one numeric instrument id. --- # `outcomeTrades` Streams executed trades for several Outcome instruments at once over the node `/ws` connection. A subscription carries the `instrumentIds` to follow. WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `outcomeTrades` · type `outcomeTrades` One public tape across several Outcome instruments (every leg of an event). - Access: Public - Markets: Outcome only - Not this? [`trades`](/api/websocket/trades.md) — when you follow a single market ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "outcomeTrades", "subscription": { "type": "outcomeTrades", "instrumentIds": [ 20165500, 20165501 ] } } ``` ### Subscription parameters - `instrumentIds` (integer · uint64[], required) - `type` (string, required) Values: `"outcomeTrades"`. ### Server message - `channel` (string) Values: `"outcomeTrades"`. - `data` (object[]) - `coin` (string) - `hash` (string) - `px` (string) - `side` (string) — Taker side: "A" = buy (ask), "B" = sell (bid). Values: `"A"`, `"B"`. - `sz` (string) - `tid` (integer · uint64) - `time` (integer · int64) - `instrumentId` (integer · uint64) - `users` (string[]) — [takerAddress, makerAddress]. ### Message example ```json { "channel": "outcomeTrades", "data": [ { "coin": "OUTCOME-20165500", "instrumentId": 20165500, "side": "A", "px": "0.55", "sz": "10", "time": 1788935653280, "hash": "0xabc123", "tid": 887766, "users": [ "" ] } ] } ``` Use this channel when one tape needs to span multiple markets — an event page showing every leg, or a portfolio-wide activity feed. For a single market the shared [`trades`](/api/websocket/trades.md) channel is simpler: pass the Outcome instrument id as `coin`. Pushes are not filtered per instrument, so route on `instrumentId` (or `coin`) when you follow several markets on one connection. `side` is the taker side of the underlying YES book, not the displayed leg. --- # `outcomeOrderUpdates` Streams matching-engine lifecycle updates for one user's Outcome orders over the node `/ws` connection. WebSocket `wss://rpc.txflow-testnet.xyz/ws` · channel `outcomeOrderUpdates` · type `outcomeOrderUpdates` Track the matching lifecycle of a user’s Outcome orders: filled, rejected, cancelled. - Access: By user address - Markets: Outcome only - Snapshot: [`outcomeFrontendOpenOrders`](/api/info/outcomeFrontendOpenOrders.md) - Not this? [`outcomeOrderUpdate`](/api/websocket/outcomeOrderUpdate.md) — when you need to know if an async submission (`traceId`) failed ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "type": "outcomeOrderUpdates", "subscription": { "type": "outcomeOrderUpdates", "user": "0xabc123abc123abc123abc123abc123abc123abc1", "instrumentIds": [ 9001 ] } } ``` ### Subscription parameters - `type` (string, required) Values: `"outcomeOrderUpdates"`. - `user` (string · address, required) - `instrumentIds` (integer · uint64[], optional) — Optional filter. Omit or send an empty list for all Outcome instruments. ### Server message - `channel` (string) Values: `"outcomeOrderUpdates"`. - `data` (object[]) - `order` (object) - `asset` (integer · uint64) - `limitPx` (string) - `oid` (integer · uint64) - `orderType` (string) - `origSz` (string) - `outcome` (string) Values: `"Yes"`, `"No"`. - `side` (string) Values: `"B"`, `"A"`. - `avgFillPrice` (string, nullable) - `coin` (string) - `isTrigger` (boolean) - `reduceOnly` (boolean) - `sz` (string) - `timestamp` (integer · int64) - `status` (string) - `statusTimestamp` (integer · int64) ### Message example ```json { "channel": "outcomeOrderUpdates", "data": [ { "order": { "asset": 1000001, "coin": "OUTCOME-1000001", "side": "B", "orderType": "Market", "limitPx": "0.550", "sz": "0", "origSz": "10", "avgFillPrice": "0.549", "oid": 179297706115, "timestamp": 1700000000000, "reduceOnly": false, "outcome": "Yes", "isTrigger": false }, "status": "Filled", "statusTimestamp": 1700000000000 } ] } ``` Omit `instrumentIds`, or send an empty list, to follow every Outcome market for the user. When present, the list filters the stream to those instruments. The `outcome` field is the displayed leg (`Yes` or `No`); `side` remains the underlying YES-book side (`B` or `A`). Use this channel for filled, rejected, and canceled lifecycle events. The separate [`outcomeOrderUpdate`](/api/websocket/outcomeOrderUpdate.md) channel reports orders that fail in the gateway's asynchronous acceptance path. --- # `ticker` Pushes last price, 24-hour change, high/low, and volume for one instrument every few seconds, starting right after you subscribe. Served by the API service at `/api-ws`. WebSocket `wss://rpc.txflow-testnet.xyz/api-ws` · channel `ticker` · type `ticker` Show last price, 24-hour change, high/low, and volume for one instrument. - Access: Public - Not this? [`activeAssetCtx`](/api/websocket/activeAssetCtx.md) — when you need mark, oracle, and funding ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "subscription": { "type": "ticker", "coin": "BTC", "instrumentId": 1 } } ``` ### Subscription parameters - `coin` (string, required) — Instrument name (`BTC` or `BTC-USDC`); for an Outcome market, its `instrument_id` as a string. - `instrumentId` (integer, required) — The same instrument's id (`index` from `perpMeta`). Must match `coin`, or the subscription is rejected. - `type` (string, required) Values: `"ticker"`. ### Server message - `channel` (string) Values: `"ticker"`. - `data` (object) - `coin` (string) — Base name. - `coinVlm` (string) — 24-hour volume, in base units. - `dayHighPx` (string) - `dayLowPx` (string) - `dayNtlVlm` (string) — 24-hour notional volume, in quote. - `instrumentId` (integer) - `lastPrice` (string) - `prevDayPx` (string) — Price 24 hours ago. ### Message example ```json { "channel": "ticker", "data": { "coin": "BTC", "instrumentId": 1, "lastPrice": "83280.6", "prevDayPx": "83110.4", "dayHighPx": "84032.7", "dayLowPx": "80896.1", "dayNtlVlm": "32544402.28", "coinVlm": "393.4642" } } ``` `coin` and `instrumentId` must name the same instrument — `{ "coin": "BTC", "instrumentId": 1 }` — or the subscription is rejected with `instrument_id … does not match coin …`. For an Outcome market pass its `instrument_id` as both. Each push is a full snapshot, sent even when nothing changed; fields are empty strings until the instrument has traded. --- # `trades` (API service) Sends the latest 300 trades for one instrument, newest first, right after you subscribe and again every few seconds. This is the `/api-ws` channel named `trades`; the node `/ws` [`trades`](/api/websocket/trades.md) channel pushes each trade as it happens instead. WebSocket `wss://rpc.txflow-testnet.xyz/api-ws` · channel `trades` · type `trades` Show the latest 300 trades for one instrument from `/api-ws`, refreshed every few seconds. - Access: Public - Not this? [`trades`](/api/websocket/trades.md) — when you want each trade pushed as it happens from `/ws` ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "subscription": { "type": "trades", "coin": "BTC" } } ``` ### Subscription parameters - `coin` (string, required) — Instrument name (`BTC`, `BTC-USDC`) or numeric id; for an Outcome market, its `instrument_id` as a string. - `type` (string, required) Values: `"trades"`. ### Server message - `channel` (string) Values: `"trades"`. - `data` (object[]) — Newest first. Each push repeats the full list — de-duplicate by `tid`. - `coin` (string) — Base name. - `hash` (string) - `instrumentId` (integer) - `instrumentType` (string) - `marketType` (string) - `px` (string) - `side` (string) — Taker side: "B" = buy, "A" = sell. Values: `"B"`, `"A"`. - `symbol` (string) - `sz` (string) - `tid` (integer) — Trade id. - `time` (integer) — Unix ms. - `users` (string · address[]) — The two counterparties. ### Message example ```json { "channel": "trades", "data": [ { "coin": "BTC", "symbol": "BTC-USDC", "side": "B", "px": "83280.7", "sz": "0.0053", "time": 1791894528994, "hash": "0xed3383f38be30c011dfc126d880b5575ff0b713f81056f75ce58a3ec64f9b971", "tid": 323976318000002, "users": [ "0xe3f0e9c1cfb95590f165ed6df5e1a9f53d117c24", "0x079e74e124151e4a411df17ea7e3ef97f4d049e3" ], "instrumentId": 1, "instrumentType": "perps", "marketType": "perps" } ] } ``` Every push repeats the whole list, so de-duplicate by `tid`. `side` is the taker's side. --- # `candle` Streams the open candle for one instrument and interval. This channel is served by the API service at `/api-ws`, not the node `/ws` endpoint. WebSocket `wss://rpc.txflow-testnet.xyz/api-ws` · channel `candle` · type `candle` Keep the last bar of a chart live after seeding it with `candleSnapshot`. - Access: Public - Snapshot: [`candleSnapshot`](/api/info/candleSnapshot.md) - Not this? [`multiCandle`](/api/websocket/multiCandle.md) — when you chart several coins at once ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "subscription": { "type": "candle", "coin": "19127212", "interval": "1m" } } ``` ### Subscription parameters - `coin` (string, required) - `interval` (string, required) - `type` (string, required) Values: `"candle"`. ### Server message - `channel` (string) Values: `"candle"`. - `data` (object) - `c` (number) - `h` (number) - `i` (string) - `l` (number) - `o` (number) - `s` (string) - `t` (integer · int64) — Bar open time in milliseconds. - `v` (number) - `n` (integer) - `T` (integer · int64) — Bar close time in milliseconds. ### Message example ```json { "channel": "candle", "data": { "t": 0, "T": 0, "s": "19127212", "i": "1m", "o": 0.52, "h": 0.56, "l": 0.51, "c": 0.55, "v": 120, "n": 8 } } ``` Seed the chart with [`candleSnapshot`](/api/info/candleSnapshot.md), then replace the current bucket whenever another frame with the same `t` arrives. For an Outcome instrument, pass its numeric `instrument_id` as `coin`. OHLC values are YES prices/probabilities in `[0, 1]`; a NO chart uses their complement. --- # `multiCandle` Streams the open candle of up to 8 coins on one subscription. Served by the API service at `/api-ws`. WebSocket `wss://rpc.txflow-testnet.xyz/api-ws` · channel `multiCandle` · type `multiCandle` Keep the last bar of up to 8 coins live on one subscription, e.g. a markets list with sparklines. - Access: Public - Not this? [`candle`](/api/websocket/candle.md) — when you chart a single coin ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "subscription": { "type": "multiCandle", "coins": [ "BTC", "ETH" ], "interval": "1m" } } ``` ### Subscription parameters - `coins` (string[], required) — Up to 8 coins. Use base names (`BTC`): candles are keyed by base name, so `BTC-USDC` would key the first push differently from the rest. - `interval` (string, required) Values: `"1m"`, `"3m"`, `"5m"`, `"15m"`, `"30m"`, `"1h"`, `"2h"`, `"4h"`, `"8h"`, `"12h"`, `"1d"`. - `type` (string, required) Values: `"multiCandle"`. ### Server message - `channel` (string) Values: `"multiCandle"`. - `data` (object) - `candles` (object) — Coin → its latest candle. A push carries only the coins that changed. - `interval` (string) ### Message example ```json { "channel": "multiCandle", "data": { "interval": "1m", "candles": { "BTC": { "t": 1791894480000, "T": 1791894539999, "s": "BTC", "i": "1m", "o": "83264.9", "h": "83290.7", "l": "83264.9", "c": "83276.9", "v": "0.0804", "n": 17 } } } } ``` `data.candles` maps each coin to its latest candle and carries only the coins that changed since the previous push (updates are coalesced over about 200 ms). Subscribe with base names such as `BTC`: candles are keyed by their `s` field, which is the base name, so subscribing with `BTC-USDC` keys the first push differently from later ones. Seed each chart with [`candleSnapshot`](/api/info/candleSnapshot.md); for a single coin use [`candle`](/api/websocket/candle.md). --- # `outcomeOrderUpdate` Reports an Outcome order accepted through the delayed submission path that then **failed** before reaching the book. Successful submissions are not pushed. Unlike node channels, this subscription uses the API service's `/api-ws` endpoint. WebSocket `wss://rpc.txflow-testnet.xyz/api-ws` · channel `outcomeOrderUpdate` · type `outcomeOrderUpdate` Catch async Outcome order submissions that fail (by `traceId`). Only failures are pushed; not a fill signal. - Access: By user address - Markets: Outcome only - Not this? [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md) — when you need fills and cancels ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "subscription": { "type": "outcomeOrderUpdate", "user": "0xabc123abc123abc123abc123abc123abc123abc1" } } ``` ### Subscription parameters - `type` (string, required) Values: `"outcomeOrderUpdate"`. - `user` (string · address, required) ### Server message - `channel` (string) Values: `"outcomeOrderUpdate"`. - `data` (object) - `body` (object, nullable) - `response` (string) - `status` (string) Values: `"err"`. - `done` (boolean) - `status` (string) Values: `"done"`. - `success` (boolean) - `traceId` (string) - `user` (string · address) ### Message example ```json { "channel": "outcomeOrderUpdate", "data": { "user": "0x0000000000000000000000000000000000000000", "traceId": "abc123def456", "status": "done", "done": true, "success": false, "body": { "status": "err", "response": "Place order failed: Outcome limit orders only support GTC, GTD, and ALO orders" } } } ``` Correlate `data.traceId` with the `traceId` returned by `POST /exchange`. Pushes carry `success: false` with the reason in `body`; no push means the order was submitted. Follow [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md) for matching lifecycle events or query [`outcomeFrontendOpenOrders`](/api/info/outcomeFrontendOpenOrders.md). `body` can be null (transport failure) or contain the node's `err` response. Code `20002` means the agent is no longer authorized and the account must authorize it again before resubmitting. --- # `userHistoricalOrders` Sends a user's order history once, as a snapshot, right after you subscribe. Nothing is pushed after that. Served by the API service at `/api-ws`. WebSocket `wss://rpc.txflow-testnet.xyz/api-ws` · channel `userHistoricalOrders` · type `userHistoricalOrders` Load a user’s order history over `/api-ws` — one snapshot, no updates. - Access: By user address - Not this? [`historicalOrders`](/api/info/historicalOrders.md) — when you need paging or filters ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "subscription": { "type": "userHistoricalOrders", "user": "0xabc123abc123abc123abc123abc123abc123abc1" } } ``` ### Subscription parameters - `type` (string, required) Values: `"userHistoricalOrders"`. - `user` (string · address, required) — Account address (the owner, not an agent). ### Server message - `channel` (string) Values: `"userHistoricalOrders"`. - `data` (object) - `isSnapshot` (boolean) — Always `true`: this is the only push. - `orderHistory` (object[]) - `order` (object) - `coin` (string) - `instrumentId` (integer · uint64) - `instrumentType` (string) Values: `"perps"`, `"outcome"`. - `limitPx` (string) - `marketType` (string) Values: `"perps"`, `"outcome"`. - `oid` (integer · uint64) - `orderType` (string) — `Limit` or `Market`. - `side` (string) — `B` buy / `A` sell. - `status` (string) - `sz` (string) — Remaining size. - `timestamp` (integer · uint64) - `avgFillPrice` (string) - `cancelSource` (string) - `children` (object[]) — Attached TP/SL children when present. - `cloid` (string, nullable) - `dir` (string) — Display label — do not filter on this. - `entryPrice` (string) - `fee` (string) - `filledAmount` (string) - `isPositionTpsl` (boolean) - `isTrigger` (boolean) - `leverage` (integer) - `marginMode` (string) - `origSz` (string) - `pnl` (string) - `positionMode` (string) - `reduceOnly` (boolean) - `roe` (string) - `symbol` (string) — Display symbol when known. - `tif` (string) - `triggerCondition` (string) - `triggerPx` (string) - `status` (string) — Terminal order status. - `statusTimestamp` (integer · uint64) — When this status was recorded, Unix milliseconds. - `user` (string · address) — The account, lower-cased. ### Message example ```json { "channel": "userHistoricalOrders", "data": { "isSnapshot": true, "user": "0xabc123abc123abc123abc123abc123abc123abc1", "orderHistory": [ { "order": { "coin": "BTC", "symbol": "BTC-USDC", "side": "B", "limitPx": "65000.0", "sz": "0.01", "oid": 123456, "timestamp": 1720000000000, "triggerCondition": "", "isTrigger": false, "triggerPx": "0", "children": [ null ], "isPositionTpsl": false, "reduceOnly": false, "dir": "Open Long", "orderType": "Limit", "origSz": "0.01", "tif": "Gtc", "cloid": "my-order-001", "avgFillPrice": "65010.5", "positionMode": "OneWay", "marginMode": "cross", "leverage": 10, "pnl": "12.5", "entryPrice": "64000.0", "roe": "1.95", "fee": "0.02", "filledAmount": "0.01", "cancelSource": "UserCanceled", "status": "filled", "instrumentId": 1, "instrumentType": "perps", "marketType": "perps" }, "status": "filled", "statusTimestamp": 1720000000000 } ] } } ``` Rows have the same shape as [`historicalOrders`](/api/info/historicalOrders.md). To refresh, subscribe again or query `historicalOrders`, which also supports paging and filters; for live order changes use [`orderUpdates`](/api/websocket/orderUpdates.md). --- # `userFills` Sends a user's fill history once, as a snapshot, right after you subscribe. Nothing is pushed after that. Served by the API service at `/api-ws`. WebSocket `wss://rpc.txflow-testnet.xyz/api-ws` · channel `userFills` · type `userFills` Load a user’s fill history over `/api-ws` — one snapshot, no updates. - Access: By user address - Not this? [`userFills`](/api/info/userFills.md) — when you need paging or filters ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "subscription": { "type": "userFills", "user": "0xabc123abc123abc123abc123abc123abc123abc1" } } ``` ### Subscription parameters - `type` (string, required) Values: `"userFills"`. - `user` (string · address, required) — Account address (the owner, not an agent). - `aggregateByTime` (boolean, optional) — Accepted but currently has no effect. ### Server message - `channel` (string) Values: `"userFills"`. - `data` (object) - `fills` (object[]) - `coin` (string) - `fee` (string) - `instrumentId` (integer · uint64) - `oid` (integer · uint64) - `px` (string) - `side` (string) - `sz` (string) - `time` (integer · uint64) - `closedPnl` (string) - `crossed` (boolean, nullable) — Legacy liquidity-role flag. `userFills` returns `null` — use `tradeSide` to tell maker from taker. - `dir` (string) — Display label. - `entryPrice` (string) - `feeToken` (string) - `hash` (string) - `instrumentType` (string) Values: `"perps"`, `"outcome"`. - `leverage` (integer) - `marginMode` (string) - `marketType` (string) Values: `"perps"`, `"outcome"`. - `roe` (string) - `startPosition` (string) - `symbol` (string) - `tid` (integer · uint64) - `tradeSide` (string) — Liquidity role of this fill leg. Omitted when the engine did not record one; matches the request's `role` filter. Values: `"Maker"`, `"Taker"`. - `twapId` (integer · uint64) - `isSnapshot` (boolean) — Always `true`: this is the only push. - `user` (string · address) — The account, lower-cased. ### Message example ```json { "channel": "userFills", "data": { "isSnapshot": true, "user": "0xabc123abc123abc123abc123abc123abc123abc1", "fills": [ { "coin": "BTC", "symbol": "BTC-USDC", "px": "65010.5", "sz": "0.01", "side": "B", "time": 1720000000000, "startPosition": "0", "dir": "Open Long", "closedPnl": "0", "hash": "", "oid": 123456, "crossed": null, "fee": "0.02", "tid": 987654, "feeToken": "USDC", "twapId": 0, "entryPrice": "0", "roe": "", "tradeSide": "Taker", "marginMode": "cross", "leverage": 10, "instrumentId": 1, "instrumentType": "perps", "marketType": "perps" } ] } } ``` Rows have the same shape as [`userFills`](/api/info/userFills.md). To refresh, subscribe again or query `userFills`, which also supports paging and filters. --- # `webData2` (API service) Sends a user's open orders once, as a snapshot, right after you subscribe. Nothing is pushed after that. This is the `/api-ws` channel named `webData2`; the node `/ws` [`webData2`](/api/websocket/webData2.md) channel is a different, live aggregate. WebSocket `wss://rpc.txflow-testnet.xyz/api-ws` · channel `webData2` · type `webData2` Load a user’s open orders over `/api-ws` — one snapshot, no updates. - Access: By user address - Not this? [`orderUpdates`](/api/websocket/orderUpdates.md) — when you need the orders kept live ### Subscribe Send this frame after the socket opens, and `{"method":"ping"}` every 10 seconds to keep it alive. ```json { "method": "subscribe", "subscription": { "type": "webData2", "user": "0xabc123abc123abc123abc123abc123abc123abc1" } } ``` ### Subscription parameters - `type` (string, required) Values: `"webData2"`. - `user` (string · address, required) — Account address (the owner, not an agent). ### Server message - `channel` (string) Values: `"webData2"`. - `data` (object) - `isSnapshot` (boolean) — Always `true`: this is the only push. - `openOrders` (object[]) - `coin` (string) - `instrumentId` (integer · uint64) - `instrumentType` (string) Values: `"perps"`, `"outcome"`. - `limitPx` (string) - `marketType` (string) Values: `"perps"`, `"outcome"`. - `oid` (integer · uint64) - `orderType` (string) — `Limit` or `Market`. - `side` (string) — `B` buy / `A` sell. - `status` (string) - `sz` (string) — Remaining size. - `timestamp` (integer · uint64) - `avgFillPrice` (string) - `cancelSource` (string) - `children` (object[]) — Attached TP/SL children when present. - `cloid` (string, nullable) - `dir` (string) — Display label — do not filter on this. - `entryPrice` (string) - `fee` (string) - `filledAmount` (string) - `isPositionTpsl` (boolean) - `isTrigger` (boolean) - `leverage` (integer) - `marginMode` (string) - `origSz` (string) - `pnl` (string) - `positionMode` (string) - `reduceOnly` (boolean) - `roe` (string) - `symbol` (string) — Display symbol when known. - `tif` (string) - `triggerCondition` (string) - `triggerPx` (string) - `user` (string · address) — The account, lower-cased. ### Message example ```json { "channel": "webData2", "data": { "isSnapshot": true, "user": "0xabc123abc123abc123abc123abc123abc123abc1", "openOrders": [ { "coin": "BTC", "symbol": "BTC-USDC", "side": "B", "limitPx": "65000.0", "sz": "0.01", "oid": 123456, "timestamp": 1720000000000, "triggerCondition": "", "isTrigger": false, "triggerPx": "0", "children": [ null ], "isPositionTpsl": false, "reduceOnly": false, "dir": "Open Long", "orderType": "Limit", "origSz": "0.01", "tif": "Gtc", "cloid": "my-order-001", "avgFillPrice": "65010.5", "positionMode": "OneWay", "marginMode": "cross", "leverage": 10, "pnl": "12.5", "entryPrice": "64000.0", "roe": "1.95", "fee": "0.02", "filledAmount": "0.01", "cancelSource": "UserCanceled", "status": "filled", "instrumentId": 1, "instrumentType": "perps", "marketType": "perps" } ] } } ``` Rows have the same shape as the `order` rows of [`historicalOrders`](/api/info/historicalOrders.md). To keep open orders live, use [`orderUpdates`](/api/websocket/orderUpdates.md) or poll [`frontendOpenOrders`](/api/info/frontendOpenOrders.md). --- # Exchange API (Beta) `POST /exchange` carries every **signed write**: orders, cancels, leverage and margin changes, agent authorization, fund movements, and builder-code actions. `action.type` selects the operation; each one has its own page. > **Mainnet access is whitelisted:** On mainnet, `POST /exchange` is open only to whitelisted accounts. Apply for access at [txflow.com/apply](https://txflow.com/apply). Testnet needs no application, so every action on this page can be run there today. How to sign and wrap an action is covered once, in [Signing](/api/signing.md). ## Actions Trading actions are signed by an **agent key**. Fund movements and authorizations are signed by the **account key** — the user's wallet. A new account deposits, approves a builder code with `approveBuilderFee`, then authorizes its agent with `approveAgent`; see [Set up an account](/api/flows.md#set-up-an-account). | Action | Signed by | What it does | | ------ | --------- | ------------ | | **Account setup** | | | | [`approveBuilderFee`](/api/exchange/approveBuilderFee.md) | Account key | Approve or update a builder's maximum fee rate — step 2 of setup | | [`approveAgent`](/api/exchange/approveAgent.md) | Account key | Authorize — or revoke — an agent key — step 3 of setup | | **Funds** | | | | [`tokenSend`](/api/exchange/tokenSend.md) | Account key | Move tokens between sub-accounts, to another user, or to a TxRelay peg | | [`withdraw3`](/api/exchange/withdraw3.md) | Account key | Withdraw USDC to Arbitrum via Bridge2 | | [`vaultTransfer`](/api/exchange/vaultTransfer.md) | Agent key | Deposit USDC into a vault, or withdraw from it | | **Orders** | | | | [`order`](/api/exchange/order.md) | Agent key | Place up to 50 orders in one action | | [`cancel`](/api/exchange/cancel.md) | Agent key | Cancel up to 200 orders by order id | | [`cancelByCloid`](/api/exchange/cancelByCloid.md) | Agent key | Cancel up to 200 orders by client order id | | [`modify2`](/api/exchange/modify2.md) | Agent key | Amend a resting order's price and size in place, keeping its order id and queue position | | [`modifyTpsl`](/api/exchange/modifyTpsl.md) | Agent key | Change or remove a position's take-profit / stop-loss pair | | [`twapOrder`](/api/exchange/twapOrder.md) | Agent key | Execute a perp order in slices over a fixed duration | | [`twapCancel`](/api/exchange/twapCancel.md) | Agent key | Stop a running TWAP | | [`outcomeOperate`](/api/exchange/outcomeOperate.md) | Agent key | Split collateral into YES/NO shares, or merge a complete set back | | **Margin** (perpetuals) | | | | [`updatePositionMode`](/api/exchange/updatePositionMode.md) | Agent key | Switch the account between one-way and hedge mode | | [`updateLeverage`](/api/exchange/updateLeverage.md) | Agent key | Set leverage for one instrument | | [`updateMarginMode`](/api/exchange/updateMarginMode.md) | Agent key | Switch an instrument between cross and isolated margin | | [`updateIsolatedMargin`](/api/exchange/updateIsolatedMargin.md) | Agent key | Add or remove margin on an isolated position | | **Builder codes** | | | | [`revokeBuilderFee`](/api/exchange/revokeBuilderFee.md) | Account key | Remove a builder-fee approval | | [`claimBuilderRewards`](/api/exchange/claimBuilderRewards.md) | Agent key | Claim the builder's unclaimed rewards | | **Referral** | | | | [`setReferrer`](/api/exchange/setReferrer.md) | Agent key | Bind the account to a referrer's code | | [`registerReferrer`](/api/exchange/registerReferrer.md) | Agent key | Create the account's own referral code | Builder-fee actions need protocol version 47 or later. Cross-chain deposit addresses come from the [TxRelay API](/api/txrelay.md), not `/exchange`. ### Outcome markets Outcome orders use the same `order`, `cancel`, `cancelByCloid`, and `modify2` actions, with the market's instrument id as the asset. An Outcome order can be accepted **asynchronously**: an `ok` response means it was queued, not matched. A queued order that fails is pushed on [`outcomeOrderUpdate`](/api/websocket/outcomeOrderUpdate.md) (successes are not); the matching lifecycle is on [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md). ### Market lifecycle actions Creating markets, pushing oracle prices, and settling markets go through `outcomeDeploy`. It is **permissioned** — only registered outcome deployers and platform hot keys can sign it, and oracle pushes must also come from the instrument's `oracle_updater` or the exchange's configured oracle addresses — so it is not part of this reference. Integrations trade existing markets and read them through [`outcomeMeta`](/api/info/outcomeMeta.md); the protocol model is in [TIP 3](/tip/outcome.md). ## Attach a builder to an order Once the user has approved a builder with [`approveBuilderFee`](/api/exchange/approveBuilderFee.md), an `order` action can carry one `builder` object: ```json { "type": "order", "grouping": "na", "orders": [ { "a": 1, "b": true, "p": "50000.0", "s": "0.01", "r": false, "t": { "limit": { "tif": "Gtc" } } } ], "builder": { "b": "0x2e4D75dF2eE2653Ea7A113Cc1Fa36d45e923061F", "f": 10 } } ``` | Field | Type | Description | | ----- | ---- | ----------- | | `builder.b` | address | Builder wallet address | | `builder.f` | uint32 | Fee in tenths of a basis point: `rate = f / 100,000`; `f = 10` is 1 bp (`0.01%`) | The order signature covers `builder`. The fee is charged on top of exchange fees, and may exceed neither the user's approval nor the market cap: `f <= 100` (`0.1%`) for perpetuals, `f <= 1000` (`1%`) for Outcome. If the builder is unapproved, the market is unsupported, or either cap is exceeded, the fee is dropped and the order still goes through. Fees accrue only while the builder holds at least 100 USDC in its perpetual account; [`builderState`](/api/info/builderState.md) reports that balance, eligibility, and accrued rewards. See the [Builder Program technical documentation](/builders/technical.md) for the end-to-end flow. ## Response Every action returns its response type and a **single** status, however many orders or cancels it carries: ```json { "status": "ok", "code": 0, "data": { "type": "PlaceOrder", "data": { "statuses": ["success"] } } } ``` - `"success"` means the transaction was **accepted by consensus**; it has not executed yet. Send `"confirm": "executed"` next to `action` in the request body to wait for the execution result instead: the reply then carries `"confirm": "executed"`, a failed execution comes back as `{ "error": "" }` in `statuses`, and a timeout returns HTTP 202 `{ "status": "pending", "txHash": … }`. - The response never says whether an order rested, filled, or was cancelled. Read that from [`orderUpdates`](/api/websocket/orderUpdates.md) or [`frontendOpenOrders`](/api/info/frontendOpenOrders.md). - `data.type` is the action's internal name and may differ from the request — `order` returns `PlaceOrder`; `cancel` and `cancelByCloid` both return `CancelOrder`. - A rejected request returns `"status": "err"` with a non-zero `code` and the reason in `message` — for example `{ "status": "err", "code": 10000, "message": "Price precision does not match the tick size: 0.1" }`. Some gateway rejections, such as rate limits, put the reason in `response` instead. - An Outcome order can instead be acknowledged asynchronously with `{ "status": "ok", "traceId": "…" }` — see [Outcome markets](#outcome-markets). --- # `approveBuilderFee` Approves a builder to charge the signing user up to `maxFeeRate`. Submitting the action again for the same builder updates the existing rate. This is step 2 of [account setup](/api/flows.md#set-up-an-account): after the deposit, the user's wallet approves the builder you trade through, then approves the agent with [`approveAgent`](/api/exchange/approveAgent.md). Read the result back with [`maxBuilderFee`](/api/info/maxBuilderFee.md) or [`approvedBuilders`](/api/info/approvedBuilders.md). `POST /exchange` · type `approveBuilderFee` Let a builder charge this user a fee, up to a max rate. · [Direct EIP-712, signed by the account key](/api/signing.md#flow-1--direct-eip-712) - Access: Account-signed ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `approveBuilderFee`. Values: `"approveBuilderFee"`. - `txflowNetwork` (string, required) — TxFlow network name used by the EIP-712 action and domain. - `chainId` (integer · uint32, required) — TxFlow chain ID. - `apiVersion` (integer · uint32, required) — TxFlow signing API version. - `builder` (string · address, required) — Builder wallet to approve. - `maxFeeRate` (string, required) — Maximum percentage, for example `0.02%`. Must be greater than zero and at most `1%`. - `nonce` (integer · uint64, required) — Fresh millisecond timestamp. Direct EIP-712 actions carry the nonce inside the action as well as in the outer payload; the two must match. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"approveBuilderFee","txflowNetwork":"TxFlow-Testnet","chainId":2511,"apiVersion":1,"builder":"0x2e4D75dF2eE2653Ea7A113Cc1Fa36d45e923061F","maxFeeRate":"0.02%","nonce":1757992743994},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · ApproveBuilderFee (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"ApproveBuilderFee"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "ApproveBuilderFee", "data": { "statuses": [ "success" ] } } } ``` A user may approve up to 20 distinct builders. The builder cannot be the zero address, the signing user, or a vault. Updating an already approved builder does not consume another slot. `maxFeeRate` is a percentage string, greater than `0%` and at most `1%` — the highest per-market cap, since an approval is builder-level rather than per-market. The lower perpetual cap still applies per order. ## Signing This action is signed directly by the **account key** as the `ApproveBuilderFee` EIP-712 primary type: ``` ApproveBuilderFee(string txflowNetwork,uint32 chainId,uint32 apiVersion,address builder,string maxFeeRate,uint64 nonce) ``` The signed message contains the action's business fields without the wire-level `type` discriminator: ```json { "type": "approveBuilderFee", "txflowNetwork": "TxFlow-Testnet", "chainId": 2511, "apiVersion": 1, "builder": "0x2e4D75dF2eE2653Ea7A113Cc1Fa36d45e923061F", "maxFeeRate": "0.02%", "nonce": 1757992743994 } ``` The distinct `ApproveBuilderFee` typehash separates this action from `revokeBuilderFee`. The replay nonce is the body's own `nonce`, so send the same value in the envelope. The recovered signer must be the approving user itself; an authorized agent cannot approve on the owner's behalf. When the outer payload carries `signatureChainId`, use it for `domain.chainId` while keeping the action's business `chainId` unchanged in the struct hash. See [Signing](/api/signing.md#builder-fee-actions). ## Next steps The approval takes effect once the action is included in a block; subsequent orders may carry this builder up to the approved rate — see [Attach a builder to an order](/api/exchange.md#attach-a-builder-to-an-order). --- # `approveAgent` Authorizes an agent wallet to sign actions on behalf of the signing account. Most Exchange API actions — `order`, `cancel`, `modify2`, the margin actions — are **agent-signed**, so approving an agent is the prerequisite for programmatic trading: the agent key signs day to day, while the account's own key stays offline except for this approval and the direct EIP-712 actions. Approve the agent as the last step of [account setup](/api/flows.md#set-up-an-account): first deposit, then approve a builder code with [`approveBuilderFee`](/api/exchange/approveBuilderFee.md), then approve the agent. The same action also revokes an agent — see [Revoking an agent](#revoking-an-agent). `POST /exchange` · type `approveAgent` Authorize (or revoke) the agent key that signs day-to-day trading. Do this once before trading. · [Direct EIP-712, signed by the account key](/api/signing.md#flow-1--direct-eip-712) - Access: Account-signed - Not this? [`approvedAgents`](/api/info/approvedAgents.md) — when you want to list current agents ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `approveAgent`. Values: `"approveAgent"`. - `txflowNetwork` (string, required) — TxFlow network name used by the EIP-712 action and domain. - `chainId` (integer · uint32, required) — TxFlow chain ID. - `apiVersion` (integer · uint32, required) — TxFlow signing API version. - `agentAddress` (string · address, required) — Agent wallet to authorize. The zero address revokes instead. - `agentName` (string, nullable, optional) — Label for this agent, at most 64 characters. Omitted or `null` is treated as the empty label. - `nonce` (integer · uint64, required) — Fresh millisecond timestamp. Direct EIP-712 actions carry the nonce inside the action as well as in the outer payload; the two must match. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"approveAgent","txflowNetwork":"TxFlow-Testnet","chainId":2511,"apiVersion":1,"agentAddress":"0x0202020202020202020202020202020202020202","agentName":"my-trading-bot","nonce":1757992743994},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · ApproveAgent (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"ApproveAgent"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "ApproveAgent", "data": { "statuses": [ "success" ] } } } ``` `txflowNetwork`, `chainId`, and `apiVersion` must match the node's configuration, otherwise the request is rejected with `Network mismatch: expected , got ` (likewise `Chain ID mismatch` and `API version mismatch`). ## Agents are keyed by label An account holds a map of `agentName` → `agentAddress`. The label, not the address, is the key: - Approving a **new** label adds an agent. An account may hold at most 100 labels; beyond that the request fails with `Max agents per account reached (100)`. - Approving an **existing** label rebinds it to the new address and drops the old one, which is how you rotate an agent key. This does not consume another slot. - Omitting `agentName` uses the empty label — a single unnamed agent per account. Sending a second unnamed approval replaces the first rather than adding one. An agent address may only be bound to one account. Reusing an address already registered as another account's agent fails with `Agent address is already used`, and approving your own address fails with `Cannot approve self as agent`. ## Revoking an agent Set `agentAddress` to the zero address and name the label to revoke: ```json { "type": "approveAgent", "txflowNetwork": "TxFlow-Testnet", "chainId": 2511, "apiVersion": 1, "agentAddress": "0x0000000000000000000000000000000000000000", "agentName": "my-trading-bot", "nonce": 1757992743994 } ``` The label must already exist, otherwise the request fails with `Agent with this label does not exist`. Revocation removes the label and frees the address for reuse. ## Preconditions | Error | Cause | | ----- | ----- | | `Please deposit first` | The signing address has no account yet — deposit before approving an agent | | `Cannot approve self as agent` | `agentAddress` equals the signing address | | `Agent address is already used` | That address is already an agent of some account | | `Agent with this label does not exist` | Revoking (zero address) a label the account does not hold | | `Max agents per account reached (100)` | Adding a new label at the 100-label limit | | `Agent name length exceeds maximum of 64 characters` | `agentName` is too long | On a multi-sig account this action must arrive wrapped in a `multiSig` payload — an agent cannot approve further agents, and the converted account's original key can no longer single-sign. ## Signing This is a direct EIP-712 user-signed action, signed by the **account** key rather than an agent. Its exact primary type is: ```text ApproveAgent(string txflowNetwork,uint32 chainId,uint32 apiVersion,address agentAddress,string agentName,uint64 nonce) ``` When `agentName` is `null` or omitted, hash the empty string `""` in its place. If the outer `signatureChainId` is set, use it as the EIP-712 domain chain ID; the action's `chainId` field does not change. See [Signing](/api/signing.md). ## Next steps Once included in a block, the agent may sign on the account's behalf. List an account's agents with [`approvedAgents`](/api/info/approvedAgents.md), and resolve which account an agent belongs to with [`agentOwner`](/api/info/agentOwner.md). --- # `tokenSend` Moves a token between TxFlow sub-accounts, or sends it to another address when `toAddress` is present. Outcome trading settles on the `outcome` sub-account, so collateral commonly moves from `perp` to `outcome` before the first trade. The same action also sends to another user or a [TxRelay](/api/txrelay.md) peg. Direct EIP-712, signed by the **account** key — an agent cannot move funds. `POST /exchange` · type `tokenSend` Move funds between your sub-accounts (e.g. perp → outcome), to another user, or to a TxRelay peg. · [Direct EIP-712, signed by the account key](/api/signing.md#flow-1--direct-eip-712) - Access: Account-signed - Not this? [`withdraw3`](/api/exchange/withdraw3.md) — when you are withdrawing USDC to Arbitrum ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `tokenSend`. Values: `"tokenSend"`. - `txflowNetwork` (string, required) — TxFlow network name used by the EIP-712 action and domain. - `chainId` (integer · uint32, required) — TxFlow chain ID. - `apiVersion` (integer · uint32, required) — TxFlow signing API version. - `toAddress` (string · address, nullable, optional) — `null` or omitted transfers between your own sub-accounts (`TransferToken`). A non-null address sends to that user or a TxRelay peg (`SendToken`) and may charge a one-time activation fee. - `token` (string, required) — `NAME:address` of the TxFlow-side token. USDC uses the network USDC contract. - `amount` (string, required) — Decimal amount in token units, not wei. - `nonce` (integer · uint64, required) — Fresh millisecond timestamp. Direct EIP-712 actions carry the nonce inside the action as well as in the outer payload; the two must match. - `fromAccountType` (string, required) — Source sub-account. `perps` is accepted as an alias of `perp`. Values: `"perp"`, `"spot"`, `"outcome"`. - `toAccountType` (string, required) — Destination sub-account on the recipient. Values: `"perp"`, `"spot"`, `"outcome"`. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"tokenSend","txflowNetwork":"TxFlow-Testnet","chainId":2511,"apiVersion":1,"token":"USDC:0xd04f0d19E8FbA49cfA541E272F9C60a2ad720464","amount":"100","nonce":1757992743994,"fromAccountType":"perp","toAccountType":"outcome"},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · TokenSend (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"TokenSend"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "TokenSend", "data": { "statuses": [ "success" ] } } } ``` For a transfer between your own sub-accounts, omit `toAddress`. The EIP-712 primary type is `TransferToken`. When `toAddress` is present, the primary type is `SendToken`. `token` is `NAME:address` (for example the testnet USDC id used in [Try Testnet](/try-testnet/quickstart.md)). `fromAccountType` and `toAccountType` name the sub-accounts: `perp`, `outcome`, or `spot` (non-USDC token balances, used for cross-chain withdrawals below). Both the action and the outer exchange envelope carry the same nonce. ## Two EIP-712 primary types | `toAddress` | Primary type | Typical use | | ----------- | ------------ | ----------- | | `null` or omitted | `TransferToken` | Between your own sub-accounts (e.g. perp ↔ outcome) | | A non-zero address | `SendToken` | Another TxFlow user, or the TxRelay peg for a cross-chain withdraw | ``` TransferToken(string txflowNetwork,string amount,string fromAccountType,string toAccountType,string token,uint64 nonce) SendToken(string txflowNetwork,address toAddress,string amount,string fromAccountType,string toAccountType,string token,uint64 nonce) ``` The wire action always uses `type: "tokenSend"`. Hash the empty string for omitted `toAddress` is **not** used — the `TransferToken` typehash is selected instead. The first `SendToken` to a new destination may charge a one-time **1 USDC** activation fee (`0` after that destination has been activated, when sending to yourself, or when the destination is waived). ## Cross-chain withdraw (USDC and other assets) 1. `POST` [TxRelay deposit-address](/api/txrelay/deposit-address.md) with `src_chain: "txflow"`, `dst_chain` set to the external network, and `dst_address` set to the user's destination. 2. `tokenSend` the asset **to the peg** returned as `deposit_address`: - USDC: `fromAccountType` / `toAccountType` = `perp` - other assets: `spot` → `spot` Native Arbitrum USDC outflows use [`withdraw3`](/api/exchange/withdraw3.md), not this action. ## Signing This action is signed directly by the account key; an agent key cannot move funds. `signatureChainId`, when supplied in the outer envelope, selects the EIP-712 domain chain id without changing the action's business `chainId`. See [Signing](/api/signing.md). ## Next steps Confirm with [`userNonFundingLedgerUpdates`](/api/info/userNonFundingLedgerUpdates.md) (`deltaTypes` including `tokenSend`) and sub-account reads such as [`perpClearinghouseState`](/api/info/perpClearinghouseState.md). --- # `withdraw3` Sends **USDC only** from the perp account to an Arbitrum address via Bridge2. The frontend never calls the Bridge2 contract for this path — the node records the withdrawal and Bridge2 releases USDC after the protocol delay. Direct EIP-712, signed by the **account** key. An agent cannot withdraw. `POST /exchange` · type `withdraw3` Withdraw USDC from the perp account to an Arbitrum address. · [Direct EIP-712, signed by the account key](/api/signing.md#flow-1--direct-eip-712) - Access: Account-signed - Not this? [`tokenSend`](/api/exchange/tokenSend.md) — when it is another token or another chain (via TxRelay) ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `withdraw3`. Values: `"withdraw3"`. - `txflowNetwork` (string, required) — TxFlow network name used by the EIP-712 action and domain. - `chainId` (integer · uint32, required) — TxFlow chain ID. - `apiVersion` (integer · uint32, required) — TxFlow signing API version. - `destination` (string · address, required) — Arbitrum address that receives USDC. Must be non-zero. - `signatureChainId` (string, required) — Hex string (no `0x`) of the EVM chain the wallet is connected to when signing. Testnet Bridge2 is Arbitrum Sepolia (`421614` → `66eee`); mainnet is Arbitrum One. Distinct from the action `chainId`, which is the TxFlow chain. - `tokenAddress` (string · address, required) — TxFlow-side USDC contract. Withdrawals are USDC-only. - `amount` (string, required) — Decimal USDC amount. Minimum is `2`. A flat 1 USDC withdrawal fee is charged on top of the amount sent out. - `nonce` (integer · uint64, required) — Fresh millisecond timestamp. Direct EIP-712 actions carry the nonce inside the action as well as in the outer payload; the two must match. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"withdraw3","txflowNetwork":"TxFlow-Testnet","chainId":2511,"apiVersion":1,"destination":"0x0202020202020202020202020202020202020202","signatureChainId":"66eee","tokenAddress":"0xd04f0d19E8FbA49cfA541E272F9C60a2ad720464","amount":"10","nonce":1757992743994},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · Withdraw3 (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"Withdraw3"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "Withdraw3", "data": { "statuses": [ "success" ] } } } ``` `tokenAddress` is the TxFlow-side USDC contract: `0xd04f0d19E8FbA49cfA541E272F9C60a2ad720464`. Besides it, callers only supply `destination`, `amount`, and which Arbitrum network the wallet is on. ## Preconditions | Constraint | Detail | | ---------- | ------ | | Minimum amount | `2` USDC | | Fee | Flat **1 USDC**, charged regardless of destination chain in the product UI; this action itself always pays out on Arbitrum | | Token | TxFlow USDC contract only — other assets go through [`tokenSend`](/api/exchange/tokenSend.md) + [TxRelay](/api/txrelay.md) | | Destination | Non-zero Arbitrum address | `signatureChainId` on the **action** is a hex string of the wallet's connected EVM chain (Arbitrum Sepolia `421614` → `66eee` on testnet). The action `chainId` remains the TxFlow chain (`2511` / `869`). The envelope may also set numeric `signatureChainId` for the EIP-712 **domain**. ## Signing ``` Withdraw3(string txflowNetwork,address destination,string signatureChainId,address tokenAddress,string amount,uint64 nonce) ``` See [Signing](/api/signing.md). Multi-sig accounts cannot single-sign this action. ## Next steps Track status with [`userNonFundingLedgerUpdates`](/api/info/userNonFundingLedgerUpdates.md). For Ethereum, Base, Polygon, Solana, or other non-Arbitrum destinations, use TxRelay deposit-address plus [`tokenSend`](/api/exchange/tokenSend.md) instead of `withdraw3`. --- # `vaultTransfer` Moves USDC between the account's perp balance and a vault. `isDeposit: true` deposits `usd` from the perp account into the vault; `isDeposit: false` withdraws `usd` from the account's vault equity back to it. The action is signed by an approved agent. `POST /exchange` · type `vaultTransfer` Deposit USDC from the perp account into a vault, or withdraw it back. · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Markets: Perp only - Not this? [`vaultUpdates`](/api/websocket/vaultUpdates.md) — when you want to confirm the transfer and the new vault equity ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `vaultTransfer`. Values: `"vaultTransfer"`. - `vaultAddress` (string · address, required) — Vault to deposit into or withdraw from. Hex on the wire; hashed as its 20 raw bytes. - `usd` (string, required) — USDC amount in token units, at most 6 decimal places. - `isDeposit` (boolean, required) — `true` deposits into the vault, `false` withdraws from it. - `token` (string, required) — `NAME:address` of the network USDC contract. Vault transfers are USDC-only. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"vaultTransfer","vaultAddress":"0x0303030303030303030303030303030303030303","usd":"100","isDeposit":true,"token":"USDC:0xd04f0d19E8FbA49cfA541E272F9C60a2ad720464"},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · VaultTransfer (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"VaultTransfer"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "VaultTransfer", "data": { "statuses": [ "success" ] } } } ``` ## Signing `vaultTransfer` is agent-signed with two differences from other actions: - `vaultAddress` inside the action is hashed as its **20 raw bytes** (MessagePack binary), while the JSON body carries the hex string. - The hash always ends with a `0x00` vault flag. Send the envelope `vaultAddress` as `null`; the target vault is already in the action. Keep the key order `vaultAddress`, `usd`, `isDeposit`, `token`. See [MessagePack rules](/api/signing.md#messagepack-rules). ## Next steps Confirm the transfer and the new vault equity on [`vaultUpdates`](/api/websocket/vaultUpdates.md). The perp balance change shows in [`clearinghouseState`](/api/info/clearinghouseState.md). --- # `order` Places one or more orders. Every order in the batch is signed together and applied in array order. `POST /exchange` · type `order` Place one or more orders (limit, market, trigger, TP/SL). · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Not this? [`modify2`](/api/exchange/modify2.md) — when you want to change price or size of a resting order without losing queue position ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `order`. Values: `"order"`. - `grouping` (string, required) — How the batch is interpreted as a group. Values: `"na"`, `"normalTpsl"`, `"positionTpsl"`, `"normalTrigger"`, `"reversePosition"`, `"closeAll"`. - `orders` (OrderBody[], required) — Order bodies; at most 50 per action. - `a` (integer · uint32, required) — Instrument's external id — the `index` value from `perpMeta`; Outcome markets use the `instrument_id`. Long form: `asset`. - `b` (boolean, required) — `true` to buy, `false` to sell. Long form: `isBuy`. - `p` (string, required) — Limit price. The literal `"bbo"` prices at the current best bid/offer. Long form: `limitPx`. - `s` (string, required) — Size in base units. Long form: `sz`. - `r` (boolean, optional) — Reduce-only. Defaults to `false`. Long form: `reduceOnly`. On a perpetual the node may set this to `true` by itself when your available margin does not cover the order and the order would only shrink an existing position — see Auto-conversion to reduce-only on the order page. - `t` (oneOf, required) — Either a `limit` or a `trigger` body. Long form: `orderType`. - One of · variant 1: - `limit` (object, required) - `tif` (string, required) — Time-in-force. Values: `"Gtc"`, `"Ioc"`, `"Alo"`, `"Gtd"`, `"FrontendMarket"`. - One of · variant 2: - `trigger` (object, required) - `isMarket` (boolean, required) — Fill at market once triggered. - `triggerPx` (string, required) — Price at which the order activates. - `tpsl` (string, optional) — Take-profit or stop-loss. Values: `"tp"`, `"sl"`. - `c` (string, optional) — Client order id, used later by `cancelByCloid`. Long form: `cloid`. - `m` (string, optional) — Margin mode. Long form: `marginMode`. Optional: when omitted the node routes the order to the account's current margin mode for this market (its saved position profile, `cross` for an account that has never set one) — so the same body can land on a different mode after an `updateMarginMode`. API clients should send `m` explicitly. The margin mode is part of the position key, so it also selects which position the order opens into or reduces. Values: `"cross"`, `"isolated"`. - `expiresAt` (integer · uint64, optional) — Required when `tif` is `Gtd`. Unix milliseconds, and must be in the future. - `builder` (Builder, optional) — Optional builder code. The order signature covers this object. - `b` (string · address, required) — Builder wallet address. - `f` (integer · uint32, required) — Fee in tenths of a basis point: `rate = f / 100,000`, so `f = 10` is 1 bp (`0.01%`). ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"order","grouping":"na","orders":[{"a":1,"b":true,"p":"50000.0","s":"0.01","r":false,"t":{"limit":{"tif":"Gtc"}}}]},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · PlaceOrder (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"PlaceOrder"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "PlaceOrder", "data": { "statuses": [ "success" ] } } } ``` ## Outcome orders For an Outcome market, `a` is its `instrument_id`. YES is the long leg and NO the short leg; `b` and `r` together encode the leg and direction: | Intent | `b` | `r` | | ------ | --- | --- | | Buy YES | `true` | `false` | | Buy NO | `false` | `false` | | Sell YES | `false` | `true` | | Sell NO | `true` | `true` | `p` is the price of the traded leg, between 0 and 1: buying NO at `0.30` sends `b: false, r: false, p: "0.30"`. Outcome orders use `grouping: "na"` and do not support trigger/TP-SL order types. Time-in-force must be `Gtc`, `Gtd`, or `Alo`; an `Ioc` order is rejected with `Outcome limit orders only support GTC, GTD, and ALO orders`, reported asynchronously on [`outcomeOrderUpdate`](/api/websocket/outcomeOrderUpdate.md). The initial HTTP response can be an asynchronous acceptance (`{"status":"ok","traceId":…}`) rather than a fill. If the queued order fails, [`outcomeOrderUpdate`](/api/websocket/outcomeOrderUpdate.md) pushes the reason under that `traceId`; successes are not pushed. Follow the matching lifecycle on [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md) or read [`outcomeFrontendOpenOrders`](/api/info/outcomeFrontendOpenOrders.md). Order bodies use abbreviated keys. The long forms noted above are accepted on input, but the abbreviated keys are what the signature covers. The optional `builder` object is documented under [Attach a builder to an order](/api/exchange.md#attach-a-builder-to-an-order). ## Order body Fields as listed under `orders` in **Action fields** above. ### Price, size, and client order id | Rule | Rejected with | | ---- | ------------- | | `p` is a multiple of the instrument's `priceTick` from [`perpMeta`](/api/info/perpMeta.md), with no more decimals than the tick | `Price precision does not match the tick size: ` / `Tick size does not match` | | `s` has at most `szDecimals` decimals and lies between the minimum size and `maxLimitOrderSize` / `maxMarketOrderSize` | `Minimum order amount is ` / `Maximum limit order amount is …` | | A limit price stays inside the oracle price band | `Order buy price is not within the price limit (Minimum price: …; Maximum price: …)` | | `c` is any string up to 64 bytes, unique among your open orders. A `0x` prefix is reserved for liquidity-provider accounts, on every market | `cloid length exceeds maximum` / `Duplicate client order ID ` / `Invalid cloid format` | ## Order type A limit order carries a time-in-force: ```json { "limit": { "tif": "Gtc" } } ``` | `tif` | Behavior | | ----- | -------- | | `Gtc` | Good-til-canceled; rests on the book | | `Ioc` | Immediate-or-cancel; unfilled remainder is canceled | | `Alo` | Add-liquidity-only (post-only); rejected if it would take | | `Gtd` | Good-til-date; requires `expiresAt` | | `FrontendMarket` | Treated as a market order | A trigger order carries a trigger price instead: ```json { "trigger": { "isMarket": true, "triggerPx": "48000.0", "tpsl": "sl" } } ``` `tpsl` is `tp` (take-profit) or `sl` (stop-loss). Trigger orders are only accepted under a `normalTpsl`, `positionTpsl`, or `normalTrigger` grouping; sending one with `grouping: "na"` is rejected with `Trigger orders are not supported for grouping na`. ### Auto-conversion to reduce-only On a **perpetual**, an order you sent with `r: false` can come back as a reduce-only order. When the available margin in your perp account does not cover the order's required margin, the node does not reject it outright — it first checks whether the order would only shrink an existing position, and if so flips `r` to `true` and places it with no margin frozen. All of the following must hold, or the order is rejected with `Insufficient margin in account` as before: | Condition | | --------- | | The market is a perpetual (spot and Outcome orders are rejected, never converted) | | The account is in **One-Way** position mode | | You hold a position in that market, in the margin mode the order routes to, on the **opposite** side (a buy reduces a short, a sell reduces a long) | | The order size does not exceed that position's size | | The account is not a liquidity-provider / market-maker account (those skip the margin freeze entirely, so they never reach this path) | Two consequences to plan for: - **The converted order can no longer open or flip.** It will only shrink the position, and the remainder past the position size is not placed. - **Attached TP/SL children are dropped.** A reduce-only order cannot carry protection orders, so the `normalTpsl` children prepared with the parent are removed at conversion time. Re-arm the protection on the resulting position yourself. The action response does not report the conversion — its `statuses` carry one entry for the whole action, not per-order results. Read `reduceOnly` back from [`frontendOpenOrders`](/api/info/frontendOpenOrders.md) (or [`historicalOrders`](/api/info/historicalOrders.md) once the order is terminal) to see the effective flag, and send `r: true` explicitly whenever reduce-only is what you actually want. ### Triggered is not filled A TP/SL that reaches its trigger price is **fired**, not guaranteed to execute. Two separate things happen: 1. **Trigger** — the protection order's trigger state becomes `Triggered`, and in a TP/SL pair the **other side is canceled at the same moment** (`Canceled` trigger state). This is OCO: it happens on trigger, not on fill. 2. **Execution** — the fired order is then submitted to the matcher like any other order, and can be fully filled, partially filled, canceled, or rejected. A fired order that does not fill does **not** revert to a pending protection order — it is in a terminal state and the position is left unprotected. Common reasons the fired leg does not fill: | Outcome | Why | | ------- | --- | | Canceled with no or partial fill | A fired market order carries the oracle price-protection band; the remainder past the band, or past the book's available liquidity, is canceled | | Rejected | The limit price falls outside the price band or off the tick, or the instrument is halted / delisted, or the order is over the open-interest cap | | Skipped entirely | The underlying position is already flat (closed, liquidated, or closed by the other side of the pair) when the trigger fires | Because the sibling was already canceled in step 1, a failed fill leaves **neither** protection order live. Re-arm the protection yourself after confirming the outcome. Confirm the result rather than assuming it: read the pair back with [`tpslOrders`](/api/info/tpslOrders.md) (live) and [`historicalTpslOrders`](/api/info/historicalTpslOrders.md) (terminal, carrying `cancelSource` / `cancelReason`), check the resulting order on [`historicalOrders`](/api/info/historicalOrders.md), and follow [`orderUpdates`](/api/websocket/orderUpdates.md) for the live transitions. ## Next steps Read resting orders back with [`frontendOpenOrders`](/api/info/frontendOpenOrders.md). To reprice or resize a resting order, use [`modify2`](/api/exchange/modify2.md). --- # `cancel` Cancels orders by exchange-assigned order id. To cancel by your own client order id, use [`cancelByCloid`](/api/exchange/cancelByCloid.md). `POST /exchange` · type `cancel` Cancel orders by the exchange-assigned order id. · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Not this? [`cancelByCloid`](/api/exchange/cancelByCloid.md) — when you track orders by your own client id ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `cancel`. Values: `"cancel"`. - `cancels` (CancelBody[], required) — Cancel bodies; at most 200 per action. - `a` (integer · uint32, required) — Instrument's external id — the `index` value from `perpMeta`; Outcome markets use the `instrument_id`. Long form: `asset`. - `o` (integer · uint64, required) — Order id to cancel, as an integer. A numeric string is parsed too, but the node hashes the value as an integer, so sign it as one. Long form: `oid`. - `grouping` (string, optional) — Selects which order family the id refers to. Defaults to `na`. Values: `"na"`, `"tpsl"`, `"trigger"`. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"cancel","cancels":[{"a":1,"o":123456789}]},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · CancelOrder (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"CancelOrder"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "CancelOrder", "data": { "statuses": [ "success" ] } } } ``` ## Cancel body Fields as listed under `cancels` in **Action fields** above. ## Next steps The response is a single `success` for the whole action and does not report which orders were cancelled — confirm the result with [`frontendOpenOrders`](/api/info/frontendOpenOrders.md). For an Outcome order, `a` is the market's `instrument_id`. Confirm it with [`outcomeFrontendOpenOrders`](/api/info/outcomeFrontendOpenOrders.md) and follow the result on [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md). --- # `cancelByCloid` Cancels orders by the `cloid` supplied when the order was placed. Note that this action's bodies use **full field names**, not the abbreviated keys used by [`cancel`](/api/exchange/cancel.md). Outcome orders use the same action, with the market's `instrument_id` as `asset`. `POST /exchange` · type `cancelByCloid` Cancel orders by the client order id (a plain string) you set when placing them. · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Not this? [`cancel`](/api/exchange/cancel.md) — when you have the exchange order id ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `cancelByCloid`. Values: `"cancelByCloid"`. - `cancels` (CancelByCloidBody[], required) — Cancel bodies; at most 200 per action. - `asset` (integer · uint32, required) — Instrument's external id — the `index` value from `perpMeta`; Outcome markets use the `instrument_id`. - `cloid` (string, required) — Client order id set as `c` on the original order. - `grouping` (string, optional) — Defaults to `na`. Values: `"na"`, `"tpsl"`, `"trigger"`. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"cancelByCloid","cancels":[{"asset":1,"cloid":"my-order-001"}]},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · CancelOrder (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"CancelOrder"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "CancelOrder", "data": { "statuses": [ "success" ] } } } ``` ## Cancel body Fields as listed under `cancels` in **Action fields** above. ## Next steps The response `type` is `CancelOrder`, the same as [`cancel`](/api/exchange/cancel.md). Confirm the result with [`frontendOpenOrders`](/api/info/frontendOpenOrders.md). --- # `modify2` Amends the price and size of a resting order **in place**. The order is not canceled and re-placed: it keeps its order id, its already-filled quantity, and its original sequence number, so it is not sent to the back of the queue at the new price. The action can also attach, change, or clear the order's take-profit / stop-loss children in the same signed request. `POST /exchange` · type `modify2` Amend price / size of a resting order in place; it keeps its id and queue priority. · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Not this? [`order`](/api/exchange/order.md) — when you need to change side, instrument, or TIF (cancel and re-place) ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `modify2`. Values: `"modify2"`. - `order` (Modify2Body, required) — The amendment for the resting order. - `o` (integer · uint64, required) — Order to amend, as an integer. A numeric string is parsed too, but the node hashes the value as an integer, so sign it as one. Long form: `oid`. - `p` (string, required) — New limit price. `"bbo"` is **not** accepted. Long form: `limitPx`. - `s` (string, required) — New total size in base units, not a delta. Long form: `sz`. - `t` (TriggerOrderType, optional) — Only used by `tpsl` entries, where it carries the `trigger` body. Long form: `orderType`. - `trigger` (object, required) - `isMarket` (boolean, required) — Fill at market once triggered. - `triggerPx` (string, required) — Price at which the order activates. - `tpsl` (string, optional) — Take-profit or stop-loss. Values: `"tp"`, `"sl"`. - `tpsl` (array, nullable, optional) — Exactly two bodies, one take-profit and one stop-loss. Omitted, `null`, or empty clears the order’s existing TPSL children. - `o` (integer · uint64, required) — Order to amend, as an integer. A numeric string is parsed too, but the node hashes the value as an integer, so sign it as one. Long form: `oid`. - `p` (string, required) — New limit price. `"bbo"` is **not** accepted. Long form: `limitPx`. - `s` (string, required) — New total size in base units, not a delta. Long form: `sz`. - `t` (TriggerOrderType, optional) — Only used by `tpsl` entries, where it carries the `trigger` body. Long form: `orderType`. - `trigger` (object, required) - `isMarket` (boolean, required) — Fill at market once triggered. - `triggerPx` (string, required) — Price at which the order activates. - `tpsl` (string, optional) — Take-profit or stop-loss. Values: `"tp"`, `"sl"`. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"modify2","order":{"o":444555666,"p":"52000.0","s":"0.03"}},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · Modify2 (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"Modify2"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "Modify2", "data": { "statuses": [ "success" ] } } } ``` ## Modify body Fields as listed under `order` in **Action fields** above. Only price and size are amendable. Side, instrument, reduce-only, margin mode, and time-in-force stay as they were on the original order — there is no way to change them with `modify2`. Outcome resting limit orders use the same action. Their amended price remains the YES-side book price, including when the UI displays the complementary NO price. Outcome orders do not use the `tpsl` extension. ## Size changes `s` is the new **total** size, not a delta. The already-filled quantity is preserved: | New size | Result | | -------- | ------ | | Larger than the current total | The increase is added to the unfilled remainder | | Smaller, but still above the filled quantity | The remainder becomes `newSize - filled` | | At or below the filled quantity | The order closes as fully filled at the quantity already executed | A request that changes neither price nor size is accepted and leaves the resting order alone; any `tpsl` change in the same action still applies. ## Take-profit / stop-loss When `tpsl` is present it must contain exactly two bodies — one with `tpsl: "tp"` and one with `tpsl: "sl"`: ```json { "o": 444555667, "p": "0", "s": "0.03", "t": { "trigger": { "isMarket": true, "triggerPx": "48000.0", "tpsl": "sl" } } } ``` If the order already has TPSL children, each `o` must be one of those two child order ids; otherwise the request is rejected with `Invalid TPSL order ID: . Expected or `. If the order has no TPSL children yet, the two bodies are created and bound to it, sized from the amended order size. Because `modify2` amends in place rather than canceling and re-placing, the order keeps its existing TP/SL children when you omit `tpsl` — unlike the cancel-then-place `modify` / `batchModify` actions, which delete the protection with the parent order and cannot re-attach it. Use `modify2` whenever the order being changed carries protection. ## Preconditions | Error | Cause | | ----- | ----- | | `Modify2 does not support BBO price.` | `p` is the literal `"bbo"` | | `Order limit price is invalid` / `Order size is invalid` | `p` or `s` is unparsable or not positive | | `Order not found` | No resting order with that `o` | | `Permission denied: you are not authorized to modify this order.` | The order belongs to another account | | `This instrument only supports isolated margin mode` | The resting order is on cross margin for an isolated-only instrument | | `Modify order check failed: ` | The amended order fails the leverage or available-balance check | The new price is additionally checked against the instrument's tick size and the oracle price band, and a size increase is checked against the open-interest cap. Amending is rejected while the instrument is halted or delisted. ## Next steps The response confirms acceptance, not the final book state — read the amended order back with [`frontendOpenOrders`](/api/info/frontendOpenOrders.md), and its TPSL children with [`tpslOrders`](/api/info/tpslOrders.md). For Outcome, read it back with [`outcomeFrontendOpenOrders`](/api/info/outcomeFrontendOpenOrders.md) and follow [`outcomeOrderUpdates`](/api/websocket/outcomeOrderUpdates.md). --- # `modifyTpsl` Changes the take-profit / stop-loss pair attached to an open position: trigger prices, sizes, and market or limit execution. The action is signed by an approved agent. `POST /exchange` · type `modifyTpsl` Change the trigger price, size, or limit price of a position’s TP/SL pair, or remove the pair. · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Markets: Perp only - Not this? [`positionTpslOrders`](/api/info/positionTpslOrders.md) — when you need the pair’s order ids first - Not this? [`modify2`](/api/exchange/modify2.md) — when the TP/SL is attached to a resting order rather than a position ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `modifyTpsl`. Values: `"modifyTpsl"`. - `tpsl` (ModifyTpslBody[], required) — Exactly two legs: the stop-loss and the take-profit of one pair, stop-loss first. - `o` (integer · uint64, required) — Id of the existing TP or SL order, from `positionTpslOrders`, as an integer. Long form: `oid`. - `p` (string, required) — Limit price for a limit leg (`isMarket: false`). Empty string for a market leg. Long form: `limitPx`. - `s` (string, required) — Size in base units. `"0"` closes the entire position; a positive size closes that much; `""` leaves the leg unset. Long form: `sz`. - `t` (TriggerOrderType, required) — The leg's `trigger` body. `tpsl` is required here and selects which leg this is. Long form: `orderType`. - `trigger` (object, required) - `isMarket` (boolean, required) — Fill at market once triggered. - `triggerPx` (string, required) — Price at which the order activates. - `tpsl` (string, optional) — Take-profit or stop-loss. Values: `"tp"`, `"sl"`. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"modifyTpsl","tpsl":[{"o":123456790,"p":"","s":"0","t":{"trigger":{"isMarket":true,"triggerPx":"48000","tpsl":"sl"}}},{"o":123456791,"p":"","s":"0","t":{"trigger":{"isMarket":true,"triggerPx":"56000","tpsl":"tp"}}}]},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · ModifyTpsl (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"ModifyTpsl"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "ModifyTpsl", "data": { "statuses": [ "success" ] } } } ``` ## TP/SL leg Fields as listed under `tpsl` in **Action fields** above. Always send both legs of the pair, stop-loss first. Each `o` is one of the pair's two order ids, read from [`positionTpslOrders`](/api/info/positionTpslOrders.md). To keep one leg unchanged, resend its current values under its existing `o`. | Leg | `p` | `s` | `t.trigger` | | --- | --- | --- | --- | | Market, whole position | `""` | `"0"` | `isMarket: true` | | Market, partial | `""` | size | `isMarket: true` | | Limit | limit price | `"0"` or size | `isMarket: false` | | Unset | `""` | `""` | `triggerPx: ""` | Setting both legs to unset removes the pair. ## Signing Keep the leg key order `o`, `p`, `s`, `t`, and the trigger key order `isMarket`, `triggerPx`, `tpsl`. Sign `o` as an integer, even though a numeric string is accepted on the wire. See [MessagePack rules](/api/signing.md#messagepack-rules). ## Next steps Read the updated pair back with [`positionTpslOrders`](/api/info/positionTpslOrders.md). --- # `twapOrder` Places a time-weighted average price (TWAP) order on a perp. The node splits the total size `s` into market slices sent every `interval` seconds until the `m`-minute duration ends. The action is signed by an approved agent. `POST /exchange` · type `twapOrder` Execute a large perp order in slices at a fixed interval over 5 minutes to 24 hours. · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Markets: Perp only - Not this? [`order`](/api/exchange/order.md) — when you want the whole size on the book at once ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `twapOrder`. Values: `"twapOrder"`. - `twap` (TwapBody, required) — The TWAP parameters. - `a` (integer · uint32, required) — Perp instrument's external id — the `index` value from `perpMeta`. - `b` (boolean, required) — `true` to buy, `false` to sell. - `s` (string, required) — Total size to execute across all slices, in base units. - `r` (boolean, required) — Reduce-only. Every slice inherits it. - `m` (integer · uint32, required) — Total duration in **minutes**, from `5` to `1440`. - `t` (boolean, required) — Randomize slice timing. Always `false`. Values: `false`. - `interval` (integer, required) — Seconds between slices. Values: `5`, `10`, `15`, `30`, `60`. - `marginMode` (string, optional) — Margin mode for the slices. When omitted, the account's current margin mode for this instrument applies. Values: `"cross"`, `"isolated"`. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"twapOrder","twap":{"a":1,"b":true,"s":"0.1","r":false,"m":30,"t":false,"interval":30,"marginMode":"cross"}},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · twapOrder (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `status` (object) - `running` (object) - `twapId` (string) — Id of the new TWAP, as a decimal string. Pass it to `twapCancel`. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"twapOrder"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "twapOrder", "data": { "status": { "running": { "twapId": "8642097531" } } } } } ``` ## TWAP body Fields as listed under `twap` in **Action fields** above. Inside `twap`, `m` is the duration in minutes and `t` is the randomize flag. ## Signing Hash the body as `{ "twap": { … } }` with the keys in the order shown in the request example: `a`, `b`, `s`, `r`, `m`, `t`, `interval`, then `marginMode` if you send it. Send the envelope `vaultAddress` as `null`. See [Signing](/api/signing.md#flow-2--agent-signing). ## Next steps The response returns the new TWAP's id in `data.data.status.running.twapId`. Pass it to [`twapCancel`](/api/exchange/twapCancel.md) to stop the TWAP early. - Follow progress with [`twapStates`](/api/websocket/twapStates.md) and slice fills with [`userTwapSliceFills`](/api/websocket/userTwapSliceFills.md). - Finished and cancelled TWAPs are listed by [`twapHistory`](/api/info/twapHistory.md). --- # `twapCancel` Cancels a running TWAP order before its duration ends. The action is signed by an approved agent. `POST /exchange` · type `twapCancel` Stop a running TWAP before its duration ends. · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Markets: Perp only - Not this? [`twapStates`](/api/websocket/twapStates.md) — when you need the ids of the running TWAPs - Not this? [`cancel`](/api/exchange/cancel.md) — when it is a regular resting order ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `twapCancel`. Values: `"twapCancel"`. - `a` (integer · uint32, required) — Perp instrument's external id — the same `a` the TWAP was placed with. - `t` (string, required) — TWAP id, as a decimal string — the `twapId` returned by `twapOrder`. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"twapCancel","a":1,"t":"8642097531"},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · twapCancel (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `status` (string) — The TWAP was cancelled. Values: `"success"`. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"twapCancel"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "twapCancel", "data": { "status": "success" } } } ``` `a` is the instrument the TWAP was placed on, and `t` is the `twapId` returned by [`twapOrder`](/api/exchange/twapOrder.md). The running TWAPs and their ids are in [`twapStates`](/api/websocket/twapStates.md). ## Signing Sign `t` as a **string**, exactly as sent, and keep the key order `a`, `t`. Send the envelope `vaultAddress` as `null`. See [Signing](/api/signing.md#flow-2--agent-signing). ## Next steps On success the response carries `data.data.status: "success"`. The cancelled TWAP then appears in [`twapHistory`](/api/info/twapHistory.md) and on [`userTwapHistory`](/api/websocket/userTwapHistory.md). --- # `outcomeOperate` Splits Outcome collateral into a complete YES + NO set, or merges a matched YES + NO set back into collateral. Send exactly one of `split` or `merge`; each array must contain 1–50 entries. `POST /exchange` · type `outcomeOperate` Split collateral into a YES + NO set, or merge a set back into collateral. · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Markets: Outcome only ### Action fields Listed in the node's field order — keep it when encoding the action. - One of · variant 1 — type = "outcomeOperate": - `type` (string, required) — Must be `outcomeOperate`. Values: `"outcomeOperate"`. - `split` (OutcomeOperateBody[], required) — Complete sets to create; mutually exclusive with `merge`. - `a` (integer · uint64, required) — Outcome instrument id. Long form: `asset`. - `s` (string, required) — Number of complete sets to split or merge. Long form: `sz`. - One of · variant 2 — type = "outcomeOperate": - `type` (string, required) — Must be `outcomeOperate`. Values: `"outcomeOperate"`. - `merge` (OutcomeOperateBody[], required) — Complete sets to redeem; mutually exclusive with `split`. - `a` (integer · uint64, required) — Outcome instrument id. Long form: `asset`. - `s` (string, required) — Number of complete sets to split or merge. Long form: `sz`. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"outcomeOperate","split":[{"a":19127212,"s":"10"}]},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · OutcomeOperate (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"OutcomeOperate"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "OutcomeOperate", "data": { "statuses": [ "success" ] } } } ``` ## Split For each `{ a, s }`, `a` is the Outcome instrument id and `s` is the number of complete sets. A successful split freezes `s` units of Outcome-account collateral and creates a long (YES) and short (NO) position of size `s`, both at the protocol's fixed split price of `0.5`. ## Merge Merge is the inverse. The account must hold both YES and NO legs in at least size `s`; the operation closes that matched amount and releases `s` units of collateral. Entries are checked independently during execution. An entry that cannot be split or merged may be skipped, so confirm the resulting positions with [`outcomePositions`](/api/info/outcomePositions.md) and balances with [`clearinghouseState`](/api/info/clearinghouseState.md) (`accountType: "outcome"`). --- # `updatePositionMode` Switches the account between one-way and hedge position mode. In one-way mode a market holds a single net position **per margin mode**; in hedge mode it can hold a long and a short at the same time — see [Margin & Leverage](/trade/margin-and-leverage.md). The net is per margin mode, not per market: a position is keyed by account + market + margin mode + position mode + side, so a cross long and an isolated short in the same market are two independent positions even in one-way mode. Keep a market on one margin mode if you want a single net exposure for it. The setting is **account-wide** and applies to perpetuals only. There is no per-instrument variant. `POST /exchange` · type `updatePositionMode` Switch the whole account between one-way and hedge mode (must be flat on perps). · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Markets: Perp only ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `updatePositionMode`. Values: `"updatePositionMode"`. - `isOneway` (boolean, required) — `true` for one-way, `false` for hedge. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"updatePositionMode","isOneway":true},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · UpdatePositionMode (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"UpdatePositionMode"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "UpdatePositionMode", "data": { "statuses": [ "success" ] } } } ``` `isOneway` is the only field. Do not send an `asset` — the action carries no instrument, and any extra key would change the MessagePack bytes your client hashes without changing what the node hashes, so the signature would fail to recover. ## Preconditions The account must be flat on perpetuals before the mode can change: | Error | Cause | | ----- | ----- | | `You have open perps orders or positions` | Any perpetual open order, position, or ungrouped trigger order exists | | `Cannot change position mode while a TWAP order is active` | A TWAP job is still running | | `Reverse position in progress for this coin and margin mode` | A reverse-position job is still running | | `Liquidator cannot update position mode` | Liquidator accounts are fixed to hedge mode | Outcome-market orders and positions do **not** block the switch, since position mode only applies to perpetuals. Cancel the perpetual orders and close the positions in question, then retry. ## Next steps The active mode is reflected in [`activeAssetData`](/api/info/activeAssetData.md): one-way returns a single `leverage.value`, hedge returns separate `leverage.long_value` and `leverage.short_value`. Set those per-side leverages with [`updateLeverage`](/api/exchange/updateLeverage.md). --- # `updateLeverage` Sets the leverage the account uses for one instrument. `POST /exchange` · type `updateLeverage` Set leverage for one instrument (requires no open orders in scope). · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Markets: Perp only ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `updateLeverage`. Values: `"updateLeverage"`. - `asset` (integer · uint32, required) — Instrument's external id — the `index` value from `perpMeta`; Outcome markets use the `instrument_id`. - `leverage` (integer · int32, required) — Target leverage. Must be positive in one-way mode; in hedge mode with isolated margin the sign selects which side is affected. - `marginMode` (string, optional) — Defaults to the instrument's current mode. Values: `"cross"`, `"isolated"`. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"updateLeverage","asset":1,"leverage":20,"marginMode":"cross"},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · UpdateLeverage (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"UpdateLeverage"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "UpdateLeverage", "data": { "statuses": [ "success" ] } } } ``` ## Preconditions The action is rejected while the scope it touches still has working orders: | Error | Cause | | ----- | ----- | | `Cannot update leverage while you have open orders` | Non-TWAP orders exist for that instrument and margin mode | | `Cannot update leverage while you have TPSL orders` | Take-profit / stop-loss orders exist in scope | | `Cannot update leverage while you have trigger orders` | Trigger orders exist in scope | | `Maximum leverage exceeded for ` | Above the margin tier's cap for the current position size | | `Margin tier not found` | No margin tier is configured for the instrument | With no open position, the cap is the first margin tier's maximum; with a position, it is the tier matching that position's size. Cancel the orders in scope first, then retry. ## Next steps Read the applied leverage back from [`clearinghouseState`](/api/info/clearinghouseState.md) or [`activeAssetData`](/api/info/activeAssetData.md). --- # `updateMarginMode` Switches an instrument between cross and isolated margin. `POST /exchange` · type `updateMarginMode` Switch one instrument between cross and isolated margin. · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Markets: Perp only - Not this? [`updateIsolatedMargin`](/api/exchange/updateIsolatedMargin.md) — when you want to add or remove margin on an isolated position ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `updateMarginMode`. Values: `"updateMarginMode"`. - `asset` (integer · uint32, required) — Instrument's external id — the `index` value from `perpMeta`; Outcome markets use the `instrument_id`. - `isCross` (boolean, required) — `true` for cross margin, `false` for isolated. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"updateMarginMode","asset":1,"isCross":true},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · UpdateMarginMode (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"UpdateMarginMode"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "UpdateMarginMode", "data": { "statuses": [ "success" ] } } } ``` Note that this action takes a boolean `isCross`, whereas [`updateLeverage`](/api/exchange/updateLeverage.md) takes a `marginMode` string. ## Next steps Once an instrument is on isolated margin, adjust that position's margin with [`updateIsolatedMargin`](/api/exchange/updateIsolatedMargin.md). --- # `updateIsolatedMargin` Adds or removes margin on an existing isolated position. `POST /exchange` · type `updateIsolatedMargin` Add or remove margin on an existing isolated position. · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Markets: Perp only ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `updateIsolatedMargin`. Values: `"updateIsolatedMargin"`. - `asset` (integer · uint32, required) — Instrument's external id — the `index` value from `perpMeta`; Outcome markets use the `instrument_id`. - `isBuy` (boolean, required) — `true` targets the long position, `false` the short. - `ntli` (string, required) — Signed USDC amount in integer wei, 6 decimals — positive adds margin, negative removes it. Must be an integer string. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"updateIsolatedMargin","asset":1,"isBuy":true,"ntli":"1000000000"},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · UpdateIsolatedMargin (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"UpdateIsolatedMargin"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "UpdateIsolatedMargin", "data": { "statuses": [ "success" ] } } } ``` `ntli` must be an **integer** string: `"1000000"` is 1 USDC, and `"-1000000"` removes 1 USDC. A decimal string such as `"1.5"` is rejected with `Invalid ntli value`. The example above adds 1,000 USDC. ## Preconditions | Error | Cause | | ----- | ----- | | `Position not found` | No isolated position on the requested instrument and side | | `Amount must be greater than 0` | `ntli` resolves to zero | | `Margin reduction exceeds the maximum allowable amount for this position` | Removal would drop margin below the position's required initial margin | When removing margin, the removable amount is the position's margin less its required initial margin (`entryPrice × size / leverage`). Unrealized losses reduce that headroom; unrealized profit does not increase it. Adding margin is additionally bounded by the account's available balance. ## Next steps Read the resulting position margin from [`clearinghouseState`](/api/info/clearinghouseState.md). --- # `revokeBuilderFee` Removes the signing user's approval for one builder. The action fails with `Builder not approved` if no active approval exists. `POST /exchange` · type `revokeBuilderFee` Withdraw a user’s fee approval for one builder. · [Direct EIP-712, signed by the account key](/api/signing.md#flow-1--direct-eip-712) - Access: Account-signed ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `revokeBuilderFee`. Values: `"revokeBuilderFee"`. - `txflowNetwork` (string, required) — TxFlow network name used by the EIP-712 action and domain. - `chainId` (integer · uint32, required) — TxFlow chain ID. - `apiVersion` (integer · uint32, required) — TxFlow signing API version. - `builder` (string · address, required) — Builder wallet whose approval is removed. - `nonce` (integer · uint64, required) — Fresh millisecond timestamp. Direct EIP-712 actions carry the nonce inside the action as well as in the outer payload; the two must match. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"revokeBuilderFee","txflowNetwork":"TxFlow-Testnet","chainId":2511,"apiVersion":1,"builder":"0x2e4D75dF2eE2653Ea7A113Cc1Fa36d45e923061F","nonce":1757992743994},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · RevokeBuilderFee (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"RevokeBuilderFee"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "RevokeBuilderFee", "data": { "statuses": [ "success" ] } } } ``` ## Signing Like [`approveBuilderFee`](/api/exchange/approveBuilderFee.md), this action is signed directly by the **account key**. Its EIP-712 primary type is distinct from the approval type: ``` RevokeBuilderFee(string txflowNetwork,uint32 chainId,uint32 apiVersion,address builder,uint64 nonce) ``` ```json { "type": "revokeBuilderFee", "txflowNetwork": "TxFlow-Testnet", "chainId": 2511, "apiVersion": 1, "builder": "0x2e4D75dF2eE2653Ea7A113Cc1Fa36d45e923061F", "nonce": 1757992743994 } ``` The replay nonce is the body's own `nonce`. When the outer payload carries `signatureChainId`, it overrides only `domain.chainId`; the business `chainId` inside the signed struct remains unchanged. See [Signing](/api/signing.md#builder-fee-actions). ## Next steps After propagation, the approval's `maxFeeRate` is `0` and its status is `REVOKED`. --- # `claimBuilderRewards` Claims the acting builder's full `unclaimedRewards` balance. The action fails with `No unclaimed builder rewards` when the claimable balance is zero. `POST /exchange` · type `claimBuilderRewards` Claim a builder’s full unclaimed reward balance. · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Not this? [`builderState`](/api/info/builderState.md) — when you want to check the claimable amount first ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `claimBuilderRewards`; there are no additional action fields. Values: `"claimBuilderRewards"`. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"claimBuilderRewards"},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · ClaimBuilderRewards (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"ClaimBuilderRewards"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "ClaimBuilderRewards", "data": { "statuses": [ "success" ] } } } ``` ## Signing This is an agent-signed action. Unlike older field-less actions, its MessagePack preimage is the **whole tagged action**: ```json { "type": "claimBuilderRewards" } ``` Serialize that map with named fields, then append the outer `nonce` as eight big-endian bytes and the vault flag/address before computing the action hash. Omitting `type` produces the wrong signing hash. An authorized agent may submit the claim for its owner. When `vaultAddress` is present, normal vault-agent authorization rules apply. See [Builder-fee actions](/api/signing.md#builder-fee-actions). ## Next steps A claim moves the full `claimable` balance into `totalClaimed`; `totalEarned` is unchanged. Read those balances — and whether the address is eligible to accrue at all — with [`builderState`](/api/info/builderState.md). --- # `setReferrer` Binds the account to a referrer by their referral code. The action is signed by an approved agent, for the account itself. `POST /exchange` · type `setReferrer` Bind the account to a referrer’s code. · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Not this? [`registerReferrer`](/api/exchange/registerReferrer.md) — when you want to create your own code instead ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `setReferrer`. Values: `"setReferrer"`. - `code` (string, required) — The referrer's code, as returned in their `referral` state. - `exchangeId` (integer · uint32, optional) — Referral channel. Omit, or send `0`, for the default platform channel. When sent, it is part of the signed body. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"setReferrer","code":"TXFLOW2026"},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · SetReferrer (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"SetReferrer"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "SetReferrer", "data": { "statuses": [ "success" ] } } } ``` Send the envelope `vaultAddress` as `null`. The signed body is `{ "code": … }`, followed by `exchangeId` only when you send it. ## Next steps The bound referrer appears as `referredBy` in [`referral`](/api/info/referral.md). --- # `registerReferrer` Registers a referral code for the account, so other users can bind to it with [`setReferrer`](/api/exchange/setReferrer.md). The action is signed by an approved agent, for the account itself. `POST /exchange` · type `registerReferrer` Create the account’s own referral code once it is eligible. · [Agent-signed](/api/signing.md#flow-2--agent-signing) - Access: Agent-signed - Not this? [`referral`](/api/info/referral.md) — when you want to check eligibility (`referrerState.stage`) first ### Action fields Listed in the node's field order — keep it when encoding the action. - `type` (string, required) — Must be `registerReferrer`. Values: `"registerReferrer"`. - `code` (string, required) — 6–15 characters, uppercase letters `A–Z` and digits `0–9` only. - `exchangeId` (integer · uint32, optional) — Referral channel. Omit, or send `0`, for the default platform channel. When sent, it is part of the signed body. ### Request example The signed envelope; `signature` and `nonce` are placeholders — see Signing. ```bash curl -X POST https://rpc.txflow-testnet.xyz/exchange \ -H 'Content-Type: application/json' \ -H 'x-api-version: 2026-08-16' \ -d '{"action":{"type":"registerReferrer","code":"TXFLOW2026"},"signature":{"r":"0x","s":"0x","v":27},"nonce":1757992743994,"vaultAddress":null,"signatureChainId":null}' ``` ### Response · RegisterReferrer (200) - `code` (integer) — `0` on success. - `data` (object) - `data` (object) - `statuses` (ActionStatus[]) — Exactly one entry for the whole action, however many orders or cancels it carries. - One of · variant 1: - string — Accepted. By default this means accepted by consensus, not yet executed; send `"confirm": "executed"` in the request body to wait for the execution result. - One of · variant 2: - `error` (string) — Why execution failed. - `type` (string) — The action's internal name, which does not always match the request `type`. Values: `"RegisterReferrer"`. - `status` (string) — A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`). Values: `"ok"`. ### Response example ```json { "status": "ok", "code": 0, "data": { "type": "RegisterReferrer", "data": { "statuses": [ "success" ] } } } ``` The account can register a code once [`referral`](/api/info/referral.md) reports `referrerState.stage` as `needToCreateCode`. Send the envelope `vaultAddress` as `null`. The signed body is `{ "code": … }`, followed by `exchangeId` only when you send it. ## Next steps Once registered, `referrerState.stage` in [`referral`](/api/info/referral.md) is `ready` and `referrerState.data.code` holds the code. --- # TxRelay API (Beta) [TxRelay](https://txrelay.com/) is the official **cross-chain bridge** for TxFlow. Deposits from Ethereum, Base, Solana, and other networks, and withdrawals back to those networks, go through these `/api/v1/bridge` routes. They are served by a host of their own — **not** the node and not the API service. - **Content-Type:** `application/json` ## Networks | | Mainnet | Testnet | |---|---|---| | Host — `/api/v1/bridge` | `https://api-bridge.txflow.com` | `https://devnet-bridge.txflow.net` | ## Channel `channel_id` selects which product the peg / deposit address is issued for. **TxFlow clients always send `0`.** A different channel (for example `1`) returns a different `deposit_address` for the same route. | `channel_id` | Product | | ------------ | ------- | | `0` | TxFlow | | `1` | YN (do not mix with TxFlow routes) | ## How it fits with `/info` and `/exchange` | Step | Deposit (external → TxFlow) | Withdraw (TxFlow → external) | | ---- | --------------------------- | ---------------------------- | | 1 | [`GET /networks`](/api/txrelay/networks.md) | same | | 2 | [`POST /deposit-address`](/api/txrelay/deposit-address.md) with `dst_chain: "txflow"` | `src_chain: "txflow"`, `dst_chain` = external chain | | 3 | User sends the asset on the source chain to `deposit_address` | [`tokenSend`](/api/exchange/tokenSend.md) the TxFlow asset to that peg | Native Arbitrum USDC **out** is [`withdraw3`](/api/exchange/withdraw3.md), not TxRelay. ## Routes | Method | Path | Returns | | ------ | ---- | ------- | | `GET` | [`/api/v1/bridge/networks`](/api/txrelay/networks.md) | Chains, tokens, fees, and enable flags | | `POST` | [`/api/v1/bridge/deposit-address`](/api/txrelay/deposit-address.md) | One-time deposit / peg address for a route | > **Tip:** Always send the **token and chain** that match the issued address. A mismatch is not credited automatically; see [Ecosystem](/getting-started/ecosystem.md) for recovery. --- # `networks` Lists every chain TxRelay can route, each with its tokens, min/max amounts, fees, and whether deposit or withdraw is enabled. `GET /api/v1/bridge/networks` Chains, tokens, fees, and deposit / withdraw enable flags ### Request example ```bash curl -X GET https://devnet-bridge.txflow.net/api/v1/bridge/networks ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `networks` (object[]) - `chain` (string) — Route key (`arbitrum`, `solana`, `txflow`, …). Use this as `src_chain` / `dst_chain`, not the display name. The list includes a `txflow` network; empty `chain_id` is normal there. - `chain_id` (string) — EVM chain id when applicable; may be empty for Solana or TxFlow. - `chain_name` (string) - `chain_type` (string) — e.g. `evm`, `solana`. - `deposit_confirmations` (integer) - `deposit_enabled` (boolean) - `deposit_eta_minutes` (integer) - `support_memo` (boolean) — When `true`, deposits need the `memo` from deposit-address. - `tokens` (object[]) — Assets on this chain that route to TxFlow. - `decimals` (integer) - `deposit_enabled` (boolean) - `deposit_fee` (string) — Bridge fee in token units. - `display_symbol` (string) — UI ticker. - `dst_token` (string) — Optional TxFlow counterpart, `NAME:address`. Use this as [`tokenSend`](/api/exchange/tokenSend.md) `token`. - `max_amount` (string) - `min_amount` (string) - `name` (string) - `symbol` (string) — Wire id passed as `symbol` to deposit-address (not `display_symbol`). - `token_address` (string) — Contract on that chain; empty for some native assets. - `withdraw_enabled` (boolean) - `withdraw_fee` (string) — Bridge fee in token units. - `withdraw_confirmations` (integer) - `withdraw_enabled` (boolean) - `withdraw_eta_minutes` (integer) - `message` (string) ### Response example ```json { "code": 0, "data": { "networks": [ { "chain": "arbitrum", "chain_name": "Arbitrum One", "chain_id": "42161", "chain_type": "evm", "deposit_enabled": true, "withdraw_enabled": true, "deposit_confirmations": 10, "withdraw_confirmations": 1, "deposit_eta_minutes": 2, "withdraw_eta_minutes": 15, "support_memo": false, "tokens": [ { "symbol": "USDCARB", "display_symbol": "USDC", "name": "USD Coin", "token_address": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", "decimals": 6, "deposit_fee": "0", "withdraw_fee": "1", "min_amount": "5", "max_amount": "1000000", "deposit_enabled": true, "withdraw_enabled": true, "dst_token": "USDC:0xd04f0d19E8FbA49cfA541E272F9C60a2ad720464" } ] } ] }, "message": "success" } ``` ## Next steps Issue an address with [`deposit-address`](/api/txrelay/deposit-address.md). --- # `deposit-address` Returns the address the user (or a later [`tokenSend`](/api/exchange/tokenSend.md)) must pay for **one** source → destination route. The same path is used for deposits into TxFlow and for withdrawals out of TxFlow; only `src_chain` / `dst_chain` swap. `POST /api/v1/bridge/deposit-address` One-time deposit / peg address for a single source → destination route ### Request body - `channel_id` (integer, required) — `0` for TxFlow. Do not mix with other products. - `dst_address` (string · address, required) — Credit address on `dst_chain` (TxFlow user for deposits; external wallet for withdraws). - `dst_chain` (string, required) — Destination chain key. - `src_chain` (string, required) — Source [`chain`](/api/txrelay/networks.md) key. - `symbol` (string, required) — Token `symbol` from networks (not `display_symbol`). ### Request example ```bash curl -X POST https://devnet-bridge.txflow.net/api/v1/bridge/deposit-address \ -H 'Content-Type: application/json' \ -d '{"channel_id":0,"src_chain":"arbitrum","dst_chain":"txflow","dst_address":"0x0000000000000000000000000000000000000001","symbol":"USDCARB"}' ``` ### Response (200) - `code` (integer) — `0` on success. - `data` (object) - `deposit_address` (string) — Pay this address (deposit) or [`tokenSend`](/api/exchange/tokenSend.md) to it (withdraw). - `from` (string) — Optional source-chain peg. - `memo` (string, nullable) — Memo / tag when the source chain `support_memo` is true. - `message` (string) ### Response example ```json { "code": 0, "data": { "deposit_address": "0xabc…", "from": "0xdef…", "memo": null }, "message": "success" } ``` ### Deposit (external → TxFlow) | Field | Value | | ----- | ----- | | `src_chain` | User's source network (`ethereum`, `solana`, …) | | `dst_chain` | `"txflow"` | | `dst_address` | User's TxFlow address | | `deposit_address` (response) | Address to pay on `src_chain` (QR / copy) | ### Withdraw (TxFlow → external) | Field | Value | | ----- | ----- | | `src_chain` | `"txflow"` | | `dst_chain` | Target network | | `dst_address` | User's address on that network | | `deposit_address` (response) | **Peg** to [`tokenSend`](/api/exchange/tokenSend.md) on TxFlow | ## Next steps After a withdraw peg is issued, submit [`tokenSend`](/api/exchange/tokenSend.md) (USDC `perp` → `perp`; other assets `spot` → `spot`). --- # Official Links Always verify you are on an official TxFlow domain before connecting your wallet or signing any transaction. | Content | URL | |---|---| | Website | [app.txflow.com](https://app.txflow.com/) | | Docs | [docs.txflow.com](https://docs.txflow.com/) | | Twitter / X | [x.com/TxFlow_L1](https://x.com/TxFlow_L1) | | Discord | [discord.gg/txflow](https://discord.gg/txflow) | | Telegram | [t.me/txflow_tg](https://t.me/txflow_tg) | | Download App | [app.txflow.com/download](https://app.txflow.com/download) | > **Warning:** TxFlow will **never DM** you first asking for your private key, seed phrase, or to sign a transaction. Treat any such message as a scam. Nothing on this site ever needs your seed phrase. Trading is authorized by an [agent key](/api/signing.md) that can only trade — it can never move your funds — and the [testnet playground](/try-testnet/quickstart.md) generates a throwaway key in your browser that should never be reused or funded with real assets. --- # Brand Kit Download our official logos and brand assets. - [**TxFlow-Brandkit.zip**](/brand/TxFlow-Brandkit.zip) (145 KB) The archive's `Logo/` folder contains the wordmark in five PNG variants: | File | Use | |---|---| | `TxFlow-Logo_Primary.png` | the default mark | | `TxFlow-Logo_Secondary.png` | the alternate mark | | `TxFlow-Logo_Black.png` | on light backgrounds | | `TxFlow-Logo_White.png` | on dark backgrounds | | `TxFlow-Logo_Mono.png` | single-colour reproduction | and the icon in two variants, each as SVG and PNG: | File | Use | |---|---| | `TxFlow-Icon_Primary.svg` / `.png` | the standalone icon | | `TxFlow-Icon_Tile.svg` / `.png` | the icon on a square tile, for avatars and app icons | ## Colours The accent this documentation uses, per theme: | Theme | Accent | |---|---| | Light | `#33dc26` | | Dark | `#8df885` | --- # Audits The TxFlow bridge contract has been audited by **OpenZeppelin**. | Scope | Auditor | Report | |---|---|---| | Bridge contract | OpenZeppelin | [PDF · 215 KB](/audits/TxFlow-Bridge-Audit-OpenZeppelin.pdf) | The report lists each finding with its severity and the resolution. ## "Resolved" in the report ≠ patched on Mainnet The resolution status next to each finding records that TxFlow addressed it in the reviewed source and that OpenZeppelin accepted the response. It does **not** assert that the deployed Mainnet bytecode changed. The Mainnet bridge, [`0xcDE3F99bCB4c91e19124e41730489EAeFEc24565`](https://arbiscan.io/address/0xcDE3F99bCB4c91e19124e41730489EAeFEc24565), is **not upgradeable** — no proxy, no admin-controlled implementation slot. Shipping a code change there means deploying a new contract and migrating the USDC it custodies, which moves user funds and interrupts deposits and withdrawals. For that reason some findings marked resolved in the report — including **M-01 (`updateValidatorSet` can be replayed to block finalization)** — are not reflected in the bytecode currently running on Arbitrum. TxFlow treats this as a known and accepted deployment constraint, not as an open unknown. What stands in for the code change meanwhile: - **Operational containment.** Lockers can vote to pause the bridge, and a cold-quorum `emergencyUnlock` rotates the validator set and restores operation. M-01's impact is bounded by exactly this path: a replayed `updateValidatorSet` can keep resetting the dispute period and block `finalizeValidatorSetUpdate`, forcing the rotation through `emergencyUnlock` — it cannot move or steal funds. - **Cold multi-signature control** over validator-set rotation and emergency recovery, so the fallback route stays under the same quorum the bridge already trusts. - A migration path to the fixed contract is being worked through internally, to be sequenced so custodied assets are not put at risk. Reports on these specific findings are therefore already known to TxFlow and are **not reward-eligible** under the [Bug Bounty Program](/resources/bug-bounty.md) — the program is still interested in anything outside them. Everything below the bridge — matching, clearing, margin, and liquidation — runs inside the [deterministic state machine](/txcore/consensus-execution.md) rather than in contracts, and is verifiable a different way: any node can replay the block stream and confirm every balance, position, and fill for itself. --- # Bug Bounty Program Report a security vulnerability in TxFlow to **[security@txflow.com](mailto:security@txflow.com)**. Rewards run up to **150,000 USDC** for a Critical finding, paid for responsible disclosure and scaled to severity. > **Warning:** Do **not** test against TxFlow Mainnet or any production system. Use [TxFlow Testnet](/try-testnet/quickstart.md), a local fork, or another non-production environment. Testing in production is prohibited and disqualifies the report. ## In scope **TxFlow Mainnet** — security vulnerabilities that could cause: - loss of user funds - unauthorized state transitions - incorrect order matching - incorrect liquidation or settlement - node or API outages - state divergence - logical errors in TxFlow Core **TxFlow Testnet** — testnet-only features and their interaction with TxFlow Core, nodes, and API services. Payouts for testnet-only issues may be lower than for a Mainnet issue of the same severity. Other experimental Testnet features are **not** in scope unless TxFlow announces otherwise — reports are still welcome. ### Where the code lives TxFlow does **not** publish its node, API, or frontend source in a public repository. There is nothing to clone and review: scope is the running Mainnet and Testnet systems above, plus the bridge contract deployed on Arbitrum at [`0xcDE3F99bCB4c91e19124e41730489EAeFEc24565`](https://arbiscan.io/address/0xcDE3F99bCB4c91e19124e41730489EAeFEc24565) (see [Audits](/resources/audits.md)). A GitHub repository, npm package, or PyPI project whose name contains "TxFlow" is **not** ours unless it is listed on [Official Links](/resources/official-links.md). Several unaffiliated projects use the name. Findings against code we do not run are out of scope and not reward-eligible, so confirm the target against that page before you spend time on it — and if you are unsure whether something belongs to TxFlow, ask at **security@txflow.com** first. ## Submitting a report Include all of the following: - a clear explanation of the bug - an impact assessment - detailed reproduction steps - a proof of concept, where applicable Send it to **[security@txflow.com](mailto:security@txflow.com)**. If several people report the same issue, the **first valid report** received is the one eligible for a reward. An issue is not a new, reward-eligible report if, before your submission, it was already recorded internally by TxFlow, disclosed in a public audit report, under investigation or remediation, or covered by an earlier valid report from someone else. TxFlow determines whether an issue was already known. TxFlow will not pursue legal action over security research conducted in good faith and in compliance with this program. ## Acknowledgement without a reward Not every valid report earns a payout — an issue can be already known, below the severity floor, or outside the reward scope while still being a genuine, well-written contribution. For those, TxFlow offers **public acknowledgement** instead of a reward: - Ask for it in your report, or in reply once we have triaged it. Acknowledgement is opt-in; we credit nobody by default. - Tell us the name or handle you want credited, and whether you want a link alongside it. - Credit is published only after the issue is resolved and TxFlow authorizes disclosure. Everything in [Eligibility](#eligibility) about confidentiality until then still applies. - You can decline credit, or ask to be listed anonymously. Acknowledgement is not a reward and does not change the reward decision for a report. Where a report does qualify for a payout, you can take both. ## Prohibited activity - Testing on Mainnet or production systems. - Phishing or other social engineering. - Extended or large-scale DDoS. Demonstrating mishandling of a temporary load spike is allowed only to the extent reasonably necessary, and without material disruption. - Testing third-party systems, apps, wallets, browser extensions, or websites, unless the issue leads directly to an in-scope TxFlow vulnerability. - Ransom demands or threats. - Public disclosure before a fix ships and TxFlow authorizes disclosure. - Publishing, or threatening to publish, personal or otherwise sensitive information without consent. - Exploiting a vulnerability for financial gain beyond the reward available here. - Bypassing these procedures or acting outside the stated scope. ## Eligibility - Submit directly to **security@txflow.com**. Do not use external sites unless TxFlow announces an official platform. - Comply with any applicable KYC/KYB, sanctions screening, and payout procedures. - Be able to receive a reward through a payment method TxFlow designates. - Keep the vulnerability and related communications confidential until TxFlow authorizes disclosure. - TxFlow must be able to **reproduce** the finding. - Contributors to the affected code are not eligible in relation to that code. ## Not eligible - Issues already known to TxFlow — internally recorded, disclosed in a public audit report, already being tracked or remediated, or previously reported by someone else in a valid report. - Reports without enough detail — no step-by-step instructions, reproducible example, or proof of concept. - Issues requiring highly unlikely or unreasonable user behaviour. - Issues caused by outdated software, unsupported environments, or systems TxFlow no longer supports. - Issues requiring root access, a jailbroken or compromised device, or operator compromise unrelated to a TxFlow vulnerability. - Issues in third-party libraries, extensions, tools, wallets, or applications that do not lead to a direct TxFlow vulnerability. - Non-security bugs, including minor performance issues with no meaningful security impact. - Issues contingent on extreme or unrealistic market conditions. ## Reward classification Ranges are maximums. The actual amount reflects both **impact and likelihood**, and classification examples are illustrative and subject to change. | Severity | Up to | Examples | |---|---|---| | **Critical** | 150,000 USDC | significant loss of user funds; unauthorized withdrawal or asset transfer; violation of Mainnet execution invariants; incorrect order matching, margin accounting, liquidation, or settlement producing an incorrect final state or financial loss; permanent or repeatable Mainnet state corruption | | **High** | 30,000 USDC | Mainnet outage; severe TxFlow Core / node / API availability failure; state divergence or processing failure that does *not* produce an incorrect final state or direct fund loss | | **Medium** | 10,000 USDC | API authentication or authorization issues; material data integrity issues; service degradation with meaningful security impact | ## General conditions - No payment is made for submissions that fall outside scope or fail the eligibility criteria. - TxFlow determines the validity, classification, and reward amount of any submission at its sole discretion. - All submissions may be used by TxFlow for remediation, testing, and security improvement. --- # Points Program Terms These terms are part of the TxFlow [Terms of Use](https://txflow.com/terms). By taking part in the Points Program, you agree to them. If anything here conflicts with the Terms of Use, the Terms of Use apply. For a plain-language overview of the program, see [How Points Work](/points/how-points-work.md). ## 1. Who can take part 1.1 The Points Program is open to anyone with an active TxFlow account, subject to the regional and eligibility requirements in the Terms of Use. 1.2 TxFlow-operated accounts are not eligible for Points. 1.3 Testnet or simulated activity, and trading on unsupported or delisted markets (after delisting takes effect), do not earn Points. ## 2. How Points are scored 2.1 Your Points reflect your trading as a whole, not any single action. 2.2 Scoring weights and formulas are proprietary. TxFlow may add, remove or refine scoring factors during or between seasons to keep the program fair. 2.3 Weekly Points are displayed each week on your Points dashboard. If farming is detected later, only the Points linked to that activity can be adjusted, including deductions for wallets flagged for Sybil activity. 2.4 Third-party calculators and community estimates are helpful, but they are not endorsed by TxFlow and do not determine your Points. ## 3. Keeping the game fair 3.1 The following activity takes Points away from genuine traders, so we don't allow it: - Wash trading or self-matching: trading with yourself or with accounts you control to inflate trading metrics. - Sybil farming: deliberately creating or operating multiple accounts or wallets, manually or in an automated way, to farm Points systematically. - Artificial volume: cycling trades with no genuine market intent to inflate volume. - Referral abuse: fake or inactive referred accounts, signing up under your own referral code (directly or through another wallet you control), or circular referral arrangements. - Exploiting bugs or unintended behavior to gain Points. - Getting around integrity checks or risk controls, or trying to reverse-engineer the scoring methodology. - Market manipulation, such as spoofing or layering. 3.2 These rules apply whether activity happens in one account, across several accounts, or through coordination with other users, and across all assets, pairs and products. Having another trading purpose alongside farming does not change the assessment. 3.3 If several wallets are clearly controlled by the same person or group, we may treat them as one user, so the pool stays fair for everyone else. 3.4 We review activity on an ongoing basis. If farming is found, the Points linked to it, including Points earned by wallets flagged for Sybil activity, may be reduced, withheld or deducted, including retroactively. Repeated or serious farming may lead to exclusion from the Points Program. 3.5 We want to get this right. If you think your eligibility or Points were affected by mistake, contact official TxFlow support and we'll review it. 3.6 Exclusion from the Points Program does not affect your access to the platform. You can keep trading as normal. ## 4. Your Points 4.1 Points are non-transferable and stay with the wallet that earned them. TxFlow will not move Points to another wallet, including after wallet theft, lost private keys or lost access. 4.2 Points measure your contribution to TxFlow. They are not a currency. 4.3 Trading carries real costs and risks. Trade at a pace and size that suits you. ## 5. Changes to the program 5.1 We may refine the program as it grows, including its rules, scoring, pool sizes and schedule. We'll tell you about material changes in advance through official channels. 5.2 If the program ends, we will tell you how accumulated Points will be treated. ## 6. Looking ahead 6.1 Points may be one of the factors considered for any future token distribution. We will announce the details of any such distribution separately, through official TxFlow channels. 6.2 Your Points are a record of your trading contribution. Full details of any future distribution will be shared through official TxFlow channels when they are ready. ## 7. Good to know 7.1 TxFlow makes no promise about the future value of any token that may be connected to the Points Program. 7.2 Taking part in the Points Program is not an investment or a promise of future returns. 7.3 Trading carries risk. TxFlow is not liable for trading losses connected with the Points Program, or for losses from Points adjustments, program changes or exclusion, except where the law requires otherwise.