Skip to main content

Quick Start: Standalone Integration

This guide takes you from zero to a live prediction markets integration. You will implement the wallet endpoints your server must expose, verify HMAC signatures on platform requests, set up the auth session flow, and embed the widget in your frontend.

Follow the steps in order — each section builds on the previous.

Prerequisites

Before you start, confirm you have:

  • A server reachable over HTTPS that the platform can call for wallet operations
  • A shared HMAC secret provisioned by your account manager (used to verify platform requests)
  • A bettor identity system — you must be able to look up a bettor by their ID and debit or credit their balance
  • The following values from your onboarding pack:
VariableDescription
WIDGET_BASE_URLBase URL of the prediction widget iFrame
PLATFORM_HMAC_SECRETShared secret for verifying X-Payload-Signature headers

Step 1: Implement Wallet Endpoints

The platform makes three server-to-server calls to your backend during the bet lifecycle. You must implement all three.

EndpointWhen CalledPurpose
POST /v1/withdrawalsBet placementDebit stake from bettor balance
POST /v1/depositsSettlementCredit payout to bettor balance
DELETE /v1/rollbacks/{id}Failed bet compensationReverse a prior withdrawal

Request Shape

Every wallet call carries these headers:

HeaderValue
X-Payload-SignatureHMAC-SHA256 hex of the raw request body
X-TimestampISO 8601 UTC timestamp
X-NonceUUID v4, unique per request
Content-Typeapplication/json

Example: Withdrawal

curl -X POST https://your-backend.example.com/v1/withdrawals \
-H "Content-Type: application/json" \
-H "X-Payload-Signature: 37f9186da8bef5457f94d56d1c76dc37..." \
-H "X-Timestamp: 2024-03-04T12:00:00Z" \
-H "X-Nonce: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
-d '{
"transaction_id": "txn_01HZABC...",
"bettor_id": "bettor_42",
"amount": "10.50",
"currency": "USD",
"metadata": { "market_id": "mkt_..." }
}'

Expected response (HTTP 200):

{
"transaction_id": "your-internal-txn-id",
"balance": "489.50"
}

Idempotency

The platform may retry wallet calls on network timeouts. Your endpoints must be idempotent — if you receive a request with a transaction_id you have already processed, return the same response you returned the first time. Do not debit or credit the bettor a second time.

For full request/response schemas and deposit amount rules, see the Wallet Endpoints reference.

Step 2: Verify HMAC Signatures

Every wallet call includes an X-Payload-Signature header. Verify this before processing any request.

Algorithm

signature = HMAC-SHA256(raw_request_body, PLATFORM_HMAC_SECRET)
expected = hex_encode(signature)
valid = timing_safe_equal(expected, X-Payload-Signature)

Use a timing-safe comparison to prevent timing attacks.

Node.js Middleware

const crypto = require('crypto');

function verifyPlatformRequest(req, res, next) {
const signature = req.headers['x-payload-signature'];
const timestamp = req.headers['x-timestamp'];

if (!signature || !timestamp) {
return res.status(401).json({ error: 'Missing signature headers' });
}

// Reject requests older than 5 minutes
const age = Date.now() - new Date(timestamp).getTime();
if (age > 5 * 60 * 1000) {
return res.status(401).json({ error: 'Request timestamp too old' });
}

// Verify HMAC
const expected = crypto
.createHmac('sha256', process.env.PLATFORM_HMAC_SECRET)
.update(req.rawBody) // use raw body, not parsed JSON
.digest('hex');

const valid = crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature),
);

if (!valid) {
return res.status(401).json({ error: 'Invalid signature' });
}

next();
}
Raw body required

Sign and verify against the raw request bytes before JSON parsing. If your framework parses the body first, configure it to preserve the original buffer (e.g., Express verify option).

For Go and Python examples, see Wallet Endpoints — Verification Examples.

Step 3: Implement the Auth Session Flow

The widget uses a one-time session_request_id to obtain a platform JWT. Your backend must implement two endpoints: a session endpoint to exchange session request IDs for your auth token, and an auth endpoint to validate that token and return bettor identity.

How It Works

1. Bettor opens your platform
2. Your backend generates a short-lived session_request_id (1–2 min TTL, single-use)
3. Your page loads the widget iFrame: WIDGET_BASE_URL?session_request_id=<id>
4. Widget calls POST /api/v1/auth/session on the platform gateway with the session_request_id
5. Platform calls POST /v1/session on your backend with the session_request_id
6. Your backend validates it, returns your auth_token, marks it used
7. Platform calls POST /v1/auth to verify the auth_token and retrieve bettor identity
8. Platform issues a session token and returns it to the widget
9. Widget uses the session token for all subsequent API calls

Session Endpoint

The platform calls POST /v1/session on your backend (server-to-server).

# Platform sends:
POST /v1/session
Content-Type: application/json
X-Payload-Signature: <hmac>
X-Timestamp: 2024-03-04T12:00:00Z
X-Nonce: a1b2c3d4-e5f6-7890-abcd-ef1234567890

{ "session_request_id": "sreq_01HZABC..." }
# Your backend responds:
HTTP 200
{
"auth_token": "<your auth token>"
}

Return your system's auth token for the bettor. The platform stores this for subsequent wallet operations — the format is up to you, the platform treats it as opaque.

Auth Endpoint

The platform calls POST /v1/auth to verify an auth token and retrieve bettor identity:

# Platform sends:
POST /v1/auth
Content-Type: application/json
Session: <auth_token>
X-Payload-Signature: <hmac>
X-Timestamp: 2024-03-04T12:00:00Z
X-Nonce: a1b2c3d4-e5f6-7890-abcd-ef1234567890

{}
# Your backend responds:
HTTP 200
{
"accountId": "bettor-42",
"currency": "USD"
}
FieldTypeDescription
accountIdstringBettor's account ID in your system
currencystringISO 4217 currency code (e.g., "USD")

For the full auth architecture including sequence diagram, see the Authentication Flow reference.

Step 4: Embed the Widget

With wallet endpoints and session flow in place, embed the widget on your platform page.

Basic iFrame

<!-- In your page template, after generating session_request_id server-side -->
<iframe
src="https://widget.predictionmarkets.io?session_request_id=SREQ_ID&locale=en&theme=dark"
width="100%"
height="600"
frameborder="0"
allow="clipboard-write"
title="Prediction Markets"
></iframe>

Replace SREQ_ID with the session_request_id your backend generated for this session. Never reuse a session_request_id across page loads.

The widget is fully customizable to match your platform branding — colors, typography, layout, and theme are configured during onboarding. For details, see Widget Embedding.

Responsive Sizing

.predictions-widget-container {
position: relative;
width: 100%;
max-width: 1200px;
min-height: 400px;
}

.predictions-widget-container iframe {
width: 100%;
height: 100%;
min-height: 400px;
border: none;
}

For URL parameters, responsive CSS details, and security considerations, see the Widget Embedding reference.

Go-Live Checklist

Run through this before enabling predictions for real bettors:

  • POST /v1/withdrawals implemented and idempotent
  • POST /v1/deposits implemented and handles zero-amount deposits (lost bets)
  • DELETE /v1/rollbacks/{id} implemented and credits bettor back
  • HMAC signature verification active on all three wallet endpoints
  • Timestamp validation rejects requests older than 5 minutes
  • Nonce deduplication in place (optional but recommended)
  • Session endpoint validates session_request_id, returns auth_token, marks it single-use
  • Auth endpoint returns accountId and currency for a valid Session token
  • Session request IDs are single-use and expire after 1–2 minutes
  • Widget iFrame loads with fresh session_request_id on each page render
  • Tested end-to-end with the sandbox environment provided during onboarding

See Also