Reconciliation
Match Phoenix wallet operations to the operator ledger
Reconciliation proves that Phoenix Prediction wallet operations and the operator wallet ledger agree.
The practical question is simple: every Phoenix Prediction idempotency key should affect the operator wallet exactly once.
Reconcile by environment
Keep sandbox and prod reports separate. The same player identifier or market pattern can appear in both environments during rollout, but money movement belongs to only one ledger.
What to Store
For every money-moving wallet operation, store:
| Field | Why |
|---|---|
idempotency_key | The primary join key. Always present, opaque, and platform-generated |
operation | reserve_cash, release_cash, capture_cash, or credit_cash |
operator_id and environment | Environment-scoped reconciliation (sandbox or prod) |
player.external_id | Operator-side player account |
currency_code and amount object | Exact wallet movement amount |
references object | Store it verbatim. The only keys Phoenix ever sends are order_id (reserve, capture, release), taker_order_id plus maker_order_id (trade and merge credits), and claim_side (settlement payouts) |
| Request fingerprint | Detects idempotency key reuse with different payloads |
| Request body | Audit and support |
| Response body | Proves what your wallet returned |
| Final available and reserved balance | Support and accounting |
| Signature result | Security audit |
| Processed timestamp | Ordering and investigation |
Join on idempotency_key first. It is the platform-generated dedup identity and the only field guaranteed on every operation, so it is the spine of every report. The references keys are correlation hints to map a wallet move back to a Phoenix order or settlement: order_id ties a reserve, capture, or release to one buy order; taker_order_id plus maker_order_id tie a sell-proceeds or merge credit to the matched trade; claim_side tags a settlement payout. Treat idempotency_key as opaque and never parse it; its internal prefix records the trigger (for example reserve:, capture:, release:, credit:, settle:) but that format is internal and not a contract.
Daily Reconciliation
Run these checks at least daily during launch:
Count reserve_cash operations against accepted buy orders.
Count capture_cash operations against buy-side fills.
Count release_cash operations against cancelled, expired, and partially filled orders.
Count credit_cash operations against sell proceeds, settlement payouts, refunds, and voids.
Find duplicate idempotency keys and confirm they had one wallet effect.
Compare Phoenix trading ledger totals with operator available and reserved wallet movement.
Player Support Flow
When a player asks about a prediction:
- Find the player by your
player.external_id. - Find Phoenix Prediction wallet transactions by
idempotency_key(primary), then narrow with thereferenceskeys you stored:order_idfor a reserve, capture, or release;taker_order_idandmaker_order_idfor a trade or merge credit;claim_sidefor a settlement payout. - Confirm whether cash was reserved for the order.
- Confirm whether reserved cash was captured or released.
- Confirm whether settlement, refund, void, or correction credit was applied.
- Compare final balance with your wallet ledger.
Do not rely only on iframe UI state for wallet support. The operator wallet ledger is the financial source of truth.
Phoenix-Side Integrity Sweep
Phoenix can run a periodic integrity sweep that independently reconciles its trading subledger against your wallet. For each outstanding move it believes it issued, the sweep calls POST /wallet/transactions/status with the original move envelope and compares your reported status and balance against its own expectation.
The sweep only detects drift. When the two sides disagree, or the status probe is unreadable, it raises an alert (telemetry plus a structured log) and a human resolves it from a runbook. It never re-moves or rewrites money.
Two consequences for your wallet:
- Keep
POST /wallet/transactions/statusqueryable at all times. The sweep relies on it to read your view of a move, and a persistently unreachable status endpoint is itself flagged as an integrity concern. - The status endpoint must never move money. It reports the stored outcome for an idempotency key and nothing else, so a status probe during reconciliation can never change a balance.
The sweep is an optional safety net, not a substitute for your own daily reconciliation. Phoenix may call the status endpoint independently of any timeout, so do not assume a status request always follows an uncertain money move.
Common Mismatches
| Symptom | Likely cause |
|---|---|
| Player cash reserved twice | Duplicate reserve_cash idempotency key was not handled correctly |
| Player has stuck reserved cash | release_cash failed, is still retrying, or was applied against the wrong order_id |
| Player missing proceeds or payout | credit_cash failed or returned a terminal error |
| Operator captured too much | Cumulative capture plus release exceeded the original reservation |
| Operator ledger has unknown Phoenix transaction | Missing request logging or shared wallet endpoint reuse |
| Phoenix reports wallet error | Operation returned non-retryable error, timed out, or could not be reconciled by status lookup |
Launch Recommendation
During the first production rollout, reconcile more often than usual. Prediction markets create new support questions because players are watching open positions, market close, resolution, and wallet settlement as separate moments.