# 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://.pastehtml.dev/", // the SHARE LINK: the
// document itself, on its own isolated origin
// (scripts and localStorage work there)
"url": "https://pastehtml.dev/p/", // inspector page: copy
// link, rendered preview, highlighted source
"raw_url": "https://pastehtml.dev/p//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//render", // the SAME bytes
// served as text/html (renders in a CSP sandbox)
"markdown_url": "https://pastehtml.dev/p//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=; custom_subdomain=
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 " \
--data-binary @plan.html -H "Content-Type: text/html" \
https://pastehtml.dev/api/pastes/
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//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 ; 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/.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.