Sign in to drawer.

Continue with GitHub Continue with Google

Free tier · no card required

On this page

Publish anything your agent makes.

Drawer is the share button for the agent era. Tell any agent — Claude, Cursor, Codex, ChatGPT — "publish this to drawer" and it gets a stable URL at <you>.drawer.so. Versioned, password-gateable, yours.

Why it matters: your agent's output goes from copy-pasted-and-lost to a real URL the moment it's made. Every change is a new version (nothing is lost), every artifact is something the agent can read back, edit in place, and re-share without setup. Anything that runs in a browser is publishable.

Quickstart

  1. Sign in at drawer.so with GitHub or Google. You get a personal subdomain (<you>.drawer.so).
  2. Connect your agent (see below — one line for Claude Code, two fields for Claude Desktop / ChatGPT).
  3. Ask your agent: "publish this to drawer." You get back a URL.

That's it. Everything else on this page is optional depth.

Connect your agent

OAuth is the default — point your client at https://drawer.so/mcp with no key. First tool call opens a browser, you sign in, done. No keys to paste or rotate. API keys still work for CLI scripts / CI; swap in --header "Authorization: Bearer YOUR_DRAWER_KEY" wherever you see the URL below.

1
The "Add custom connector" dialog opens automatically. Sign in to Claude if you aren't already.
2
Fill in the connector fields:
Name
Drawer
MCP URL
https://drawer.so/mcp
3
Click Connect
Claude routes you to Drawer to sign in, then back to Claude. Drawer appears as a tool in your agent.

Walkthrough: Claude Desktop setup

1
Install Claude Code and sign in
Use the latest Claude Code build, then authenticate in your terminal.
2
Add Drawer as an MCP server:
claude mcp add --transport http drawer https://drawer.so/mcp
3
Start Claude Code and verify Drawer appears as a tool
You can now publish and update artifacts directly from terminal sessions.
1
Enable developer mode in ChatGPT
Settings → Apps → Advanced settings → toggle on Developer Mode.
2
Click Create to add a new app
Opens the custom MCP app dialog.
3
Fill in the app fields:
Name
Drawer
MCP URL
https://drawer.so/mcp
4
Click Connect on the Drawer sign-in page
You'll be returned to ChatGPT settings with Drawer's tool list loaded.

Walkthrough: ChatGPT setup

1
Install and sign in:
npm install -g drawer-so && drawer auth login
Publish files and directories from the terminal. Useful for scripts, CI, and one-off uploads.

Older Claude Desktop without custom-connector UI? Use the mcp-remote stdio bridge in claude_desktop_config.json:

{ "mcpServers": { "drawer": { "command": "npx", "args": ["-y", "mcp-remote", "https://drawer.so/mcp"] } } }

First publish

"Publish this HTML to drawer: <h1>hello</h1>"

The agent calls create_artifact and hands back your URL. Default visibility is private — only you can view it, signed in to drawer.so in the same browser. Say unlisted or public to share.

What you can publish

Format Notes
Single HTML file Preferred. Inline CSS + JS.
Markdown Rendered at view time. Tables, code, images.
Multi-file site HTML + CSS + JS + assets. Pass {files: {...}} or upload a zip. Entry is index.html. Sub-folders with index.html serve at the folder URL; .html files also serve extensionless.
Interactive mini-app Browser JS, canvas, audio, localStorage, fetch to any HTTPS API. No server code.
Image / PDF Single-file artifacts. Use request_upload or the CLI.

Size: 100 MB per artifact (200 MB uncompressed for zips). MCP create_artifact is capped around 30 KB by LLM output budget — use patch_artifact, request_upload, or the CLI for anything bigger.

Drawer is a static host with browser-side dynamic capability. Anything the browser can do, calls to any HTTPS API, localStorage for per-visitor state. No server code, no shared DB, no server-side auth other than the password tier.

MCP tools

Your agent has these on connect. JSON in, JSON out. Each tool's full description and parameters are returned by MCP tools/list.

Create + edit

Tool One-liner
create_artifact New artifact (raw HTML/markdown or {files: {...}}). Defaults to private.
update_artifact Full content replace. Old version preserved at /v/<version_id>.
patch_artifact Surgical find/replace edits. Same semantics as Claude Code's Edit.
regex_replace_artifact Regex find/replace on one file. Cheap for repeated patterns.
add_files_to_artifact Merge new files into an existing artifact (you supply the bytes).
rename_file_in_artifact Move a file inside an artifact. Preserves entry_file.
delete_file_from_artifact Remove a file from an artifact.

Read + navigate

Tool One-liner
list_artifacts Your artifacts with titles, URLs, tags, favorite state. favorited:true to filter.
list_tags Your tags with counts.
get_artifact Metadata + version history. Prefer read_artifact_file / grep_artifact for content.
read_artifact_file Read ONE file (or a line window). Pair with grep_artifact.
grep_artifact Search across an artifact's text files. The primary navigation tool.
diff_artifact Unified diff between two versions.
debug_artifact Diagnostic snapshot: files, render health, warnings, quota.

Visibility, slugs, and favorites

Tool One-liner
publish_artifact Flip visibility (private / unlisted / public). public requires confirmation.
unpublish_artifact Make an artifact private. Sister of publish_artifact.
rename_slug Promote /abc123 to a custom slug like /docs or /resume.
favorite_artifact / unfavorite_artifact Star / unstar. Surfaces under Favorites in the dashboard.

Versions, uploads, and lifecycle

Tool One-liner
revert_artifact Undo. Defaults to "the version before current."
delete_artifact Permanent. Always confirm.
request_upload Hand the user a drop-a-file page that creates a NEW artifact.
request_upload_to_artifact Hand the user a drop-a-file page that becomes a NEW VERSION of an existing artifact.
await_upload Long-poll for the user's drop to land. Up to 300 s per call.

The agent loop

Save → iterate → publish → undo. update_artifact / patch_artifact / regex_replace_artifact push new versions at the same URL — but never change who can see it. publish_artifact is the only path to flip visibility. Old versions stay reachable at /v/<version_id> forever. If a patch fails (text not found, not unique), no new version is created and the artifact is unchanged.

The CLI

For publishing files from disk, scripting, or anything over the ~30 KB MCP payload limit.

npm install -g drawer-so

drawer auth login                          # paste API key (also: logout, whoami)
drawer create ./report.html                # private by default
drawer create ./report.html --unlisted     # create + share with a URL in one step
drawer create ./site --title "demo"        # directory zipped + uploaded
drawer update ABC ./report.html            # push a new version
drawer publish ABC --visibility unlisted   # flip an existing artifact
drawer publish ABC --public --yes          # confirm-public in one step
drawer unpublish ABC                       # back to private
drawer patch ABC --find "v1" --replace "v2"   # surgical edit (see also --force)
drawer regex ABC "\\bv1\\b" "v2"             # regex find/replace on one file
drawer read ABC index.html --lines 50:120  # read one file (or slice)
drawer grep ABC "TODO"                     # search without pulling bodies
drawer get ABC                             # show metadata + version list
drawer list                                # your artifacts
drawer open ABC                            # open the URL in your browser
drawer revert ABC                          # supports --force
drawer diff ABC v1 v2
drawer debug ABC                           # diagnostics: files, render, quota
drawer favorite ABC                        # star (also: fav, star)
drawer unfavorite ABC                      # unstar (also: unfav, unstar)
drawer tags                                # list your tags with counts
drawer tag ABC docs client-x               # add tags (preserves existing)
drawer untag ABC client-x                  # remove tags
drawer files add ABC ./logo.png            # add a file to an existing artifact
drawer files rename ABC old.png new.png    # rename a file inside an artifact
drawer files rm ABC stale.png              # remove a file from an artifact
drawer upload-request ABC                  # mint a user-facing upload URL and wait
drawer delete ABC                          # permanent (asks first)

--force on update, patch, and revert skips the implicit drift check. Use only when you mean it. --expected-version <id> pins the write to a known current_version_id (returns 409 stale_artifact on miss).

Visibility tiers

Default is private. Set visibility on create_artifact (one-shot) or call publish_artifact(id, visibility) later to flip an existing artifact. Switching to public always requires a confirmation round-trip — the first call returns needs_confirmation with the exact follow-up payload to send.

Tier Who can view On profile Indexed
private (default) Only the signed-in owner. Anyone else gets a 404 — existence isn't leaked. No No
unlisted Anyone with the URL. No No
public Anyone with the URL. Yes Yes

Password modifier

password is an independent field, not a tier. Stack it on unlisted or public — visitors hit a password form before the artifact loads. Combining password with private is rejected (private is already owner-only).

curl -X POST https://drawer.so/api/artifacts \
  -H "Authorization: Bearer YOUR_DRAWER_KEY" \
  -H "Content-Type: application/json" \
  --data '{"content":"<h1>WIP</h1>","visibility":"unlisted","password":"hunter2"}'

The owner is always exempt from the password gate when signed in. To remove a password, call publish_artifact(id, visibility, password: null) — or drawer publish ABC --unlisted --clear-password from the CLI.

Versions & state

Every change creates a new immutable version. Nothing is lost unless you delete_artifact.

  • Undo last change → revert_artifact({artifact_id})
  • Revert to a specific version → revert_artifact({artifact_id, to_version_id})
  • What changed? → diff_artifact({artifact_id, from_version_id})
  • View any past version directly → <you>.drawer.so/<id>/v/<version_id>

Drift detection. Drawer tracks "what version did this principal last actually read?" on the server. Mutating calls (update_artifact, patch_artifact, regex_replace_artifact, revert_artifact, add_files_to_artifact, rename_file_in_artifact, delete_file_from_artifact) check it automatically. If the artifact has moved since your last read, you get version_drift (HTTP 409) — re-read with read_artifact_file / grep_artifact and retry, or pass force: true to overwrite. A principal is your API key or dashboard session; two agents on one key are one principal. For atomic edits against a known version, pass expected_version_id — returns 409 stale_artifact on miss.

The CLI exposes --force on update, patch, and revert for the force: true case.

Dashboard workspace

The dashboard is a file-manager view over your artifacts.

  • Sidebar quick views. All / Favorites / Recent (last 7 days). Tags listed underneath with counts; click to filter.
  • Star to favorite. Click the star next to any artifact. Agents and the CLI can star too: favorite_artifact, drawer favorite <id>.
  • List or grid view. Toggle in the toolbar; the choice persists across sessions.
  • Sort + group. Recently updated / created / name / views. Group by tag or visibility for a foldered feel without the rigidity of folders.
  • Multi-select. Click a checkbox, cmd/ctrl-click a row, or shift-click a range. Bulk-tag, change visibility, favorite, or delete from the action bar.

Plans

  • Free — 5 private artifacts, 1 API key, 25 MB / file, 100 MB / artifact, 1 GB account. Drawer badge on every artifact. Forever.
  • Pro — $20 / mo or $180 / yr — unlimited private artifacts, unlimited named API keys (one per agent), 250 MB / file, 1 GB / artifact, 25 GB account. No Drawer branding.

Full breakdown on the pricing page. Billing is Stripe; cancel anytime.

REST API

All endpoints at https://drawer.so/api/* with Bearer auth.

curl -X POST https://drawer.so/api/artifacts \
  -H "Authorization: Bearer YOUR_DRAWER_KEY" \
  -H "Content-Type: application/json" \
  --data '{"content":"<h1>hi</h1>","title":"test","visibility":"private"}'

Raw file upload (skips JSON-encoding overhead — dispatches by Content-Type: HTML / markdown / text become viewer-rendered artifacts; images / PDFs become single-file artifacts; zip bodies are unpacked server-side with index.html as the entry):

curl -X POST https://drawer.so/api/artifacts/upload \
  -H "Authorization: Bearer YOUR_DRAWER_KEY" \
  -H "Content-Type: text/html" \
  -H "X-Drawer-Title: Big File" \
  -H "X-Drawer-Visibility: unlisted" \
  --data-binary @./big-file.html

Updates go to POST /api/artifacts/:id/upload and accept an optional X-Drawer-Expected-Version-Id header for atomic version-pinned writes (returns 409 stale_artifact on miss).

Limits

Per-file, per-artifact, and account storage caps are plan-dependent — see Plans above.

Mutating API calls per key 60 / min, 1000 / hour
Read API calls per key 300 / min
OAuth start per IP 10 / min

Reserved subdomains: www, api, app, admin, content, mail, docs, auth, dashboard, plus thousands of brand names and trademarks. If your OAuth login collides, signup auto-suggests <name>-2.

Not supported: server-side code (Node, Python, PHP), shared databases, custom OAuth/SAML, WebSockets to Drawer (you can open them from your artifact JS to any external server).

FAQ

Where do files live?

Globally distributed object storage with versioned snapshots. Files are isolated per account — nothing is shared, mixed, or used to train models. Export anything via the API at any time. No egress fees.

Can I change my subdomain?

Yes, on the dashboard. Old URLs 301 to the new handle for 30 days, then the released handle becomes claimable. Your own old handle is reclaimable at any time with no wait.

Can I publish a folder?

Yes — zip it, or drawer create ./folder (CLI auto-zips). Entry is index.html; assets at <url>/<asset-path>. Sub-folders containing their own index.html serve at the folder URL (<url>/quickstart/ → quickstart/index.html), and root-level .html files also serve extensionless (<url>/about → about.html).

Why are agent-published files so big?

MCP transports content as a JSON-escaped string — every newline and quote bloats the payload, and the LLM emits every byte. For files over 30 KB, use the CLI, REST upload, or request_upload. For small edits to big files, patch_artifact not update_artifact.

Can others see my artifact id?

Only if you share the URL. Drawer doesn't surface IDs in any public listing. Profile pages show only public artifacts.

What happens to deleted artifacts?

Artifact, all versions, all R2 files removed. Not reversible.

Can my agent automate the dashboard?

Yes — MCP tools cover everything the dashboard does except changing your handle.

Security details?

Per-user data isolation enforced at the storage layer. Credentials are hashed at rest; password-gated artifacts use a key-stretching function. CSRF on session writes. Detailed security overview available on request.