Integration Guide
Everything you need to integrate Scrollo's video-slot engine into your casino platform. Two API calls to go live.
# Overview
Scrollo exposes a simple REST API with two endpoints your platform calls directly, plus a callback contract where we call you for wallet operations.
# Quick Start
- Obtain API credentials from our team
GET /v1/games— list available game topics for your lobbyPOST /v1/games/init— start a session, receive an iframe URL- Embed the iframe in your frontend
- Handle 4 callbacks on your server:
balance,bet,win,refund - Ship it
# Authentication
All requests to the Scrollo API must include your API key in the request header.
| Credential | Purpose | Provided By |
|---|---|---|
API_KEY | Authenticates your init and game-listing requests | Scrollo (contact us) |
CALLBACK_URL | Your HTTPS endpoint that receives wallet events | You provide during onboarding |
CALLBACK_API_KEY | Included as x-api-key header in our callbacks to you | You provide during onboarding |
# List Games
Retrieve the full catalogue of available game topics. Each game represents a themed video collection (e.g. luxury lifestyle, cars, cats) that players can choose from in your lobby.
curl https://api.scrollo.win/v1/games \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json" {
"games": [
{
"uuid": "luxury",
"name": "💎 Luxury Lifestyle",
"description": null,
"media_tag": "luxury",
"thumbnail_url": "https://scrollo.fra1.digitaloceanspaces.com/thumbnails/games/luxury.jpg",
"video_count": 39
},
{
"uuid": "cars",
"name": "🏎️ Luxury Cars",
"description": null,
"media_tag": "cars",
"thumbnail_url": "https://scrollo.fra1.digitaloceanspaces.com/thumbnails/games/cars.avif",
"video_count": 12
}
]
} Response fields
| Field | Type | Description |
|---|---|---|
uuid | string | Unique game identifier — pass this as game_uuid when initializing a session |
name | string | Display name for the game topic |
description | string | null | Optional description text |
media_tag | string | Internal content tag used to categorize the video pool |
thumbnail_url | string | null | URL to a preview image — use this in your lobby cards |
video_count | number | Number of videos currently in this topic's pool |
Use this endpoint to dynamically populate your games lobby. Each game is a themed content category — display them as selectable cards using the thumbnail_url and name fields. When a player picks a game, pass its uuid to the session init endpoint.
# Initialize Session
Start a new game session when a player selects a game. You receive an iframe URL to embed directly in your frontend.
{
"session_id": "your_unique_session_id",
"player_id": "your_player_id",
"currency": "USD",
"game_uuid": "luxury"
} Request fields
| Field | Type | Required | Description |
|---|---|---|---|
session_id | string | Yes | Your unique session identifier |
player_id | string | Yes | Your internal player identifier |
currency | string | Yes | ISO 4217 currency code (e.g. USD, EUR) |
game_uuid | string | Yes | Game identifier from the /v1/games listing |
{
"iframe_url": "https://api.scrollo.win/embed?token=...",
"token": "eyJhbGciOiJIUzI1NiIs..."
} # Embed the Game
Drop the returned iframe_url into a standard iframe. The game handles its own UI, sound, and fullscreen.
<iframe
src="{iframe_url}"
width="100%"
height="100%"
frameborder="0"
allow="autoplay; fullscreen"
></iframe> # Callback Events
Scrollo sends HTTP POST requests to your CALLBACK_URL for all wallet operations.
Every request includes the x-api-key header set to your CALLBACK_API_KEY.
This is the critical integration point.
CALLBACK Balance Inquiry
Scrollo requests the player's current balance before displaying the game and before each round.
{
"event": "balance",
"session_id": "your_session_id",
"player_id": "your_player_id"
} {
"success": true,
"balance_cents": 50000
} CALLBACK Bet Transaction
Player placed a bet. Debit the amount from their wallet.
{
"event": "bet",
"session_id": "your_session_id",
"player_id": "your_player_id",
"transaction_id": "txn_bet_abc123",
"amount": 1000,
"currency": "USD"
} {
"success": true
} CALLBACK Win Transaction
Player won the round. Credit the winnings to their wallet.
{
"event": "win",
"session_id": "your_session_id",
"player_id": "your_player_id",
"transaction_id": "txn_win_def456",
"amount": 2500,
"currency": "USD"
} {
"success": true
} CALLBACK Refund Transaction
Round was cancelled (player skipped the video or timeout occurred). Credit the original bet amount back.
{
"event": "refund",
"session_id": "your_session_id",
"player_id": "your_player_id",
"transaction_id": "txn_refund_ghi789",
"amount": 1000,
"currency": "USD"
} {
"success": true
} # Idempotency
Every transaction includes a unique transaction_id.
Your callback handler must be idempotent — if you receive a duplicate
transaction_id, return {"success": true} without processing the transaction again.
Network retries will cause duplicate deliveries; your system must handle this gracefully.
# Reference Implementation
A minimal Node.js / Express server implementing the full callback contract:
import express from "express";
const app = express();
const CALLBACK_API_KEY = process.env.SCROLLO_CALLBACK_KEY;
const processedTransactions = new Set();
app.use(express.json());
app.post("/api/scrollo/callback", async (req, res) => {
// 1. Authenticate
if (req.headers["x-api-key"] !== CALLBACK_API_KEY) {
return res.status(401).json({ success: false });
}
const { event, player_id, transaction_id, amount } = req.body;
switch (event) {
case "balance":
const balance = await getPlayerBalance(player_id);
return res.json({ success: true, balance_cents: balance });
case "bet":
if (processedTransactions.has(transaction_id)) {
return res.json({ success: true }); // idempotent
}
await debitPlayer(player_id, amount);
processedTransactions.add(transaction_id);
return res.json({ success: true });
case "win":
case "refund":
if (processedTransactions.has(transaction_id)) {
return res.json({ success: true }); // idempotent
}
await creditPlayer(player_id, amount);
processedTransactions.add(transaction_id);
return res.json({ success: true });
}
});
app.listen(3000);