Skip to main content

Wallet Endpoints

Operators integrate with the platform by implementing these endpoints:

Authentication

EndpointWhen CalledPurpose
POST /v1/sessionWidget loadExchange session_request_id for a JWT
POST /v1/authSession checkValidate session and return player info

Transactions

EndpointWhen CalledPurpose
POST /v1/withdrawalsBet placementDebit stake from bettor's balance
POST /v1/depositsSettlementCredit payout to bettor's balance
DELETE /v1/rollbacks/{id}Withdrawal compensationReverse a failed withdrawal

All transaction endpoints must be idempotent — the platform may retry on network failures.

When rollbacks happen

The platform calls DELETE /v1/rollbacks/{id} only when a withdrawal succeeded but the bet placement failed in a subsequent step. The {id} is the transaction ID returned by the operator from the original POST /v1/withdrawals call.

Rollbacks are never called for deposits or settlements — only for withdrawals that need to be reversed.

The operator must credit the player's balance back by the original withdrawal amount.

Deposit amounts by settlement outcome
OutcomeAmountMeaning
Wonstake × oddsBettor's winnings
Lost0No payout — the platform still sends the call so the operator can close the bet
Voided / RefundedstakeFull stake returned

Negative amounts are never sent. Operators must handle zero-amount deposits as a settlement notification, not as a no-op.

Request Signing (HMAC-SHA256)

Every request from the platform includes an X-Payload-Signature header containing an HMAC-SHA256 signature of the raw request body. Operators must verify this signature before processing.

Algorithm

signature = HMAC-SHA256(request_body, shared_secret)
header = hex_encode(signature)

Headers Sent

HeaderValue
X-Payload-SignatureHex-encoded HMAC-SHA256 of the request body
X-TimestampISO 8601 UTC timestamp (for replay prevention)
X-NonceUUID v4 (for replay prevention)
Content-Typeapplication/json

Verification Example

Given:

  • Body: {"amount":"10.50"}
  • Secret: test-secret
  • Expected signature: 37f9186da8bef5457f94d56d1c76dc37f8c8854e35751cf7eb795da23d593329

Go

package main

import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
)

func VerifySignature(body []byte, signature, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(body)
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signature))
}

func main() {
body := []byte(`{"amount":"10.50"}`)
secret := "test-secret"

mac := hmac.New(sha256.New, []byte(secret))
mac.Write(body)
sig := hex.EncodeToString(mac.Sum(nil))

fmt.Println(sig)
// Output: 37f9186da8bef5457f94d56d1c76dc37f8c8854e35751cf7eb795da23d593329
}

Node.js

const crypto = require('crypto');

function verifySignature(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature),
);
}

// Test vector
const body = '{"amount":"10.50"}';
const secret = 'test-secret';
const sig = crypto.createHmac('sha256', secret).update(body).digest('hex');
console.log(sig);
// Output: 37f9186da8bef5457f94d56d1c76dc37f8c8854e35751cf7eb795da23d593329

Python

import hashlib
import hmac

def verify_signature(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)

# Test vector
body = b'{"amount":"10.50"}'
secret = "test-secret"
sig = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
print(sig)
# Output: 37f9186da8bef5457f94d56d1c76dc37f8c8854e35751cf7eb795da23d593329

Replay Prevention

Operators should also validate:

  1. Timestamp — reject requests where X-Timestamp is older than 5 minutes
  2. Nonce — store seen nonces and reject duplicates within the timestamp window

See Also