LobbyLab is a hosted multiplayer backend. Your game keeps its own UI, engine and domain; players join LobbyLab rooms over WebSockets through @lobbylab/client, and LobbyLab keeps the rooms, invite codes, matchmaking and shared game state. Game rules that run on LobbyLab's servers come later. Request an alpha key at https://lobbylab.gg/developers.
Status: milestone 1, implemented behind LOBBYLAB_DEVAPI_ENABLED=1 (default off). Base URL after deployment: https://lobbylab.gg. These routes are the implemented subset; /docs/api remains the broader design preview. No live deployment is implied.
Account dashboard routes
Use the existing LobbyLab account access token in Authorization: Bearer. The browser dashboard obtains and refreshes it through the existing sign-in flow. API keys cannot manage accounts, projects or keys. Only an active project owner's account may manage its projects.
| Method | Path | Body or result |
|---|---|---|
| GET | /api/v1/dev/config | {enabled}; public flag discovery |
| GET | /api/v1/dev/projects | {projects, limits} |
| POST | /api/v1/dev/projects | {name, origins}; returns {project} |
| GET | /api/v1/dev/projects/:projectId/keys | {keys}; metadata only |
| POST | /api/v1/dev/projects/:projectId/keys | {kind: publishable or secret, label}; returns {key, value} once |
| DELETE | /api/v1/dev/projects/:projectId/keys/:keyId | 204, idempotent revoke |
| GET | /api/v1/dev/projects/:projectId/usage | {usage, limits} |
Project origins are exact HTTPS origins or HTTP localhost/127.0.0.1, without paths, wildcards or trailing slashes. Origins are fixed in milestone 1; create a new project to use another list. Test keys contain 32 random bytes and only their SHA-256 digest is stored. Secret keys are backend-only; publishable keys are browser-safe and may be copied into your game. Native requests with no Origin are allowed; an Origin allowlist is a browser control, not a proof of a player's identity.
Game routes
Use a project key in Authorization: Bearer.
| Method | Path | Body or result |
|---|---|---|
| GET | /v1/client.js | ES module; flag-gated, no key needed |
| POST | /v1/rooms | {type?: default, maxPlayers?: 2..16, meta?: object, name?: string}; returns {room, seat} |
| POST | /v1/rooms/join | {code, name?: string}; returns {room, seat} |
| GET | /v1/usage | {usage, limits}; secret key only |
room: {id, code, kind: turns, maxPlayers, expiresAt}. seat: {roomId, memberId, credential, credentialExpiresAt, wsUrl}. The server chooses room kind, limits and write rules. Each call makes a new anonymous seat; no client-selected member id is accepted. There are no player tokens or authenticated developer user ids yet, and room create/join has no retry/idempotency contract in this subset. Do not automatically retry an ambiguous create/join.
Room codes are scoped to a project. Seats are opaque random mp1. credentials, hashed at rest, scoped to the room, member and issuing key, valid until room expiry (at most 24 hours). Revoking a key revokes its seats too.
Socket
Connect to the response's wsUrl (/v1/ws), then send the existing multiplayer-wire/v0 hello with sessionId = room.id, credential = seat.credential and a per-connection logicalStreamId. The socket checks the project's origin before sending ready or snapshots. /ws remains the consumer socket and keeps its existing auth.
Send intent envelopes with intentId and clientSequence. Supported names are lobbylab.state (ops, guards, ifVersion), lobbylab.send (topic, data, to, echo), and lobbylab.host (lock, meta, close only). Read the SDK doc. Server replies ready, snapshot, ack, rejection and pong. input/live messages are unsupported.
Receipt ids are namespaced by member and deduplicated by the authority runtime's rolling 512-receipt window. The SDK does not automatically replay in-flight intents. Message bodies are transient and not persisted; shared/private state is stored until room data retention expires.
Defaults and configuration
All numeric overrides are positive integers with prefix LOBBYLAB_DEVAPI_.
| Suffix | Default | Scope |
|---|---|---|
| PROJECTS | 2 | account |
| KEYS | 8 | active keys/project |
| ROOMS_PER_MONTH | 1000 | project, UTC calendar month |
| MESSAGES_PER_MONTH | 100000 | inbound state/message/host intents/project, including refused intents; deduplicated replays do not add usage |
| CONNECTED_MINUTES_PER_MONTH | 10000 | project; connected sockets, stored in ms |
| CONCURRENT_CONNECTIONS | 20 | account across projects/keys |
| REQUESTS_PER_MINUTE | 120 | key REST requests; separately key hellos/resyncs and account dashboard |
| INTENTS_PER_SECOND | 5 | key and room |
| ROOM_LIFETIME_MINUTES | 120 | room |
LOBBYLAB_DEVAPI_ORIGIN defaults to https://lobbylab.gg; for local tests set it to the actual http://127.0.0.1:port. The server returns the corresponding WSS/WS URL. Configuring another origin does not provision DNS or a proxy. Rate limits are persisted fixed windows; responses give Retry-After. Quotas are free-alpha operational limits, not the business plan's eventual peak-CCU paid tiers.
Usage fields: month, rooms, messages, connected_ms, connectedMinutes. Aggregate usage is kept for later billing; no charges, invoices, overages or Stripe calls exist. See rooms for meter checkpoint accuracy.
The single alpha process also bounds all open API sockets, including unfinished hellos, at CONCURRENT_CONNECTIONS * PROJECTS + 20 (60 with defaults). This is a process safety bound, not an account entitlement. Room/state/message sizes are fixed; the table lists configurable quotas and rates.
Errors
Non-2xx responses contain {error, next, retryAfterSec?}. Common codes: devapi_disabled/devapi_unavailable (503), auth_required/invalid_access_token/invalid_api_key/api_key_revoked/invalid_seat (401), account_unavailable/origin_not_allowed/secret_key_in_browser/secret_key_required/quota_exceeded/project_limit_reached/key_limit_reached/ccu_limit_reached (403), project_not_found/key_not_found/room_not_found (404), room_closed/room_full/room_locked/seat_already_connected (409), rate_limited (429), invalid_request (400).
Socket rejections include invalid_request, forbidden_path, invalid_path, invalid_operation, version_conflict, guard_failed, host_required, too_large, unsupported_message and not_implemented. Fix permissions or state, respect retryAfterSec for rate limits, and create a new room for an ended/full room.
Enablement for the reviewer
Run the platform migration command in the deployment runbook before starting new code (devapi migration 0001). A pending migration otherwise leaves the shared database unavailable, including identity. The flag may stay off while the migration and code ship. Enable only in the intended server environment. Then sign in, create a project and run the two-tab quickstart. The share proxy already forwards /v1 HTTP and WebSocket paths; check its tests before deployment.
Turn the flag off and restart to close API sockets and hide the dashboard. Existing projects, hashed keys and usage remain. npm publishing, deployment and enabling production are separate operator actions.