Skip to main content

Set up a game and give players access

Bringing a game online involves two roles:

  • The admin (an org owner / studio) sets the game up: an organization, an app, and one or more access tiers.
  • Players then get access to that app — either automatically (open‑by‑default) or by an explicit grant.

Entitlements — who may play, at which tier, and which runtime permission keys that tier carries — are defined here on the Management API. The Game API enforces them at runtime: on a player's first connect it mirrors the entitlement and provisions the matching world‑grid permissions, and Buddy (the UDP server) checks both app‑ and grid‑level permissions on every spatial message.

All operations below are available in the Management UI and via GraphQL with a user session token or an org API token. Exact field names per version are in the schema reference.


1. Admin: set up your game

a. Create an organization

You become the org owner (full manage_apps / manage_environments permissions).

mutation { createOrganization(input: { name: "Acme Games", slug: "acme" }) { orgId } }

b. Create an app

Creating an app automatically provisions a free, default access tier holding the standard runtime permission keys (access, teleport, update_voxel_data, use_voice_chat). That single step makes the app immediately playable and is what enables open‑by‑default access (next section).

mutation {
createApp(input: { orgId: "ACME_ORG_ID", name: "My World", slug: "my-world", status: LIVE, visibility: PUBLIC }) {
appId
}
}

createApp requires the manage_apps permission on the org (org owners have it by default). status is one of DRAFT, LIVE, ARCHIVED; visibility is PUBLIC, UNLISTED, or PRIVATE (only PUBLIC + LIVE apps surface in the marketplace).

Manage the app over its lifecycle (also manage_apps):

# Update metadata: name, description, visibility, status, metadata.
mutation { updateApp(appId: "APP_ID", input: { description: "Now with voxels", visibility: UNLISTED }) { appId } }

# Take an app offline without deleting it (sets status to ARCHIVED).
mutation { archiveApp(appId: "APP_ID") { appId status } }

Read an app's current state any time with the app(appId) query — including the routing fields (gameApiUrl, splitMode, deploymentTarget) covered in step d.

c. (Optional) Define additional access tiers

Add tiers for premium/paid access, or to grant a narrower/broader set of permissions. List assignable keys with the runtimePermissions query.

mutation {
createAccessTier(input: {
appId: "APP_ID",
name: "Premium",
isFree: false,
isDefault: false,
permissionKeys: ["access", "teleport", "update_voxel_data", "use_voice_chat"]
}) { tierId }
}

Tier operations require the manage_access_tiers permission. Edit or retire tiers with updateAccessTier(tierId, input) and archiveAccessTier(tierId), and list an app's tiers (no auth) with appAccessTiers(appId).

Opting out of open‑by‑default: if you do not want anonymous/auto access, remove (or never create) a tier that is both isFree and isDefault. Players then only get access through an explicit grant (below).

d. Choose where the app runs

Your app needs a Game API to serve runtime traffic. Today you provision a developer sandbox and link your app to it; that gives the app a gameApiUrl and turns on split‑mode routing. See Dedicated environments for the step‑by‑step flow.

Availability: the developer sandbox (environmentClass: "dev_single") is available now. Shared platform hosting (publishAppToShared) and multi‑VM dedicated environments are coming soon — the API rejects them for non‑preview accounts today.

Whichever hosting you use, query the app's routing fields (gameApiUrl, splitMode, deploymentTarget) before a player joins so the client connects to the right Game API. See Shared environment & billing for the shared model and routing fields, and Loading an app's Game API for the client walkthrough.


2. How players get access

There are two paths — most public games use the first.

Open‑by‑default (zero‑friction)

If the app has a free + default tier (the one createApp made for you), then any player is automatically granted that tier the first time they connect — no per‑player admin action. The Game API resolves the entitlement on connect, mirrors it into the per‑tenant game database, and grants the player the app's default world‑grid permissions. From that point Buddy authorizes their spatial traffic.

Explicit grant (admin‑controlled)

For premium tiers, or for apps that opted out of open‑by‑default, the admin grants a specific player a specific tier:

mutation { grantAppAccess(input: { appId: "APP_ID", userId: "PLAYER_USER_ID", tierId: "TIER_ID" }) { appUserAccessId status } }

Revoke a player's access with revokeAppAccess(appId, userId). Granting and revoking both require manage_access_tiers.

The grant propagates to the Game API automatically (the matching grid permissions are provisioned server‑side via replica‑sync), so the player is authorized end to end — again with no direct database access.


3. The player's flow

  1. Register or log in through the Management API (register / login) — returns the identity session token. (Browser games can use the SDK's guest/anonymous auth; see CrowdyJS and the Build a game tutorial.)
  2. Mint an app token for this app — mintAppToken (native/same-origin) or the browser portal flow (client.portal). This is the app-scoped token used for gameplay; the session token is rejected by the Game API. The first mint auto-grants access on free tiers. See Portals & app-scoped tokens.
  3. Connect with CrowdyJS, the Unreal SDK, or a raw UDP client, pointed at the app's gameApiUrl (returned by the mint), sending the app-scoped token as the Bearer.
  4. Play — send actor, voxel, text, audio, and client‑event updates. The Game API and Buddy authorize each message against the player's tier and grid permissions. Refresh the app token (refreshAppToken) before it expires.

What happens under the hood

Management API Game API (per app `gameApiUrl`) Buddy (UDP)
────────────── ────────────────────────────── ───────────
app + access tier (free, default)
│ open-by-default on 1st connect (or explicit grantAppAccess)
└────────────────────────────► mirror app_user_access
+ grant default world-grid permissions ──► enforces app + grid
permissions per message

A player with an active tier on an app is authorized everywhere that matters — no manual grid setup, no database writes by the integrator.


Quick reference

GoalAdmin doesPlayers get access via
Open/public gamecreateApp (keeps the free default tier)Open‑by‑default on first connect
Invite‑only / paidcreateApp, then remove the free default tier and define paid tiersExplicit grantAppAccess per player (or after purchase)
Premium tier on an open gamecreateApp + createAccessTier (premium)Free tier auto; premium via grantAppAccess

See the GraphQL schema reference for exact inputs, and the Dev tier page for integration‑testing endpoints.