API Reference
The PredSouq REST API gives your platform full programmatic control over markets, trading, reports, and configuration. All endpoints are served from your tenant's own domain — there is no shared PredSouq infrastructure in the request path.
Authentication
All authenticated requests require an x-api-key header. Keys are issued per-tenant during onboarding and never shared across tenants. There are two key types with different permission scopes.
GET https://your-domain.com/v1/markets x-api-key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxx
Errors & Rate Limits
All errors return a JSON body with a single error key describing the problem. HTTP status codes follow standard semantics.
| Status | Meaning | Common cause |
|---|---|---|
| 200 | OK | Request succeeded |
| 400 | Bad Request | Missing or invalid body/params |
| 401 | Unauthorized | Missing or invalid x-api-key |
| 403 | Forbidden | Public key used on admin endpoint |
| 404 | Not Found | Market or resource does not exist |
| 429 | Too Many Requests | 20 req/min per IP exceeded |
| 500 | Server Error | Unexpected failure — contact support |
{
"error": "Market not found"
}Retry-After header is included on 429 responses and indicates seconds until the window resets. Implement exponential backoff for automated clients.
Webhook Verification
Inbound webhook endpoints (KYC, payments) do not require an API key — authentication is instead verified via HMAC-SHA256 signature. Each PSP/KYC relay signs the payload with a shared secret configured in your tenant settings.
- 1Extract the
X-Signature(or provider-specific) header from the inbound request. - 2Compute
HMAC-SHA256(webhookSecret, rawRequestBody)using the shared secret from your dashboard. - 3Compare the computed hex digest with the header value using a constant-time comparison. Reject if they differ.
- 4Validate the timestamp (if provided) to reject replays older than 5 minutes.
// Pseudo-code — adapt to your webhook framework const sig = req.headers['x-signature']; const digest = crypto .createHmac('sha256', process.env.WEBHOOK_SECRET) .update(rawBody) .digest('hex'); if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(digest))) { return res.status(401).json({ error: 'Invalid signature' }); }
Health
Returns the current health of the API server and database connection. Use this endpoint for uptime monitoring and load balancer checks — no authentication required.
curl https://your-domain.com/health
{
"ok": true,
"service": "predsouq-api",
"db": true,
"uptimeSec": 86432
}Markets — Public
Read-only market endpoints are accessible with a public key. These power trader-facing UIs — listing, search, price history, and real-time depth.
Returns a paginated list of open markets. Supports filtering by category, full-text search, and market mechanism kind.
| Parameter | Type | Required | Description |
|---|---|---|---|
| category | string | optional | Filter by category slug, e.g. sports, crypto, macro |
| q | string | optional | Full-text search across market questions and descriptions |
| kind | string | optional | Market mechanism: cpmm (constant-product) or lmsr (logarithmic) |
curl https://your-domain.com/v1/markets \ ?category=crypto&kind=cpmm \ -H "x-api-key: pk_live_xxx"
[
{
"id": "mkt_01hx",
"question": "BTC above $100k by Q3?",
"category": "crypto",
"kind": "cpmm",
"yesPrice": 0.67,
"volume24h": 4210.50,
"closeAt": "2026-09-30T23:59:00Z"
}
]Returns a single market with current odds, cumulative volume, close time, resolution reference, and market metadata.
curl https://your-domain.com/v1/markets/mkt_01hx \ -H "x-api-key: pk_live_xxx"
{
"id": "mkt_01hx",
"question": "BTC above $100k by Q3?",
"kind": "cpmm",
"status": "open",
"yesPrice": 0.67,
"noPrice": 0.33,
"volume": 18420.00,
"closeAt": "2026-09-30T23:59:00Z",
"resolutionRef": "Coinbase BTC/USD close price",
"meta": {}
}Returns a time-series array of { t, yes, no } price points suitable for charting. Points are spaced at approximately 1-hour intervals for the lifetime of the market.
curl https://your-domain.com/v1/markets/mkt_01hx/history \ -H "x-api-key: pk_live_xxx"
[
{
"t": "2026-06-01T00:00:00Z",
"yes": 0.50,
"no": 0.50
},
{
"t": "2026-06-01T01:00:00Z",
"yes": 0.53,
"no": 0.47
}
]Returns order book depth for both the YES and NO sides. Use this to render depth charts or estimate price impact before placing a trade.
curl https://your-domain.com/v1/markets/mkt_01hx/depth \ -H "x-api-key: pk_live_xxx"
{
"yes": [
{ "price": 0.67, "size": 500 },
{ "price": 0.65, "size": 1200 }
],
"no": [
{ "price": 0.33, "size": 800 },
{ "price": 0.35, "size": 600 }
]
}Returns the top markets sorted by 24-hour volume descending. Useful for featured sections and homepage carousels. Returns up to 20 results.
curl https://your-domain.com/v1/markets/trending \ -H "x-api-key: pk_live_xxx"
[
{
"id": "mkt_01hx",
"question": "BTC above $100k by Q3?",
"yesPrice": 0.67,
"volume24h": 4210.50
}
]Markets — Admin
Market lifecycle management requires an admin key. These endpoints handle creation, suspension, and settlement — they should only be called from your secure backend.
Creates a new prediction market. The market opens immediately and accepts trades until closeAt. Set p0 to define the initial YES probability (e.g. 0.5 for 50/50).
| Field | Type | Required | Description |
|---|---|---|---|
| question | string | required | The market question displayed to traders |
| category | string | required | Category slug for grouping and filtering |
| kind | string | required | cpmm or lmsr |
| p0 | number | required | Initial YES probability, 0–1 (e.g. 0.5) |
| closeAt | string | required | ISO 8601 timestamp when trading closes |
| resolutionRef | string | required | Human-readable source for resolution (e.g. official result URL) |
| meta | object | optional | Arbitrary key-value metadata stored with the market |
curl -X POST https://your-domain.com/v1/markets \ -H "x-api-key: ak_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "question": "BTC above $100k by Q3?", "category": "crypto", "kind": "cpmm", "p0": 0.5, "closeAt": "2026-09-30T23:59:00Z", "resolutionRef": "Coinbase BTC/USD daily close" }'
{
"id": "mkt_01hx",
"status": "open",
"yesPrice": 0.50,
"createdAt": "2026-06-23T09:00:00Z"
}Halts all trading on a market immediately. Outstanding positions are preserved. Use this for emergency holds or pre-settlement lockdowns. Returns the updated market object.
curl -X POST \ https://your-domain.com/v1/markets/mkt_01hx/suspend \ -H "x-api-key: ak_live_xxx"
{
"id": "mkt_01hx",
"status": "suspended"
}Resumes trading on a suspended market, provided it has not yet reached its closeAt time. Returns the updated market object with status: "open".
curl -X POST \ https://your-domain.com/v1/markets/mkt_01hx/resume \ -H "x-api-key: ak_live_xxx"
{
"id": "mkt_01hx",
"status": "open"
}Settles a market with the given outcome. All winning positions are paid out and the market is closed. This action is irreversible — verify the outcome and resolution reference before calling.
| Field | Type | Required | Description |
|---|---|---|---|
| marketId | string | required | ID of the market to settle |
| outcome | string | required | "yes" or "no" |
curl -X POST https://your-domain.com/v1/settle \ -H "x-api-key: ak_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "marketId": "mkt_01hx", "outcome": "yes" }'
{
"id": "mkt_01hx",
"status": "settled",
"outcome": "yes",
"settledAt": "2026-10-01T08:00:00Z",
"payoutsProcessed": 312
}Returns the status of the built-in auto-resolution oracle for this tenant — how many markets are covered, how many price feeds are available, when the last sweep ran, and a log of recent automatic resolutions. The oracle runs every 30 seconds alongside the market lifecycle sweep; no configuration is required for the 40+ supported instruments.
curl -G https://your-domain.com/v1/oracle/status \ -H "x-api-key: sk_admin_…"
Response
{
"supportedFeeds": 41,
"eligibleMarkets": 14,
"lastSweepAt": "2026-06-23T09:00:30.000Z",
"recentResolutions": [
{
"ts": "2026-06-23T09:00:30.000Z",
"marketId": "mkt_01j…",
"ref": "lbma:xau",
"price": 3341.20,
"strike": 3350,
"outcome": 1,
"question": "XAU/USD ≥ 3,350 at Friday's LBMA PM auction?"
}
]
}Oracle feed reference keys
| lbma:xau · lbma:xag | Gold / Silver (CME futures proxy) |
| wmr:eurusd · wmr:gbpusd · wmr:usdjpy · wmr:usdchf · wmr:audusd · wmr:usdcad · wmr:nzdusd · wmr:eurgbp · wmr:eurjpy · wmr:usdaed · wmr:usdsgd · wmr:usdzar | FX (open.er-api.com) |
| cmebrr:btc · cmebrr:eth · cmebrr:sol · cmebrr:bnb · cmebrr:xrp · cmebrr:ada · cmebrr:avax | Crypto (CoinGecko) |
| cme:spx · cme:nas100 · cme:us30 · xetra:ger40 · lse:uk100 · ose:jp225 · asx:aus200 · hkex:hk50 | Equity indices (Yahoo Finance) |
| ice:brent · nymex:wti · nymex:ngas | Energy (Yahoo Finance) |
| nasdaq:aapl · nasdaq:tsla · nasdaq:nvda · nasdaq:amzn · nasdaq:msft · nasdaq:googl · nasdaq:meta · nyse:xom · nyse:jpm | Single stocks (Yahoo Finance) |
Markets without a resolution_ref mapping to a supported feed (politics, sports, macro events) require manual settlement via POST /v1/settle. To use a custom oracle instead, set resolution_url in Policy.
Returns a live summary of all open prediction trades that have been tagged as CFD hedges, aggregated by symbol. Your risk engine can poll or subscribe to this endpoint and subtract the activeCostMinor (or a risk-weighted fraction) from the maintenance margin requirement of the correlated CFD position — effectively cross-margining prediction markets against your CFD book.
curl https://your-domain.com/v1/hedge/exposure \ -H "x-api-key: sk_admin_…"
Response
{
"hedgeBook": [
{
"symbol": "EURUSD",
"openTrades": 34,
"longHedges": 22,
"shortHedges": 12,
"totalNotionalMinor": 850000000,
"activeCostMinor": 12400000
}
],
"updatedAt": "2026-06-23T10:15:00.000Z"
}Tagging a trade as a hedge — POST /v1/orders
Include a hedgeRef object in the order body to tag a prediction trade as a CFD hedge. The tag is stored on the trade record and included in the order.filled webhook.
{
"marketId": "mkt_01j…",
"selection": { "outcome": 1 },
"amount": 10000,
"hedgeRef": {
"symbol": "EURUSD",
"direction": "long",
"notional": 500000000,
"externalRef": "MT5-12345678"
}
}Trading
Trading endpoints require a public key plus an authenticated user context — either a session cookie (browser) or a signed JWT passed as Authorization: Bearer <token>.
Places a trade on behalf of the authenticated user. Returns the executed trade, final price, and updated position. Trades are rejected if the market is suspended or closed.
| Field | Type | Required | Description |
|---|---|---|---|
| marketId | string | required | Target market ID |
| selection.side | string | required | "yes" or "no" |
| amount | number | required | Amount in the platform's base currency (USD) |
curl -X POST https://your-domain.com/v1/trade \ -H "x-api-key: pk_live_xxx" \ -H "Authorization: Bearer <user-jwt>" \ -H "Content-Type: application/json" \ -d '{ "marketId": "mkt_01hx", "selection": { "side": "yes" }, "amount": 50 }'
{
"tradeId": "tr_9xkl",
"marketId": "mkt_01hx",
"side": "yes",
"amount": 50,
"shares": 74.63,
"avgPrice": 0.6699,
"ts": "2026-06-23T09:14:32Z"
}Returns all open positions for the authenticated user across all markets. Includes current market price for unrealized P&L calculation.
curl https://your-domain.com/v1/positions \ -H "x-api-key: pk_live_xxx" \ -H "Authorization: Bearer <user-jwt>"
[
{
"marketId": "mkt_01hx",
"question": "BTC above $100k by Q3?",
"side": "yes",
"shares": 74.63,
"avgCost": 0.6699,
"currentPrice": 0.72,
"unrealizedPnl": 3.75
}
]Submit appropriateness questionnaire answers for the authenticated trader. The platform scores the answers (sum of numeric values, max 20) and maps the score to an exposure cap tier defined in tenant Policy. Returns the score and the resulting cap. If suitabilityRequired is enabled in Policy, trades are blocked until this endpoint has been called successfully.
| answers | object | required | Key/value map of question IDs to numeric answer scores (0–4 each). Keys are operator-defined. |
curl -X POST https://your-domain.com/v1/suitability \ -H "x-api-key: sk_public_…" \ -H "Authorization: Bearer <trader-jwt>" \ -d '{"answers":{"experience":3,"income":2,"risk_tolerance":4,"knowledge":2,"horizon":3}}'
Response
{
"score": 14,
"exposureCapMinor": 25000000,
"passed": true
}Returns the authenticated trader's current suitability status — whether they've completed the assessment, their score, and their exposure cap.
{ "completed": false }
// or when completed:
{ "completed": true, "score": 14, "exposureCapMinor": 25000000, "updatedAt": "2026-06-23T…" }Default cap thresholds (configurable in Policy → suitabilityThresholds): score 0–8 → 500 units; 9–14 → 2,500 units; 15–20 → no cap. A score below the first threshold blocks trading entirely until re-assessed.
Reports
Operator-facing analytics and risk dashboards. All report endpoints require an admin key and return aggregate data for the tenant.
Returns a P&L summary, total volume, and count of settled markets for the tenant. Defaults to the trailing 30 days.
curl https://your-domain.com/v1/reports/overview \ -H "x-api-key: ak_live_xxx"
{
"periodDays": 30,
"totalVolume": 284100.00,
"houseRevenue": 5682.00,
"settledMarkets": 14,
"activeTraders": 892
}Detailed house revenue breakdown — fees collected, spread income, and net margin per market category.
curl https://your-domain.com/v1/reports/house \ -H "x-api-key: ak_live_xxx"
{
"totalFees": 3420.50,
"spreadIncome": 2261.50,
"byCategory": [
{
"category": "crypto",
"revenue": 2100.00,
"volume": 105000.00
}
]
}Trader leaderboard and activity summary. Returns top traders by volume, win rate, and total P&L, plus aggregate activity stats.
Deposit and withdrawal summary — total inflows, outflows, pending withdrawals, and net platform balance movement.
Live exposure snapshot — shows the maximum payout liability per open market. Use to monitor platform solvency in real time.
curl https://your-domain.com/v1/risk \ -H "x-api-key: ak_live_xxx"
[
{
"marketId": "mkt_01hx",
"question": "BTC above $100k by Q3?",
"maxLiability": 18420.00,
"yesPayout": 18420.00,
"noPayout": 9100.00
}
]Audit
The audit log provides a tamper-evident record of every platform event — trades, settlements, policy changes, and admin actions. Entries are chained via HMAC so the log can be independently verified.
Returns paginated audit log entries in reverse-chronological order. Each entry includes the event type, actor, timestamp, and a chain hash linking it to the previous entry.
curl https://your-domain.com/v1/audit \ -H "x-api-key: ak_live_xxx"
{
"entries": [
{
"seq": 4182,
"type": "market.settled",
"actor": "[email protected]",
"payload": { "marketId": "mkt_01hx", "outcome": "yes" },
"hash": "a3f9c...",
"ts": "2026-10-01T08:00:00Z"
}
],
"cursor": "eyJzZXEiOjQxODF9"
}Returns the current chain verification status — whether all entries are intact, the hash of the latest entry, and the total entry count.
curl https://your-domain.com/v1/audit/status \ -H "x-api-key: ak_live_xxx"
{
"verified": true,
"head": "a3f9c8b2d4e1...",
"count": 4182
}Triggers a full ledger reconciliation — verifies that all balances, trades, and payouts are internally consistent. Returns a discrepancy amount (should be 0 on a healthy ledger). This is a heavyweight operation; run off-peak.
curl -X POST https://your-domain.com/v1/reconcile \ -H "x-api-key: ak_live_xxx"
{
"ok": true,
"discrepancy": 0.00,
"checkedAt": "2026-06-23T02:00:00Z"
}Policy
Tenant-level configuration for fees, trading limits, and jurisdiction rules. Changes take effect immediately and are recorded in the audit log.
Returns the active tenant policy including fee rates, per-trade and per-user limits, and jurisdiction allow/blocklists.
curl https://your-domain.com/v1/policy \ -H "x-api-key: ak_live_xxx"
{
"feeBps": 200,
"maxTradeUsd": 10000,
"maxUserExposureUsd": 50000,
"jurisdictions": {
"allowed": ["AE", "SA", "KW"],
"blocked": ["US", "GB"]
}
}Updates the tenant policy. Send only the fields you want to change — unspecified fields retain their current values. All changes are audit-logged with the actor identity.
curl -X PUT https://your-domain.com/v1/policy \ -H "x-api-key: ak_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "feeBps": 150, "maxTradeUsd": 5000 }'
{
"feeBps": 150,
"maxTradeUsd": 5000,
"maxUserExposureUsd": 50000,
"updatedAt": "2026-06-23T09:30:00Z"
}Inbound Webhooks
These endpoints receive push notifications from your KYC provider and payment processor. They do not require an API key — authenticate exclusively via HMAC signature as described in the Webhook Verification section. Respond with HTTP 200 within 5 seconds or the provider will retry.
Receives KYC verification results from your identity provider. On a passed status, the user's account is automatically unlocked for trading. Failed checks trigger a notification to the user.
{
"userId": "usr_88zq",
"status": "passed",
"provider": "sumsub",
"verifiedAt": "2026-06-23T09:00:00Z"
}HTTP 200
{
"received": true
}Receives deposit confirmation events from your payment processor (non-Stripe). Credits the user's wallet on a deposit.completed event type.
{
"event": "deposit.completed",
"userId": "usr_88zq",
"amountUsd": 500.00,
"txRef": "psp_tx_99ab"
}HTTP 200
{
"received": true
}Stripe webhook endpoint. Uses Stripe's native Stripe-Signature header for verification rather than the generic HMAC scheme. Handles payment_intent.succeeded, checkout.session.completed, and related events.
Embed SDK
The Embed SDK lets you mount a fully functional PredSouq trading interface inside any web page via a single script tag. The embed renders in an isolated iframe — no build step or framework required.
Content-Security-Policy, add frame-src https://your-domain.com to allow the embed iframe to load.
<!-- 1. Load the SDK --> <script src="https://your-domain.com/assets/embed.js"></script> <!-- 2. Add a mount target --> <div id="predsouq-widget"></div> <!-- 3. Mount --> <script> PredSouq.embed("#predsouq-widget", { view: "market", // "market" | "markets" | "portfolio" theme: "dark", // "dark" | "light" height: 600 // iframe height in px }); </script>