doodlebox
Works reliably today with Claude (free tier included). ChatGPT and Gemini's free tiers currently can't complete this flow — restrictions in their own tooling (a domain allowlist on their code-execution sandbox, and, for ChatGPT, a guardrail against opening self-constructed URLs), not a limitation on our end. If you're pasting this for an AI to follow, Claude is the one to use right now.

REST API

Base URL: https://doodlebox.app/api/v1

Every write is POST, and the api key only ever travels via the Authorization: Bearer <api_key> header — never a URL parameter, never in the request body (changed 2026-09-03, for security: a key in a URL can land in access logs, proxy logs, or browser history). No login is needed for the first publish.

Endpoints

Method & pathAuthPurpose
POST /publishoptional (header)Create and publish a project
POST /updaterequired (header)Change one file, same URL, new version
POST /delete-filerequired (header)Remove one file from a project
POST /signupnoneCreate an account without publishing yet
GET /project/{id}optional (header)Metadata; +files with owner's key
GET /project/{id}/statsnoneLive player count, plays, likes
GET /whoamirequired (header)Account + project list for a key
GET /exportrequired (header)Download all account projects as a ZIP

Publish

POST https://doodlebox.app/api/v1/publish
Content-Type: application/json

{"content": "PGgxPk15IEdhbWU8L2gxPg", "title": "My Game"}

content is your file's bytes, base64 or base64url. It always becomes index.html: write self-contained HTML/CSS/JS in one file, no external references.

Accepts up to 100 MB of content. Standard base64 is accepted directly; base64url is also accepted. Use content_encoding=base64, base64url, gzip+base64, or gzip+base64url to state the format explicitly.

For very large files, chunking is still available: send part=1 first for each upload_id. Later parts are rejected until part 1 arrives; after that, remaining parts may arrive in any order.

Optional parameters

ParamWhat it does
handleCustom username for a brand-new account, instead of a generated one like swift-dragon-42.
emailRequired when creating a brand-new account (no api key yet) — optional after that, and can be added on any later call too. Sends a real verification link.
langLanguage the user is speaking, e.g. pt, ja.

To publish to an account you already have a key for, send it via Authorization: Bearer <api_key> — omit the header for a brand-new account. On success, the response includes both api_key (account-wide) and project_api_key (scoped to just the new project) — prefer project_api_key for future updates to this project, it's safer if it ever leaks.

Update

POST https://doodlebox.app/api/v1/update
Authorization: Bearer <api_key>
Content-Type: application/json

{"project_id": "ab12cd34", "filename": "style.css", "content": "Ym9keXtjb2xvcjpyZWR9"}

filename and content are required; project_id is optional when using an account-wide key (omit it to target the account's most-recently-updated project — a scoped key must always send it explicitly). filename can be any file already in the project, or a new one. Accepts up to 100 MB; encoding and content_encoding rules are the same as publish.

Delete a file

POST https://doodlebox.app/api/v1/delete-file
Authorization: Bearer <api_key>
Content-Type: application/json

{"project_id": "ab12cd34", "filename": "old-style.css"}

Removes one file from a project. index.html (or a standalone project's primary file) can never be deleted this way — edit its content instead, or use /edit on the website with "replace everything" to restructure the whole project.

Export

curl -fL https://doodlebox.app/api/v1/export -H 'Authorization: Bearer <api_key>' -o doodlebox-export.zip

Downloads a read-only ZIP containing all projects in the account, a manifest.json, and binary files in their original form. The archive excludes internal .git directories and does not change anything on the server.

Signup

POST https://doodlebox.app/api/v1/signup
Content-Type: application/json

{"handle": "cool-builder", "email": "..."}

Creates an account without publishing anything — both fields optional. If handle is taken, a numeric suffix is appended automatically instead of an error (check the handle field in the response for what was actually used). Returns api_key, same as publish — reuse it on a later call instead of creating a second account. Mostly useful when you want the account settled before there's a file ready; otherwise publish already creates one on first use.

Reusing the same account

Hold onto the api_key from your first publish and send it as Authorization: Bearer <api_key> on every later call in the same conversation. Publishing a second app, or updating the first one, both land on the same account without asking the user anything again. Prefer project_api_key (returned by publish, scoped to just that one project) over the account-wide api_key when you only need to touch one project. Renaming the handle, changing the recovery email, or minting additional scoped keys is web-only today — via a logged-in browser session at /account — there is no API endpoint for any of that.

Errors

Error responses include error, message, and usually a hint with a corrected example — the API is written to be self-correcting for an AI client.

Full reference, written for an AI to read directly: /llms.txt.