BrewPage API — REST Reference

Publish HTML, Markdown, files, KV entries, JSON documents, and multi-file sites over HTTPS. No signup required to publish. Keep the X-Owner-Token or the management link from the response to edit or delete later.

API docs & tools

Human-oriented walk-through of every resource: /help.

Basics — URL, auth, limits

Base URL
https://brewpage.app/api/*
Publish auth
None. Every POST returns ownerToken and manageUrl; send the token as X-Owner-Token to edit or delete later.
Namespace
Send ns=public for anything meant to be found or shared (docs, guides, demos, portfolios, reports, landing pages): listed in the public gallery and sitemap, so Google, Bing and AI search index it and people discover it. Public pages are served right on the brewpage.app domain, which gives them the strongest reach in search; a page whose code needs its own origin (cookies, storage) gets its own subdomain automatically. Anyone can find and read a public page: never put secrets, tokens or personal data there. No ns (default): private. Unlisted and not indexed; anyone who has the link can still open it. Public pages, sites and files are listed; JSON and KV are never listed or indexed. BrewPage MCP generates an unlisted namespace when omitted; pass public explicitly to list it. Other namespaces are excluded from the gallery, sitemap and search. Anyone with an unpassworded link can read it; an unlisted address is not a secret.
Password
Optional X-Password header for access-gated content. Programmatic readers use this header on apex API endpoints. New browser links stay clean and use trusted unlock; legacy ?p= behavior is unchanged.
Admin
/api/admin/* requires X-Admin-Password. Not for end-user traffic.
Rate limits
Uploads & reads are rate-limited per IP. 429 responses include Retry-After. Details: /help#rate-limits.
User-Agent
Required on every request. Format AgentName/version.
TTL
15 days default, 30 days max (90 with a profile token). Override with ttl query param or body field. Expired content is permanently deleted by the nightly cleanup job.

Quick start — curl

Publish HTML (public)

curl -X POST "https://brewpage.app/api/html?ns=public" \
  -H "Content-Type: application/json" \
  -d '{"content":"<h1>Hello</h1>"}'

Response: {"id":"aB3xK9mP2q","namespace":"public","link":"https://brewpage.app/public/aB3xK9mP2q","ownerToken":"...","manageUrl":"..."}. Save ownerToken.

Upload a file

curl -X POST "https://brewpage.app/api/files?ns=public" \
  -F "file=@report.pdf"

Write a KV entry

curl -X POST "https://brewpage.app/api/kv?ns=public" \
  -H "Content-Type: application/json" \
  -d '{"key":"config","value":"v1"}'

Store a JSON document

curl -X POST "https://brewpage.app/api/json?ns=public" \
  -H "Content-Type: application/json" \
  -d '{"title":"Note","body":"..."}'

Update in place (PUT)

Already published? Replace the content without changing the short URL — PUT /api/html/{ns}/{id}, PUT /api/json/{ns}/{id}, PUT /api/kv/{ns}/{id}/{key}. Send the original X-Owner-Token.

curl -X PUT https://brewpage.app/api/html/public/aB3xK9mP2q \
  -H "Content-Type: application/json" \
  -H "X-Owner-Token: $OWNER_TOKEN" \
  -d '{"content":"<h1>v2</h1>"}'

Multi-file sites republish the same way — PUT /api/sites/{ns}/{id} with your X-Owner-Token does a full replace of the file set, keeping the same short URL and ID.

curl -X PUT https://brewpage.app/api/sites/public/aB3xK9mP2q \
  -H "X-Owner-Token: $OWNER_TOKEN" \
  -F "archive=@site-v2.zip"

Immutable on update: filename, showTopBar. To change those, delete and recreate. HTML and site updates: ?tags= (sites: form field) replaces the stored tags; omit it to keep them. Token workflow: /help#owner-token.

Old publications (made before 5 October 2026) are read-only: 409 OLD_PUBLICATION_READ_ONLY; POST the content to its createUrl (same namespace) and report only the new reader URL. Keep the owner token private; never paste it into chat. HTML also adds republishUrl (browser). The old link keeps working; delete still works.

Owner actions

With X-Owner-Token: hide from gallery & search, change link, reset management link, set a password, extend 90 days (profile token), per-page stats. Profile tokens: scope publish or admin (also manages tokens), optional expiry at creation. Endpoints: OpenAPI. Guide: /help#manage.

Publication hosting

New publications with persisted routingCohort: "new-v1" support Domain (path, a branded brewpage.app link) and Subdomain (subdomain, the publication's own BrewPage subdomain). Sites and HTML pages in a non-public namespace always get a subdomain; an explicit path is still published there, with deliveryModeMatched: false and a deliveryNotice. Other publications default to the BrewPage path address, including password-protected ones, and may choose a subdomain. Public pages without a password can be indexed by search engines in both modes. Send optional X-Delivery-Mode: path|subdomain on HTML, Markdown, file, JSON, KV or site creation. Any kind may use a subdomain: HTML pages and sites are served there as authored documents, every other kind opens in the same BrewPage viewer (top bar, Download) on either address. This header is transport metadata, never a field added to an authored JSON document or upload. PUT keeps the address mode and ignores the header (with a notice); a PUT that makes a non-public BrewPage-link page HTML moves it to a subdomain, and the old link redirects there.

POST /api/html without X-Delivery-Mode: a public HTML or Markdown page whose own inline code clearly needs browser features a BrewPage link (path) does not provide may be published on its own subdomain. The response then carries deliveryModeReason: "auto:<codes>", a plain-text deliveryNotice and a subdomain link; requestedDeliveryMode stays null. Codes are comma-separated: storage, cookies, history, downloads, top-navigation, media-capture (camera, microphone, screen or location), pointer-lock, embeds, fullscreen. An explicit header always wins; with path, and on PUT or an idempotent duplicate (neither changes the address), the response may carry deliveryModeReason: "advisory:<codes>" and a notice instead. Both fields are absent when nothing applies, and not every such page is recognized. To move a page back, change its hosting with "deliveryMode":"path".

Follow the returned link; do not construct a hostname from the mixed-case resource ID. Hosts use a separate permanent lowercase identifier. New responses include managementLink, routingCohort, deliveryMode, requestedDeliveryMode, effectiveDeliveryMode, deliveryModeMatched, access, modeLocked and hostingVersion. Mode and version mismatches are reported, not rejected: the request does the closest valid thing, returns the real link, and adds a plain-text deliveryNotice with a deliveryModeReason code (both absent when nothing applies). Unrequested modes have null request/match fields. access is public, unlisted or protected. Management and owner API links remain on the apex.

A create request can return an existing publication. Its identity, cohort, mode and link remain those of the existing publication; a requested mode does not change it. Matching between new publications is supported; matching against legacy publications is not guaranteed. Legacy publications retain their URLs and behavior, have locked hosting, and report routingCohort: "old" with null mode/version/management metadata.

Owner-only GET /api/{type}/{ns}/{id}/hosting supports html|files|json|kv|sites. Owner-only PATCH at the same path supports html|files|json|kv for new publications without a required subdomain; sites, forced pages and legacy publications answer 200 unchanged with a notice. Both use X-Owner-Token and return hosting metadata with Cache-Control: private, no-store.

Change a new publication to Subdomain

curl -X PATCH https://brewpage.app/api/html/public/aB3xK9mP2q/hosting \
  -H "Content-Type: application/json" \
  -H "X-Owner-Token: $OWNER_TOKEN" \
  -d '{"deliveryMode":"subdomain","expectedVersion":1}'

Read the current version first and send it as expectedVersion. A real change increments it; a same-mode request with the current version leaves it unchanged. Switching either way retains the same host reservation and owner/content/TTL. A stale or missing version is not rejected: the change applies to the stored version (a stale one adds a deliveryNotice). Incorrect owner proof returns 403 OWNER_AUTHORIZATION_FAILED; an unknown mode or a malformed version (not a positive integer) returns 400; unavailable publications return 404. While new hosting is paused, real changes return 503 HOSTING_CREATION_PAUSED with Retry-After: 1; current-version no-ops still succeed.

Subdomain hosts serve publication content only, with no platform API. HTML pages and site HTML documents run natively (scripts, modules, forms, navigation; no sandbox, no rewriting) and carry the small BrewPage attribution badge; nothing else is injected. Files, JSON and KV are never modified. For Markdown, code/text, files, JSON and KV a browser document navigation to the subdomain returns the BrewPage viewer; any other request receives the stored bytes as before. One dedicated host serves the entire new site bundle, including every page and asset; it is not shared across a user's sites. Sites retain native assets, directory indexes and multipage links; missing paths return 404, without SPA fallback. New subdomain short URLs redirect to the returned host; active API documents use safe attachment disposition. New p query parameters do not authenticate and are removed before trusted unlock. BrewPage-mode HTML uses stricter script, cookie, embed and navigation restrictions. See hosting and password limits before choosing a mode.

Revoke existing browser reader access with owner-only POST /api/{type}/{ns}/{id}/access/revoke, using X-Owner-Token. Supported types: html|files|json|kv|sites. Protected new publications return 200 {"revoked":true}; readers must unlock again. Content, password, host and hosting version stay unchanged, and other publications' grants remain usable. Legacy or unprotected publications return 400 ACCESS_REVOKE_UNAVAILABLE; incorrect owner proof returns 403. This cannot erase previously downloaded or offline content.

Resources & limits

Resource Endpoint Size Per-NS limit
HTML POST /api/html 5 MB —
Markdown POST /api/html?format=markdown 5 MB —
File POST /api/files 5 MB (video 20 MB, audio 5 MB) 1 000 files
Site (multi-file) POST /api/sites 20 MB total (uncompressed) · 5 MB/file · 200 files —
Site republish PUT /api/sites/{ns}/{id} 20 MB total (uncompressed) · 5 MB/file · 200 files —
KV entry POST /api/kv 1 MB per value 1 000 keys
JSON document POST /api/json 1 MB per doc 10 000 docs / collection

Each upload request: up to 25 MB. Site uploads skip system files such as .DS_Store and __MACOSX/; they don't count toward the limits. Current values: GET /api/limits.

TTL: default 15 days, max 30 days for every resource (90 with a profile token). After TTL the nightly cleanup job permanently deletes content — no recovery.

Integrate your AI agent

Three ways, easiest first:

SK Claude Code skill — brewdoc:publish docs →
Install the BrewPage publish skill in Claude Code:
  /plugin marketplace add kochetkov-ma/claude-brewcode
  /plugin install brewdoc@claude-brewcode
Then run /publish to share the current document.
MCP MCP server — brewpage-mcp docs →
Install the BrewPage MCP server for my agent:
  npx -y brewpage-mcp@1.16.8
Then register it under "brewpage" in my MCP config and restart.
No install: add https://brewpage.app/mcp as a remote MCP server (no auth).
API Direct REST — one-liner for any agent docs →
curl -sS -X POST https://brewpage.app/api/html \
  -H "Content-Type: application/json" \
  -d '{"content":"<h1>hi</h1>"}' | jq -r .link

Drop this into your agent’s system prompt

When you make an HTML page or report for me, publish it on brewpage.app: POST https://brewpage.app/api/html with header Content-Type: text/html and the raw HTML as the body, then give me the "link" URL from the JSON response. Save ownerToken privately; send it as header X-Owner-Token on later publishes and updates. Add ?ns=public only for content clearly meant for others (docs, guides, demos); if personal, internal or unsure, omit ns (private). My explicit ask wins. Secrets allowed; likely ones are flagged in notices. For a secret: private namespace + password. Links last 15 days, or 30 with ttl=30 in the query. More: https://brewpage.app/llms.txt or https://brewpage.app/api/openapi.json
Works with Claude Code Codex Gemini Cursor and more

LLM integration

Two text files describe the API for LLMs:

FileSizeWhen to use
/llms.txt ~4 KB Short reference — paste into a system prompt so the LLM knows how to call the API.
/llms-full.txt ~30 KB Full reference with every endpoint, field, error code. Use for RAG or long-context agents.
OpenAPI YAML ~25% smaller than JSON Preferred for LLM context. Fewer tokens, same information.
OpenAPI JSON — Programmatic clients, MCP servers, OpenAPI codegen.
Ready-made prompts for Claude / ChatGPT / Cursor

Paste into the system prompt

Use https://brewpage.app/llms.txt as your quick reference for the BrewPage REST API.
When asked to publish a page, file, or JSON document, call the endpoints listed there.

Full-context variant

For the complete endpoint / field / error reference, fetch
https://brewpage.app/llms-full.txt and treat it as the authoritative BrewPage
API spec for this session.

Stats & views

Public aggregates live at GET /api/stats and GET /api/stats/timeseries. No auth.

What counts as a view?

The canonical definition of a view (which HTTP paths increment counters, today vs total, excluded paths) is the Views section of docs/architecture.md. When in doubt, that section is the source of truth.

Live chart with the same numbers: home page stats section.

See also