The HTTP control plane: developer accounts, projects, player storage, health, and diagnostics. Realtime gameplay happens over the WebSocket protocol; everything else is here.

Base URL: https://spawnweaver.dev.

Authentication methods

Method Used by How
Session cookie Dashboard/account/project endpoints Set by POST /api/auth/signup or /signin (HttpOnly cookie); send it with subsequent requests (curl -b jar.txt)
Project secret key Storage (full access) Authorization: Bearer sk_… — server-side callers only
Player token Storage (own data only) Authorization: Bearer <playerToken from the realtime welcome>
Admin API key /api/admin/* Authorization: Bearer <Admin__ApiKey>required; without a configured key the admin API is disabled (except local Development)
Public project key /connect, /connect/stats ?projectKey=pk_… query parameter

Error shape (RFC 7807)

Failures return application/problem+json:

{
  "title": "Unauthorized",
  "status": 401,
  "detail": "Sign in to create a project."
}

Validation failures add an errors map of field → messages:

{
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": { "name": ["Name is required."] }
}

Every response carries an X-Correlation-Id header — include it in bug reports.


Health

GET /health

No auth. Liveness/readiness probe for monitors, Docker, and tunnels.

curl https://spawnweaver.dev/health
# 200 -> { "status": "ok", "service": "Platform.Api", "version": "1.0.0.0" }

Auth & accounts

Developer accounts back the dashboard. Sign-up/sign-in are rate-limited per IP (HTTP 429 when exceeded). With a real email provider configured, sign-up requires email verification before sign-in; local/dev instances auto-verify.

POST /api/auth/signup

curl -c jar.txt -X POST https://spawnweaver.dev/api/auth/signup \
  -H "Content-Type: application/json" \
  -d '{"email":"you@studio.com","displayName":"You","password":"supersecret123"}'
# 200 -> { "userId": "user_…", "email": "you@studio.com", "displayName": "You",
#          "redirect": "/dashboard/onboarding" }   (cookie set; dev instances)
# 200 with "redirect": "/dashboard/verify-pending"  (hosted: check your inbox first)

Errors: 409 email already in use · 400 weak password (minimum length in the message) or invalid email · 429 rate-limited.

POST /api/auth/signin

curl -c jar.txt -X POST https://spawnweaver.dev/api/auth/signin \
  -H "Content-Type: application/json" \
  -d '{"email":"you@studio.com","password":"supersecret123"}'
# 200 -> { "userId": "…", "email": "…", "displayName": "…", "redirect": "/dashboard" }

Errors: 401 incorrect email or password · 403 with code: "email_not_verified" when verification is pending · 429 rate-limited.

GET /api/auth/verify?token=…

Confirms an emailed verification link. Redirects to /dashboard on success (signed in), or to /dashboard/verify-pending?expired=1 for a stale token.

POST /api/auth/verify/resend

Body {"email": "…"}. Always returns 200 (no account enumeration); a new link is sent only when the account exists and still needs verification.

POST /api/auth/signout

Ends the session. 200 -> { "redirect": "/dashboard/signin" }.

POST /api/auth/magic/request

Passwordless sign-in link by email. Body {"email": "…"}. 200 -> { "sent": true, "message": "Check your email…", "devLink": null }devLink carries the clickable link on non-production instances without a real email provider. 429 when requesting links too quickly.

GET /api/auth/magic?token=…

Consumes a magic link; redirects to /dashboard (signed in) or /dashboard/signin?error=link.

Method & path Body Response
GET /api/account 200 profile: id, email, displayName, createdAtUtc, lastLoginAtUtc, organizationId, organizationName
PUT /api/account {"displayName": "…"} 200, or 400 when empty
POST /api/account/password {"currentPassword": "…", "newPassword": "…"} 200, or 400 on wrong password / weak new one
GET /api/account/sessions 200 { "sessions": [ { "id", "createdAtUtc", "lastSeenAtUtc", "expiresAtUtc", "current" } ] }
POST /api/account/sessions/revoke-all 200 — revokes every session, then re-issues one for this browser

Projects

Project management requires a signed-in developer (session cookie); projects belong to your workspace.

POST /api/projects

Creates a project and returns both keys. The secret key is returned exactly once — it is stored only as a hash and can never be retrieved again.

curl -b jar.txt -X POST https://spawnweaver.dev/api/projects \
  -H "Content-Type: application/json" \
  -d '{"name":"Duel Arena","gameType":"Arena1v1","multiplayerMode":"MatchmakingAndRooms","persistenceFeatures":["PlayerProfile"]}'
{
  "id": "proj_…",
  "name": "Duel Arena",
  "publicKey": "pk_…",
  "secretKey": "sk_…",
  "createdAtUtc": "2026-07-29T10:00:00Z",
  "slug": "duel-arena",
  "organizationId": "org_…",
  "gameType": "Arena1v1",
  "multiplayerMode": "MatchmakingAndRooms",
  "persistenceFeatures": ["PlayerProfile"],
  "environment": "Development",
  "recommendedSetup": { "exampleProject": "1v1 Matchmaking Arena", "steps": ["…"] }
}

201 Created. Only name is required; the optional onboarding fields (gameType, multiplayerMode, persistenceFeatures, targetPlatform, environment) tailor the recommendedSetup plan. Valid choices: GET /api/onboarding/options (no auth). Errors: 401 not signed in · 400 missing/too-long name.

`GET /api/projects/

Project details for the owning workspace — never includes the secret key.

curl -b jar.txt https://spawnweaver.dev/api/projects/proj_xxx
# 200 -> { "id", "name", "publicKey", "isActive", "createdAtUtc", "slug",
#          "organizationId", "gameType", "multiplayerMode", "persistenceFeatures", "environment" }

Errors: 401 not signed in · 404 unknown or not yours (existence is not probeable).

`POST /api/projects/

Regenerates the secret key; returns the new plaintext once: 200 -> { "secretKey": "sk_…" }. The old key stops working immediately. Errors: 401 / 404.

`POST /api/projects/

Rotates the public key: 200 -> { "publicKey": "pk_…" }. Disruptive — shipped clients using the old key stop connecting. Errors: 401 / 404.


Player storage

/api/storage/{projectId}/players/{playerId}/keys[…] — persistent per-player key-value data, the same data the SDK reads via storage_get(). Two accepted bearer credentials:

  • Secret key sk_… — any player in the project (server-side tooling).
  • Player token (from the realtime welcome) — only when {playerId} matches the token's own player; anything else is 401.

`PUT /api/storage/

The request body is the raw JSON value to store.

curl -X PUT https://spawnweaver.dev/api/storage/proj_xxx/players/player_1/keys/score \
  -H "Authorization: Bearer sk_xxx" -H "Content-Type: application/json" -d '42'
# 200 -> { "key": "score", "updatedAtUtc": "2026-07-29T10:00:00Z" }

Errors: 400 invalid/too-long key · 413 value over the size limit · 409 at the stored-key quota · 401 bad credential.

`GET /api/storage/

curl https://spawnweaver.dev/api/storage/proj_xxx/players/player_1/keys/score \
  -H "Authorization: Bearer sk_xxx"
# 200 -> { "key": "score", "value": "42", "updatedAtUtc": "…" }
# 404 when the key does not exist

value is the stored raw JSON as a string — parse it client-side.

`DELETE /api/storage/

204 No Content on success, 404 when the key didn't exist.

`GET /api/storage/

200 -> { "keys": ["score", "save"] } — the player's stored key names.

Quotas (defaults): 64 KB per value, 100 keys per player, 128-char keys. See Limits.


Realtime handshake & stats

GET /connect (WebSocket upgrade)

The realtime gateway. Query parameters: projectKey (required, pk_…), playerToken, sdkVersion, engine (optional). On success the server completes the WebSocket upgrade and sends session.welcome — full details in the protocol reference.

Rejections: 400 not a WebSocket upgrade request · 401 missing/unknown/inactive project key, or invalid/expired player token · 403 Origin not in the configured allowlist · 429 per-project connection limit reached.

GET /connect/stats?projectKey=pk_…

Live counts for your project (key-scoped, never node-wide) — this powers the editor dock's live status:

curl "https://spawnweaver.dev/connect/stats?projectKey=pk_xxx"
# 200 -> { "activeConnections": 3, "activeRooms": 1 }

Errors: 401 missing/unknown/inactive key.


Admin API

Read-only diagnostics under /api/admin/*. Requires Authorization: Bearer <key> matching the deployment's Admin__ApiKey. Fail-closed: when no key is configured the admin API is disabled entirely (Development environments excepted, for local convenience). All endpoints return 401 without a valid key.

curl https://your-domain/api/admin/metrics \
  -H "Authorization: Bearer $ADMIN_API_KEY"
Method & path Returns
GET /api/admin/projects Up to 100 project summaries: { "projects": [ { "id", "name", "isActive", "createdAtUtc" } ] }
GET /api/admin/projects/{id} One project summary, or 404
GET /api/admin/realtime Live snapshot: activeConnections, activeRooms, per-connection and per-room details
GET /api/admin/sessions Recent connection sessions (the session inspector's list)
GET /api/admin/sessions/{connectionId} One session's full timeline — connect, auth, actions, rejections, disconnect reason; 404 unknown
GET /api/admin/errors Protocol errors aggregated by code, with counts and affected sessions
GET /api/admin/matchmaking Current matchmaking queue contents
GET /api/admin/rooms/{roomId} One room's members, host, and metadata; 404 unknown
GET /api/admin/logs?level=warning Recent server log entries (up to 200), optionally filtered by level
GET /api/admin/metrics Metrics snapshot: connections, rooms, messages, errors
GET /api/admin/feedback Feedback submitted via the landing page form (up to 200)

These are the endpoints behind the dashboard's Debugger hub — see Debugging.


SDK installers

GET /install.ps1 · GET /install.sh

No auth. One-line installer scripts served with the deployment's own base URL baked in; they download /sdk/spawnweaver.zip and extract it into the current Godot project's addons/ folder.

iwr https://spawnweaver.dev/install.ps1 -UseBasicParsing | iex
curl -fsSL https://spawnweaver.dev/install.sh | bash

Conventions

  • All timestamps are UTC ISO-8601 (…AtUtc fields).
  • JSON field names are camelCase.
  • Ids are prefixed strings: proj_…, player_…, room_…, conn_…, org_…, user_….
  • Keys: pk_… is safe to ship in clients; sk_… must never leave your infrastructure — regenerate it immediately if it does.