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.isOpen — block new submissions when false. The payload may also include session
details (marketStatus, nextOpen, nextClose). Per-asset eligibility during off-hours can still
fail at quote time with errors such as COW_ASSET_SESSION_CLOSED — see
Ondo GM docs.
2. List Stocks & ETFs
GET /tokens requires the wallet product type on your API key (customer.productType must
include wallet). Keys with API-only scope receive 403 Forbidden. Workaround: list Ondo
strategies via GET /v2/strategies?protocol=Ondo&network=bsc — each strategy includes
assetToken (market-share symbol, address, decimals).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
bytecodewherechainId === 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 → CoW order 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 pricing UI; usequote.feeBreakdown.summaryfor fee display.
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.
Client execution
- Filter
bytecodewherechainId === 56(always BSC — shares are on BSC). - Batch and sign on BSC in a single transaction.
- Wait for async completion (CoW order 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".
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 chainId === 8453) on Base, then poll:
response.bytecode (legs where chainId === 56) on BSC, then poll the position until the
pending arrays clear.
Endpoint reference
All HTTP endpoints require
x-api-key.