Integration Docs
REST API for operators. Server-to-server only. All requests require HMAC-signed authentication.
https://dive.example/api/public/v1Quickstart
- Ask us to onboard your casino — we issue three credentials once:
apiKey,apiSecret, andwebhookSecret. - Whitelist each player wallet you want to accept (or run the game unrestricted per operator).
- Point
webhook_urlat your server. We POSTbet.placedandround.settledsigned with yourwebhookSecret. - On each player round:
POST /rounds→ play →POST /rounds/{id}/cashout. - Verify every settled round on /fairness.
Authentication
Every request MUST include four headers:
HeadersX-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:
nodeimport { 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 roundRequest 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}/cashoutResponse 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=50Response 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:
HeadersX-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):
nodeimport { 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.
envDIVE_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:
curlcurl https://dive.example/api/public/v1/health
Resources:
- OpenAPI 3.1 spec — https://dive.example/api/public/v1/openapi.json
- Integration kit (JSON manifest with copy-paste snippets) — https://dive.example/api/public/v1/integration-kit
- Embed launch endpoint —
POST /launchreturns a signedlaunchUrlpointing at/embed?t=…which you iframe into your lobby.
Embedding the game
Never expose your apiSecret to the browser. From your server, mint a short-lived launch token per player:
nodeconst { 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": "..." } }
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:
formulacrashPoint = 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.