# Billing Source: https://docs.htmldrop.app/account/billing How subscriptions, currency and invoices work. Payments are handled by Stripe. htmldrop never sees your card details. ## Subscribing and changing plans Upgrade from **Dashboard → Billing**. Changing plans, updating a card, and cancelling all happen through the billing portal linked from the same page. Annual billing charges ten months for twelve — the discount is applied to the plan price, not as a coupon, so it persists for the life of the subscription. ## Currency Prices are shown and charged in a currency resolved from your country when you sign up. There is deliberately **no currency picker**: pricing reflects the market you are actually in rather than whichever currency happens to be cheapest. The currency is fixed to the account once resolved, so a later trip abroad or a VPN cannot change what you are charged. If your country was detected wrongly, email [support@htmldrop.app](mailto:support@htmldrop.app) and we will correct it. ## Tax EU and UK VAT is added at checkout where applicable. Business customers can enter a VAT number in the checkout form. ## Invoices Every invoice is available in the billing portal, and receipts are emailed when a payment succeeds. ## Cancelling Cancel any time from the portal. Your plan stays active until the end of the period you have already paid for, then the account returns to free — drops beyond the free cap are paused rather than deleted, and become live again if you re-subscribe or delete enough to fit. ## Exporting your data `GET /api/v1/sites/export` returns every drop as a `.zip`, and the dashboard has the same button. There is no lock-in and no export fee. # Plans and limits Source: https://docs.htmldrop.app/account/plans-and-limits Exactly what each plan allows — drops, upload size, storage, versions, domains. | | Anonymous | Free | Plus | Business | | ---------------------- | ---------- | ------------------------ | --------------- | ---------------- | | Price | — | \$0 | $12/mo · $99/yr | $49/mo · $490/yr | | Signup | Not needed | Required | Required | Required | | Active drops | — | 3 | 25 | 100 | | Max per upload | 2 MB | 10 MB | 100 MB | 500 MB | | Total storage | — | 50 MB | 2 GB | 20 GB | | Versions kept | — | 3 | 10 | 30 | | Lifetime | 7 days | Live while you're active | Permanent | Permanent | | Folders and `.zip` | — | ✓ | ✓ | ✓ | | Custom domains | — | — | 1 | 10 | | Password protection | — | — | ✓ | ✓ | | htmldrop badge | Shown | Shown | Removed | Removed | | Analytics integrations | — | — | ✓ | ✓ | Caps apply when you add, never retroactively. Going over a limit — for example after a plan change — never deletes a live drop; it just stops you creating more until you delete something or upgrade. ## "Live while you're active", precisely A free drop runs a 30-day clock that resets every time the **owner** signs in. Visitor traffic does not reset it. We email before anything expires, and an expired drop can be recovered by signing back in before it is finally removed. Paid drops have no clock at all. ## Grandfathered plans Starter and Pro are no longer sold. If you are on one, nothing changes: your price, your limits and your entitlements stay exactly as they were for as long as the subscription is active. ## Which plan do I need? * **Just sharing one file with someone** — anonymous is enough. * **A handful of things you want to keep** — free. * **A portfolio, client work, anything on your own domain** — Plus. * **A team, many drops, or several domains** — Business. # ChatGPT Source: https://docs.htmldrop.app/agents/chatgpt Add htmldrop as a custom connector, then publish from any conversation. ChatGPT can talk to htmldrop through a custom MCP connector. Connectors cannot be installed from inside a chat — that is deliberate on OpenAI's side, since it is exactly what a prompt-injection attack would try — so you add it once in settings. **Settings → Apps → Advanced** and enable **Developer mode**. On Business and Enterprise workspaces an admin may need to permit custom connectors first. **Settings → Connectors → Create**, then enter: ``` https://htmldrop.app/mcp ``` Give it a name and a description — the model reads the description when deciding whether to use it, so something like "Publishes HTML or Markdown to a live URL" works well. The first publish opens a browser window for OAuth. Approve once; ChatGPT holds and refreshes the token from then on. ## Using it > Make me a one-page portfolio and publish it with htmldrop ChatGPT writes the HTML, calls `htmldrop_publish`, and hands back the live URL. ## Without a connector If you would rather not add one, ask ChatGPT for a **single self-contained HTML file**, download it, and drag it onto [htmldrop.app](https://htmldrop.app). Same result, one extra step. # CI and headless Source: https://docs.htmldrop.app/agents/ci-and-headless Publish from GitHub Actions, a container, or anywhere without a browser. OAuth needs a browser, so automated environments use an API token instead. [Dashboard → Settings → API tokens](https://htmldrop.app/dashboard/settings). Tokens look like `hsk_live_…` and are shown once. Never commit it. In GitHub Actions, add it under repository **Settings → Secrets and variables → Actions**. Use the CLI, or run the MCP server over stdio if an agent is driving. ## GitHub Actions ```yaml theme={null} name: Publish preview on: push: branches: [main] jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' - run: npm ci && npm run build - name: Publish to htmldrop env: HTMLDROP_API_TOKEN: ${{ secrets.HTMLDROP_API_TOKEN }} run: npx @htmldrop.app/cli deploy working-directory: dist ``` ## MCP over stdio ```bash theme={null} HTMLDROP_API_TOKEN=$HTMLDROP_API_TOKEN npx -y @htmldrop.app/mcp ``` ## Direct API If you would rather not add a Node dependency, the [REST API](/api/authentication) takes the same token as a bearer credential. # Claude Source: https://docs.htmldrop.app/agents/claude Add htmldrop to Claude as a custom connector, or install the desktop bundle. ## Claude (web and desktop) **Settings → Connectors → Add custom connector**, then: ``` https://htmldrop.app/mcp ``` The first publish opens a browser sign-in. Approve once and Claude keeps the connection. ## Desktop bundle Claude Desktop also accepts a one-click bundle — download [htmldrop.mcpb](https://htmldrop.app/htmldrop.mcpb) and open it. Claude installs the server and prompts for the same OAuth approval. ## Using it > Publish this page with htmldrop Claude calls `htmldrop_publish` and replies with the URL. Ask it to update and it republishes to the same slug, so the link you already shared stays correct. Claude is good at producing a single self-contained file when asked to. If a page comes back split across several files, say "make it one self-contained index.html" and publishing gets simpler. # Claude Code Source: https://docs.htmldrop.app/agents/claude-code One command, OAuth in the browser, publish from the terminal. ```bash theme={null} claude mcp add --transport http htmldrop https://htmldrop.app/mcp ``` That is the whole setup. The first tool call opens your browser for approval; tokens are stored and refreshed by Claude Code. ## Using it Inside a session: > make me a landing page for my studio and put it online Claude Code writes the file, calls `htmldrop_publish`, and prints the live URL. Because it can also read your working directory, "publish the dist folder" works once you have run your build. ## Verifying the connection ```bash theme={null} claude mcp list ``` `htmldrop` should appear as a connected HTTP server. If a call fails with an authorization error, remove and re-add the server to force a fresh sign-in: ```bash theme={null} claude mcp remove htmldrop claude mcp add --transport http htmldrop https://htmldrop.app/mcp ``` # CLI Source: https://docs.htmldrop.app/agents/cli Deploy the current directory from your terminal. ```bash theme={null} npx @htmldrop.app/cli login # opens the browser, once npx @htmldrop.app/cli deploy # publishes the current directory ``` `deploy` uploads the working directory as a drop and prints the live URL. Deploying again updates the same drop, keeping the URL and adding a version. ## Typical use ```bash theme={null} npm run build cd dist npx @htmldrop.app/cli deploy ``` ## Without a browser Set an API token and skip `login` entirely: ```bash theme={null} HTMLDROP_API_TOKEN=hsk_live_... npx @htmldrop.app/cli deploy ``` That is the same mechanism used in [CI](/agents/ci-and-headless). Install the **scoped** package. `@htmldrop.app/cli` is ours; the unscoped `htmldrop` package on npm belongs to an unrelated project. # Codex Source: https://docs.htmldrop.app/agents/codex Configure htmldrop in ~/.codex/config.toml with an API token. Codex reads MCP servers from `~/.codex/config.toml`. Add htmldrop with a stdio server and an API token: ```toml theme={null} # ~/.codex/config.toml [mcp_servers.htmldrop] command = "npx" args = ["-y", "@htmldrop.app/mcp"] env = { HTMLDROP_API_TOKEN = "hsk_live_..." } ``` Create the token at [htmldrop.app/dashboard/settings](https://htmldrop.app/dashboard/settings) under API tokens. Treat it like a password — it grants full access to your drops. Restart Codex after editing the file, then: > Publish this file with htmldrop The token path is used here because it works everywhere without a browser. If you prefer OAuth and your Codex build supports remote HTTP MCP servers, point it at `https://htmldrop.app/mcp` instead and skip the token entirely. # Cursor and Cline Source: https://docs.htmldrop.app/agents/cursor-and-cline Add the remote server to your MCP settings JSON. Both read a JSON MCP configuration. Add htmldrop as a remote server: ```json theme={null} { "mcpServers": { "htmldrop": { "url": "https://htmldrop.app/mcp" } } } ``` **Cursor**: Settings → MCP → add the server, or edit `.cursor/mcp.json` in your project. **Cline**: the MCP servers panel, or `cline_mcp_settings.json`. The first publish opens a browser for OAuth approval. No keys in the config file. ## Using it > Publish the current file to htmldrop and give me the link Since both tools can see your workspace, they can publish a built folder as easily as a single file — run your build first, then ask for the output directory to be published. # Publishing from AI agents Source: https://docs.htmldrop.app/agents/overview One MCP server, OAuth in the browser, no API keys to paste. Connect once, then publishing is just asking. htmldrop runs a hosted [MCP](https://modelcontextprotocol.io) server at `https://htmldrop.app/mcp`. Any MCP-capable assistant can connect to it and publish on your behalf — you approve once in the browser, and no keys are ever pasted into a chat. ## The fastest setup Paste this into your agent and it will configure itself from our machine-readable instructions: ``` Fetch and execute the appropriate instructions to set me up for htmldrop from https://htmldrop.app/for-agents ``` Or follow a per-client guide: [ChatGPT](/agents/chatgpt) · [Claude](/agents/claude) · [Claude Code](/agents/claude-code) · [Codex](/agents/codex) · [Cursor and Cline](/agents/cursor-and-cline) · [CLI](/agents/cli) · [CI and headless](/agents/ci-and-headless). ## What the agent can do | Tool | What it does | | ------------------ | ------------------------------------------------------------------------------------------ | | `htmldrop_publish` | Publishes HTML or Markdown and returns the live URL. Optional `slug`, `title`, `password`. | | `htmldrop_list` | Lists the drops on the account. | | `htmldrop_delete` | Deletes a drop by id. | Publishing to an existing slug updates that drop and keeps the URL, so "update my portfolio and republish" works the way you would hope. ## How authentication works The remote server is OAuth-first. On the first tool call your browser opens, you approve, and the client stores and refreshes tokens itself. Nothing to copy, and you can revoke access at any time from the dashboard. For environments with no browser — CI, containers, cron — use an API token instead. See [CI and headless](/agents/ci-and-headless). The npm packages are **scoped**: `@htmldrop.app/mcp` and `@htmldrop.app/cli`. The unscoped `htmldrop` package is an unrelated third-party project. If an assistant suggests `npx htmldrop`, correct it. # Anonymous drops Source: https://docs.htmldrop.app/api/anonymous-drops Publish without an account: two endpoints, no credentials. ## Publish HTML ```bash theme={null} curl -F "file=@page.html" https://htmldrop.app/api/v1/drops ``` `multipart/form-data` with a single `file` field. Returns: ```json theme={null} { "url": "https://wandering-fox-42.htmldrop.app", "id": "v7r6ihmnv4mn", "slug": "wandering-fox-42" } ``` ## Publish Markdown ```bash theme={null} curl -X POST https://htmldrop.app/api/v1/drops-md \ -H "Content-Type: application/json" \ -d '{"title":"Release notes","markdown":"# 1.2\n\nShipped."}' ``` | Field | Required | Notes | | ---------- | -------- | ------------------------------ | | `markdown` | yes | CommonMark source | | `title` | no | Page title and dashboard label | ## List the drops for this browser ```bash theme={null} curl https://htmldrop.app/api/v1/drops --cookie-jar jar --cookie jar ``` Anonymous ownership is carried by a signed cookie, so this returns the drops made by that browser. It is what the homepage uses to show you your own drops when you return. ## Limits * **2 MB** per upload * **7 days** lifetime, on a fixed clock * Single `.html` or `.md` files only — folders and `.zip` need an account * Per-IP and per-session caps apply to prevent bulk abuse Sign up from the same browser and these drops are claimed into your account automatically, keeping their URLs. # Authentication Source: https://docs.htmldrop.app/api/authentication API tokens for automation, OAuth for agents, and which endpoints need neither. The API lives at `https://htmldrop.app/api/v1`. There are three ways to call it. ## No credentials [Anonymous drop endpoints](/api/anonymous-drops) need nothing at all — that is what powers the homepage drop area. Those drops are capped at 2 MB and live 7 days. ## API token For scripts, CI and headless agents. Create one at [Dashboard → Settings → API tokens](https://htmldrop.app/dashboard/settings); tokens look like `hsk_live_…` and are shown once. ```bash theme={null} curl https://htmldrop.app/api/v1/sites \ -H "Authorization: Bearer $HTMLDROP_API_TOKEN" ``` A token carries the full rights of the account that created it. Store it as a secret, never commit it, and revoke it from the same settings page if it leaks. ## OAuth For MCP clients. The server implements OAuth 2.1 with PKCE and dynamic client registration, so a compliant client discovers everything it needs: ``` https://htmldrop.app/.well-known/oauth-authorization-server https://htmldrop.app/.well-known/oauth-protected-resource ``` Scopes are `mcp` and `cli`. You approve in the browser, the client stores and refreshes tokens, and access can be revoked from the dashboard. See [publishing from AI agents](/agents/overview). ## Rate limits and abuse controls Anonymous publishing is rate limited per IP and per browser session, and uploads pass an invisible bot check plus a phishing heuristic. Authenticated calls are governed by your plan's limits rather than these controls — signing in is the right fix for hitting an anonymous cap. # Errors Source: https://docs.htmldrop.app/api/errors Status codes you should expect, and what to do about each. Errors return JSON with an `error` field: ```json theme={null} { "error": "plan_limit", "detail": "drop limit reached for plan free" } ``` | Status | Meaning | What to do | | ------ | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | `400` | Malformed request — bad JSON, missing field, unsupported file type | Fix the payload. Only `.html`, `.htm`, `.md` and `.markdown` are accepted for single-file publishing | | `401` | Missing or invalid credentials | Check the `Authorization` header; the token may have been revoked | | `403` | Authenticated but not permitted — e.g. email not verified before checkout, or a feature above your plan | Verify your email, or upgrade for password protection and custom domains | | `404` | No such drop, or not yours | Confirm the id and the account the token belongs to | | `409` | Conflict — e.g. the slug is taken, or the account already has an active subscription | Pick another slug; for billing, use the portal link returned in the body | | `413` | Upload exceeds your plan's per-upload cap | See [plans and limits](/account/plans-and-limits) | | `429` | Rate limited — most often anonymous publishing from one network | Sign in; authenticated publishing is not governed by the anonymous caps | | `5xx` | Our fault | Retry with backoff. Live status is at [htmldrop.app/status](https://htmldrop.app/status) | ## Serving errors Requests to a drop's URL, rather than the API, return HTML rather than JSON: * **404** — nothing published at that address * **410** — the drop expired, or was removed by trust and safety * **503** — the owner has not verified their email yet Each is a branded page explaining what happened, safe to show to a visitor who followed an old link. # Sites Source: https://docs.htmldrop.app/api/sites Create a site, upload files and bundles to it, list, read and delete — the full authenticated surface. Base URL is `https://htmldrop.app/api/v1`. Every endpoint here needs an [API token](/api/authentication). Most authenticated endpoints require a verified email and return `403 {"error":"email_not_verified"}` otherwise. The exceptions — so a new account can 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 only affects dashboard sessions: API tokens can only be created by an already-verified account. ## Create a site ``` POST /sites ``` Both fields are optional — omit `slug` for a random one, omit `name` to leave it blank. ```bash theme={null} curl -X POST https://htmldrop.app/api/v1/sites \ -H "Authorization: Bearer $HTMLDROP_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"slug": "my-project", "name": "My Project"}' ``` Returns `201`: ```json theme={null} { "id": "st_...", "slug": "my-project", "name": "My Project", "access_mode": "public", "has_password": false, "branding_enabled": true } ``` Creating a site only reserves the slug — it has no content until you upload. ## Upload a single file ``` POST /sites/{id}/upload ``` Multipart with one `file` field: `.html`/`.htm` is served as-is, `.md` is rendered to HTML. Every upload creates a version and promotes it live. ```bash theme={null} curl -X POST https://htmldrop.app/api/v1/sites/st_.../upload \ -H "Authorization: Bearer $HTMLDROP_API_TOKEN" \ -F "file=@./index.html" ``` Returns `201`: ```json theme={null} { "version_id": "v_...", "version_number": 2, "byte_size": 4213, "file_count": 1 } ``` ## Upload a folder or .zip ``` POST /sites/{id}/upload-bundle ``` Two accepted shapes: Repeated `files` fields with a parallel `paths` field giving each file's relative path. This is what the dashboard's folder picker sends, and what multipart clients that can't preserve directory structure should use. ```bash theme={null} curl -X POST https://htmldrop.app/api/v1/sites/st_.../upload-bundle \ -H "Authorization: Bearer $HTMLDROP_API_TOKEN" \ -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" ``` One `file` field whose filename ends in `.zip`. htmldrop extracts it server-side and deploys the contents. ```bash theme={null} curl -X POST https://htmldrop.app/api/v1/sites/st_.../upload-bundle \ -H "Authorization: Bearer $HTMLDROP_API_TOKEN" \ -F "file=@./site.zip" ``` Either way `index.html` must exist at the root of the bundle. The response has the same shape as a single-file upload. ## List and read ```bash theme={null} curl https://htmldrop.app/api/v1/sites \ -H "Authorization: Bearer $HTMLDROP_API_TOKEN" curl https://htmldrop.app/api/v1/sites/st_... \ -H "Authorization: Bearer $HTMLDROP_API_TOKEN" ``` `GET /sites` returns an array; `GET /sites/{id}` returns one site in the same shape `POST /sites` returns. ## Update settings `PATCH /sites/{id}` changes a site's configuration — name, slug, password, analytics, and `spa_fallback`: ```bash theme={null} curl -X PATCH https://htmldrop.app/api/v1/sites/{id} \ -H "Authorization: Bearer $HTMLDROP_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"spa_fallback": true}' ``` ## Delete ```bash theme={null} curl -X DELETE https://htmldrop.app/api/v1/sites/st_... \ -H "Authorization: Bearer $HTMLDROP_API_TOKEN" ``` Returns `204`. Deletion is immediate and permanent. ## The authenticated account ```bash theme={null} curl https://htmldrop.app/api/v1/me \ -H "Authorization: Bearer $HTMLDROP_API_TOKEN" ``` Returns the signed-in user and tenant: email, verification status, plan, role, and the organisations available to switch between. ## Usage and export ```bash theme={null} curl https://htmldrop.app/api/v1/usage -H "Authorization: Bearer $HTMLDROP_API_TOKEN" curl https://htmldrop.app/api/v1/sites/export -H "Authorization: Bearer $HTMLDROP_API_TOKEN" -o export.zip ``` `usage` reports consumption against your plan's caps. `export` returns every drop as a `.zip` — there is no lock-in and no export fee. # Versions Source: https://docs.htmldrop.app/api/versions List a drop's history and promote an older version back to live. ## List versions ```bash theme={null} curl https://htmldrop.app/api/v1/sites/{id}/versions \ -H "Authorization: Bearer $HTMLDROP_API_TOKEN" ``` Returns the retained versions, newest first. How many are kept depends on your plan — 3 on free, 10 on Plus, 30 on Business. ## Roll back ```bash theme={null} curl -X POST \ https://htmldrop.app/api/v1/sites/{id}/versions/{version_id}/promote \ -H "Authorization: Bearer $HTMLDROP_API_TOKEN" ``` Promoting makes that version live immediately at the same URL. The rollback is itself recorded as a publish, so nothing is lost and you can roll forward again. See [updating and rolling back](/publishing/updating-and-rollback) for the concept and the dashboard equivalent. # Concepts Source: https://docs.htmldrop.app/concepts Drops, versions, slugs, and the difference between anonymous, free and paid. ## Drop A drop is one published thing at one URL. It might be a single HTML file or a whole folder — either way it is one entry in your dashboard and one address. ## Slug The subdomain a drop is served from: `slug.htmldrop.app`. Anonymous drops get a random slug. With an account you can choose one (3–63 characters, lowercase letters, numbers and hyphens), and on a paid plan you can put the drop on your own domain instead. ## Version Every upload to an existing drop creates a version. The newest is live; older ones stay retrievable so you can [roll back](/publishing/updating-and-rollback). How many are kept depends on your plan — 3 on free, 10 on Plus, 30 on Business. ## The three states an account can be in | | Anonymous | Free account | Paid | | ----------------------- | ------------- | ------------------------------------------------------------------ | --------------------------------- | | Signup needed | No | Yes | Yes | | Drop lifetime | 7 days, fixed | Live while you're active — a 30-day clock that each sign-in resets | Permanent | | Max upload | 2 MB | 10 MB | 100 MB (Plus) / 500 MB (Business) | | Folders and `.zip` | No | Yes | Yes | | Custom domain, password | No | No | Yes | Anonymous drops made in a browser are **claimed automatically** when you sign up from that same browser — you do not have to re-upload anything. # What htmldrop is Source: https://docs.htmldrop.app/introduction Turn any web-compatible file into a live, shareable URL — no build step, no framework, no signup to start. htmldrop takes something you already have — an HTML file, a Markdown document, a folder, a `.zip`, a built SPA, a p5.js sketch — and puts it on the web at a real URL you can send to anyone. There is no build step, no framework to learn, and nothing to install. Drag an `.html` file onto the homepage and get a URL back. No account. Connect once with OAuth, then tell ChatGPT or Claude to publish for you. ## What it is good at **Sharing something that isn't a document.** Email attachments and PDF exports lose interactivity. A drop keeps it — charts stay interactive, animations still run, links still work. **Everything an AI just built you.** Models are very good at producing a single self-contained HTML file. They are not good at hosting it. htmldrop closes that last step, either by drag-and-drop or by letting the agent publish directly through [MCP](/agents/overview). **Work that needs a URL, not a repository.** A portfolio, a CV, a client proposal, a one-page report. No git, no CI, no deploy config. ## What it is not It is not an application host. There is no server-side runtime, no database, and no build pipeline — htmldrop serves static files. If your project needs `npm run build`, run that yourself and drop the output folder. ## How a drop behaves Every drop is served over HTTPS at `slug.htmldrop.app`, or at your own domain on a paid plan. Uploads are versioned, so publishing again keeps the old copy and you can [roll back](/publishing/updating-and-rollback) with one click. **Anonymous drops live 7 days.** With a free account, drops stay live as long as you sign in now and then — each visit resets a 30-day clock and we email you before anything expires. Paid plans make every drop permanent. See [plans and limits](/account/plans-and-limits). # Folders and .zip bundles Source: https://docs.htmldrop.app/publishing/folders-and-zip Publish a multi-page site, a static-site-generator build, or a client-routed SPA. A free account unlocks publishing a whole directory: drag a folder onto the dashboard, or upload a `.zip`. Structure is preserved, so linked CSS, JS, images and fonts resolve exactly as they would on any static host. Folders and `.zip` bundles require an account. The homepage drop area, which needs no signup, accepts single `.html` and `.md` files only. `index.html` at the root of the upload is the landing page. Everything else is served at its relative path. ## Static site generators Ship the build output — `dist/`, `build/`, `_site/` — and every page works with no configuration. Generators that write `about/index.html` for the page at `/about` resolve automatically, as do flat builds that write `about.html`. A directory page requested without its trailing slash redirects to the canonical `/about/` form, so relative links inside the page resolve correctly. Astro, Hugo, Eleventy, Jekyll and friends all work this way. ## Single-page apps Ship `dist/` or `build/` as a bundle. For apps that route entirely on the client, turn on **SPA fallback**: Open the site and toggle **SPA fallback**. ```bash theme={null} curl -X PATCH https://htmldrop.app/api/v1/sites/{id} \ -H "Authorization: Bearer $HTMLDROP_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"spa_fallback": true}' ``` With it on, a path matching no file **and** no page — a client-side route like `/settings` — serves `index.html` so your router takes over. Leave it off for multi-page sites, so genuinely missing pages return a real 404. ## WebGL and canvas projects p5.js sketches, Three.js scenes and similar multi-asset work deploy with their textures, models and scripts intact — this is the case bundles exist for. ## Zipping a build ```bash theme={null} cd dist && zip -r ../site.zip . && cd .. ``` Then drop `site.zip` on the dashboard, or publish it through the [API](/api/sites) or an [agent](/agents/overview). ## Size limits The cap applies to the upload as a whole: **10 MB** on free, **100 MB** on Plus, **500 MB** on Business, with total storage of 50 MB, 2 GB and 20 GB. See [plans and limits](/account/plans-and-limits). Over the cap, split the assets or upgrade. # Publishing HTML Source: https://docs.htmldrop.app/publishing/html Single files, inline assets, and what does and doesn't work in a static drop. Drop an `.html` file and it is served exactly as written. No transformation, no build step, no framework detection. ## Make it self-contained A single file with its CSS and JavaScript inline is the most reliable thing you can publish. Relative references to files you did not upload will 404 — if your page needs `styles.css` or `logo.png`, either inline them or publish the whole [folder](/publishing/folders-and-zip). External URLs work normally: a CDN script, a Google Font, an image hosted elsewhere all load as they would anywhere. ## What runs Anything the browser can do on its own: * JavaScript, including canvas and WebGL — p5.js sketches, Three.js scenes * CSS animations, custom properties, container queries * `fetch()` to any API that permits cross-origin requests * Web fonts, SVG, video and audio elements ## What does not There is no server-side runtime. No PHP, no Node, no Python, no database, no environment variables. Server-rendered frameworks must be built to static output first — run `npm run build`, then publish the resulting folder. Anything in a drop is public to anyone with the URL unless you add [password protection](/sharing/password-protection). Never publish API keys, tokens or credentials — treat a drop like a public repository. ## Head control Your `` is preserved, so ``, `<meta name="description">`, Open Graph tags and favicons all behave as expected — including in link previews when the URL is pasted into Slack, WhatsApp or a social post. On free and anonymous drops a small "Made with htmldrop" badge is added in the corner. Paid plans remove it. # Publishing Markdown Source: https://docs.htmldrop.app/publishing/markdown Drop a .md file and get a styled, readable page — no theme to pick. Drop a `.md` or `.markdown` file and htmldrop renders it to a styled HTML page server-side. You do not choose a theme or write any CSS. This is the fastest route for anything that is fundamentally text: release notes, a proposal, meeting notes, a runbook, an RFC. ## What is supported Standard CommonMark, including: * Headings, lists, and nested lists * **Bold**, *italic*, `inline code`, and fenced code blocks * Links, images, and blockquotes * Tables * Horizontal rules The rendered page is responsive and readable on a phone without any work from you. ## Via the API Markdown has its own endpoint that takes JSON rather than a file upload: ```bash theme={null} curl -X POST https://htmldrop.app/api/v1/drops-md \ -H "Content-Type: application/json" \ -d '{"title":"Q3 notes","markdown":"# Q3 notes\n\nWe shipped."}' ``` The `title` becomes the page title and the dashboard label. See [anonymous drops](/api/anonymous-drops) for the full contract. <Tip> If you want precise control over layout, publish [HTML](/publishing/html) instead. Markdown is for when you want to write and be done. </Tip> # Pasting HTML or Markdown Source: https://docs.htmldrop.app/publishing/paste No file on disk? Paste markup straight into the dashboard and publish it. Sometimes the thing you want online was never a file — a snippet an assistant produced in a chat window, a note you just typed, a fragment from someone else's message. In the dashboard you can paste HTML or Markdown directly into a text box and publish it to a URL, with no intermediate save-to-disk step. <Steps> <Step title="Open the dashboard"> Go to [htmldrop.app/dashboard](https://htmldrop.app/dashboard) and start a new drop. </Step> <Step title="Paste your markup"> Choose the paste option and drop your HTML or Markdown in. Markdown is rendered to a styled page; HTML is served as-is. </Step> <Step title="Publish"> You get a live URL immediately, exactly as with an uploaded file — including versions, so you can paste a revision later and still roll back. </Step> </Steps> <Tip> If an AI assistant produced the content, connecting it via [MCP](/agents/overview) removes this step entirely — the agent publishes directly and hands you the link. </Tip> # Templates Source: https://docs.htmldrop.app/publishing/templates Ready-to-edit starting points when you don't want to begin with a blank page. [htmldrop.app/templates](https://htmldrop.app/templates) has starting points you can customise and publish in minutes — useful when you know what you want but would rather not write the markup. <CardGroup> <Card title="CV" icon="file-lines"> A clean, monospace résumé layout. </Card> <Card title="Portfolio" icon="grid"> A grid layout for showing project work. </Card> <Card title="Link in bio" icon="link"> A stacked links page for a single bio link. </Card> <Card title="Event invite" icon="calendar"> A shareable page for one event. </Card> </CardGroup> Pick one, edit the content in the dashboard, and publish. The result is an ordinary drop — same URL behaviour, same versions, same option to add a [custom domain](/sharing/custom-domains) or a [password](/sharing/password-protection) afterwards. <Tip> Templates are a starting point, not a constraint: you can export or replace the markup at any time, and a template-based drop can later be overwritten by an upload like any other. </Tip> # Updating and rolling back Source: https://docs.htmldrop.app/publishing/updating-and-rollback Every upload is a version. Publishing again never loses the old copy. Publishing to an existing drop creates a new version and makes it live immediately. The URL never changes, so a link you shared last week keeps working and simply shows the newer content. ## Rolling back Open the drop in the dashboard, find the version you want in the history, and promote it. It becomes live at once — a rollback is itself a normal publish, so nothing is destroyed and you can roll forward again. Via the API: ```bash theme={null} curl -X POST https://htmldrop.app/api/v1/sites/{site_id}/versions/{version_id}/promote \ -H "Authorization: Bearer $HTMLDROP_API_TOKEN" ``` ## How many versions are kept | Plan | Versions retained | | -------- | ----------------- | | Free | 3 | | Plus | 10 | | Business | 30 | Once the limit is reached, the oldest version is dropped as a new one arrives. Being over a limit never deletes a live drop — caps apply when you add, not retroactively. ## Updating from an agent Publishing to the same slug is an update, so this works as you would expect: > Update my portfolio and republish The agent calls `htmldrop_publish` with the existing slug, and the live URL stays the same. # Quickstart Source: https://docs.htmldrop.app/quickstart From a file on your machine to a live URL, three ways: the browser, an AI agent, or the API. ## The fastest way: drop it Go to [htmldrop.app](https://htmldrop.app) and drag an `.html` or `.md` file onto the drop area. You get a live URL immediately — no account, no email. Anonymous drops are capped at **2 MB** and stay live for **7 days**. Sign up free at any point and the drops made in that browser are claimed into your account automatically. ## From an AI agent If your assistant speaks [MCP](/agents/overview), it can publish on your behalf. Connect once: ```bash theme={null} claude mcp add --transport http htmldrop https://htmldrop.app/mcp ``` Then just ask: > Publish this page with htmldrop The agent calls `htmldrop_publish` and replies with the live URL. Setup guides: [ChatGPT](/agents/chatgpt) · [Claude](/agents/claude) · [Claude Code](/agents/claude-code) · [Codex](/agents/codex) · [Cursor and Cline](/agents/cursor-and-cline). ## From the command line ```bash theme={null} npx @htmldrop.app/cli login # browser sign-in, once npx @htmldrop.app/cli deploy # publishes the current directory ``` <Warning> The npm packages are **scoped**: `@htmldrop.app/cli` and `@htmldrop.app/mcp`. The unscoped `htmldrop` package on npm is an unrelated third-party project — do not install it. </Warning> ## From the API ```bash theme={null} curl -F "file=@page.html" https://htmldrop.app/api/v1/drops ``` That endpoint is the anonymous one — it needs no credentials and returns a share URL. For anything tied to your account, see [authentication](/api/authentication). # Analytics Source: https://docs.htmldrop.app/sharing/analytics Connect GA4, Meta Pixel, PostHog or Plausible by pasting an ID — no code edits. Paid plans can attach analytics to a drop without touching its markup. Paste an ID in the dashboard and htmldrop injects the snippet when the page is served. | Provider | What to paste | | ------------------ | ------------------------------------------ | | Google Analytics 4 | Measurement ID, e.g. `G-XXXXXXX` | | Meta Pixel | Pixel ID | | PostHog | Project API key (and host, if self-hosted) | | Plausible | The domain configured in Plausible | Because injection happens at serve time, it applies to every page of a folder upload and survives republishing — you never have to remember to re-add a tag. ## Consent banner Cookie-setting trackers (GA4 and Meta Pixel) can trigger a built-in consent banner, available in 12 languages, so a drop aimed at EU visitors can ask before those scripts run. Cookieless setups (PostHog and Plausible in their default configurations) do not require it. <Info> The consent banner covers the trackers **you** attach to your drop. It is separate from htmldrop's own analytics on our marketing site, which is governed by our [privacy policy](https://htmldrop.app/privacy). </Info> ## Built-in numbers Every drop shows basic view counts in the dashboard without any provider attached. The integrations above are for when you want your own tooling, audiences and funnels. # Custom domains Source: https://docs.htmldrop.app/sharing/custom-domains Serve a drop from your own domain, with SSL issued automatically. Paid plans let you serve a drop from a domain you own — `share.yourbrand.com` instead of `slug.htmldrop.app`. SSL is issued and renewed automatically; there is nothing to configure or pay for separately. | Plan | Custom domains | | -------- | -------------- | | Free | — | | Plus | 1 | | Business | 10 | ## Setting one up <Steps> <Step title="Add the domain"> In the dashboard, open the drop, then the domain card, and enter the hostname you want to use. </Step> <Step title="Point DNS at us"> Create the CNAME record shown to you at your DNS provider. If the domain sits behind Cloudflare, leave the record **DNS only** (grey cloud) until verification completes. </Step> <Step title="Wait for verification"> We poll for the record and issue the certificate automatically. This is usually minutes, but DNS propagation can take longer — the card shows the current state throughout. </Step> </Steps> ## Notes * **Bring your own domain.** htmldrop does not sell domains; register yours wherever you like. * **Apex domains** (`yourbrand.com` with no subdomain) depend on your DNS provider supporting ALIAS/ANAME flattening. A subdomain like `share.` or `docs.` always works. * Removing a domain is instant and the drop reverts to its `htmldrop.app` address, which never stops working. # Links and expiry Source: https://docs.htmldrop.app/sharing/links-and-expiry How long a drop stays live, what the 30-day clock actually means, and how claiming works. ## Anonymous drops A drop made without an account lives **7 days** from upload, on a fixed clock that nothing resets. It is capped at 2 MB. Anonymous drops are remembered per browser through a signed cookie, which is how the homepage can still list yours when you come back — and how they get claimed if you sign up later. ## Free accounts Free drops stay live **as long as you are active**. Each time the owner signs in, a 30-day clock resets. If a full 30 days pass with no sign-in, we email you before anything expires, and expired drops can be recovered by signing back in before they are removed for good. <Info> The clock is reset by **owner activity**, not by visitors. A page getting traffic does not keep it alive; you signing in does. </Info> ## Paid plans Drops never expire. No clock, no email, no recovery window to worry about. ## Claiming anonymous drops Sign up from the same browser and every anonymous drop made there is moved into your account automatically — same URLs, no re-upload. This is why the homepage shows a "Sign up & claim" prompt once you have drops. ## Deleting Deleting a drop is immediate and permanent: the URL stops resolving and returns a branded page explaining it is gone. Anyone still holding the link sees that page, not your content. # Password protection Source: https://docs.htmldrop.app/sharing/password-protection Put a single password in front of a drop — for client drafts and anything not meant to be found. Paid plans can require a password before a drop renders. Visitors get a simple unlock screen; there are no accounts to create and nothing for them to install. Use it for client drafts, internal documents, early ideas — anything you want to send to specific people rather than publish to the world. ## Turning it on Open the drop in the dashboard, set a password on the protection card, and save. It applies immediately, including to links already shared. Once unlocked, a visitor stays unlocked for that drop in that browser, so they are not asked again on every page of a multi-page bundle. ## What it is and isn't <Info> Password protection is **access control, not encryption**. It stops casual and search-engine access to a URL; it is not a substitute for encrypting genuinely sensitive material. Protected drops are excluded from search indexing and from the public gallery. </Info> A protected drop's URL is still a normal URL — anyone who has both the link and the password can open it, and they can pass both on. Rotate the password if a draft's audience changes.