# 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).