Positions (wallet API 1.1)
Opt in to have every wallet move name the market position it belongs to, and to receive each position's final move
By default (wallet API 1.0) a wallet move tells you which order it belongs to, not which market. Wallet API 1.1 adds the position to every move and sends a final move when each position ends. Use it when your platform tracks prediction play per market: bonus money, wagering requirements, promotions that read won or lost, per-market reports, support.
A position is one player's holding of one outcome in one market book: (player, venue_id, outcome). The currency is fixed per book.
Opting in
The version is set per operator environment (sandbox and prod separately) by Phoenix. Ask your Phoenix contact to switch an environment to 1.1; nothing changes until then.
A market book takes its environment's version when it opens (the first order your players place on that market) and keeps it for life. Books that opened before the switch keep sending 1.0 until they resolve, so a position is never split across versions. Key your handling on the api_version of each request, not on the switch date.
What changes in 1.1
Every request to POST /wallet/transactions (and its transaction_status probe) carries "api_version": "1.1". The envelope, amounts, reasons and idempotency are unchanged. references keeps every 1.0 key and adds:
| Key | Type | On | Description |
|---|---|---|---|
venue_id | string (UUID) | every move | The market book: one market on your platform, in one environment and currency. Part of the position key. |
market_id | string (UUID) | every move | The market, for naming it in reports and support. Several books share one market id, so never key positions on it. |
outcome | string | every move | "a" or "b": the side of the position the move belongs to. Same values as claim_side. |
final | boolean | every move | true on exactly one move per position: the move that ends it. false on every other move. |
mode | string | final moves only | "resolved": the market resolved, including a result of tie that pays 0.5 per share (a settled wager). "voided": the market was voided and the position refunded (a void wager). A void with the 50/50 refund basis also pays 0.5 per share, but it is a void and its final moves are "voided". |
The balance call is not a money move and stays 1.0. Echo the request's api_version in your response; Phoenix does not read it.
Amounts follow the same exact, house-favouring rules in both versions (see Exact amounts and rounding).
Which side each move names
| Move | outcome |
|---|---|
reserve_cash, capture_cash, a release | The order's side |
credit_cash for sell proceeds | The side sold |
credit_cash for a merge (two holders of opposite sides redeem a set) | Each holder's own side, one credit per holder |
credit_cash maker rebate | The order's side |
credit_cash settlement (MARKET_SETTLED) | The claim side |
A capture or credit whose amount is net of a fee still belongs to exactly one position.
The final move
When a market resolves or is voided, every position that ever held shares in the book gets exactly one final move: a credit_cash with reason: "MARKET_SETTLED", final: true and the mode.
- A winning position is credited its payout.
- A losing position is credited zero (
"value": "0"). Accept a zero-amountcredit_cash; it moves no money but ends the position. - A position sold, merged away, or turned into Yes on the other outcomes of a One winner event before the market ended (it holds nothing at the end) is also credited zero. Turning a No into Yes moves no money, so it sends no wallet move of its own.
- A voided market's refunds are the final moves, with
mode: "voided".
Under wallet API 1.0 a final move that would credit zero is never sent. Under 1.1 it is always sent. Never refuse a zero-amount final: it moves no money, but it is handled like any payout, so a refused one shows as Refused in the back office and that market's settlement for your operator stays incomplete until Phoenix resends it. Answer it as you would any other accepted credit, with an unchanged balance.
A player holding both sides of one market has two positions and receives two final moves, never a combined one.
{
"api_version": "1.1",
"operation": "credit_cash",
"idempotency_key": "01J8ZG14H70AE2F6A9C7A0DC8E92",
"operator_id": "7c1e2a64-3f5b-4d2a-9b8e-2f6a1c9d4e10",
"environment": "sandbox",
"player": { "external_id": "operator-player-123" },
"currency_code": "USDT",
"amount": { "value": "0", "scale": 6, "currency_code": "USDT" },
"reason": "MARKET_SETTLED",
"references": {
"claim_side": "b",
"venue_id": "6016496c-77c9-5b93-96ba-1829ec3eab01",
"market_id": "0f9a3c2e-1b7d-4c55-9e0a-2d4b8c6f7e13",
"outcome": "b",
"final": true,
"mode": "resolved"
}
}Final is not always the last move to arrive
final: true means no new move will be created for that position. It does not mean every earlier move has already reached you: Phoenix performs wallet moves concurrently and retries a failed one with backoff while later moves proceed. A move created before the final (for example a sell credit retried through an outage on your side, or the release of an order cancelled at settlement) can arrive after it. Apply such a late move to the already-settled position as an adjustment; do not reject it.
Reading a position
Fold the moves of one (player.external_id, venue_id, outcome):
| Move | Effect on the position |
|---|---|
capture_cash | Stake added |
credit_cash, final: false (ORDER_FILLED, MAKER_REBATE) | Cash returned before the end (a sale, a merge, a rebate) |
credit_cash, final: false, REFUND_ROUNDING_CORRECTION | A correction to a settled position's payout, after its final move |
credit_cash, final: true | The position ends: payout as credited, mode resolved or voided |
reserve_cash, release_cash | Holds and releases of order cash; no stake until a capture |
The stake is what was captured; the return is everything credited, final included.