Telos operator docs

Integrating Telos games: the operator guide

This guide takes an operator from the first invitation to live players. It explains the steps. The contracts say exactly what each call and webhook accepts and returns, and where the two differ, the contract wins:

Contract Version Where
Operator API and the five wallet calls v1.5.0 (tag api-v1.5.0) openapi.yaml

The contract was written when the platform was called Spartacus. Its header names already say Telos (X-Telos-Signature, X-Telos-Version, X-Telos-Request-Id, X-Telos-Attempt). Its servers block holds placeholders (*.spartacus.invalid): Telos gives you the real staging and production host names. v1 is frozen and changes only by adding.

You do the setup yourself, in the Telos back office, on staging first. Staging uses test players and test money; nothing there reaches real players. When staging passes every check, you ask Telos to review you for production.

#1. How an integration works

Telos games run in the player's browser and settle against your wallet in real time. Telos holds no player funds and processes no deposits or withdrawals.

Direction What Required
You call Telos GET /v1/games (catalogue, limits, currencies) yes
POST /v1/sessions (start a game session, get a launch URL) yes
GET /v1/rounds/{round_id} (outcome and fairness data) yes
skins (listSkins, unlockSkin, revokeSkin) and players (getPlayer, listPlayerRounds) no
Telos calls your wallet five wallet calls under one address: player, balance, bet, win, rollback yes, all five
Telos sends you events webhooks such as round.settled, to endpoints you choose at least one endpoint before go-live (§7)

One round of play, from the wallet's point of view:

  1. You create a session and send the player to its launch URL.
  2. Telos calls player to resolve your launch token, then balance.
  3. The player bets: Telos calls bet (debit) once.
  4. The round ends: Telos calls win exactly once for that bet, with amount: "0" for a loss. If the bet produced no outcome, Telos calls rollback instead.

#2. Your account and first sign-in

  1. Telos staff create your organisation in the back office and invite its first people. Each invitee receives an email with a link.
  2. Open the link and sign in with the code Telos emails you.
  3. Set up a second factor, a passkey or an authenticator app ("Choose how to confirm it's you"), then Accept invitation. Every later sign-in asks for the second factor. Creating, rolling or revoking a key, and creating or rotating a signing secret, ask you to confirm it is you again ("Confirm it's you").
  4. Invite your colleagues from Members, and give each the role they need. An API key can only be given permissions the person creating it holds.

Telos sets your games, your currencies (with their decimals), your stake limits and your commission. Ask your account manager to change them. GET /v1/games shows what is enabled for you.

The integration lives under Integration in the menu. It has two environments, Staging and Production (the switch at the top), and these tabs: API keys, Wallet, Webhooks, Status, Test console, Test suite, Logs, Alerts. Only people whose role includes Manage API keys and webhooks see it. Give that to the people who build the integration.

#3. API keys

On Integration → API keys, in Staging, choose Create key:

  • Name what uses it ("Game launch service"), choose its Permissions (only ones you hold) and when it Expires (never, 30 days, 90 days or a year).
  • The secret is shown once ("Copy your secret now"). Store it in your secret manager. If you lose it, roll the key; it cannot be shown again.
  • Staging keys start tk_test_, production keys tk_live_.

Every request to Telos carries a key:

Authorization: Bearer tk_test_…

Rotation without downtime. Open the key and choose Roll key. You get a new secret at once; the old one keeps working for 24 hours, then stops. Revoke key stops a key at once and cannot be undone. Several keys can be active at the same time, so one key per system is the safest pattern.

#4. Calling Telos

Create a session when the player opens a game:

POST /v1/sessions
Content-Type: application/json

{"token": "<your launch token>", "game_code": "crash", "currencies": ["EUR"], "mode": "real",
 "lang": "en", "lobby_url": "https://casino.example/lobby"}
{"session_id": "018f…", "url": "https://<games host>/s/<code>", "expires_at": "2026-10-15T08:01:00.000Z"}
  • token is your launch token for this player. We return it unchanged in the player wallet call and never parse it. Make it unguessable (a cryptographically secure random value), printable ASCII, 8–512 characters, and free of personal data. It is resolved once, within 60 seconds.
  • url is single use and expires 60 seconds after creation. Redirect the player to it straight away; do not store it. (Embedding it in an iframe on your site: ask Telos first. Which origins may frame the game is set by Telos.)
  • mode: "demo" sessions never call your wallet.
  • Several currencies turn on the in-game currency switcher. We never convert between currencies.

GET /v1/games tells you which games, currencies and limits are enabled for you. GET /v1/rounds/{id} returns a round's outcome and the data a player needs to verify it.

#5. Your wallet

On Integration → Wallet, in Staging:

  1. Wallet address. Telos sends the five signed wallet calls here. It must be a public https:// address on port 443 or 8443, with no user name or password, at most 2,000 characters. Save it; the page shows whether the game server has taken it ("In use by the game server").
  2. Signing secret. Choose Create secret and copy it (shown once). Every wallet call carries X-Telos-Signature, an HMAC-SHA256 of the body with this secret (§6).
  3. Telos's addresses. The tab lists the addresses Telos calls from in this environment. Let them through your firewall, then tick Our firewall lets these addresses through and save.

Rotating the signing secret. Choose Rotate secret: you get a new secret now, and Telos keeps signing with every secret still inside its 24-hour grace: two signatures, or three if you rotate twice in 24 hours. After that it signs only with the newest. Accept any matching signature (§6) and you can switch without failing a call. A third rotation inside 24 hours is refused until the oldest secret expires.

#6. Implementing the five wallet calls

All five are POST {your wallet address}/<name> with a JSON body. Answer within 2 seconds.

#Check the signature first

Every wallet call carries:

X-Telos-Signature: t=1789041923,v1=5257a869e7c1f0b3d4a29e8c7b6f5e4d3c2b1a09f8e7d6c5b4a39281706f5e4d
X-Telos-Version: 2026-11-01
  1. Read the raw body bytes before parsing JSON.
  2. Split the header on ,. The first item is t=<unix seconds>; each further item is v1=<64 lowercase hex>. There are at most 3 v1= items, and the header is at most 256 bytes.
  3. Reject if t differs from your clock by more than 300 seconds.
  4. Compute HMAC-SHA256(secret, "{t}.{raw body}") and compare it, in constant time, with each v1= value. Accept if any one matches. During a rotation (§5) there are two, or three.
  5. Wallet calls only: reject if X-Telos-Version is not 2026-11-01, the payload version fixed for you at onboarding. Webhook events have no version header (§7).

A failure in any step is 401 with code unauthorized.

import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(header: string, raw: Buffer, secret: string, nowSec: number): boolean {
  if (Buffer.byteLength(header) > 256) return false;
  const [first, ...rest] = header.split(",");
  const t = /^t=([0-9]{1,18})$/.exec(first ?? "")?.[1];
  if (t === undefined || rest.length === 0 || rest.length > 3) return false;
  if (Math.abs(nowSec - Number(t)) > 300) return false;
  // Every item must be a well-formed v1= entry, or the whole header is malformed.
  const digests = rest.map((item) => /^v1=([0-9a-f]{64})$/.exec(item)?.[1]);
  if (digests.some((hex) => hex === undefined)) return false;
  const want = createHmac("sha256", secret).update(`${t}.`).update(raw).digest();
  return digests.some((hex) => timingSafeEqual(Buffer.from(hex as string, "hex"), want));
}
import hashlib, hmac, re

def verify(header: str, raw: bytes, secret: bytes, now: int) -> bool:
    if len(header.encode()) > 256:
        return False
    first, *rest = header.split(",")
    m = re.fullmatch(r"t=([0-9]{1,18})", first)
    if not m or not 1 <= len(rest) <= 3 or abs(now - int(m.group(1))) > 300:
        return False
    if not all(re.fullmatch(r"v1=[0-9a-f]{64}", i) for i in rest):  # every item well-formed
        return False
    want = hmac.new(secret, m.group(1).encode() + b"." + raw, hashlib.sha256).hexdigest()
    return any(hmac.compare_digest(i[3:], want) for i in rest)

Both match the reference vectors in webhook-signature.v1.json. A retry carries a fresh t and signature over the same body bytes. X-Telos-Request-Id and X-Telos-Attempt are not signed, so a signed request can be replayed within 300 seconds; the defence is idempotency, below.

#Money

  • Amounts are strings of integer minor units; decimals gives the scale ("1000" with decimals: 2 is 10.00). Parse them with an arbitrary-precision integer type. JSON numbers cannot hold values above 2^53 exactly, and crypto amounts with 18 decimals go far past that.
  • If decimals differs from what is configured for the currency, answer 422 currency_mismatch and move nothing.

#Idempotency

Every money call carries a transaction_uuid. Store it with your result and keep it at least 120 days.

You receive You answer
an unknown transaction_uuid process it, store the result
a known one with the identical body the stored result, moving nothing
a known one with a different body 409 idempotency_conflict, moving nothing

#What each call must do

Call Does Notes
player Resolve your launch token to a player Sent once when the player opens the game. 401 token_invalid if unknown or expired; 403 user_blocked for blocked or self-excluded players. Return no balance.
balance Return balances for the requested currencies Read-only; we may retry it.
bet Debit the stake The only place to check funds, limits and player status. Sent once, never retried. If it times out we send a rollback for the same bet. A bet that arrives after a rollback named its transaction_uuid is 409 transaction_rolled_back.
win Credit the payout Exactly once per debited bet; amount: "0" for a loss. Accept it whatever the session or player state. Retried for 24 hours on an unknown outcome. A settlement is never rolled back.
rollback Return the stake of a bet that produced no outcome Unknown target or unknown user: answer 200 with status: "not_found" and record the target, so a late bet is refused. Never record a target you already hold, and answer a rollback whose user does not own the bet 200 not_found with nothing moved. Retried for 24 hours.

#Status codes

  • 4xx is final and moves no money. Use only the codes in the contract's error table, each under its condition (for example 402 insufficient_funds, 403 bet_not_allowed, 404 user_not_found).
  • 5xx means "I don't know the outcome." We retry win and rollback; for bet we roll back. Return 5xx only when you really don't know.
  • A 401 on win or rollback is retried like a 5xx for 24 hours, and alerts us, because it usually means a key or clock problem on one side.
  • 409 transaction_settled on win or rollback means a bug on our side. We alert on it; please tell us too.

#7. Webhooks (events)

Webhooks tell your systems what happened, for example round.settled. They are separate from the wallet calls.

On Integration → Webhooks, choose Add endpoint: an https:// address, and the events it receives (each shows a sample body). Every delivery is signed with the endpoint's own signing secret (shown once), using the same X-Telos-Signature scheme as a wallet call (§6, steps 1–4).

  • Deliveries carry no X-Telos-Version, so skip step 5 for events.

  • Each also carries X-Telos-Webhook-Id, X-Telos-Delivery (unique per delivery: use it to ignore a repeat) and X-Telos-Event (the event type).

  • Rotate signing secret keeps the old secret signing for 24 hours.

  • Send test event delivers a sample at once.

  • The endpoint's Deliveries list each attempt; Resend sends one again.

  • An endpoint that fails for 3 days is turned off. Fix it, then save it to turn it back on.

#8. Test on staging

Everything here runs on staging only; production is never sent test calls. Each test calls your staging wallet exactly as a real round would, signed, and shows what it sent and what came back.

#Test wallet (Wallet tab)

Name your test player (a player on your side kept for testing; Telos remembers it), then send one call at a time:

Call What Telos does
Player sends a launch token you issued and checks your wallet answers with its player
Balance reads the test player's balance
Bet bets the amount, then rolls it back: the balance ends where it was
Win bets the amount, then wins the same amount on it: the balance ends where it was
Rollback rolls back a bet it never sent: your wallet should answer that it does not know it

The amount is in minor units (100 is 1.00) and must be within your stakes; the currency defaults to your first. The result shows each call's request and response (bodies cut at 16 KiB) and a verdict on the balance: Balance back where it was, or Stake may be missing if your wallet refused to give the test stake back (Telos is alerted and puts it right by hand).

#Test console

Not available yet. The Test console and the Test suite are not offered to any operator yet; their tabs say so ("not offered yet"). They are not needed for go-live today: the go-live gate counts only the five staging checks (§10). Until they arrive, prove your wallet with the Test wallet (above) and the conformance kit (§11). This subsection and the next describe what they will do.

The console and the suite run as the test player you named on the Wallet tab: send one Test wallet call first. The console also needs a saved staging wallet and a game assigned to you.

Choose a game, Start test session, then Play a round with a stake and an outcome (win or lose). Each test round is two rounds that cancel out, so your test player needs two stakes and ends where it started. What happened shows each step: debit the stake, credit the win, send round.settled. You can also copy the launch link and open the game yourself.

#Test suite

Run all tests plays 15 scenarios against your staging wallet as your test player: player details, balance, stake, win, full and lost rounds, duplicates (same stake twice, same win twice, same rollback twice), rollbacks (a stake rolled back, a rollback of an unknown stake), and refusals (not enough money, wrong signature, expired token, stake after a timeout). The Player details scenario needs a launch token. Every test needs at least one success before you can request go-live; a scenario that passed once stays passed.

#9. Watch your integration

  • Logs lists the newest 200 calls between you and Telos per environment: wallet calls (kept 90 days) and webhook deliveries (kept 30 days), with their status, time and outcome. Your own calls to Telos are not listed yet. Filter by direction or failures, or search a call, code or round id. Telos keeps no request or response bodies here.
  • Alerts sends you an email or a webhook when something goes wrong in production: wallet success below a percentage, the slowest 5% of wallet calls above a number of milliseconds, or a webhook endpoint failing. Each rule that is on is checked every 5 minutes against the last 15 minutes (at least 10 wallet calls), and a rule that fires stays quiet for an hour. Send test checks the address. Outside email addresses (not members) need your confirmation.

#10. Go live

On Integration → Status, each environment lists its checks. Request go-live (with a note for Telos: what you are launching and when) is accepted only when every staging check is done (and, once the test suite is offered, every one of its 15 tests has passed at least once):

Staging check Done when
Create an API key a staging key is active
Set your wallet address and signing secret both are saved for staging
Let Telos's addresses through your firewall you ticked it on the Wallet tab
Test the wallet Status tab: a staging wallet call succeeded in the last 30 days. The request itself needs your latest Test wallet run to have passed (see below)
Receive a webhook an endpoint that is on has its latest delivery succeeded

Once the suite is offered, the Go-live card also shows Test suite: n of 15 passed, and all 15 must have passed.

If Request go-live is refused although the Status tab shows every check done:

  • The Status tab counts any successful staging wallet call in the last 30 days as "Test the wallet".
  • The request itself needs your latest Test wallet call to have passed, and saving a different wallet address clears that result.
  • Send one Test wallet call that passes (§8), and ask again.

Only one request can wait at a time. If Telos's game server cannot be asked about the test suite, the request is refused as temporarily unavailable: try again later.

Telos staff then review your request as an approval. When it is approved, Telos creates your production operator with the games and currencies of your approved staging setup. Game settings start on Telos's certified defaults for each game and currency; staging's settings are not copied, so ask for any change on production as a settings request. Your games in soft launch move to Live at the same moment (paused games stay paused). The Status tab shows Going live, then Live since ….

  • If production refuses the creation, it shows Not live: "Approved, but production refused it (code)". Telos fixes it with you; ask again.
  • If it is not approved, the Status tab says so. Telos's note on the request is under Approvals in the menu (roles that may request or decide approvals see it). Fix it and ask again.

Then, in Production. Nothing here is possible before approval: until then, production answers "This operator has no game server in this environment yet".

  1. Create production API keys (tk_live_).
  2. Save the production wallet address, create its signing secret, and let the production addresses through your firewall. Until a wallet address is saved, production cannot call your wallet.
  3. Add production webhook endpoints.
  4. Set up Alerts: they watch production.

Production is never sent test calls; your first real players are the test. Watch Logs and your alerts.

Before your first real player, also check what Telos does not: your wallet answers within 2 seconds at your expected peak (event deliveries, §7, get 10 seconds and are retried), idempotency records are kept 120 days, launch tokens are random, single use and free of personal data, you alert on 409 transaction_settled and repeated 401s, and you can rotate a key (§3) and a secret (§5) without downtime.

#11. Before you have an account: the contract as a mock

The operator contract runs as a mock server, so you can build your calls before your account exists:

# in a folder holding the contract file, openapi.yaml (download it from the API reference)
docker run --rm -p 127.0.0.1:4010:4010 -v "$PWD":/spec:ro stoplight/prism:5 \
  mock -h 0.0.0.0 -p 4010 /spec/openapi.yaml
export SANDBOX_KEY=tk_test_example   # a placeholder: the mock is not connected to your account
curl -s -H "Authorization: Bearer $SANDBOX_KEY" http://127.0.0.1:4010/v1/games

The mock answers with the contract's examples; it does not call your wallet.

The conformance kit (conformance: 36 checks of the signature, idempotency, money, rollback and error-code rules, including 18-decimal amounts) is the deeper proof of a wallet. How to run it, and every check, is in the conformance kit (the conformance kit).

  • It moves real balances: point it only at your staging wallet and test players.
  • Every MUST check must pass; a skipped check is not a pass. Include an 18-decimal currency if you offer one.

#12. Where to look

Need Where
Every field, status and example openapi.yaml
How the contract may change additive only; a breaking change needs /v2/
The reference wallet we test against wallet-sim
The full wallet check conformance, the conformance kit