protocol: "Ondo", network: bsc). The market-share position always lives on BSC, but clients
can fund a buy from — or receive a sell payout on — another EVM chain. Pods handles the bridge and the
async order fill.
Overview
Flow summary
Settlement is asynchronous. The on-chain transaction submits the request in seconds; the order
reaches SUCCESS only after Ondo fills it (minutes later). For crosschain, the bridge leg settles
first, then the order. Track both the on-chain receipt and the action status via WebSocket or
history polling.
Quote and bytecode come from the same call.GET /strategies/:id/bytecodereturns a pricedquote(for your UI) andbytecode[](to execute on-chain).
Authentication
Every request requires:- The key identifies your customer. Fee configuration and strategy catalog are scoped to it.
- Success responses are the bare JSON body (HTTP 200). Errors use
{ "error": { "code", "message", "details?" } }. - Keep the API key on your backend — never expose it in a browser app.
Integration flow
1. Market status
Gate buy/sell when the market is closed. Optionally pass a GM tokensymbol to enrich the same
response with per-asset tradability under asset.
asset field:
- without
symbol:assetisnull - with
symbol:assetcontains the per-symbol tradability snapshot
isOpen — block new submissions when false. The payload may also include
session details (marketStatus, nextOpen, nextClose, offhours).
symbol query
symbol is the Ondo GM token symbol and must end in lowercase on (for example NVDAon,
AAPLon). Invalid symbols return 422.
When present, asset includes:
asset.tradable is false when:
- The market is closed/paused (
ONDO_MARKET_CLOSED/ONDO_MARKET_PAUSED), unlessoffhours.isOpenistrue - The specific asset has an active
ASSET_PAUSEDrestriction (ONDO_ASSET_PAUSED)
ASSET_LIMITED restriction does not flip tradable to false; it only sets
limited: true.
Bytecode generation for Ondo request-lend / request-withdraw uses the same tradability
rules and returns 409 with those ONDO_* codes when a trade is blocked. Per-asset
eligibility during off-hours can still fail later at CoW quote time with errors such as
COW_ASSET_SESSION_CLOSED — see Ondo GM docs.
2. List Stocks & ETFs
categoryaccepts comma-separated values (stock,etf).- Each token:
{ symbol, name, address, decimals, chainId, category, priceInUSD, logoURI? }. - Ondo symbols end in
ON(e.g.AAPLON). StripONfor display if desired.
3. Resolve strategy ID
A market-share token address alone is not enough — you need the strategy id.{ data: [...], pagination }. Pick the strategy where:
assetmatches the market-share token addressnetworkId === 56
request-lend (buy) and request-withdraw (sell) from availableActions.
4. Buy — request-lend
Same-chain (BSC):
fromChainId=56, fromTokenAddress=0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d (18 decimals). The client signs on BSC.
Crosschain: fromChainId= origin chain (e.g. 8453 Base). The client signs only on the origin
chain; Pods bridges funds to BSC and fills the order asynchronously.
Response (relevant fields):
- Filter
bytecodewhereNumber(chainId) === Number(chainIdIn)(same-chain:56; crosschain: the origin chain). - Batch all filtered legs into one signed transaction (
output=bytecode+ EIP-7702, oroutput=userOperation/output=fireblocksper your wallet stack). - Wait for async completion (crosschain: bridge → BSC → market-share fill).
- Minimum: $10 USDC (enforced server-side, error
REQUEST_LEND_AMOUNT_TOO_LOW). For crosschain the minimum is checked on the bridged USDC amount that lands on BSC. - Use
quotefor the full pricing UI (funding amount, expected shares, fees).quote.tokenInalways reflectsfromChainId— read itsdecimals/chainIddirectly, no need to cross-reference §8.
Crosschain: the client only signs on the origin chain. Bridge + market-share purchase are handled by Pods.
5. Sell — request-withdraw
Same-chain (BSC):
toChainId=56, toTokenAddress=0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d. The client signs on BSC.
- Use
amountInShares(notamount) for Ondo sells. No minimum sell amount. - Response shape matches buy (
id,chainIdIn,chainIdOut,crossChain,singleUseAddress,orderUid,quote,bytecode).
toChainId= destination chain. The client signs only on BSC (shares live there);
Pods sells the market share for USDC on BSC, then bridges the proceeds to the destination chain.
quote.tokenInis always shares on BSC (decimals: 18,chainId: 56) — what the client signs away.quote.tokenOutreflects the payout leg: on same-chain sells this is BSC USDC (decimals: 18); on cross-chain sells it is alreadytoTokenAddressontoChainId(e.g. Base USDC =decimals: 6,chainId: 8453). Readquote.tokenOut.decimals/quote.tokenOut.chainIddirectly — never assume BSC decimals for a cross-chain sell, and never cross-reference §8 to guess the payout scale.quote.feeBreakdown.charges[]includes abridgeAndSlippageline for the exit and, on cross-chain sells, a second one for the payout bridge — both self-describe their owndecimals/symbol, so render each line as-is rather than assuming a shared scale.- The payout bridge is requoted at forward time with the realized settlement balance, so treat
quote.tokenOuton cross-chain sells as the estimate at bytecode time, not the final amount. bytecode[].chainIdis a decimal string (e.g."56"). Compare withNumber(leg.chainId) === 56(orNumber(chainIdIn)), never strict-equal to a numeric route field.
- Filter
bytecodewhereNumber(chainId) === 56(always BSC — shares are on BSC). - Batch and sign on BSC in a single transaction.
- Wait for async completion (market-share fill → crosschain: bridge USDC to
toChainId).
Crosschain: the client only signs on BSC. Market share → USDC redemption and the bridge payout are async.
6. Positions & pending requests
Thewallet is always normalized to BSC (same EVM address regardless of funding/payout chain).
Wallet snapshot (all positions)
earn.positions where strategy.protocol === "Ondo" — not at the
top level of the response:
earn.positions has the same { spotPosition, strategy } shape as the single-strategy
endpoint below — read earn.positions[].strategy.protocol === "Ondo" to filter for stocks & ETFs.
Single strategy position
fromActions=1 for correct accounting (principal, profit, in-flight operations).
Position shape
requestedTo* fields
These arrays represent pending async orders not yet settled (same on same-chain and crosschain):
Empty arrays mean nothing is pending.
Expected states
- Buy submitted, shares not yet credited →
requestedToLend/requestedToLendInSharespopulated. - Sell submitted, USDC not yet paid out →
requestedToWithdraw/requestedToWithdrawInSharespopulated. - Settled → all pending arrays empty;
currentPositionInShares/underlyingBalanceUSDreflect the real balance.
Available balance for sell
availableShares as amountInShares
when selling the full position.
7. Track order status
After submittingbytecode[] on-chain, the action is not complete until status is SUCCESS.
HTTP — pending actions for one strategy
Poll when you want a strategy-scoped snapshot without loading the full wallet or strategy position. The handler refreshes in-flight operations before responding.What appears in actions[]
Root response (always)
Ondo Global Markets strategies only — root also includes
orderBook:
Present even when
actions is empty.
Each item in actions[]
Base fields (every pending action)
Cross-chain pending actions include only the base block (no
suw).
suw block (Ondo Global Markets request-lend / request-withdraw only)
suw.phase lifecycle
suw.order shapes
Not posted yet (posted: false):
posted: true): adds validTo, creationDate, executionDate,
executedSellAmount, executedBuyAmount, txHash. Typical status values: open,
fulfilled, unknown (with providerError when lookup fails).
Empty pending example
hasPending is true, or until suw.phase reaches a
terminal value.
Example
orderBook or suw.
WebSocket (recommended)
action_update events where action.id matches the bytecode response id:
Re-subscribe on reconnect. Alternatively, use your customer webhook (
ACTION_UPDATE events).
History
Embedded inGET /v2/wallets/:address?include=all&fromActions=1 or:
SAMECHAIN_INVESTMENT_DEPOSIT; sells as SAMECHAIN_INVESTMENT_WITHDRAW. Ignore
INITIAL status (quote artifact).
Refresh positions
Refetch wallet/strategy endpoints after on-chain confirmation and after WebSocketSUCCESS.
8. Supported chains & USDC addresses
Stocks & ETFs settle on BSC; funding (buy) and payout (sell) can use any supported USDC chain.
When
fromChainId / toChainId are set, fromTokenAddress / toTokenAddress are required
— the API does not default to USDC. Omission returns 422 VALIDATION_ERROR. A non-USDC
funding or payout token may add an extra same-chain swap leg.
9. Errors
Preserve
error.code in your UI for user-friendly messages.
10. Integration notes
- Use the correct actions:
request-lend(buy) andrequest-withdraw(sell). Do not use the genericlend/withdraw. - Sign on the right chain: buy = funding chain (
chainIdIn); sell = always BSC; position = BSC wallet. - Atomic bytecode: never send
bytecode[]legs as separate transactions — see Executing Bytecode. - Async completion: after the origin tx, Pods completes bridge (if crosschain) + order fill;
monitor via the action status, position, or
history. - Sells use shares: pass
amountInShares(18 decimals), notamount. Gate sells on wallet position (currentPositionInShares) andsuw.phase !== 'awaiting_forward'. - Wallet output: EOA →
output=bytecode+ EIP-7702; smart account →output=userOperation; Fireblocks →output=fireblocks+accountId. - Same EVM wallet works across funding/payout chains; the market-share position always lives on BSC.
Minimal example
Buy $10 of AAPL from Base (crosschain):response.bytecode (legs where Number(chainId) === 8453) on Base, then poll:
response.bytecode (legs where Number(chainId) === 56) on BSC, then poll the position until the
pending arrays clear.
Endpoint reference
All HTTP endpoints require
x-api-key.