World HTTP API
Read as Markdown · MCP tools · World schema
Base URL and authentication
Use https://betworld.gg/api/worlds. All JSON requests use Content-Type: application/json. GLB uploads use Content-Type: model/gltf-binary and raw file bytes.
| Access | How to authenticate | Use for |
|---|---|---|
| Public | No credential | Browse published worlds and download published documents or assets |
| Owner session | Your signed-in Betworld session cookie | Create worlds, manage credentials, inspect your library and publish |
| World token | Authorization: Bearer <token> | Let your AI read and edit one world draft and upload assets to your library |
Sign in with a registered account for owner routes. In the Betworld website, use fetch with credentials: "same-origin"; your browser supplies the session cookie and Origin. Session-based write requests require Origin: https://betworld.gg. For token-based /ai/... requests, a command-line or MCP client can omit Origin. If supplied, it must match the Betworld origin.
Create a token for each world you connect. Tokens remain valid until you revoke or replace them. Store the token in your AI client's secret settings. Owner-session actions, including publishing, are available through your signed-in website session.
Route index
Paths below are relative to /api/worlds.
| Method | Path | Access | Result |
|---|---|---|---|
| GET | / | Public | Active world directory, lobby population and limits |
| GET | /mine | Owner | Your worlds, asset library, usage and earnings |
| POST | / | Owner | Create a world with a basic starting layout |
| GET | /:id/document | Public | Published document and published revision |
| GET | /:id/status | Owner | Draft revision, published revision and entity count |
| GET | /:id/draft | Owner | Your editable document and asset library |
| PUT | /:id/draft | Owner | Replace your draft using its current revision |
| POST | /:id/publish | Owner | Publish a saved revision |
| POST | /:id/ai-token | Owner | Create or replace the world's AI credential |
| DELETE | /:id/ai-token | Owner | Revoke the world's AI credential |
| POST | /assets | Owner | Upload a static GLB to your library |
| GET | /assets/:hash.glb | Public | Download an immutable converted GLB |
| GET | /ai/draft/document | World token | Read the connected draft, catalog, limits and schema |
| PUT | /ai/draft/document | World token | Replace the connected draft |
| GET | /ai/export/world | World token | Download the draft with asset URLs and Blender references |
| POST | /ai/assets/upload | World token | Upload a static GLB to the connected owner's library |
| POST | /ai/mcp | World token | Call the MCP tools using JSON-RPC |
Browse and download
GET /
Returns:
{
"worlds": [
{"id":"public-2","name":"Pyramids","description":"…","ownerId":null,"revision":1,"players":12,"capacity":50}
],
"hub": {"id":"lobby","name":"Lobby","players":4},
"limits": {"entities":1000000,"memoryBytes":500000000,"worldBytes":100000000}
}
The numbers and description above are examples; limits contains the full current limit object. Worlds are ordered by descending active player count, then ID. Creator worlds have an ownerId; built-in worlds use null. revision here identifies the published version.
GET /:id/document
Returns { "id", "name", "document", "revision" } for a published world. Use this document to inspect the layout players receive. A missing or unpublished world returns 404. Your newer draft is available through an owner or token draft route.
GET /assets/:hash.glb
Replace :hash with a returned 64-character lowercase hexadecimal asset hash. The response body is binary model/gltf-binary. Asset URLs are immutable and publicly downloadable; keep private material out of assets you upload. Cache responses for repeat visits. Missing hashes return 404.
Create and manage your worlds
POST /
Request:
{"name":"My Desert World","description":"Tables beneath the palms."}
name is trimmed and must contain 3–48 characters. description is optional, trimmed, defaults to an empty string and permits 240 characters. Extra fields are rejected. The JSON request limit is 2 KiB. You can own up to three worlds.
Success: 201 with { "id": "world-<uuid>" }. The new draft starts with the basic layout. Read its draft to get the initial revision before editing.
GET /mine
Returns:
worlds: your worlds, each withid,name,description,revision,published_revisionandupdated_at.assets: your library entries, each withhash,name,size,triangles,pixelsandmeshes.usedBytes: total converted bytes in your asset library.earnings.paidCents: earnings already paid to your balance, in cents.earnings.pendingUnits: the fractional remainder as a decimal string; 1,000,000 units equal one cent. Divide by 1,000,000 to display pending cents.limits: current creator and world budgets.
Library entries are ordered by creation time. meshes counts rendered primitive parts and contributes to the estimated memory budget. See publishing and earnings for the earning formula.
GET /:id/status
Returns { "id", "name", "revision", "publishedRevision", "entities" }. Use this lightweight request to refresh the management page after your AI saves a change. A world you do not own returns 404.
GET /:id/draft
Returns { "id", "name", "description", "document", "revision", "publishedRevision", "assets" }. document is the complete editable world. Use its revision in your next save.
Save a draft
Use PUT /:id/draft with your owner session, or PUT /ai/draft/document with the connected world's token. Both accept the same body:
{
"revision": 1,
"document": {
"version": 1,
"floor": "#dad4c4",
"walls": "#e8e1cf",
"entities": [
{"id":"blackjack:0","kind":"game","asset":"blackjack","position":[-8,0,4],"yaw":0,"scale":[1,1,1],"color":"#d7ba7f"},
{"id":"bench-east","kind":"prop","asset":"bench","position":[8,0,4],"yaw":3.141592653589793,"scale":[1,1,1],"color":"#42756b"}
]
}
}
This replaces the complete draft, including entities and optional skins. Preserve every field and entity you want to keep. The body limit is 64,000,000 bytes, including the revision/document envelope. Follow the world format and game skin contract for exact field and placement rules.
Success returns:
{
"revision": 2,
"budget": {
"entities": 2,
"bytes": 25000306,
"manifestBytes": 306,
"memoryBytes": 138667738,
"triangles": 110060,
"pixels": 8000000
}
}
Budget values are illustrative. Use the actual returned values. revision increases after every accepted save; submit that new revision next time. Publishing is a separate step.
If you receive 409, download the current draft, merge your changes into it, and retry using its revision. Keep saves sequential. For smaller edits, use MCP world_edit_entities to upsert/remove up to 1,000 entities per array.
Publish
POST /:id/publish
Request: { "revision": 2 }, using your latest saved revision. The JSON request limit is 1 KiB.
The revision must match the saved draft, the draft must contain a supported game station, all referenced assets must belong to your library, and every world budget must pass.
Success returns { "ok": true, "pending": true, "revision": 2 }. The server stores that exact draft as a pending snapshot, blocks new wagers in the world, and waits for existing bets to finish. After at least five seconds of notice and no unsettled wagers, it installs the snapshot. Later draft saves do not change the pending snapshot.
A stale revision or a different update already queued returns 409. Repeating the same queued revision succeeds without replacing or delaying it. Use an authenticated owner session.
GET /:id/update
Public, uncached metadata for polling publication progress:
{
"revision": 1,
"pending": true,
"pendingRevision": 2,
"requestedAt": "2026-11-01T12:00:00.000Z"
}
revision is the currently published revision, or zero for an unpublished world. pendingRevision and requestedAt become null when no publication is pending. While pending is true, clients show the update notice and disable new-bet controls while keeping active-game actions enabled. When revision increases, download GET /:id/document and reload the world renderer. The website does this automatically without replacing the page.
DELETE /:id/publish
Authenticated owner session, no body. Cancels a queued update and returns { "ok": true }. The existing published layout and saved draft are retained; new bets reopen. It does not roll back a publication that already completed.
Create, replace or revoke an AI token
POST /:id/ai-token
Send an authenticated owner request with no body. Response:
{
"token": "<64 lowercase hexadecimal characters>",
"endpoint": "https://betworld.gg/api/worlds/ai/mcp",
"draftEndpoint": "https://betworld.gg/api/worlds/ai/draft/document",
"expiresIn": null
}
Save the returned token immediately in your client's secret settings. Calling this route again replaces the previous credential for that world. expiresIn: null means the credential has no automatic expiry.
DELETE /:id/ai-token
Send an authenticated owner request with no body. Returns { "ok": true }. The credential stops working; your saved draft, assets and published world remain available to you.
Read the connected world with a token
GET /ai/draft/document
Returns worldId, revision, document, assets, catalog, limits and schema. The token selects your world automatically. catalog.props and catalog.games contain supported built-ins; schema describes the accepted world JSON.
curl --fail-with-body https://betworld.gg/api/worlds/ai/draft/document \
-H "Authorization: Bearer $BETWORLD_TOKEN"
GET /ai/export/world
Use this when importing a draft into Blender. It returns the same core draft data plus:
name: the world name.assets[].url: an immutable download URL for each asset in your library.sources.props,sources.games,sources.shell: GLB authoring references.gameScale: the source-to-world scale for the game library.authoring: URLs for model guides, interfaces, the skin contract, socket catalog and skin schema.
Preserve the supplied game scale when placing reference models. The Blender bridge handles coordinate conversion and reference placement for you.
Upload a prop or game-skin mesh
Use POST /assets with your owner session or POST /ai/assets/upload with your world token.
curl --fail-with-body https://betworld.gg/api/worlds/ai/assets/upload \
-H "Authorization: Bearer $BETWORLD_TOKEN" \
-H 'Content-Type: model/gltf-binary' \
-H 'X-Model-Name: Palm planter' \
--data-binary @palm-planter.glb
Send one static, self-contained GLB of at most 4,000,000 bytes. The converted result must also fit that limit. X-Model-Name is optional; it defaults to Custom prop and permits 1–80 characters after trimming. Use embedded textures within the asset limits. Your entire converted asset library can use 100,000,000 bytes.
Success: 201 with { "hash", "name", "size", "triangles", "pixels", "meshes" }. size is the converted byte count. Place the prop using asset: "upload:<hash>", or use the plain hash as a skin's body / binding asset. Save the draft after uploading to include the placement or skin.
Re-uploading identical converted content under the same owner reuses its hash and storage. Revised geometry or materials can produce a new hash; update your document to reference the returned asset. Existing assets continue to occupy library storage.
For player characters, use the separate avatar upload API, whose formats and size limits differ.
MCP transport
Send JSON-RPC requests to POST /ai/mcp with your world token. Start with initialize, honor its returned protocol version, then call tools/list or tools/call. Use Accept: application/json, text/event-stream; parse either response format. The transport is stateless; a persistent GET event stream returns 405.
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"world_download","arguments":{}}}
The MCP reference documents all seven tools: world_download, world_upload, world_edit_entities, game_skin_contract, game_skin_save, asset_upload and asset_download. Successful tool results contain structuredContent and equivalent JSON text. Check isError on tool results even when HTTP returns 200.
Errors, limits and retrying
Application validation failures return { "error": "message" }. Read that message before retrying. Rate-limit and proxy errors may use a text body; check the response Content-Type before parsing JSON.
| HTTP status | What to do |
|---|---|
| 400 | Correct the request, model, placement, ownership reference or resource budget described by error |
| 401 | Sign in to a registered account, or reconnect using your current world token |
| 403 | Use the correct origin and authentication method for the route |
| 404 | Check the world/asset ID, publication status and world ownership |
| 409 | Refresh a stale draft revision, or cancel an existing queued update before publishing a different revision |
| 413 | Reduce the request body to the route's byte limit |
| 429 | Wait for the rate-limit window indicated by response headers |
| 503 | Respect Retry-After and retry after the service has capacity |
| 500 | Retry a read later; refresh the draft before repeating a save whose outcome is uncertain |
World API routes allow 120 requests per minute per client IP. Public GLB downloads have a separate 12,000-per-minute limit. Heavy reads and writes are admitted one at a time; queue your AI's changes instead of sending parallel saves. Proxy limits can also apply.
All limits apply together: up to 1,000,000 entities, 100 MB world download, 500 MB estimated runtime memory, 64 MB manifest, 20,000,000 placed triangles, 32,000,000 decoded texture pixels and 50 players per world. Read the live limits response and returned budget while building.
In-game editor integration
GET /:id/document includes ownerId alongside id, name, document and revision. The site offers Edit this world when it matches the signed-in player. Ownership is checked independently on every draft, asset and publish operation.
GET /:id/draft also returns pendingRevision (an integer or null). The editor uses it to resume displaying a queued publication after reopening. Draft saves continue to use the current revision; queued publication uses its own stored snapshot.
Terrain and panorama routes
World documents accept optional terrain and background settings. Upload a background with POST /api/worlds/backgrounds using your owner session, or POST /api/worlds/ai/backgrounds/upload using a world Bearer credential. Download canonical WebP bytes from GET /api/worlds/backgrounds/:hash.webp. Draft, library and export responses include backgrounds metadata alongside the existing GLB assets. See terrain and backgrounds for every field, limit, header, response and error code.