Skip to content

Parlays API

The Platform API (Django) indexes the high-market-parlay program from Helius webhooks and serves the resulting state over REST.

EnvironmentBase URL
Devhttps://dev-api.gomarket.io
Productionhttps://prod-api.gomarket.io

Each has its own database.

MethodPathAuth
GET/api/parlays/user/Platform JWT
GET/api/parlays/{parlay_id}/Platform JWT
GET/api/parlays/vault/none
PATCH/api/parlays/vault/superuser
POST/api/parlays/vault/sync/superuser
GET/api/parlays/admin/superuser
POST/api/parlays/admin/{parlay_id}/void/superuser
POST/api/parlays/webhooks/helius/webhook secret
GET/api/markets/parlay-suggestions/none

Your parlays, newest first, capped at 50.

Terminal window
curl -s "https://prod-api.gomarket.io/api/parlays/user/?status=active" \
-H "Authorization: Bearer $JWT"

Query: status — active / won / lost / claimed / voided / refunded.

{
"data": [
{
"id": "3f2a…", // internal UUID
"parlay_id": 1042, // on-chain id
"user_wallet": "7xKX…",
"stake": 10000000, // micro-USDC
"combined_probability": "0.050000000000",
"potential_payout": 200000000,
"actual_payout": 0,
"status": "active",
"legs_total": 3,
"legs_won": 1,
"legs_lost": 0,
"legs_resolved": 1,
"tx_signature": "5Kx…",
"claim_tx_signature": "",
"created_at": "2026-09-14T10:22:01Z",
"settled_at": null,
"claimed_at": null,
"legs": [
{
"leg_index": 0,
"on_chain_market_id": 412,
"market_address": "9aBc…",
"selected_outcome": "YES",
"probability_at_entry": "0.500000",
"status": "won",
"resolved_at": "2026-09-15T18:00:00Z",
"question": "Will the Fed cut rates in December?"
}
]
}
]
}

Matching is by the authenticated user or a case-insensitive match on their wallet address, so slips placed before the wallet was linked still appear.

One parlay, scoped to the caller.

Terminal window
curl -s https://prod-api.gomarket.io/api/parlays/1042/ \
-H "Authorization: Bearer $JWT"

A parlay that isn’t yours returns the same 404 as one that doesn’t exist — the id space must not be probeable. Don’t treat 404 as “deleted.”

StatusOn-chainSet byClaimable
activeActive (0)create_parlay—
wonWon (1)settle_legyes — payout
lostLost (2)settle_legno
claimedClaimed (3)claim_parlayno
voidedVoided (4)settle_leg, when every leg’s market cancelledyes — stake refund
refundedRefunded (5)void_parlay (admin)no — already paid

Leg statuses: pending, won, lost, voided.

FieldUnit
stake, potential_payout, actual_payoutmicro-USDC (1e6)
probability_at_entrydecimal 0–1, 6 dp
combined_probabilitydecimal, 12 dp

probability_at_entry is locked at create time and never recomputed.


Terminal window
curl -s https://prod-api.gomarket.io/api/parlays/vault/
{
"data": {
"vault_address": "…",
"balance": 500000000,
"total_staked": 120000000,
"total_paid_out": 80000000,
"total_exposure": 210000000,
"max_payout_cap": 500000000,
"max_legs": 10,
"min_stake": 1000000,
"max_stake": 100000000,
"is_active": true,
"updated_at": "2026-09-17T09:00:00Z"
}
}

Public read. PATCH is superuser-only and accepts vault_address, max_legs, max_payout_cap, min_stake, max_stake, is_active.

Superuser. Re-reads the vault token-account balance over Solana RPC (getTokenAccountBalance) and updates the DB record. Returns 502 on RPC failure.

The API’s numbers are a mirror maintained from webhooks. On-chain is authoritative — sync before acting on them.

Useful derived value:

headroom = 0.8 × balance − total_exposure

When it nears zero, large slips start failing with ExposureLimitExceeded while small ones still succeed. → Vault & Solvency


Terminal window
curl -s "https://prod-api.gomarket.io/api/parlays/admin/?status=active&wallet=7xKX…&page=1" \
-H "Authorization: Bearer $ADMIN_JWT"

Paginated at 20 per page, returns { "count": n, "results": [...] }.

Terminal window
curl -s -X POST https://prod-api.gomarket.io/api/parlays/admin/1042/void/ \
-H "Authorization: Bearer $ADMIN_JWT"

Only valid on an active parlay; otherwise 400.


POST /api/parlays/webhooks/helius/
Authorization: <PARLAY_WEBHOOK_AUTH_SECRET>

Compared with hmac.compare_digest. Answers 503 when unconfigured, 403 on mismatch.

Accepts a Helius transaction payload (single or array). Transactions with meta.err are skipped; the rest have their logMessages decoded into parlay events and dispatched atomically.

{ "processed": 3, "skipped": 0 }

Solana webhook delivery is not ordered — a LegSettled can arrive before the ParlayCreated that made the row it references. The indexer raises DeferredParlayEvent, the webhook answers 503, and Helius retries.

Worth copying if you index this program yourself. The failure mode without it is a silently dropped settlement.

EventEffect
ParlayCreatedNew Parlay row + N ParlayLeg rows
LegSettledLeg status + parlay aggregates
ParlayClaimed→ claimed, records claim_tx_signature
ParlayVoided→ refunded (admin void_parlay)
ConfigInitialized / ConfigUpdatedConfig mirror
VaultSeeded / VaultWithdrawnVault mirror

Events are Anchor #[event]s, written to the transaction log as base64 Program data: lines prefixed with the 8-byte event discriminator.


Terminal window
curl -s https://prod-api.gomarket.io/api/markets/parlay-suggestions/

Public. Curated combo templates for the webapp’s “Suggested” cards.

Admin CRUD:

MethodPath
GET/POST/api/markets/admin/parlay-templates/
GET/PATCH/DELETE/api/markets/admin/parlay-templates/{pk}/
POST/api/markets/admin/parlay-templates/{pk}/refresh/
POST/api/markets/admin/parlay-templates/refresh-all/

Templates are previews. In the webapp, demo legs carry a synthetic negative marketId and no marketAddress, and are blocked from submission — they show the shape of a combo without being placeable.


ParlayLeg is unique on (parlay, on_chain_market_id), which enforces the no-duplicate-markets rule at the storage layer as well as on-chain.

Indexes: (user_wallet, status), (status, -created_at), (on_chain_market_id, status) — the last one supports “settle every leg waiting on this market.”