Quickstart
In this guide you start from zero and do the following: create a game and a server key in the panel, make your first server call with curl and TypeScript, sign a player in as a guest from your browser game, and do the same from Unity. It takes about half an hour. Before you begin, skim the Concepts page.
Prerequisites
- A running Open Play server and an admin account that can sign in to the panel (super admin). For installation, see the production deployment guide (
docs/production-and-adapters.tr.md). - The server’s public HTTPS address. This page calls it
OPEN_PLAY_URL(for examplehttps://game.example.com/open-play). - Node.js 22 or later and
curl;jqandopensslfor the raw REST examples.
1. Create a game and an environment
If the setup wizard has already created the first studio and game, skip this step.
- In the panel, open the Studios and games screen and create a studio with the New studio button.
- On the studio card, choose New game; enter the game’s name (and a short name if you like). The
devandprodenvironments are created automatically with the game. - In the Active game and environment selector in the top bar, choose the
devenvironment of the game you created. The rest of the panel shows the data of the selected environment.
Details: Studios and games.
2. Check the modules
On the Game settings › Modules tab, confirm that the characters module is On for the call in this guide. A disabled module returns 404 and module_disabled. Details: Modules.
3. Create a server key
- On the Game settings › API keys tab, choose New API key.
- Give it a name (for example
Game server), choose the type Server key, set the validity period, and write a short note for the audit log (8–500 characters). - Press Create. Copy the secret value on the card that opens right away and save it in your server’s secret settings. You cannot see this value again.
The key has this format: crx_live_<prefix>_<secret>. On this page we will use it as environment variables:
export OPEN_PLAY_URL="https://game.example.com/open-play"
export OPEN_PLAY_SERVER_KEY="crx_live_<prefix>_<secret>"
Warning
Do not write a real key in documents, repositories, chats, or logs. If you suspect it has leaked, rotate or delete the key in the panel.
4. Your first server call
With the server key, request the first page of characters in the environment. Because the environment comes from the key, no extra header is needed.
curl -sS "$OPEN_PLAY_URL/v1/characters?limit=1" \
-H "Authorization: Bearer $OPEN_PLAY_SERVER_KEY"
If there are no characters yet, the expected response (200) is:
{"items":[],"next_cursor":null}
The same call with the TypeScript SDK (for installation, see TypeScript SDK):
import { CrxClient, CrxError } from "@ferdiunal/open-play-ts";
/**
* Sunucu anahtarıyla ilk çağrı: ortamdaki karakterlerin ilk sayfasını okur.
* Bu kod yalnızca güvenilen arka uçta çalışır; sunucu anahtarını tarayıcıya göndermeyin.
*/
export async function listFirstCharacters(baseUrl: string, serverKey: string): Promise<number> {
const client = new CrxClient({ baseUrl, apiKey: serverKey });
try {
const page = await client.listCharacters({ query: { limit: 1 } });
return page.items.length;
} catch (error) {
if (error instanceof CrxError) {
// kind, status ve attempts güvenlidir; anahtar ve istek gövdesi hata nesnesine girmez.
console.error("Open Play çağrısı başarısız:", error.kind, error.status);
}
throw error;
}
}
// Kullanım (Node 22, ESM):
// const count = await listFirstCharacters(process.env.OPEN_PLAY_URL!, process.env.OPEN_PLAY_SERVER_KEY!);
Possible problems:
| Symptom | Cause | Fix |
|---|---|---|
401 | The key is wrong, deleted, expired, or the Bearer prefix is missing | Check the key and the header; create a new key if needed |
404, body {"code":"module_disabled",…} | The characters module is disabled in this environment | Step 2 |
403 | The key type is insufficient for this operation (a client key) | Use a server key |
| Connection/TLS error | The address is wrong or is not HTTPS | Use the public HTTPS address; http://localhost is for local development only |
5. Bind your game’s domain to the environment
The browser adds an Origin header to state-changing requests; the server checks that this origin belongs to that environment. If your game page’s domain is not defined, player sign-in is rejected with 403.
- On the Game settings › Environments and domains tab, in the Domains field of the relevant environment, enter your game page’s domain (one domain per line; for example
game.example.com). Wildcards, IPs, and ports are not accepted. - Write a reason and press Save domains.
If you do not enter a domain, the server’s global allowed list applies; that list is an operator setting. Details: Architecture and hosting.
6. Your first player sign-in from the browser (guest)
Your game page calls the API either from the same origin (reverse proxy) or, if its domain is bound to an environment, directly at the Open Play address (CORS) (see Architecture and hosting). The code below restores a stored session; if there is none, it opens a guest session. environmentId is the environment’s UUID.
import { createPlayerClient, type PlayerClient } from "@ferdiunal/open-play-ts";
/**
* Tarayıcıdaki oyun sayfasından oyuncu oturumu açar: kayıtlı oturum varsa onu geri yükler,
* yoksa misafir oturumu açar. `baseUrl` oyun sayfasıyla aynı origin'dir (aynı köken kuralı).
*/
export async function signInAsGuest(environmentId: string): Promise<PlayerClient> {
const client = createPlayerClient({ baseUrl: window.location.origin, environmentId });
// restore() yalnızca bakar; oturum yoksa misafir yaratmaz.
const restored = await client.session.restore();
const session = restored.status === "authenticated" ? restored : await client.session.signInGuest();
console.log("oyuncu:", session.playerId, "misafir mi:", session.isGuest, "ad:", session.displayName);
return client;
}
The SDK generates and stores the guest secret for you, gets the CSRF token, and manages the cookies. When you refresh the page in the same browser, restore() brings back the same player.
The same with raw REST
The script below shows what the SDK does behind the scenes (HTTPS is required; the server writes a Secure cookie):
export OPEN_PLAY_ENV="<environment-id-uuid>"
JAR="$(mktemp)"
# 1) Get a CSRF token (it is also written to the cookie jar)
TOKEN="$(curl -sS -c "$JAR" "$OPEN_PLAY_URL/v1/auth/csrf" | jq -r .token)"
# 2) A 32-byte random guest secret: 64 lowercase hexadecimal characters. As long as you keep it, it returns to the same player.
BOOTSTRAP="$(openssl rand -hex 32)"
# 3) Guest sign-in
curl -sS -b "$JAR" -c "$JAR" -X POST "$OPEN_PLAY_URL/v1/auth/sign-in/guest" \
-H "content-type: application/json" \
-H "x-crx-env: $OPEN_PLAY_ENV" \
-H "x-csrf-token: $TOKEN" \
-d "{\"bootstrap_token\":\"$BOOTSTRAP\"}"
Response (200; is_new_player is true the first time):
{
"player": { "id": "0192…", "display_name": "Guest", "email": null, "email_verified": false, "is_guest": true, "image": null, "created_at": "2026-11-03T10:00:00+00:00" },
"tokens": { "session_token": "<64 hex>", "refresh_token": "<64 hex>", "access_token": "<jwt>", "expires_in_secs": 900 },
"is_new_player": true
}
The cookie jar now holds __Secure-crx.session and __Secure-crx.refresh. Verify the session:
curl -sS -b "$JAR" "$OPEN_PLAY_URL/v1/auth/session" -H "x-crx-env: $OPEN_PLAY_ENV"
On every state-changing request the CSRF token is single-use: the response’s x-csrf-token header carries the next one. Details: Sessions and CSRF, Guest sign-in.
7. Your first player sign-in from Unity
After installing the Unity package (Unity SDK), add the component below to a GameObject. Fill in the baseUrl and environmentId fields. In a WebGL build, baseUrl must be the same origin as the page.
using System;
using OpenPlay;
using OpenPlay.Session;
using OpenPlay.Unity;
using UnityEngine;
/// <summary>
/// Misafir girişi: kayıtlı oturum varsa geri yükler, yoksa misafir oturumu açar.
/// Bir GameObject'e ekleyin; baseUrl ve environmentId alanlarını doldurun.
/// </summary>
public sealed class QuickstartGuest : MonoBehaviour
{
[SerializeField] private string baseUrl = "https://oyun.example.com"; // WebGL'de sayfayla aynı origin olmalı
[SerializeField] private string environmentId = ""; // ortam kimliği (UUID)
[SerializeField] private bool allowInsecureTransport = false; // yalnız http://localhost için
private OpenPlayClient _client;
private async void Start()
{
try
{
var options = OpenPlayUnity.Options(new Uri(baseUrl), Guid.Parse(environmentId));
options.AllowInsecureTransport = allowInsecureTransport;
_client = OpenPlayClient.Create(options);
var state = await _client.Session.RestoreAsync(); // oturum yoksa misafir YARATMAZ
if (state.Status != SessionStatus.Authenticated) state = await _client.Session.SignInGuestAsync();
if (state.Status == SessionStatus.Banned)
{
Debug.Log("Hesap askıya alındı."); // terminal durum; yalnız LogoutAsync ile çıkılır
return;
}
Debug.Log("hazır: oyuncu=" + state.PlayerId + " misafir=" + state.IsGuest);
}
catch (OpenPlayException ex)
{
Debug.LogError("Open Play hatası: " + ex.Kind + " (" + ex.Code + ")");
}
}
private async void OnDestroy()
{
if (_client != null) await _client.DisposeAsync();
}
}
Note
The Unity SDK is verified only with Unity 6000.6.7f1. For details, see Unity SDK.
What is next?
- Authentication: email, Google, account linking, session management.
- Characters, Economy, Store and IAP: connect your first feature.
- Webhooks: receive events on your server, signed.
- Production checklist: before you go live.