Reference
API Reference
Everything the CLI, SDK, and portal UI do goes through these HTTP APIs. Authenticated endpoints accept Authorization: Bearer mtl_api_… (scoped) or mtl_cli_… tokens — see API Tokens. Three origins are involved: the portal (https://www.metaloot.app), the Studio (https://studio.metaloot.app), and your game's own domain (<slug>.metaloot.app).
OAuth (Portal)#
The identity provider behind Sign in with Metaloot — full protocol walkthrough in Authentication.
| GET /oauth/authorize | Authorization endpoint. Params: response_type=code, client_id, redirect_uri, scope, state. |
| POST /api/oauth/token | Exchange an authorization code (form-encoded: grant_type, client_id, client_secret, code, redirect_uri). Server-side only. |
| GET /api/oauth/userinfo | The player profile for a bearer access token; sub is the stable player id. |
Portal API#
| GET /api/cli/whoami | Identify the bearer token → { user: { id, name, email? }, tokenLabel }. The cheapest way to validate a token. |
| DELETE /api/cli/tokens | Revoke the calling token (what metaloot logout uses). |
| GET | POST /api/tokens · DELETE /api/tokens/{id} | Manage scoped API tokens (browser session required) — see API Tokens. |
| POST /api/games | Create a game: { title, description, url?, tags[], visibility? }, where visibility is public or private. Scope: games:write. |
| PATCH /api/games/{id} | Update title, description, url (re-syncs the auth redirect URI), playable, or visibility. Scope: games:write. |
| POST /api/oauth/apps | Issue OAuth credentials for a game: { name, homepageUrl, gameId, redirectUris } → client ID plus one-time secret. Scope: oauth:write. |
| POST /api/deploy | Start a deploy: returns { deployId, uploads: [{ path, uploadUrl }] } presigned URLs. Scope: deployments:write. |
| POST /api/deploy/{deployId}/finalize | Atomically flip the site live: returns { url, playUrl, manageUrl, auth, multiplayer }. Scope: deployments:write. |
curl https://www.metaloot.app/api/cli/whoami -H "Authorization: Bearer mtl_api_…"AI API (Portal)#
Credit-billed generation for games — the walkthrough lives in AI Generation. Both routes take Authorization: Bearer mtl_api_… with the ai:generate scope, send Access-Control-Allow-Origin: *, and answer OPTIONS, so browser games can call them from any origin.
| POST /api/ai/generate | Body: { type: "text" | "object" | "image", prompt, system?, schema?, model?, gameId?, maxOutputTokens?, temperature?, aspectRatio?, imageSize? }. prompt is required and capped at 8000 characters; schema (JSON Schema) is only valid for type: "object"; maxOutputTokens defaults to 4096 and caps at 16384; aspectRatio / imageSize ("1K" | "2K") apply to images only. |
| → 200 | { id, type, model, result, creditsUsed, balance }. result is the assistant string for text, the parsed object for object, and { b64Json, contentType } for image — base64 always, since upstream image URLs are not publicly fetchable. |
| → errors | { error: { message, code } } — 400 invalid_request, 402 insufficient_credits (with balance and required), 502 upstream_error, 503 ai_not_configured. Nothing is charged unless generation succeeded. |
| GET /api/ai/credits | { balance, ledger: [{ amount, kind, description, createdAt }] } — the last 20 ledger entries. First call also grants the 100 free signup credits. |
| POST /api/stripe/checkout | Browser-session only. Body: { packId } → { url } to redirect to. Returns 503 billing_not_configured until live Stripe keys are set. |
| POST /api/stripe/webhook | Stripe-signed only (stripe-signature). Grants the purchased credits on checkout.session.completed, idempotent on the event id. |
curl -X POST https://www.metaloot.app/api/ai/generate \
-H "Authorization: Bearer mtl_api_…" \
-H "Content-Type: application/json" \
-d '{"type":"text","prompt":"Name three swamp-level hazards."}'Studio Assets API#
Public asset endpoints send Access-Control-Allow-Origin: * (GET/HEAD/OPTIONS), so games can hot-link them from any origin. Private assets return the same headers only to their owner (bearer token with assets:read) and 404 to everyone else.
| GET /api/assets/{id-or-slug}/file | The hosted file. Query: variant=auto|source|lod (default auto; response carries X-Metaloot-Variant), and path=<file> for one file out of a pack (omit for the pack ZIP). |
| GET /api/assets/{id}/animation/{preset} | A rigged animation preset as its own GLB (rigged model + that clip). |
| Asset metadata & listing | Use the SDK's getAsset(), listAssets(), and getAssetManifest(), or the CLI with --json — the underlying routes are versioned with the SDK. |
Cache behavior:
| public assets | Cache-Control: public, max-age=86400 |
| private assets | Cache-Control: private, no-store |
| curated pack files | Cache-Control: public, max-age=31536000, immutable |
Blender Worker API (via Studio)#
Local agents should normally use metaloot blender. The CLI calls these authenticated Studio routes; clients never receive the private worker origin or service token. Read operations require assets:read, job submission requires assets:generate, and cancellation requires assets:write.
| GET /api/blender/health | Live executor type, worker capacity, active/queued counts, and starter-library health. |
| GET /api/blender/v1/capabilities | Supported job actions, request inputs, output artifacts, script-size limit, queue limit, and timeout. |
| GET /api/blender/v1/templates | Versioned Blender starting scenes, parameter schemas, recommended starter ids, and agent guidance. |
| GET /api/blender/v1/library | Semantic starter catalog with kinds, data-block names, provenance, licenses, checksums, quality, and coordinate metadata. |
| GET | POST /api/blender/v1/jobs | List owner-scoped jobs or submit a headless Blender job with { name, action?, template?, parameters?, starterAssets?, script?, sourceJobId?, faceLimit? }. |
| GET /api/blender/v1/jobs/{id} | Job status, progress, queue position, action, timing, and terminal error. |
| POST /api/blender/v1/jobs/{id}/cancel | Cancel an owner-scoped queued or running job. |
| POST /api/blender/v1/jobs/{id}/actions | Queue revise, render, inspect, or export work against a completed job, optionally with another bpy script. |
| GET /api/blender/v1/jobs/{id}/artifacts[/{name}] | List or download model.glb, preview.png, inspection.json, and scene.blend. |
| POST /api/generations | High-level Studio asset creation. The metaloot blender create command sends provider: "blender" plus the selected template, starters, parameters, and script; Studio tracks the job and persists successful outputs as a normal asset. |
Game Domain Routes#
Provisioned on every deployed game at https://<slug>.metaloot.app, alongside your static files:
| GET /auth/metaloot/start | Begin sign-in (redirects to the portal). |
| GET /auth/metaloot/callback | OAuth callback — sets the signed session cookie. |
| GET /auth/metaloot/session | Current session: { signedIn, user, scope, expiresAt } or { signedIn: false }. |
| GET | POST /auth/metaloot/logout | Clear the game session. |
| GET /mp | Multiplayer availability: { enabled, version, rooms, client, docs }. |
| wss://…/mp/rooms/{roomId} | Room WebSocket. Authenticated by the same-origin session cookie; unauthenticated upgrades get 401 { error: "auth_required", signIn: "/auth/metaloot/start" }. Room ids match [A-Za-z0-9_.~-]{1,64}. |
| GET /__metaloot/multiplayer.js | Zero-install multiplayer client (ES module). |
| GET /__metaloot/auth-widget.js | The injected sign-in widget. |
| GET /__metaloot/assets/{id-or-slug}.glb | Same-origin, edge-cached proxy to the Studio file endpoint. Public assets only; Access-Control-Allow-Origin: *, max-age=86400. |
/auth/metaloot/, /mp/, or /__metaloot/. Everything else falls through to your static files with SPA fallback to index.html.