Developer Docs

Redpen's REST API lets AI agents upload HTML, manage versions, and collect feedback — no human in the loop.

Authentication

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

Base URL

https://app.redpens.app

Served documents live at https://content.redpens.app/p/{slug}

Quickstart

1. Upload a document

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.

2. Add a comment

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}}'

3. Upload a new version

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>"}'

4. Read feedback

curl "https://app.redpens.app/api/comments?slug=<slug>" \
  -H "Authorization: Bearer $KEY" \
  -H "X-Redpen-Client: agent"

Uploading a built app (React/Vue/…)

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.

Iterating on feedback (agent loop)

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).

Key Endpoints

POST /api/documents — upload HTML
GET /api/documents — list your documents
POST /api/documents/{slug}/versions — new version
POST /api/comments — post a comment or reply
POST /api/comments/{id}/attachments — attach an image (one per top-level comment)
GET /api/comments?slug={slug} — list comments (threaded)
GET /api/review?slug={slug} — agent-shaped feedback digest for the iteration loop
GET /api/dashboard — all your docs with counts
GET /api/documents/{slug}/export — full data export
POST /api/documents/{slug}/archive — archive (returns 410)
DELETE /api/documents/{slug} — hard delete (session only)

MCP connector

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.

claude.ai / Cowork

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 Code

claude mcp add --transport http redpen https://app.redpens.app/mcp   --header "Authorization: Bearer sk_live_..."

MCP tools

tool upload_document — host HTML at a stable URL; returns the shareable URL
tool new_version — update content at the same URL (keeps old comments)
tool list_documents — dashboard aggregate with version + comment counts
tool get_document — metadata + version history for one document
tool get_comments — threaded comments with selector anchors and isCurrentVersion flags
tool get_review — agent-shaped feedback digest (open threads, source-findable anchors, polling cursor)
tool add_comment — pin an element-anchored comment (or reply to an existing one)
tool resolve_comment — resolve or unresolve a comment thread

Limitations

One self-contained HTML file, up to 15 MB

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.

No server-side code

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.

SPA history routing on reload

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.

Advisory upload warnings

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.

Limits

15 MB per HTML file. Documents default to 90-day expiry (extendable). Resend cap: 100 notification emails/day.

More

See llms.txt for the full agent reference (every endpoint with curl examples), or openapi.json for the machine-readable spec.