Integration Docs

REST API for operators. Server-to-server only. All requests require HMAC-signed authentication.

Base URLhttps://dive.example/api/public/v1

Quickstart

  1. Ask us to onboard your casino — we issue three credentials once: apiKey, apiSecret, and webhookSecret.
  2. Whitelist each player wallet you want to accept (or run the game unrestricted per operator).
  3. Point webhook_url at your server. We POST bet.placed and round.settled signed with your webhookSecret.
  4. On each player round: POST /rounds → play → POST /rounds/{id}/cashout.
  5. Verify every settled round on /fairness.

Authentication

Every request MUST include four headers:

Headers
X-Api-Key: <apiKey> X-Secret-Check: sha256(apiSecret) (hex, lowercase) X-Timestamp: <unix seconds> (skew must be within ±60s) X-Signature: HMAC_SHA256(sha256(apiSecret), `${ts}.${METHOD}.${path}.${sha256(body)}`)

Node.js example:

node
import { createHash, createHmac } from "node:crypto"; const API_KEY = process.env.DSDX_API_KEY; const API_SECRET = process.env.DSDX_API_SECRET; const BASE = "https://dive.example/api/public/v1"; async function call(method, path, body = null) { const ts = Math.floor(Date.now() / 1000).toString(); const bodyStr = body ? JSON.stringify(body) : ""; const bodyHash = createHash("sha256").update(bodyStr).digest("hex"); const secretHash = createHash("sha256").update(API_SECRET).digest("hex"); const canonical = `${ts}.${method.toUpperCase()}.${path}.${bodyHash}`; const signature = createHmac("sha256", secretHash).update(canonical).digest("hex"); const res = await fetch(BASE + path.replace("/api/public/v1", ""), { method, headers: { "content-type": "application/json", "x-api-key": API_KEY, "x-secret-check": secretHash, "x-timestamp": ts, "x-signature": signature, "x-idempotency-key": crypto.randomUUID(), }, body: bodyStr || undefined, }); return res.json(); } const round = await call("POST", "/api/public/v1/rounds", { bet: 5, currency: "USDC", clientSeed: "player-seed", playerWallet: "0xabc..." });

Idempotency

Include an X-Idempotency-Key (UUID recommended) on every POST. If the same key is retried within 24 hours we return the original response — never a duplicate charge. If you reuse a key with a different body we return 409 idempotency_conflict.

Endpoints

POST /rounds → create round
Request body: { "bet": 5, "currency": "USDC", "clientSeed": "player-seed", "playerWallet": "0xabc..." } Response 201: { "data": { "roundId": "uuid", "serverSeedHash": "hex", "nonce": 1712345678901, "clientSeed": "player-seed", "startedAt": "2026-07-02T12:34:56Z", "serverNow": "2026-07-02T12:34:56Z" } }
POST /rounds/{id}/cashout
Response 200: { "data": { "outcome": "cashed" | "crashed", "crashPoint": 3.42, "cashedAt": 2.10 | null, "payout": 10.50, "serverSeed": "hex-revealed" } }
GET /rounds/{id}
Response 200: { "data": { "status": "diving" | "cashed" | "crashed" | "voided", "startedAt": "...", "serverNow": "...", "crashPoint": 3.42 | null, // null while diving "serverSeed": "hex" | null, // null while diving "cashedAt": 2.10 | null, "payout": 10.50, "bet": 5.00 } }
GET /rounds?wallet=0x..&status=cashed&since=ISO&limit=50
Response 200: { "data": [ ...round records for your operator... ] }
GET /health
{ "ok": true, "service": "dive-and-cash", "version": "1.0" }

Webhooks

We POST JSON to your webhook_url for these events:

  • bet.placed — round started; debit the player.
  • round.settled — round finished (cashed or crashed); credit the payout.
  • webhook.test — manual test from the admin console.

Every delivery includes:

Headers
X-DSDX-Event: round.settled X-DSDX-Timestamp: <unix seconds> X-DSDX-Signature: HMAC_SHA256(webhookSecret, `${ts}.${rawBody}`)
Body
{ "id": "delivery-uuid", "event": "round.settled", "created_at": "2026-07-02T12:34:56Z", "data": { "round_id": "...", "wallet": "...", "payout": 10.5, "currency": "USDC" } }

Verify signature (node):

node
import { createHmac, timingSafeEqual } from "node:crypto"; app.post("/webhooks/dsdx", express.raw({type:"application/json"}), (req, res) => { const ts = req.header("x-dsdx-timestamp"); const sig = req.header("x-dsdx-signature"); const expected = createHmac("sha256", process.env.DSDX_WEBHOOK_SECRET) .update(`${ts}.${req.body.toString()}`).digest("hex"); if (!sig || !timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) { return res.status(401).send("bad signature"); } const event = JSON.parse(req.body.toString()); // ... credit / debit the player res.status(200).send("ok"); });

Non-2xx responses are retried on this schedule: 1s → 5s → 30s → 5m → 30m (up to 6 attempts). After that the delivery is marked dead and surfaced in the admin console for manual retry.

Sandbox — public credentials

Try the API right now, no onboarding required. The public sandbox is rate-limited to 30 req/min and shares an operator across all evaluators — do NOT put real money through it.

env
DIVE_API_KEY=pk_sandbox_demo_key DIVE_API_SECRET=sk_sandbox_demo_secret DIVE_API_BASE=https://dive.example/api/public/v1

Quick smoke test:

curl
curl https://dive.example/api/public/v1/health

Resources:

Embedding the game

Never expose your apiSecret to the browser. From your server, mint a short-lived launch token per player:

node
const { launchUrl } = await call("POST", "/api/public/v1/launch", { playerId: "kyc-verified-user-42", currency: "USDC", ttlSeconds: 900, }); // Then in your lobby page: // <iframe src={launchUrl} allow="autoplay" style="width:100%;height:100vh;border:0" />

The iframe posts window.parent.postMessage({type:"dsdx.embed.ready", session}, "*") after verifying the token, and re-posts on round events so your lobby can update the player's balance in real time.

Errors

All errors return JSON: { "error": { "code": "...", "message": "..." } }

401 missing_auth
Missing X-Api-Key / X-Timestamp / X-Signature.
401 timestamp_skew
Clock drift > 60s.
401 invalid_signature
Signature does not verify.
401 invalid_secret
X-Secret-Check does not match.
403 operator_disabled
Operator is revoked.
409 idempotency_conflict
Idempotency-Key reused with different body.
429 rate_limited
Too many requests. See Retry-After.
400 invalid_body
Zod validation failed.
400 start_failed / cashout_failed
Round engine rejected the request.
404 poll_failed
Round not found or not yours.

Provably Fair

The server generates serverSeed before the round starts and returns only serverSeedHash (SHA-256(serverSeed)). After the round finalizes, the raw serverSeed is revealed and the crash point is deterministic:

formula
crashPoint = crashFromHash(SHA-256(serverSeed + ":" + clientSeed + ":" + nonce))

Anyone can re-derive the outcome at /fairness. See the whitepaper for the full spec.

Limits

  • Bet range: 0.01 – 1000 (per round).
  • Default rate limit: 120 requests / minute / API key. Adjustable per operator.
  • Timestamp skew tolerance: ±60 seconds.
  • Idempotency window: 24 hours.
  • Webhook attempt timeout: 10 seconds.