Portfolio
Reconcile AGARA collateral, positions, open orders, fills, activities, and withdrawals.
Portfolio reads require a personal access token with portfolio:read. These
docs cover the AGARA exchange; keep the returned exchange fields in your
models so each row remains self-identifying.
Summary
GET /trade/v1/portfolio/summary
Use the summary before sizing a new order and after any deposit, withdrawal, position operation, or automatic redemption.
curl "$AGARA_BASE_URL/trade/v1/portfolio/summary?exchanges=AGARA" \
-H "Authorization: Bearer $AGARA_API_TOKEN"{
"summaries": [
{
"exchange": "AGARA",
"cash_balance_micro": "250000000",
"free_cash_micro": "250000000",
"positions_value_micro": "18500000",
"portfolio_value_micro": "268500000",
"open_cost_basis_micro": "17200000",
"open_unrealized_pnl_micro": "1300000",
"accounting_status": "AVAILABLE",
"as_of": "2026-07-20T12:00:00.000Z"
}
]
}| Field | Meaning |
|---|---|
cash_balance_micro | Current collateral balance |
free_cash_micro | Cash available to AGARA's BUY validation |
positions_value_micro | Marked value of open positions |
portfolio_value_micro | Cash plus marked positions |
open_cost_basis_micro | Cost basis still attached to open positions |
open_unrealized_pnl_micro | Marked value less open cost basis |
as_of | Timestamp of this exchange snapshot |
Nonterminal BUY commitments are not a separate summary field. Fully page
POST /trade/v1/orders/list and account for
their remaining collateral when your strategy computes its own available
quoting budget. Use open orders for the portfolio's resting and
active subset.
Positions
POST /trade/v1/portfolio/positions/list
Positions are returned in one unpaginated response. Treat the AGARA subset as
complete only when unavailable_exchanges does not include AGARA; require the
array to be empty for a complete multi-exchange response.
{
"condition_ids": [],
"exchanges": ["AGARA"]
}An empty condition_ids array returns every current position. Use one or more
condition IDs to narrow the snapshot.
Positions in the most recently registered markets appear first. When market registration times match, positions are ordered by current value, highest first, then by exchange name alphabetically. Positions with unavailable market registration times appear last.
{
"positions": [
{
"exchange": "AGARA",
"condition_id": "0x...",
"token_id": "1234567890",
"shares_micro": "5000000",
"available_shares_micro": "3000000",
"avg_price_micro": "430000",
"open_cost_basis_micro": "2150000",
"accounting_status": "AVAILABLE",
"current_price_micro": "470000",
"current_value_micro": "2350000",
"to_win_micro": "5000000",
"profit_loss_micro": "200000",
"profit_loss_percent": "9.302325581395348837",
"redeemable": false,
"mergeable": false,
"mergeable_shares_micro": null
}
],
"markets": {
"1234567890": {
"market_id": "1f9e566d-7823-49fb-a5c5-5a193eba3209",
"market_title": "Will City A win?",
"outcome_name": "Yes",
"logo_url": null,
"event_slug": "city-a-vs-city-b",
"state": "ACTIVE",
"neg_risk_id": null,
"display": { "market": {}, "outcome": {} }
}
},
"events": {
"city-a-vs-city-b": {
"event_title": "City A vs City B",
"display": { "event": {} }
}
},
"unavailable_exchanges": [],
"as_of": "2026-07-20T12:00:00.000Z"
}Average entry price uses the moving weighted-average acquisition cost of held shares, excluding trading fees. Selling removes proportional cost; closing a position clears its cost, so a later purchase starts a new average. Splitting a complete set assigns 50ยข per share to each outcome.
Holdings update immediately. The average and cost can briefly reflect earlier
activity. open_cost_basis_micro applies the latest available average to the
returned held quantity; use that field when calculating P&L instead of multiplying
by the rounded avg_price_micro. profit_loss_percent is a percentage string:
"9.3" means 9.3%.
Keep displaying shares when accounting_status is PENDING, and display an
updating indicator for null average, cost or P&L fields. Retry your normal
portfolio read to receive updated accounting. If the status is UNAVAILABLE,
display those amounts as unavailable rather than zero. AVAILABLE means an
average is available; it does not guarantee that it includes the latest fill.
Quoted current value remains available even while cost is pending. When neither
a quote nor an acquisition average is available, current price and value are
also null. Summary cost and P&L are null if any held position lacks cost;
summary position and portfolio values are null only if a holding cannot be
valued. Cash remains available, and an empty portfolio has zero position totals.
shares_micro is your total holding. Use available_shares_micro as the maximum
size for a new sell order โ it excludes shares your own open sell orders have
already committed, so "0" means every share you hold is already up for sale.
mergeable and mergeable_shares_micro are advisory snapshots for a held,
available complete pair. redeemable identifies a resolved position; supported
standalone markets are handled by automatic redemption.
The sidecar's state tells you where each position's market is in its lifecycle:
once it reaches PROPOSED a winner or a split has been proposed, so the
position can no longer be sold and its payout is on the way. Positions in a
market with a neg_risk_id are the exception: those are not redeemed
automatically.
Retry when unavailable_exchanges contains AGARA because that snapshot is
missing AGARA rows.
to_win_micro is what the position pays if its outcome wins: one collateral
unit per share. When a market's proposal or resolution is a
split, it is your shares times
that outcome's split price, rounded down: 10 YES shares on a split that pays
37ยข per YES share show "3700000".
Open orders
POST /trade/v1/portfolio/open-orders/list
{
"token_ids": [],
"exchanges": ["AGARA"],
"limit": 100
}The response contains:
orders: the active subset used by the portfolio view, in the same shape documented under Orders;markets: metadata keyed bytoken_id, including each market'sstate,neg_risk_id, and raw market/outcomedisplayobjects;events: metadata keyed by event slug, including the raw eventdisplay;pagination:next_cursorand the appliedlimit;as_of: the snapshot time.
Orders are newest first. Pass pagination.next_cursor back as cursor with
the same token_ids and exchanges filters. A null cursor means the walk is
complete.
This is an active-order view, not lifecycle history. For cancellation recovery
or an audit of how each order ended, fully page
POST /trade/v1/orders/list and follow each
tracked order to a terminal state.
Trades
GET /trade/v1/portfolio/trades
Query parameters are limit (default and maximum 500) and an opaque
cursor from the previous response.
{
"trades": [
{
"exchange": "AGARA",
"trade_id": "<uuid>",
"fill_id": "42",
"order_id": "<uuid>",
"token_id": "1234567890",
"side": "BUY",
"shares_micro": "1000000",
"price_micro": "470000",
"fee_micro": "0",
"role": "MAKER",
"status": "CONFIRMED",
"transaction_hash": "0x...",
"executed_at": "2026-07-20T11:58:00.000Z"
}
],
"markets": {},
"events": {},
"pagination": { "next_cursor": null, "limit": 500 },
"unavailable_exchanges": [],
"as_of": "2026-07-20T12:00:00.000Z"
}fill_id is serialized as a string. transaction_hash remains null until
settlement supplies one. Any page can be partial, so check
unavailable_exchanges after every response. If an AGARA-only page contains
AGARA, discard the entire accumulated walk and restart without a cursor. Do
the same for a multi-exchange walk whenever the array is nonempty. Continue
only after every page satisfies the condition for your requested exchange set,
and stop when next_cursor is null.
Activities
GET /trade/v1/portfolio/activities
Activities show AGARA history sorted newest first. limit
defaults to 50 and accepts 1 through 500; pass the opaque cursor to
continue to older activities. Each page returns current data; as_of is the time
that page was read. Refresh from page one to see newer activities.
The type discriminator selects one of these wire shapes:
| Type | Fields in addition to type, exchange, id, and created_at |
|---|---|
ORDER | order_id, current status, fill_status, condition_id, token_id, side, filled_shares_micro, average_fill_price_micro, filled_amount_micro, fees_micro |
SPLIT | condition_id, shares_micro, amount_micro, tx_hash |
MERGE | condition_id, shares_micro, amount_micro, tx_hash |
REDEEM | condition_id, redeemed token_id, shares_micro, amount_micro, tx_hash |
DEPOSIT | amount_micro, chain_id, token_address, counterparty_address, tx_hash |
WITHDRAWAL | amount_micro, chain_id, token_address, counterparty_address, tx_hash |
An ORDER appears after its first recorded fill, including while the remaining
shares are still open. One row collects all fills for that order. Later fills
update its totals and current status; created_at stays at the first fill.
Unfilled orders do not appear here. fill_status is FULL or PARTIAL.
A REDEEM appears separately for each outcome you held. shares_micro is
positive for both outcomes. amount_micro is the collateral received: the
shares times the price their outcome paid, rounded down. For winners, that is
one unit per share. For losers, it is zero: 7 losing shares give
shares_micro: "7000000" and amount_micro: "0". On a
split resolution, each outcome
pays its own price, so 7 NO shares paid at 63ยข give amount_micro: "4410000".
Confirmed LP rewards appear as LP_PAYOUT, with amount_micro, epoch_date, and
tx_hash. The same reward transfer does not also appear as a deposit.
If a cursor returns 422, restart from page one.
{
"activities": [
{
"type": "REDEEM",
"exchange": "AGARA",
"id": "redeem:<uuid>",
"condition_id": "0x...",
"token_id": "1234567890",
"shares_micro": "5000000",
"amount_micro": "5000000",
"tx_hash": "0x...",
"created_at": "2026-07-20T11:55:00.000Z"
}
],
"markets": {},
"conditions": {},
"events": {},
"pagination": { "next_cursor": null, "limit": 50 },
"as_of": "2026-07-20T12:00:00.000Z"
}The metadata sidecars cover only rows present on the current page:
markets is keyed by outcome token ID, conditions by condition ID, and
events by event slug. Activities do not return unavailable_exchanges.
Position operations
Split and merge convert between collateral and complete binary token pairs:
POST /trade/v1/portfolio/positions/splitrequirespositions:split;POST /trade/v1/portfolio/positions/mergerequirespositions:merge.
Both accept a scoped personal access token and return 201 with
batch_hash, status: "PENDING", and as_of. Poll
GET /trade/v1/batches/{batch_hash} for settlement. If it finishes as
FAILED or FAILED_DIVERGENT, inspect the typed failure object and wait for
unwound_at before treating provisional inventory as restored. If a lower
batch is FAILED_DIVERGENT, a higher failed batch's unwound_at alone is not
proof of restoration; keep mutations paused and follow the
stranded-sequence recovery
rule. See
Positions for request shapes, validation, and
cross-match settlement.
Supported resolved standalone markets use automatic redemption; no client-submitted operation is required.
Withdrawals
The standard withdrawal flow requires a current browser session. A personal access token alone cannot execute it.
- Same-chain withdrawal sends
USDCto an address on Base. - Send to a wallet address, not the token's own address. A request that names
the token as its recipient is refused with a
422error, because the token's own address is not a wallet. - Accepted AGARA withdrawals return
201with a pending batch hash. Poll the batch until it reachesSETTLEDbefore treating destination funds as final; a terminal failure carries the same typedfailureobject. - Requests queue per account and settle in order. A
409means the queue is full; retry once a queued request finishes. - The minimum is
10000micro units. The environment's batch ceiling can impose a tighter bound.
For a cross-chain withdrawal:
- Use a personal access token to read
GET /trade/v1/portfolio/bridge/withdraw/supported-assets. - Request
POST /trade/v1/portfolio/bridge/withdraw/quotewith the destination chain, token, recipient, and source amount. - Review the current quote, then confirm in the signed-in web flow.
If a route refunds to the AGARA account, the return appears as a DEPOSIT
activity. A quoted route minimum can be higher than the standard withdrawal
minimum. Request a new quote before retrying.
Withdrawing to Solana. When Solana is offered, its supported asset has
address_type SOL and a base58 token address. Send both values back exactly
as listed.
- Set
recipient_addressto the base58 address of a Solana wallet you control, exactly as the wallet shows it. Solana addresses are case-sensitive, so never change their letter case. - Some addresses are refused with a
422error before anything moves: a wallet's associated token account, a program-controlled wallet such as a multisig vault, and the USDC token address itself. Any other address is accepted exactly as entered. - The wallet must already have a USDC account; an empty one is fine. If it
does not, the quote and the withdrawal answer
422 recipient_token_account_missingand nothing moves. Receiving any amount of USDC in that wallet once creates the account; then request a new quote. SETTLEDmeans the USDC has left your AGARA account. It usually reaches the Solana wallet within a minute. A withdrawal to Solana cannot be reversed or refunded, and USDC sent to an address that no wallet controls is lost, so check the address before you confirm.
Common pattern
Before placing a new quote, fetch summary and fully page the order list. Use the
summary's free_cash_micro as AGARA's current BUY-validation cash, then apply
your own risk limits and account for every remaining nonterminal commitment.
Use the portfolio open-orders endpoint for the resting-liquidity view.
Every portfolio endpoint's full request and response shape is in the Portfolio API reference.