Predictu
S2S Protocol

S2S Protocol Overview

The S2S (Server-to-Server) protocol is Predictu's callback-based integration system for casino operators. It follows the same architectural pattern used by established gaming aggregators like Betby and SoftSwiss - Predictu calls the operator's backend for every wallet operation, and the operator remains the sole owner of player balances at all times.

Key principle: The operator owns the wallet. Predictu owns the game logic. Every debit, credit, and balance check is delegated to the operator via a signed HTTP callback. Predictu never stores or controls real player funds.

How It Works

When a player interacts with the Predictu prediction market widget embedded on the casino site, every financial operation triggers an outbound HTTP POST from Predictu to the operator's registered callback_url. The operator processes the request, debits or credits the player's wallet, and returns a response.

1
Player places a trade - The player clicks "Buy 10 shares of YES at 52c" inside the embedded Predictu widget on the casino site.
2
Predictu validates & prices - The trade executor validates the request, checks risk limits, and calculates the spread-adjusted execution price.
3
Predictu sends S2S callback - A JWT-signed BET_MAKE request is POSTed to the operator's callback_url containing the player ID, amount to debit, and full bet context (market, outcome, odds, shares).
4
Operator processes the request - The operator verifies the JWT, checks idempotency, debits the player's wallet, records the transaction, and returns{ status: "OK", balance: 95000, transaction_id: "op-tx-123" }.
5
Predictu records the trade - On receiving OK, Predictu records the trade, updates the player's position, and adjusts casino exposure.
6
UI updates in real time - The widget shows the player their new position and updated balance. The operator's own UI can also refresh via their internal APIs.
  Player (Browser)         Predictu Platform           Operator Backend
  ─────────────────        ──────────────────          ──────────────────
        │                         │                          │
        │  Click "Buy YES"        │                          │
        ├────────────────────────►│                          │
        │                         │                          │
        │                         │  POST /api/callback      │
        │                         │  Authorization: Bearer   │
        │                         │  { method: "BET_MAKE",   │
        │                         │    params: { amount,     │
        │                         │    player_id, bet } }    │
        │                         ├─────────────────────────►│
        │                         │                          │
        │                         │  { status: "OK",         │
        │                         │    balance: 95000,       │
        │                         │    transaction_id }      │
        │                         │◄─────────────────────────┤
        │                         │                          │
        │  Trade confirmed +      │                          │
        │  balance updated        │                          │
        │◄────────────────────────┤                          │
        │                         │                          │

Request Envelope

Every S2S callback from Predictu uses the same top-level JSON envelope. Themethod field determines the type of operation, and the paramsobject contains method-specific data.

{
  "method": "BET_MAKE",
  "timestamp": "2026-03-19T14:30:00.000Z",
  "request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "operator_id": "op_abc123",
  "params": {
    "player_id": "player_456",
    "transaction_id": "tx_789",
    "amount": 5200,
    "currency": "USD",
    "bet": {
      "market_id": "mkt_001",
      "market_title": "Will Bitcoin exceed $100k by June 2026?",
      "outcome": "yes",
      "odds": 1.92,
      "shares": 100,
      "execution_price": 52
    }
  }
}

Envelope Fields

FieldTypeDescription
methodstringThe callback method being invoked (e.g. BET_MAKE, BALANCE). See supported methods below.
timestampstringISO 8601 timestamp of when Predictu generated the request. Example: 2026-03-19T14:30:00.000Z. Always UTC.
request_idstringUUID v4 - the idempotency key for this request. If you receive the same request_id twice, return DUPLICATE_TRANSACTION with the cached result.
operator_idstringYour operator UUID in Predictu's system. Use this to route multi-brand callbacks if you operate multiple skins.
paramsobjectMethod-specific parameters. Structure varies per method. See Callback Methods for full schemas.

Response Envelope

The operator must respond with a JSON object containing at minimum a status field. Additional fields depend on the method.

{
  "status": "OK",
  "balance": 95000,
  "transaction_id": "op-tx-abc-123"
}

Response Fields

FieldTypeRequiredDescription
statusstringYesOne of the response status codes listed below.
balancenumberRecommendedPlayer's current balance in subunits after the operation. Used by Predictu to keep the UI in sync.
transaction_idstringRecommendedYour internal transaction ID for this operation. Stored by Predictu for reconciliation.
error_messagestringNoHuman-readable error description. Only used when status is not OK.

Response Status Codes

Predictu recognizes five standard status codes in the response. Your callback endpoint must return one of these in the status field.

StatusMeaningPredictu Behavior
OKOperation succeeded. The wallet was updated.Marks the S2S transaction as success. Proceeds with trade execution.
INSUFFICIENT_FUNDSPlayer does not have enough balance for the debit.Marks as failed. No retry. Returns "Insufficient balance" to the player.
PLAYER_NOT_FOUNDThe player_id does not exist in the operator's system.Marks as failed. No retry. This typically indicates a mapping issue.
DUPLICATE_TRANSACTIONThis request_id has already been processed. Return the cached result.Treated as success. Predictu uses the cached balance and transaction ID.
ERRORGeneric server error on the operator side.Marks as retrying. Predictu will retry with exponential backoff up to the configured max_retries (default 3).
Important: INSUFFICIENT_FUNDS and PLAYER_NOT_FOUND are business-level errors that are never retried. Only ERROR and HTTP-level failures (timeouts, 5xx) trigger automatic retries.

Monetary Amounts - Subunits

All monetary values in the S2S protocol are expressed in subunits (cents). This avoids floating-point precision issues and is standard practice in payment systems.

Display AmountSubunit ValueField Example
$1.00100"amount": 100
$52.005200"amount": 5200
$0.5050"amount": 50
$1,000.00100000"balance": 100000
$0.000"amount": 0 (used in BET_LOST)
Never use floats. Amounts like 52.00 must be sent as 5200. Predictu's internal helpers toSubunits(52.00) and fromSubunits(5200) handle the conversion. Your operator backend should use the same pattern.
// Conversion helpers (reference implementation)
function toSubunits(dollars) {
  return Math.round(dollars * 100);
}

function fromSubunits(subunits) {
  return Math.round(subunits) / 100;
}

// Examples:
toSubunits(1.00)    // → 100
toSubunits(52.00)   // → 5200
fromSubunits(100)   // → 1.00
fromSubunits(5200)  // → 52.00

Supported Methods

The S2S protocol defines eight callback methods covering the full lifecycle of a prediction market bet - from placement through settlement or early sale.

MethodDirectionMoney MovementDescription
PING -NoneHealth check. Operator should respond with OK. Used during onboarding and monitoring.
BALANCE -NoneFetch a player's current balance. Called before every trade to validate funds.
BET_MAKEDebitPlayer → OperatorDebit player for a new bet. Contains full bet context (market, outcome, odds, shares, execution price).
BET_WINCreditOperator → PlayerCredit player for a winning bet after market resolution. Amount = shares × $1.00 (100 subunits per share).
BET_LOST -None (amount: 0)Notify operator of a losing bet after market resolution. No money movement - informational only for bookkeeping.
BET_REFUNDCreditOperator → PlayerFull refund of the original bet cost. Triggered when a market is voided or cancelled.
BET_ROLLBACKCreditOperator → PlayerReverse a BET_MAKE debit that failed downstream. Compensating transaction for atomicity.
BET_SELLCreditOperator → PlayerCredit player for selling a position before market resolution. Includes realized P&L data.

Bet Lifecycle

A prediction market bet follows one of three paths after placement. Every path starts with BET_MAKE and ends with exactly one terminal callback.

Path A: Winning Bet

BET_MAKE (debit $52)  →  market resolves YES  →  BET_WIN (credit $100)
    Player profits: $100 - $52 = $48

Path B: Losing Bet

BET_MAKE (debit $52)  →  market resolves NO  →  BET_LOST (amount: 0)
    Player loses: $52 (already debited)

Path C: Early Sale

BET_MAKE (debit $52)  →  price moves to 65¢  →  BET_SELL (credit $63.40)
    Player profits: $63.40 - $52.00 = $11.40

Path D: Market Void / Refund

BET_MAKE (debit $52)  →  market voided  →  BET_REFUND (credit $52)
    Player is made whole: $0 net impact

Path E: Failed Trade (Rollback)

BET_MAKE (debit $52)  →  trade recording fails  →  BET_ROLLBACK (credit $52)
    Player is made whole: $0 net impact (compensating transaction)

Retry Behavior

When a callback fails due to a network error, HTTP 5xx response, or ERROR status, Predictu automatically retries with exponential backoff.

ConfigDefaultDescription
max_retries3Maximum number of retry attempts after the initial attempt.
retry_backoff_ms1000Base backoff interval in milliseconds. Multiplied by 2^(attempt-1).
timeout_ms10000HTTP request timeout per attempt.
Attempt 1:  immediate
Attempt 2:  after 1,000ms   (1s)
Attempt 3:  after 2,000ms   (2s)
Attempt 4:  after 4,000ms   (4s)

Total max wait: ~7 seconds before the transaction is marked as failed.
Idempotency is critical. Because Predictu retries failed requests with the same request_id, your callback handler must be idempotent. If you see arequest_id you have already processed, return DUPLICATE_TRANSACTIONwith the cached balance and transaction ID.

Request Headers

Every S2S callback includes these HTTP headers:

HeaderExample ValueDescription
Content-Typeapplication/jsonAlways JSON.
AuthorizationBearer eyJhbGciOiJSUzI1NiIs...Signed JWT. See JWT Authentication.
X-Request-Ida1b2c3d4-e5f6-7890-abcd-ef1234567890Same as request_id in the body. Convenience header for logging.
X-Predictu-MethodBET_MAKESame as method in the body. Convenience header for routing.

Operator Configuration

Each operator has an S2S configuration record stored in Predictu's operator_api_configtable. This is managed via the God Mode admin dashboard during operator onboarding.

FieldTypeDescription
callback_urlstringHTTPS endpoint where Predictu will POST all S2S callbacks.
public_key_pemstringPublic key in PEM format. Operator fetches this to verify JWT signatures.
is_sandboxbooleanWhether this config is for sandbox/testing or production.
timeout_msnumberHTTP request timeout per attempt (default 10000ms).
max_retriesnumberMaximum retry attempts (default 3).
retry_backoff_msnumberBase backoff interval in ms (default 1000).
ip_whiteliststring[]Optional list of allowed IPs. If set, Predictu only sends callbacks to these IPs.
is_activebooleanKill switch. Set to false to immediately stop all S2S callbacks.

Transaction Recording

Every S2S callback is recorded in two tables for full auditability:

s2s_transactions

The primary transaction record. One row per callback attempt. Tracks the full lifecycle from pending through success or failed.

ColumnTypeDescription
iduuidPrimary key
operator_iduuidFK to operators table
request_iduuidIdempotency key (unique)
methodtextS2S method name
player_idtextOperator's player ID
user_iduuidPredictu's internal user ID
amount_subunitsintegerAmount in cents
currencytextCurrency code (e.g. USD)
parent_transaction_idtextLinks to originating BET_MAKE
statustextpending | success | failed | retrying
operator_statustextStatus returned by operator
operator_transaction_idtextOperator's transaction ID
operator_balance_afterintegerBalance reported by operator
attempt_countintegerNumber of delivery attempts
last_errortextLast error message if any
market_idtextRelated market ID
trade_iduuidRelated trade ID
is_sandboxbooleanSandbox vs production flag
metadatajsonbFull params snapshot

s2s_callback_log

Detailed HTTP-level log of every callback attempt. Includes the full request body, response status code, response body, and round-trip duration. Used for debugging and operator support.

Next Steps