Redpen's REST API lets AI agents upload HTML, manage versions, and collect feedback — no human in the loop.
Create an API key at /keys. Pass it as a Bearer token:
Authorization: Bearer sk_live_<your-key>
Scopes: upload (create docs/versions, archive), read (list/get/export), comment (post/resolve comments).
All state-changing requests also need: X-Redpen-Client: agent
https://app.redpens.app
Served documents live at https://content.redpens.app/p/{slug}
curl -X POST https://app.redpens.app/api/documents \
-H "Authorization: Bearer $KEY" \
-H "X-Redpen-Client: agent" \
-H "Content-Type: application/json" \
-d '{"html":"<html><body><h1>Hello</h1></body></html>","title":"My Doc"}'
Uploads require sign-in to view by default. Adding "visibility":"link"
makes the document public — anyone with the URL can open it with no
sign-in, including people outside the company. Only send it when
external sharing is what was asked for.
curl -X POST https://app.redpens.app/api/comments \
-H "Authorization: Bearer $KEY" \
-H "X-Redpen-Client: agent" \
-H "Content-Type: application/json" \
-d '{"slug":"<slug>","body":"Great work!","selector":"h1","rect":{"x":0,"y":0,"w":1,"h":1}}'
curl -X POST https://app.redpens.app/api/documents/<slug>/versions \
-H "Authorization: Bearer $KEY" \
-H "X-Redpen-Client: agent" \
-H "Content-Type: application/json" \
-d '{"html":"<html><body><h1>Hello v2</h1></body></html>"}'
curl "https://app.redpens.app/api/comments?slug=<slug>" \ -H "Authorization: Bearer $KEY" \ -H "X-Redpen-Client: agent"
A bundled single-file app is megabytes, and how it reaches Redpen decides whether that costs nothing or costs a fortune. Passing HTML in the JSON body — or to the MCP upload_document tool — means an agent must generate every character as output tokens: slow, expensive, and often over the model's per-response output limit, so it fails outright.
Instead, let your bundler write the file and upload the file — curl reads it from disk, so the agent spends zero tokens on the content.
# 1. Build ONE self-contained file (vite-plugin-singlefile, webpack, …) npm run build # -> dist/index.html, everything inlined # 2. Upload the file curl -X POST https://app.redpens.app/api/documents \ -H "Authorization: Bearer $REDPEN_API_KEY" -H "X-Redpen-Client: agent" \ -F "file=@dist/index.html" # 3. Iterating? Same URL, comments preserved curl -X POST https://app.redpens.app/api/documents/$SLUG/versions \ -H "Authorization: Bearer $REDPEN_API_KEY" -H "X-Redpen-Client: agent" \ -F "file=@dist/index.html"
Raw body works too: -H "Content-Type: text/html" --data-binary @dist/index.html. Exception: in claude.ai / Cowork sandboxes there's no shell or network egress, so MCP is the only channel — build the file there, then upload it by hand on the home page.
Use GET /api/review to act on comments programmatically. It returns open threads with a source-findable anchor (quote = the element's text, plus reproduceNote/breadcrumbs) — the selector is rendered-DOM, so prefer quote when locating the element in your source. Each response includes a cursor; pass it back as ?since= to fetch only new activity.
# 1. Read open feedback (grab .cursor from the response)
curl "https://app.redpens.app/api/review?slug=$SLUG" \
-H "Authorization: Bearer $KEY" -H "X-Redpen-Client: agent"
# 2. Edit → 3. ship a new version at the same URL
curl -X POST https://app.redpens.app/api/documents/$SLUG/versions \
-H "Authorization: Bearer $KEY" -H "X-Redpen-Client: agent" \
-H "Content-Type: application/json" -d '{"html":"…v2…"}'
# 4. Resolve the addressed thread (optionally reply first with parentId)
curl -X POST https://app.redpens.app/api/comments/$ID/resolve \
-H "Authorization: Bearer $KEY" -H "X-Redpen-Client: agent"
# 5. Poll: only threads with new activity since last time
curl "https://app.redpens.app/api/review?slug=$SLUG&since=$CURSOR" \
-H "Authorization: Bearer $KEY" -H "X-Redpen-Client: agent"
Polling is idempotent — step 5 returns nothing until a new comment or reply lands, so it drops straight into a scheduled task or Claude Code's /loop. No always-on server or webhook is needed (a push trigger is not offered yet; polling covers it).
/api/documents — upload HTML
/api/documents — list your documents
/api/documents/{slug}/versions — new version
/api/comments — post a comment or reply
/api/comments/{id}/attachments — attach an image (one per top-level comment)
/api/comments?slug={slug} — list comments (threaded)
/api/review?slug={slug} — agent-shaped feedback digest for the iteration loop
/api/dashboard — all your docs with counts
/api/documents/{slug}/export — full data export
/api/documents/{slug}/archive — archive (returns 410)
/api/documents/{slug} — hard delete (session only)
Redpen exposes a native MCP (Model Context Protocol) endpoint for claude.ai, Cowork sandboxes, and Claude Code — clients that cannot make direct egress POSTs but whose MCP traffic routes through Anthropic.
Transport: Streamable HTTP (MCP spec 2025-03-26), stateless, JSON responses. No server-initiated SSE or session IDs.
Settings → Connectors → Add custom connector → URL:
https://app.redpens.app/mcp/sk_live_YOURKEY
The URL contains your API key — treat it as a secret. Revoke it at /keys if exposed. The path-key form is required because claude.ai connectors cannot set custom HTTP headers.
claude mcp add --transport http redpen https://app.redpens.app/mcp --header "Authorization: Bearer sk_live_..."
upload_document — host HTML at a stable URL; returns the shareable URL
new_version — update content at the same URL (keeps old comments)
list_documents — dashboard aggregate with version + comment counts
get_document — metadata + version history for one document
get_comments — threaded comments with selector anchors and isCurrentVersion flags
get_review — agent-shaped feedback digest (open threads, source-findable anchors, polling cursor)
add_comment — pin an element-anchored comment (or reply to an existing one)
resolve_comment — resolve or unresolve a comment thread
Each document must be a single HTML file. All CSS, JavaScript, and images must be inlined or referenced via absolute URLs (https://…, data:, //…). There is no multi-file hosting — relative paths like src="assets/app.js" or href="styles.css" will 404. Tools like vite-plugin-singlefile can bundle a project into one inlined file.
Redpen serves static HTML only. Client-side JavaScript calling external CORS-friendly APIs works fine; any server-side logic (PHP, Node handlers, etc.) will not run.
All sub-paths under /p/{slug}/… serve the same document, so reloads after client-side navigation return HTTP 200. Whether the exact deep route is restored depends on how your Vite app configures BrowserRouter (set basename accordingly). Hash-based routing (/#/about) also works and restores the route on reload without any configuration.
When you upload a document, the API checks for relative asset references and <base href> tags. Any issues are returned as a warnings[] array in the response body — uploads are never blocked, but the warnings signal that assets may not load as expected.
15 MB per HTML file. Documents default to 90-day expiry (extendable). Resend cap: 100 notification emails/day.
See llms.txt for the full agent reference (every endpoint with curl examples), or openapi.json for the machine-readable spec.