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:
| Variable | Description |
|---|---|
WIDGET_BASE_URL | Base URL of the prediction widget iFrame |
PLATFORM_HMAC_SECRET | Shared 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.
| Endpoint | When Called | Purpose |
|---|---|---|
POST /v1/withdrawals | Bet placement | Debit stake from bettor balance |
POST /v1/deposits | Settlement | Credit payout to bettor balance |
DELETE /v1/rollbacks/{id} | Failed bet compensation | Reverse a prior withdrawal |
Request Shape
Every wallet call carries these headers:
| Header | Value |
|---|---|
X-Payload-Signature | HMAC-SHA256 hex of the raw request body |
X-Timestamp | ISO 8601 UTC timestamp |
X-Nonce | UUID v4, unique per request |
Content-Type | application/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();
}
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"
}
| Field | Type | Description |
|---|---|---|
accountId | string | Bettor's account ID in your system |
currency | string | ISO 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/withdrawalsimplemented and idempotent -
POST /v1/depositsimplemented 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
accountIdandcurrencyfor a validSessiontoken - Session request IDs are single-use and expire after 1–2 minutes
- Widget iFrame loads with fresh
session_request_idon each page render - Tested end-to-end with the sandbox environment provided during onboarding
See Also
- Wallet Endpoints — full request/response schemas, rollback rules, deposit amount table
- Authentication Flow — two-JWT architecture, sequence diagram, key points
- Widget Embedding — URL parameters, responsive sizing, CSP requirements
- Standalone vs Bundled — comparison of integration paths