Skip to main content
Ondo Global Markets brings tokenized stocks & ETFs on-chain as DeFi strategies (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

ItemValue
Strategy ID formatOndo-<TICKER>-bsc (e.g. Ondo-AAPL-bsc)
Position chainBSC (56)
Strategy assetMarket-share token on BSC (symbol ends in ON, e.g. AAPLON)
Underlying referenceUSDC (6 decimals)
Buy / Sell actionsrequest-lend (buy) / request-withdraw (sell)
Minimum buy$10 USDC (strategy.metadata.minRequestLendUsd)
AuthHeader x-api-key: <your-key>
Flow summary
Same-chain (BSC):
  Buy:  USDC (BSC)          → [client signs on BSC] → market shares (BSC)
  Sell: market share (BSC)  → [client signs on BSC] → USDC (BSC)

Crosschain:
  Buy:  USDC (origin)      → [client signs on origin chain] → market shares (BSC)
  Sell: market share (BSC) → [client signs on BSC]          → USDC (destination chain)

Position: always queried with the wallet on BSC
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/bytecode returns a priced quote (for your UI) and bytecode[] (to execute on-chain).

Authentication

Every request requires:
x-api-key: <your-api-key>
  • 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

BUY (request-lend)                          SELL (request-withdraw)
──────────────────                          ───────────────────────
1. GET /ondo/stocks/market-status           (same)
2. GET /tokens?category=stock&chainId=56    (same)
3. GET /v2/strategies?protocol=Ondo&network=bsc  → strategyId
4. GET /strategies/:strategyId/bytecode
     action=request-lend                      action=request-withdraw
     amount=<fundingBaseUnits>                 amountInShares=<shareWei>
     fromChainId / fromTokenAddress            toChainId / toTokenAddress
5. Show quote; validate $10 minimum (buy)    (same)
6. Execute bytecode[] on the correct chain    (same)
7. Track action status until SUCCESS          (same)
   (WebSocket, GET /strategies/:id/status, or position/history polling)
8. Refresh positions                          (same)

1. Market status

Gate buy/sell when the market is closed.
GET /ondo/stocks/market-status
Key field: isOpen — block submit when false.

2. List Stocks & ETFs

GET /tokens?category=stock&chainId=56&limit=300
  • category accepts comma-separated values (stock, etf).
  • Each token: { symbol, name, address, decimals, chainId, category, priceInUSD, logoURI? }.
  • Ondo symbols end in ON (e.g. AAPLON). Strip ON for display if desired.

3. Resolve strategy ID

A market-share token address alone is not enough — you need the strategy id.
GET /v2/strategies?protocol=Ondo&network=bsc
Returns { data: [...], pagination }. Pick the strategy where:
  • asset matches the market-share token address
  • networkId === 56
Use actions request-lend (buy) and request-withdraw (sell) from availableActions.

4. Buy — request-lend

GET /strategies/:strategyId/bytecode
ParameterRequiredDescription
actionyesrequest-lend
walletyesAddress that signs on the funding chain
amountyesFunding token in base units (USDC = 6 decimals)
fromChainIdyesFunding chain (56 for same-chain BSC, or another supported chain)
fromTokenAddressyes*Funding token on fromChainId; omit to default to USDC on that chain
* When fromChainId is set, fromTokenAddress defaults to USDC on that chain. Same-chain (BSC): fromChainId=56, fromTokenAddress= USDC on BSC. 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):
{
  "id": "<actionId>",              // correlate status & WebSocket
  "chainIdIn": 8453,               // funding chain (where the client signs)
  "chainIdOut": 56,                // position chain (BSC)
  "crossChain": { "isCrossChain": true, "chainIdIn": 8453, "chainIdOut": 56 },
  "singleUseAddress": "0x…",       // SUW that executes the CoW order
  "orderUid": "0x…",               // CoW order id
  "quote": {
    "tokenIn":  { "amount", "amountInUSD", "symbol", "decimals", ... },
    "tokenOut": { "amount", "minAmountOut", "amountInUSD", ... },
    "feeBreakdown": { "summary": { "platformFeesTotalInUSD", "bridgeAndSlippageImpactOutUSD" } }
  },
  "bytecode": [
    { "to": "0x…", "value": "0", "data": "0x…", "chainId": "8453" }
  ]
}
Client execution
  1. Filter bytecode where chainId === chainIdIn (same-chain: 56; crosschain: the origin chain).
  2. Sign and broadcast on that chain in a single transaction.
  3. 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 quote for pricing UI; use quote.feeBreakdown.summary for fee display.
Crosschain: the client only signs on the origin chain. Bridge + market-share purchase are handled by Pods.

5. Sell — request-withdraw

GET /strategies/:strategyId/bytecode
ParameterRequiredDescription
actionyesrequest-withdraw
walletyesAddress on BSC (where market shares are held)
amountInSharesyesShare amount in wei (18 decimals)
toChainIdyesPayout chain (56 for same-chain BSC, or another supported chain)
toTokenAddressyes*Payout token on toChainId; omit to default to USDC on that chain
* When toChainId is set, toTokenAddress defaults to USDC on that chain.
  • Use amountInShares (not amount) for Ondo sells. No minimum sell amount.
  • Response shape matches buy (id, chainIdIn, chainIdOut, crossChain, singleUseAddress, orderUid, quote, bytecode).
Same-chain (BSC): toChainId=56, toTokenAddress= USDC on BSC. The client signs on BSC. Crosschain: 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
  1. Filter bytecode where chainId === 56 (always BSC — shares are on BSC).
  2. Sign and broadcast on BSC.
  3. 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

The wallet is always normalized to BSC (same EVM address regardless of funding/payout chain).

Wallet snapshot (all positions)

GET /v2/wallets/:address?include=all
Stocks & ETFs appear under earn.positions where strategy.protocol === "Ondo".

Single strategy position

GET /v2/wallets/:address/strategies/:strategyId
or
GET /v2/strategies/:strategyId?wallet=<address>&fromActions=1
Use fromActions=1 for correct accounting (principal, profit, in-flight operations).

Position shape

{
  "spotPosition": {
    "currentPositionInShares": {
      "value": "<raw>",
      "decimals": 18,
      "symbol": "AAPLON",
    },
    "underlyingBalanceUSD": 14.98,
    "requestedToLend": [],              // pending buy, USDC
    "requestedToLendInShares": [],      // pending buy, shares
    "requestedToWithdraw": [],          // pending sell, USDC
    "requestedToWithdrawInShares": [],  // pending sell, shares
    "profitInUSD": 0.12,
  },
  "strategy": {
    "protocol": "Ondo",
    "id": "Ondo-AAPL-bsc",
    "asset": "0x…",
    "networkId": 56,
    "metadata": { "ondoSymbol": "AAPL", "minRequestLendUsd": 10 },
  },
}

requestedTo* fields

These arrays represent pending async orders not yet settled (same on same-chain and crosschain):
FieldMeaning
requestedToLendUSDC committed to a pending buy
requestedToLendInSharesEquivalent shares for pending buy
requestedToWithdrawUSDC value of a pending sell
requestedToWithdrawInSharesShares committed to a pending sell
Empty arrays mean nothing is pending. Expected states
  • Buy submitted, shares not yet credited → requestedToLend / requestedToLendInShares populated.
  • Sell submitted, USDC not yet paid out → requestedToWithdraw / requestedToWithdrawInShares populated.
  • Settled → all pending arrays empty; currentPositionInShares / underlyingBalanceUSD reflect the real balance.
Suggested polling: every 5–15 s until the pending arrays clear (or use WebSocket/webhook below).

Available balance for sell

availableShares = currentPositionInShares.value − Σ requestedToWithdrawInShares
availableUSD    = underlyingBalanceUSD − Σ requestedToWithdraw   (clamp ≥ 0)
Do not subtract pending buys from available shares. Use availableShares as amountInShares when selling the full position.

7. Track order status

After submitting bytecode[] 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.
GET /strategies/:strategyId/status?wallet=<address>
ParameterRequiredDescription
strategyIdyes (path)Strategy slug or id (e.g. Ondo-AAPL-bsc)
walletyesSame wallet used in the bytecode call

What appears in actions[]

Action stateListed?Where to track instead
PENDINGyesThis endpoint
INITIAL (user has not sent the funding tx yet)noGET /actions/:id, WebSocket
SUCCESS, REFUNDED, FAILED, etc.noHistory, wallet position, WebSocket

Root response (always)

{
  "strategyId": "Ondo-AAPL-bsc",
  "wallet": "0x49A54d4d925868475Eb8d2cb2c784481ca755e8A",
  "hasPending": true,
  "message": "1 pending action(s) for this strategy.",
  "actions": []
}
FieldTypeDescription
strategyIdstringStrategy slug
walletstringChecksummed address from query
hasPendingbooleantrue when actions.length > 0
messagestringHuman-readable summary
actionsarrayPending actions only (see below)
Ondo Global Markets strategies only — root also includes orderBook:
"orderBook": {
  "requestLend": {
    "canOpenOrder": true,
    "code": null,
    "errorType": null,
    "message": null,
    "retryable": false,
    "probeSellAmount": "100000000000000000000",
    "probeBuyAmount": "…"
  },
  "requestWithdraw": { /* same shape when sell is supported */ }
}
orderBook fieldMeaning
canOpenOrderWhether a new buy/sell request is likely to succeed right now
codeStable error code when canOpenOrder is false
errorTypeAdditional machine-readable reason (nullable)
messageHuman-readable reason (nullable)
retryableClient may retry later (e.g. market closed)
probeSellAmountAmount used for the availability probe (raw units)
probeBuyAmountExpected output from probe when canOpenOrder is true (optional)
Present even when actions is empty.

Each item in actions[]

Base fields (every pending action)
{
  "id": "674a1b2c3d4e5f6789012345",
  "type": "SAMECHAIN_INVESTMENT_DEPOSIT",
  "status": "PENDING",
  "createdAt": "2026-05-29T12:00:00.000Z",
  "updatedAt": "2026-05-29T12:10:00.000Z",
  "currentStep": {
    "stepIndex": 2,
    "status": "PENDING",
    "legs": [{ "kind": "SWAP", "status": "PENDING", "operator": null }]
  },
  "steps": [
    { "stepIndex": 0, "status": "SUCCESS", "legs": [] }
  ]
}
FieldNotes
typeBuy: SAMECHAIN_INVESTMENT_DEPOSIT or CROSSCHAIN_INVESTMENT_DEPOSIT. Sell: SAMECHAIN_INVESTMENT_WITHDRAW or CROSSCHAIN_INVESTMENT_WITHDRAW
currentStepFirst step still INITIAL or PENDING; null if none
stepsFull pipeline snapshot
steps[].legs[]{ kind, status, operator } — leg kind examples: SWAP, PRESIGN, TRANSFER, BRIDGE
Cross-chain pending actions include only the base block (no suw). suw block (Ondo Global Markets request-lend / request-withdraw only)
"suw": {
  "singleUseAddress": "0x…",
  "phase": "order_in_progress",
  "fundsReturnedToWallet": false,
  "order": {
    "orderUid": "0x…",
    "posted": true,
    "quoteId": "…",
    "action": "request-lend",
    "sellAmount": "10000000000000000000",
    "buyAmount": "…",
    "status": "open",
    "validTo": 1710000000,
    "creationDate": "…",
    "executionDate": null,
    "executedSellAmount": null,
    "executedBuyAmount": null,
    "txHash": null
  },
  "balances": {
    "underlying": {
      "address": "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
      "symbol": "USDC",
      "decimals": 18,
      "amountRaw": "10000000000000000000",
      "amountHumanized": "10.0"
    },
    "strategy": {
      "address": "0x…",
      "symbol": "AAPLON",
      "decimals": 18,
      "amountRaw": "0",
      "amountHumanized": "0.0"
    }
  },
  "timing": {
    "requestedAt": "2026-05-29T12:00:00.000Z",
    "ageSeconds": 1800,
    "deadlineSeconds": 7200,
    "refundAt": "2026-05-29T14:00:00.000Z",
    "secondsUntilRefund": 120
  },
  "pricing": {
    "side": "BUY",
    "referencePriceUsd": 200,
    "livePriceUsd": 214,
    "priceSource": "ondo",
    "driftBps": 700,
    "slippageBps": 100
  },
  "suwSteps": [
    {
      "stepIndex": 1,
      "label": "presign",
      "status": "SUCCESS",
      "txHash": "0x…",
      "legs": []
    }
  ]
}
suw fieldMeaning
singleUseAddressEphemeral wallet executing the async settlement
phaseHigh-level lifecycle — see table below
fundsReturnedToWalletFinal transfer to the user wallet completed
orderSettlement order metadata; null if not created yet
balancesTokens on the single-use wallet during settlement; null after funds returned
timingRequest age, SLA, refund countdown
pricingReference vs live market price; null if unavailable
suwStepsInternal settlement substeps (presign, swap_and_settlement, forward_to_wallet)

suw.phase lifecycle

PhaseUI meaning
awaiting_transferUser has not completed the funding transfer
awaiting_presignFunds received; Pods preparing settlement
order_in_progressSettlement order open, awaiting fill
awaiting_forwardFill complete; final transfer to user wallet pending
completedFully settled
refunded / expired / failed / cancelledTerminal — drops out of actions[] on next poll

suw.order shapes

Not posted yet (posted: false):
{
  "orderUid": "0x…",
  "posted": false,
  "quoteId": "…",
  "action": "request-lend",
  "sellAmount": "…",
  "buyAmount": "…",
  "status": "not_posted",
  "lastPostError": null,
  "lastPostAttemptAt": null,
  "canOpenOrder": true,
  "code": null,
  "message": null,
  "retryable": true
}
Posted / live (posted: true): adds validTo, creationDate, executionDate, executedSellAmount, executedBuyAmount, txHash. Typical status values: open, fulfilled, unknown (with providerError when lookup fails).

Empty pending example

{
  "strategyId": "Ondo-AAPL-bsc",
  "wallet": "0x…",
  "hasPending": false,
  "orderBook": {
    "requestLend": { "canOpenOrder": true, "code": null, "errorType": null, "message": null, "retryable": false, "probeSellAmount": "100000000000000000000" },
    "requestWithdraw": { "canOpenOrder": true, "code": null, "errorType": null, "message": null, "retryable": false, "probeSellAmount": "1000000000000000000" }
  },
  "message": "No pending actions for this strategy. You are all caught up.",
  "actions": []
}
Suggested polling: every 5–15 s while hasPending is true, or until suw.phase reaches a terminal value. Example
curl -H "x-api-key: $API_KEY" \
  "https://api.pods.finance/strategies/Ondo-AAPL-bsc/status?wallet=0xYOUR_WALLET"
Non-Ondo strategies return the same root envelope without orderBook or suw.
wss://<api-host>/updates
{ "type": "subscribe", "channel": "<walletAddress>" }
Listen for action_update events where action.id matches the bytecode response id:
StatusMeaning
PENDINGAwaiting settlement
SUCCESSOrder filled
FAILEDOrder failed
REFUNDEDOrder refunded
Re-subscribe on reconnect. Alternatively, use your customer webhook (ACTION_UPDATE events).

History

Embedded in GET /v2/wallets/:address?include=all&fromActions=1 or:
GET /wallets/:wallet/history/:strategyId
Market-share buys appear as 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 WebSocket SUCCESS.

8. Supported chains & USDC addresses

Stocks & ETFs settle on BSC; funding (buy) and payout (sell) can use any supported USDC chain.
NetworkChain IDUSDC
BSC560x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d
Base84530x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Arbitrum421610xaf88d065e77c8cC2239327C5EDb3A432268e5831
Polygon1370x3c499c542cEF5E3811e1192ce70d8cC03d5c3359
When fromChainId / toChainId are set, fromTokenAddress / toTokenAddress can be omitted — the API defaults to USDC on that chain. A non-USDC token triggers an extra same-chain swap leg.

9. Errors

CodeHTTPWhen
REQUEST_LEND_AMOUNT_TOO_LOW422Buy below $10 USDC
LEND_AMOUNT_TOO_SMALL422Amount too small for quote
WITHDRAW_AMOUNT_TOO_SMALL422Sell amount too small
WITHDRAW_AMOUNT_REQUIRED400Missing amount/amountInShares
FROM_TOKEN_NOT_SUPPORTED400Funding token not supported
FROM_TOKEN_REQUIRED400Crosschain buy without funding token
CROSSCHAIN_BRIDGE_NO_OUTPUT422Bridge returned no USDC for the buy
USDC_NOT_SUPPORTED400USDC not configured for the chain
UNSUPPORTED_CHAIN400Unknown fromChainId/toChainId
QUOTE_NOT_FOUND404Re-quote
QUOTE_EXPIRED400Re-quote
NO_API_KEY / INVALID_API_KEY401Auth failure
Preserve error.code in your UI for user-friendly messages.

10. Integration notes

  1. Use the correct actions: request-lend (buy) and request-withdraw (sell). Do not use the generic lend/withdraw.
  2. Sign on the right chain: buy = funding chain (chainIdIn); sell = always BSC; position = BSC wallet.
  3. Async completion: after the origin tx, Pods completes bridge (if crosschain) + order fill; monitor via the action status, position, or history.
  4. Sells use shares: pass amountInShares (18 decimals), not amount.
  5. Smart accounts: pass output=userOperation or output=fireblocks (with accountId) on the bytecode endpoint.
  6. 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):
curl -H "x-api-key: $API_KEY" \
  "https://api.pods.finance/strategies/Ondo-AAPL-bsc/bytecode\
?action=request-lend&amount=10000000&wallet=0xYOUR_WALLET\
&fromChainId=8453&fromTokenAddress=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
Sign response.bytecode (legs where chainId === 8453) on Base, then poll:
curl -H "x-api-key: $API_KEY" \
  "https://api.pods.finance/strategies/Ondo-AAPL-bsc?wallet=0xYOUR_WALLET&fromActions=1"
Sell shares, receive USDC on Arbitrum (crosschain):
curl -H "x-api-key: $API_KEY" \
  "https://api.pods.finance/strategies/Ondo-AAPL-bsc/bytecode\
?action=request-withdraw&amountInShares=1000000000000000000&wallet=0xYOUR_WALLET\
&toChainId=42161&toTokenAddress=0xaf88d065e77c8cC2239327C5EDb3A432268e5831"
Sign response.bytecode (legs where chainId === 56) on BSC, then poll the position until the pending arrays clear.

Endpoint reference

ConcernMethod & Path
Market open?GET /ondo/stocks/market-status
List Stocks & ETFsGET /tokens?category=stock&chainId=56
List strategiesGET /v2/strategies?protocol=Ondo&network=bsc
Strategy + positionGET /v2/strategies/:id?wallet=<addr>&fromActions=1
Position per walletGET /v2/wallets/:addr/strategies/:id
Quote + bytecodeGET /strategies/:id/bytecode
Pending actionsGET /strategies/:id/status?wallet=<addr>
Wallet snapshotGET /v2/wallets/:addr?include=all
HistoryGET /wallets/:wallet/history/:strategyId
Status updateswss://<host>/updates
All HTTP endpoints require x-api-key.