# htmldrop — full reference for LLMs
> htmldrop makes anything web-compatible live instantly. Drop an HTML file, a Markdown doc, a folder, a .zip, a built SPA, or a p5.js/Three.js/exported-design demo — it's live at a real, shareable URL right away. No build step, no framework, no signup to start.
## Getting started
## The idea
htmldrop makes anything web-compatible live instantly. If a browser can render
it, htmldrop can host it — a single HTML file, a Markdown doc, a whole folder or
`.zip` (with linked CSS/JS/images), a built single-page app, a p5.js or Three.js
sketch, or an exported design preview. You get a real `slug.htmldrop.app` URL you
can share right away — no build step, no framework, no signup to start.
## The fastest path
Go to [htmldrop.app](https://htmldrop.app/) and drop a file on the dropzone. You
get a live URL in seconds. Anonymous drops are free (up to 2 MB, 7-day URL) and
need no account.
## Keep it around
[Create a free account](https://htmldrop.app/dashboard/signup) to keep your drops
(3 sites, 10 MB per upload, no fixed expiry while you stay active) and to unlock
password protection, custom domains, and version history on paid plans (from
$3/mo). See [Publishing](/docs/publishing) for every way to publish and the
[API](/docs/api) and [Agents](/docs/agents) docs to automate it.
## Publishing
## Browser drop
The fastest path: drag any file onto the dropzone at
[htmldrop.app](https://htmldrop.app/), or onto the dropzone in the
[dashboard](https://htmldrop.app/dashboard) if you're signed in. Drop it, and
you get a live `slug.htmldrop.app` URL back immediately — no build step, no
CLI.
## Upload a single file
Upload a single `.html` or `.htm` file and it's served as-is. Upload a
single `.md` file and htmldrop renders it to HTML for you — handy for
READMEs, changelogs, or a quick note you want a URL for.
## Bundle: folder or `.zip`
For anything bigger than one file, upload a folder or a `.zip` archive.
htmldrop keeps the folder structure intact and serves `index.html` at the
root, so linked CSS, JS, images, fonts, and other assets deploy alongside it
and resolve exactly like they would on any static host.
This is what makes htmldrop work for:
- **Built single-page apps** (React, Vue, Svelte, etc.) — ship the
`dist`/`build` output as a bundle. Client-side routing works, but SPA
fallback is **opt-in per site**: you must enable **SPA fallback** for the
site (toggle it in the dashboard, or `PATCH /api/v1/sites/{id}` with
`{"spa_fallback": true}`). With it on, requests for a path that doesn't
match a file on disk (e.g. a client-side route like `/settings`) serve
`index.html`, so your router's `history` navigation, deep links, and page
refreshes work. With it off — the default — those unknown paths return
a 404.
- **WebGL and canvas demos** — p5.js sketches, Three.js scenes, and similar
multi-asset projects deploy with all their textures, models, and scripts
intact.
Bundles respect the same per-plan upload cap as single files: **10 MB per
upload on the free plan**, scaling up to **250 MB per upload on Pro**. If
your bundle is over the cap for your plan, split assets or upgrade.
```bash
# example: zip a built SPA and drop it on htmldrop.app
cd dist && zip -r ../site.zip . && cd ..
# then drag site.zip onto the dropzone, or publish it via the API/agents —
# see /docs/api and /docs/agents
```
## Paste HTML or Markdown
No file handy? In the dashboard you can paste HTML or Markdown directly
into a text box and publish it straight to a URL — useful for quick notes,
snippets, or content generated elsewhere that you don't want to save to
disk first.
## Templates
Don't want to start from a blank page? [/templates](https://htmldrop.app/templates)
has ready-to-edit starting points you can customize and publish in minutes:
- **CV** — a clean, monospace résumé layout.
- **Portfolio** — a grid layout for showcasing projects.
- **Link-in-bio** — a stacked links page for a single bio link.
- **Event invite** — a shareable page for a single event.
## Automating publishing
Everything above works interactively in the browser. If you want to publish
from a script, CI pipeline, or an AI agent instead, see the
[API docs](/docs/api) for the REST endpoints and the [Agents & MCP
docs](/docs/agents) for the MCP server and tool-based workflow.
## API reference
## Base URL
```
https://htmldrop.app/api/v1
```
Every endpoint below is relative to this base URL.
## Authentication
Programmatic clients (scripts, CI, the MCP server) authenticate with a
bearer API token:
```
Authorization: Bearer hsk_live_...
```
Create a token in the dashboard at
[/dashboard/settings](https://htmldrop.app/dashboard/settings) under **API
tokens**. The full token is shown once at creation time — store it
somewhere safe.
Interactive OAuth clients (agents, third-party integrations) can instead
use OAuth 2.1: the authorization server's metadata is published at
[`/.well-known/oauth-authorization-server`](https://htmldrop.app/.well-known/oauth-authorization-server)
so a compliant client can discover the authorize/token endpoints and
register itself without any hardcoded URLs.
The dashboard's own session cookie also satisfies `requireAuth` on these
routes, but a bearer token is what you want for anything outside a
browser.
**Verified email required.** Most authenticated endpoints require the
account's email to be verified — an unverified session gets back `403
{"error":"email_not_verified"}`. The exceptions, scoped to let a brand-new
account get one site live before confirming, are `POST /sites`, `GET
/sites`, `GET /sites/{id}`, `DELETE /sites/{id}`, `POST
/sites/{id}/upload`, and `POST /sites/{id}/upload-bundle`. In practice this
mostly matters for dashboard sessions — API tokens can only be minted by an
already-verified account, so any request authenticated with `Bearer
hsk_live_…` has already cleared that gate.
## Create a site
```
POST /sites
```
Body is JSON. Both fields are optional — omit `slug` to get a random one,
omit `name` to leave it blank.
```bash
curl -X POST https://htmldrop.app/api/v1/sites \
-H "Authorization: Bearer hsk_live_..." \
-H "Content-Type: application/json" \
-d '{"slug": "my-project", "name": "My Project"}'
```
Returns `201` with the created site:
```json
{
"id": "st_...",
"slug": "my-project",
"name": "My Project",
"access_mode": "public",
"has_password": false,
"branding_enabled": true,
...
}
```
A site has no content until you upload to it — creating it just reserves
the slug.
## Upload a single file
```
POST /sites/{id}/upload
```
Multipart form with one `file` field: an `.html`/`.htm` file served as-is,
or a `.md` file rendered to HTML. Every successful upload creates a new
version and immediately promotes it live.
```bash
curl -X POST https://htmldrop.app/api/v1/sites/st_.../upload \
-H "Authorization: Bearer hsk_live_..." \
-F "file=@./index.html"
```
Returns `201`:
```json
{
"version_id": "v_...",
"version_number": 2,
"byte_size": 4213,
"file_count": 1
}
```
## Upload a multi-file bundle
```
POST /sites/{id}/upload-bundle
```
Multipart form for anything bigger than one file — a folder or a `.zip`.
There are two accepted shapes:
- Repeated `files` fields (one per file) with a parallel `paths` field
giving each file's relative path (e.g. `paths=index.html`,
`paths=css/style.css`) — this is what the dashboard's folder-picker
sends, and multipart clients that can't preserve directory structure in
the filename should send it too.
- A single `file` field whose filename ends in `.zip` — htmldrop extracts
it server-side and deploys the contents.
Either way, `index.html` must exist at the root of the bundle. Like a
single-file upload, this creates a new version and promotes it live.
```bash
# repeated files + paths
curl -X POST https://htmldrop.app/api/v1/sites/st_.../upload-bundle \
-H "Authorization: Bearer hsk_live_..." \
-F "files=@./dist/index.html" -F "paths=index.html" \
-F "files=@./dist/assets/app.css" -F "paths=assets/app.css" \
-F "files=@./dist/assets/app.js" -F "paths=assets/app.js"
# or a single zip
curl -X POST https://htmldrop.app/api/v1/sites/st_.../upload-bundle \
-H "Authorization: Bearer hsk_live_..." \
-F "file=@./site.zip"
```
Returns `201` with the same shape as a single-file upload (`version_id`,
`version_number`, `byte_size`, `file_count`).
## List and get sites
```
GET /sites
GET /sites/{id}
```
```bash
curl https://htmldrop.app/api/v1/sites \
-H "Authorization: Bearer hsk_live_..."
curl https://htmldrop.app/api/v1/sites/st_... \
-H "Authorization: Bearer hsk_live_..."
```
`GET /sites` returns a JSON array of site objects; `GET /sites/{id}`
returns a single one, in the same shape `POST /sites` returns.
## Delete a site
```
DELETE /sites/{id}
```
```bash
curl -X DELETE https://htmldrop.app/api/v1/sites/st_... \
-H "Authorization: Bearer hsk_live_..."
```
Returns `204` with no body.
## The authenticated account
```
GET /me
```
```bash
curl https://htmldrop.app/api/v1/me \
-H "Authorization: Bearer hsk_live_..."
```
Returns the signed-in user and tenant: email, verification status, plan,
role, and the org memberships available to switch between.
## Plan limits
Caps are enforced on site creation, upload, and storage. Anonymous
(no-account) drops are capped at 2 MB per file.
| Plan | Sites | Max upload | Storage |
|---|---|---|---|
| Free | 3 | 10 MB | 50 MB |
| Starter ($3/mo) | 10 | 25 MB | 250 MB |
| Plus ($10/mo) | 25 | 100 MB | 2 GB |
| Pro ($16/mo) | 100 | 250 MB | 5 GB |
| Business ($49/mo) | 100 | 500 MB | 20 GB |
Exceeding a cap on create or upload returns `402` with `{"error":
"plan_limit"}` (site count) or `{"error": "plan_storage_limit"}` (storage),
each with a `kind` field naming what was hit.
## Errors
Non-2xx responses are JSON: `{"error": ""}`, sometimes with extra
fields for context. Common codes:
| Status | Code | Meaning |
|---|---|---|
| 400 | `slug_invalid` | Slug failed validation |
| 400 | `missing_file` / `missing_files` | Upload had no file(s) attached |
| 402 | `plan_limit` | Site-count or feature cap reached for your plan |
| 402 | `plan_storage_limit` | Storage cap reached for your plan |
| 403 | `email_not_verified` | Account email isn't verified yet |
| 404 | `not_found` | Site (or other resource) doesn't exist for your tenant |
| 409 | `slug_taken` | Requested slug is already in use |
| 413 | `file_too_large` / `bundle_too_large` | Upload exceeds your plan's per-upload cap |
| 429 | `rate_limited` | Too many requests; see `Retry-After` below |
| 451 | `quarantined` | Upload tripped an abuse heuristic |
`429` responses carry a `Retry-After` header (seconds) telling you how long
to wait before retrying.
## Agents & MCP
## The MCP server
htmldrop ships an [MCP](https://modelcontextprotocol.io/) server,
`@htmldrop.app/mcp`, so an agent can publish and manage sites directly as
tool calls instead of shelling out to `curl`. Run it with:
```bash
npx -y @htmldrop.app/mcp
```
It exposes three tools:
- `htmldrop_publish` — publish HTML or Markdown content to a live URL.
- `htmldrop_list` — list the sites in your account.
- `htmldrop_delete` — delete a site by its id (the opaque id returned by
htmldrop_list — not the slug).
Multi-file **bundles, folders, and `.zip` archives are published via the
REST API** (`POST /sites/{id}/upload-bundle`) or the dashboard, not via the
MCP `htmldrop_publish` tool — see the [API docs](/docs/api).
Authentication is a single environment variable:
```bash
HTMLDROP_API_TOKEN=hsk_live_...
```
## Get a token
1. Go to [/dashboard/settings](https://htmldrop.app/dashboard/settings).
2. Under **API tokens**, click **Create**.
3. Copy the `hsk_live_…` token — it's shown once, so store it somewhere
safe (a secrets manager or your MCP client's env config).
## Minimal client config
Most MCP clients read a JSON config that maps a server name to a command to
launch. For Claude Desktop, that's `claude_desktop_config.json`:
```json
{
"mcpServers": {
"htmldrop": {
"command": "npx",
"args": ["-y", "@htmldrop.app/mcp"],
"env": {
"HTMLDROP_API_TOKEN": "hsk_live_..."
}
}
}
}
```
Other MCP-compatible clients use the same shape — a `command`/`args` pair
to launch the server and an `env` block for the token — just in whatever
config file that client expects.
## Set up your client
Step-by-step install instructions for specific clients:
- [Claude Code](/with/claude-code)
- [Claude Desktop](/with/claude-desktop)
- [Cursor](/with/cursor)
- [Cline](/with/cline)
Once connected, ask your agent to publish some HTML or Markdown and it will
call `htmldrop_publish` and hand back the live URL. For raw HTTP access instead
of MCP, see the [API docs](/docs/api); for every way to publish
interactively, see [Publishing](/docs/publishing).