# Quickstart Six steps from an API key to an embedded game, a seventh to a played match — the same hosts, paths and payloads production uses. ## The hosts | What | Where | Called by | | --- | --- | --- | | API | `https://api.peritusgames.com` | your backend | | Game frame | `https://games.peritusgames.com` | your page (iframe) | | Partner portal | `https://peritusgames.com/portal` | your team (browser) | One API host serves both environments; which one a call lands in follows from the key, never the URL — see [Keys and environments](/docs/authentication.md). ## Before you start - Your account's **partner id** — 3–32 characters of lowercase letters, digits and hyphens (`acme` below). - A **test API key**, minted by your first admin in the portal. - The **origin** that will embed the frame, registered in the portal — until then the frame refuses to render. See [Embedding a game](/docs/embedding.md). ## 1. Mint a session, from your server ``` curl -X POST https://api.peritusgames.com/partner/sessions \ -H "Authorization: Bearer pgs_test_" \ -H "Content-Type: application/json" \ -d '{ "playerId": "u_8813", "gameId": "streakpeaks", "displayName": "Ada Lovelace" }' ``` ## 2. Read the response ``` 200 { "uid": "ptr:test:acme:u_8813", // a TEST key — a live one gives ptr:acme:u_8813 "token": "eyJhbGciOiJIUzI1NiJ9…", "expiresAt": 1754211600, "displayName": "Ada Lovelace" } ``` Valid one hour, scoped to that player and game; mint per session, do not store it. The `uid` is derived from `playerId` — store it. Full rules in [Identifying your player](/docs/players.md). ## 3. Embed the frame ``` ``` The path segment is your partner id, not your key — no credentials here. Live drops `/test`. ## 4. Answer the handshake ``` const GAME_ORIGIN = "https://games.peritusgames.com"; window.addEventListener("message", async (e) => { if (e.origin !== GAME_ORIGIN) return; if (e.data?.type === "ready") { frame.contentWindow.postMessage({ type: "init", token: session.token, uid: session.uid, dealerUrl: "https://api.peritusgames.com", gameId: "streakpeaks", }, GAME_ORIGIN); } if (e.data?.type === "needToken") { const fresh = await mintSession(); // same player, same game frame.contentWindow.postMessage( { type: "token", token: fresh.token }, GAME_ORIGIN, ); } }); ``` Every message your page sends and trusts is listed in [Embedding a game](/docs/embedding.md). ## 5. Read the result from your server ``` curl "https://api.peritusgames.com/partner/results?limit=50" \ -H "Authorization: Bearer pgs_test_" ``` The authoritative outcome — the frame never reports a score to your page. See [Match results](/docs/results.md). ## 6. Go live Move your backend to a live key and your page to the live embed path; the base URL stays the same. Live access is per game, requested from the portal. ## 7. Play for a prize This step seats a match. Live play needs a live key plus two more calls — `GET /partner/tiers` for the ladder, `POST /partner/matches` to seat a player — free of charge with a test key. The handshake's `init` message must also carry `seating: "host"`, which puts the tier cards in the frame — omit it and your player has no way in. To seat from your own page instead, send `route`. [Paid matches](/docs/paid-play.md) has the full treatment. > **A paid match seats two players of the same operator** — never paired with ours or another platform's. Plan your liquidity on that. ## The games, and their ids The `gameId` in every call is one of these, licensed per environment: test instant and self-service, live requested the same way. | Game | `gameId` | | --- | --- | | Streak Peaks | `streakpeaks` | | Poker Squares | `pokersquares` | | Spades | `spades` | | Streets | `streets` | | Dice 13 | `dice13` | | Press | `press` | | Squeeze | `squeeze` | | 21 | `twentyone` | > Each game runs, playable and without an account, from [the games page](/games).