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.

Base URL
https://your-domain.com
Protocol
HTTPS only · JSON request & response
API Version
v1
Rate Limit
20 requests / min per IP

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.

Admin
Full access — market creation, settlement, reports, audit log, policy management, and all public endpoints. Never expose in client-side code or embed scripts.
Public
Trader-facing read and trade operations. Safe to embed in web and mobile clients. Cannot access admin, reports, audit, or policy endpoints.
Request header
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.

StatusMeaningCommon cause
200OKRequest succeeded
400Bad RequestMissing or invalid body/params
401UnauthorizedMissing or invalid x-api-key
403ForbiddenPublic key used on admin endpoint
404Not FoundMarket or resource does not exist
429Too Many Requests20 req/min per IP exceeded
500Server ErrorUnexpected failure — contact support
Error response
{
  "error": "Market not found"
}
Rate limits. The 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.

  1. 1
    Extract the X-Signature (or provider-specific) header from the inbound request.
  2. 2
    Compute HMAC-SHA256(webhookSecret, rawRequestBody) using the shared secret from your dashboard.
  3. 3
    Compare the computed hex digest with the header value using a constant-time comparison. Reject if they differ.
  4. 4
    Validate the timestamp (if provided) to reject replays older than 5 minutes.
Node.js verification example
// 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

GET /health No auth

Returns the current health of the API server and database connection. Use this endpoint for uptime monitoring and load balancer checks — no authentication required.

Request
curl https://your-domain.com/health
Response
{
  "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.

GET /v1/markets Public key

Returns a paginated list of open markets. Supports filtering by category, full-text search, and market mechanism kind.

Query parameters
ParameterTypeRequiredDescription
categorystringoptionalFilter by category slug, e.g. sports, crypto, macro
qstringoptionalFull-text search across market questions and descriptions
kindstringoptionalMarket mechanism: cpmm (constant-product) or lmsr (logarithmic)
Request
curl https://your-domain.com/v1/markets \
  ?category=crypto&kind=cpmm \
  -H "x-api-key: pk_live_xxx"
Response
[
  {
    "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"
  }
]
GET /v1/markets/:id Public key

Returns a single market with current odds, cumulative volume, close time, resolution reference, and market metadata.

Request
curl https://your-domain.com/v1/markets/mkt_01hx \
  -H "x-api-key: pk_live_xxx"
Response
{
  "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": {}
}
GET /v1/markets/:id/history Public key

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.

Request
curl https://your-domain.com/v1/markets/mkt_01hx/history \
  -H "x-api-key: pk_live_xxx"
Response
[
  {
    "t": "2026-06-01T00:00:00Z",
    "yes": 0.50,
    "no": 0.50
  },
  {
    "t": "2026-06-01T01:00:00Z",
    "yes": 0.53,
    "no": 0.47
  }
]
GET /v1/markets/:id/depth Public key

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.

Request
curl https://your-domain.com/v1/markets/mkt_01hx/depth \
  -H "x-api-key: pk_live_xxx"
Response
{
  "yes": [
    { "price": 0.67, "size": 500 },
    { "price": 0.65, "size": 1200 }
  ],
  "no": [
    { "price": 0.33, "size": 800 },
    { "price": 0.35, "size": 600 }
  ]
}

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.

POST /v1/markets Admin key

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).

Request body
FieldTypeRequiredDescription
questionstringrequiredThe market question displayed to traders
categorystringrequiredCategory slug for grouping and filtering
kindstringrequiredcpmm or lmsr
p0numberrequiredInitial YES probability, 0–1 (e.g. 0.5)
closeAtstringrequiredISO 8601 timestamp when trading closes
resolutionRefstringrequiredHuman-readable source for resolution (e.g. official result URL)
metaobjectoptionalArbitrary key-value metadata stored with the market
Request
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"
  }'
Response
{
  "id": "mkt_01hx",
  "status": "open",
  "yesPrice": 0.50,
  "createdAt": "2026-06-23T09:00:00Z"
}
POST /v1/markets/:id/suspend Admin key

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.

Request
curl -X POST \
  https://your-domain.com/v1/markets/mkt_01hx/suspend \
  -H "x-api-key: ak_live_xxx"
Response
{
  "id": "mkt_01hx",
  "status": "suspended"
}
POST /v1/markets/:id/resume Admin key

Resumes trading on a suspended market, provided it has not yet reached its closeAt time. Returns the updated market object with status: "open".

Request
curl -X POST \
  https://your-domain.com/v1/markets/mkt_01hx/resume \
  -H "x-api-key: ak_live_xxx"
Response
{
  "id": "mkt_01hx",
  "status": "open"
}
POST /v1/settle Admin key

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.

Request body
FieldTypeRequiredDescription
marketIdstringrequiredID of the market to settle
outcomestringrequired"yes" or "no"
Request
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"
  }'
Response
{
  "id": "mkt_01hx",
  "status": "settled",
  "outcome": "yes",
  "settledAt": "2026-10-01T08:00:00Z",
  "payoutsProcessed": 312
}
GET /v1/oracle/status admin

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:xagGold / 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:usdzarFX (open.er-api.com)
cmebrr:btc · cmebrr:eth · cmebrr:sol · cmebrr:bnb · cmebrr:xrp · cmebrr:ada · cmebrr:avaxCrypto (CoinGecko)
cme:spx · cme:nas100 · cme:us30 · xetra:ger40 · lse:uk100 · ose:jp225 · asx:aus200 · hkex:hk50Equity indices (Yahoo Finance)
ice:brent · nymex:wti · nymex:ngasEnergy (Yahoo Finance)
nasdaq:aapl · nasdaq:tsla · nasdaq:nvda · nasdaq:amzn · nasdaq:msft · nasdaq:googl · nasdaq:meta · nyse:xom · nyse:jpmSingle 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.

GET /v1/hedge/exposure admin

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>.

POST /v1/trade Public key + user session

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.

Request body
FieldTypeRequiredDescription
marketIdstringrequiredTarget market ID
selection.sidestringrequired"yes" or "no"
amountnumberrequiredAmount in the platform's base currency (USD)
Request
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
  }'
Response
{
  "tradeId": "tr_9xkl",
  "marketId": "mkt_01hx",
  "side": "yes",
  "amount": 50,
  "shares": 74.63,
  "avgPrice": 0.6699,
  "ts": "2026-06-23T09:14:32Z"
}
GET /v1/positions Public key + user session

Returns all open positions for the authenticated user across all markets. Includes current market price for unrealized P&L calculation.

Request
curl https://your-domain.com/v1/positions \
  -H "x-api-key: pk_live_xxx" \
  -H "Authorization: Bearer <user-jwt>"
Response
[
  {
    "marketId": "mkt_01hx",
    "question": "BTC above $100k by Q3?",
    "side": "yes",
    "shares": 74.63,
    "avgCost": 0.6699,
    "currentPrice": 0.72,
    "unrealizedPnl": 3.75
  }
]
POST /v1/suitability public

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.

answersobjectrequiredKey/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
}
GET /v1/suitability public

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.

GET /v1/reports/overview Admin key

Returns a P&L summary, total volume, and count of settled markets for the tenant. Defaults to the trailing 30 days.

Request
curl https://your-domain.com/v1/reports/overview \
  -H "x-api-key: ak_live_xxx"
Response
{
  "periodDays": 30,
  "totalVolume": 284100.00,
  "houseRevenue": 5682.00,
  "settledMarkets": 14,
  "activeTraders": 892
}
GET /v1/reports/house Admin key

Detailed house revenue breakdown — fees collected, spread income, and net margin per market category.

Request
curl https://your-domain.com/v1/reports/house \
  -H "x-api-key: ak_live_xxx"
Response
{
  "totalFees": 3420.50,
  "spreadIncome": 2261.50,
  "byCategory": [
    {
      "category": "crypto",
      "revenue": 2100.00,
      "volume": 105000.00
    }
  ]
}
GET /v1/reports/traders Admin key

Trader leaderboard and activity summary. Returns top traders by volume, win rate, and total P&L, plus aggregate activity stats.

GET /v1/reports/funding Admin key

Deposit and withdrawal summary — total inflows, outflows, pending withdrawals, and net platform balance movement.

GET /v1/risk Admin key

Live exposure snapshot — shows the maximum payout liability per open market. Use to monitor platform solvency in real time.

Request
curl https://your-domain.com/v1/risk \
  -H "x-api-key: ak_live_xxx"
Response
[
  {
    "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.

GET /v1/audit Admin key

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.

Request
curl https://your-domain.com/v1/audit \
  -H "x-api-key: ak_live_xxx"
Response
{
  "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"
}
GET /v1/audit/status Admin key

Returns the current chain verification status — whether all entries are intact, the hash of the latest entry, and the total entry count.

Request
curl https://your-domain.com/v1/audit/status \
  -H "x-api-key: ak_live_xxx"
Response
{
  "verified": true,
  "head": "a3f9c8b2d4e1...",
  "count": 4182
}
POST /v1/reconcile Admin key

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.

Request
curl -X POST https://your-domain.com/v1/reconcile \
  -H "x-api-key: ak_live_xxx"
Response
{
  "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.

GET /v1/policy Admin key

Returns the active tenant policy including fee rates, per-trade and per-user limits, and jurisdiction allow/blocklists.

Request
curl https://your-domain.com/v1/policy \
  -H "x-api-key: ak_live_xxx"
Response
{
  "feeBps": 200,
  "maxTradeUsd": 10000,
  "maxUserExposureUsd": 50000,
  "jurisdictions": {
    "allowed": ["AE", "SA", "KW"],
    "blocked": ["US", "GB"]
  }
}
PUT /v1/policy Admin key

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.

Request
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
  }'
Response
{
  "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.

POST /kyc/inbound/:tenantId HMAC verified

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.

Inbound payload
{
  "userId": "usr_88zq",
  "status": "passed",
  "provider": "sumsub",
  "verifiedAt": "2026-06-23T09:00:00Z"
}
Expected response
HTTP 200
{
  "received": true
}
POST /payments/inbound/:tenantId HMAC verified

Receives deposit confirmation events from your payment processor (non-Stripe). Credits the user's wallet on a deposit.completed event type.

Inbound payload
{
  "event": "deposit.completed",
  "userId": "usr_88zq",
  "amountUsd": 500.00,
  "txRef": "psp_tx_99ab"
}
Expected response
HTTP 200
{
  "received": true
}
POST /payments/stripe/:tenantId Stripe-Signature verified

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.

CSP note. If your site has a Content-Security-Policy, add frame-src https://your-domain.com to allow the embed iframe to load.
Installation
<!-- 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>
PredSouq.embed( selector, options )
Option
Type
Description
selector
string
CSS selector for the mount target element
view
string
market — single market widget. markets — full browse view. portfolio — user positions
theme
string
dark or light. Defaults to dark
height
number
Iframe height in pixels. Defaults to 600
PredSouq API Reference · v1 · Questions? [email protected]