# pastehtml.dev > Publish a single self-contained HTML or Markdown file and get a private, > shareable link served on its own isolated origin. Anonymous publishing needs > no key; an account API key (prefix pht_) publishes into a user's dashboard and > folders. This is the agent integration guide; the complete machine reference > is at https://pastehtml.dev/llms-full.txt. Publish a single HTML file, get a private shareable link. Anonymous API publishing needs no account key. If the user gives you a pastehtml.dev account API key, you can publish into their account and target a folder. This file is the integration guide for AI agents. When a user says "publish this on pastehtml.dev" or "paste it at pastehtml.dev", follow this contract. ## Publish POST https://pastehtml.dev/api/pastes Two equivalent body forms: # Multipart file upload curl -F "file=@plan.html" https://pastehtml.dev/api/pastes # Raw HTML body (filename is optional, defaults to untitled.html) curl --data-binary @plan.html -H "Content-Type: text/html" \ "https://pastehtml.dev/api/pastes?filename=plan.html" Response 201: { "token": "...", // the paste's public ID "title": "...", // extracted from the document's "custom_subdomain": null, // or the claimed custom subdomain "password_protected": false, "views_count": 0, "folder": null, // or { "id": 123, "name": "Roadmap" } "owner": null, // or { "id": 123 } for account-owned pastes "account_paste": false, "live_url": "https://<token>.pastehtml.dev/", // the SHARE LINK: the // document itself, on its own isolated origin // (scripts and localStorage work there) "url": "https://pastehtml.dev/p/<token>", // inspector page: copy // link, rendered preview, highlighted source "raw_url": "https://pastehtml.dev/p/<token>/raw", // the paste's exact // bytes as text/plain -- fetch this to read a // paste's source back; it is never altered in transit "render_url": "https://pastehtml.dev/p/<token>/render", // the SAME bytes // served as text/html (renders in a CSP sandbox) "markdown_url": "https://pastehtml.dev/p/<token>/markdown", // the paste // converted to Markdown (text/markdown); a lossy, // best-effort prose view, not the canonical bytes "update_token": "..." // secret; revealed ONLY here, never recoverable. // Valid while the paste stays anonymous (a normal // update does NOT rotate it); claiming the paste // into an account retires it -- the account key // becomes the sole update credential. } 422 with { "errors": [...] } when the content or options are invalid. Anonymous publishes may set password=<secret>; custom_subdomain=<letters-numbers-hyphens> requires an account API key (see below), so memorable origins can't be squatted anonymously. Account-key publishes return the same paste fields but omit update_token; use the account API key to update account-owned pastes. ## Publish into a user account with an API key If the user provides an account API key, send it in a header: Authorization: Bearer pht_... or: X-PasteHTML-API-Key: pht_... Then POST /api/pastes normally. The paste is saved to the key owner's account and appears in their dashboard. Some keys may be scoped to a default folder; those keys always publish there, can update only pastes already in that folder, reject attempts to target a different folder, only list their scoped folder, and cannot create unrelated folders. To place an unscoped key's paste in a folder, pass exactly one of: folder_name=Roadmap # finds or creates that folder under the account folder_id=123 # uses an existing folder owned by the account Missing or invalid keys return 401 when a folder is requested. Invalid/revoked keys are never silently treated as anonymous publishing. Discover folders for a key. Unscoped keys list every folder in the account; folder-scoped keys list only their scoped folder: curl -H "Authorization: Bearer pht_..." https://pastehtml.dev/api/folders Create a folder explicitly with an unscoped key. Nested form params and a top-level `name` are both accepted; folder-scoped keys get 403 on creation: curl -X POST -H "Authorization: Bearer pht_..." \ -d "folder[name]=Roadmap" https://pastehtml.dev/api/folders curl -X POST -H "Authorization: Bearer pht_..." \ -d "name=Roadmap" https://pastehtml.dev/api/folders ## MCP server (OAuth, no API key) If you are a coding agent that speaks the Model Context Protocol, you can skip the HTTP API above and connect to the remote MCP server instead. It lives at https://pastehtml.dev/mcp (Streamable HTTP) and authorizes over OAuth in the user's browser -- there is no pht_ key to handle. # Claude Code claude mcp add --transport http pastehtml https://pastehtml.dev/mcp # Codex codex mcp add pastehtml --url https://pastehtml.dev/mcp The first call opens a browser consent screen; once the user approves it, the agent acts inside their account. Auth is OAuth 2.1 + PKCE with Dynamic Client Registration (no client secret), scopes mcp:read, mcp:pastes:write, and mcp:folders:write; the user manages or revokes agents under "Connected agents". Ten tools are exposed -- pastes are permanent, so there is no delete-paste tool: create_paste publish a new HTML or Markdown paste (optionally into a folder) update_paste republish an existing paste's content (overwrites it) configure_paste change a paste's password, custom subdomain, or folder get_paste fetch one paste's metadata, URLs, and stored content get_paste_stats aggregate view analytics for a paste list_pastes page through the account's pastes (optionally by folder) list_folders list folders with their paste counts create_folder create a new, empty folder rename_folder rename a folder delete_folder delete a folder (its pastes survive, unfiled) Everything else in this guide -- self-contained documents, the 2 MB paste limit, always showing the user the live_url -- applies equally when publishing over MCP, with one encoding caveat. A whole MCP request is capped at 8 MB, and a paste's content travels as a JSON string, which escaping expands: quotes and backslashes double, and some JSON encoders emit six-byte \uXXXX escapes for the characters < > &. Typical MCP clients leave < > & raw, so they publish up to the full 2 MB comfortably. A client whose encoder escapes < > & should keep a paste that is heavy in those characters below ~1.3 MB to stay within the request cap. ## Publish Markdown Upload Markdown and the server renders it into a styled, self-contained HTML page in the pastehtml.dev look -- GitHub-flavored (tables, task lists, strikethrough, footnotes, autolinks) with fenced `mermaid` blocks rendered as diagrams. Markdown is detected by a .md/.markdown filename or a text/markdown content type: # Multipart file upload curl -F "file=@notes.md" https://pastehtml.dev/api/pastes # Raw Markdown body curl --data-binary @notes.md -H "Content-Type: text/markdown" \ https://pastehtml.dev/api/pastes The stored paste IS HTML, so the response is exactly the same shape as an HTML publish and every URL (live_url, url, raw_url, render_url, markdown_url) works as usual. Raw HTML embedded in the Markdown is dropped -- use the plain HTML upload above if you need arbitrary markup. ## Update A paste can be changed by its update_token, or by an account API key that owns it. Account-key creation does not reveal a new update_token, so keep using the account key for future revisions unless the user gave you a separate update_token: curl -X PATCH -H "Authorization: Bearer <update_token>" \ --data-binary @plan.html -H "Content-Type: text/html" \ https://pastehtml.dev/api/pastes/<token> When Authorization is already used for an account API key and you also have an anonymous paste's update_token, send the paste secret as `X-Update-Token: ...`; that lets the agent claim the anonymous paste into the account and optionally move it with folder_name, folder_id, or clear_folder. Accepts the same two body forms as publishing plus custom_subdomain, password, clear_password=1 (remove password protection), folder_id, folder_name, and clear_folder=1 (move an owned paste back to All pastes). Responses: 200 with the refreshed { token, title, custom_subdomain, folder, owner, account_paste, password_protected, views_count, live_url, url, raw_url, render_url, markdown_url }, 403 for a wrong or missing update token or for an account API key that does not own the paste, 404 for an unknown paste, 422 for invalid content or options. An account API key can update pastes owned by that account; use clear_folder=1 to move an owned paste back to All pastes. ## Read a paste back GET https://pastehtml.dev/p/<token>/raw Returns the paste's exact bytes as text/plain -- use this when you need to read back what was published (for example to diff before an update). The render_url and live_url variants serve the same content as text/html and can be rewritten by a CDN in transit, so prefer /raw for byte-exact reads. Password-protected pastes require the password gate before raw, render, markdown, or live content is served. ## Recipes for common requests Two published guides cover variations that need a specific technique. Each is a live, self-contained page that walks through it end to end and ships an in-browser tool. Read the guide to reproduce the approach, build the resulting HTML, and publish it through the API above -- or just share the guide link with the user. - Password-protected / encrypted ("safe") HTML page -- the user wants a page only someone with the password can open, encrypted in the browser so pastehtml.dev only ever stores ciphertext: https://pastehtml.dev/lock-it-up - Publish a Markdown (.md) file as a rendered HTML page -- the user hands you Markdown and wants a styled, self-contained page. Simplest path is to just upload the .md (see "Publish Markdown" above); this guide is the no-server alternative that builds the page in the browser and adds a live preview: https://pastehtml.dev/mark-it-down ## Rules - HTML or Markdown: .html, .htm, .md, or .markdown, UTF-8, at most 2 MB. - Make the document self-contained: inline CSS/JS or load from CDNs. Give it a meaningful <title>; it becomes the paste's display title. - Each paste runs on its own origin (the live_url subdomain), fully isolated from other pastes and from pastehtml.dev itself. Scripts and localStorage work there -- documents that track state (for example review-progress checklists) should persist via localStorage and tell readers to open the live_url. - Pastes can never be deleted, by anyone. Random links are unguessable and excluded from search engines; optional paste passwords add a simple access gate, but never publish secrets unless the document itself encrypts them. - API paste publish/update rate limit: 20 requests per minute and 1,000 per day per IP. Folder discovery/creation also requires an account API key and is rate limited. - Account API keys start with pht_. Treat them as passwords; never put them in the published HTML, paste content, URLs, or user-visible final answer. ## Agent etiquette 1. After publishing or updating, always show the user the `live_url` -- the document link is the whole point of the task. Mention `url` too when the user may want the source view. 2. If the user supplied an account API key, use it from your secret store or the user's message only for the request. Do not reveal it back. 3. If a response includes an `update_token`, save it somewhere durable for the project (for example a file like `.pastehtml/<token>.update-token`, or your task notes) so a later "update that doc" request works. Account-key publishes may omit it; in that case use the account key for revisions. 4. When asked to revise an already-published document, PATCH the existing paste instead of publishing a duplicate, so the user's shared link stays current.