fmrl API v1
Publish a page from any agent or script and get a link. No account: mint a key, publish, share the URL. Pages last seven days unless someone keeps them from the page itself. Base URL: https://fmrl.site/api/v1.
Install the plugin instead
Claude Code:
/plugin marketplace add toogreatwtf/fmrl-plugin
/plugin install fmrl@fmrl-plugin
Any MCP client (Claude Desktop, Cursor, Codex, Windsurf, and the rest):
{ "mcpServers": { "fmrl": { "command": "npx", "args": ["-y", "fmrl-mcp"] } } }
No key step. The server mints a key the first time it publishes and keeps it in your config directory (~/.config/fmrl/credentials.json, or %APPDATA%\fmrl\credentials.json on Windows). FMRL_API_KEY overrides it; FMRL_API_URL points the server at another host (default https://fmrl.site).
curl
curl -sX POST https://fmrl.site/api/v1/keys
curl -sX POST https://fmrl.site/api/v1/publish \
-H "Authorization: Bearer fmrl_…" -H "Content-Type: application/json" \
-d '{"format":"md","content":"# hello"}'
Conventions
JSON in and out. Every route except key minting takes Authorization: Bearer fmrl_…. Errors are {"error": {"code": "…", "message": "…"}} with the HTTP status.
| Status | code |
When |
|---|---|---|
| 400 | bad_request |
malformed JSON, missing content, a format other than html or md, a label over 64 characters, or encrypted with format md |
| 401 | invalid_key |
missing, malformed, or unknown key |
| 402 | quota_exhausted |
the key's free publishes for the month are used; error.resets_at says when it resets |
| 404 | not_found |
no such page, an expired or removed page, or a page this key does not own (on delete) |
| 413 | too_large |
content over 2 MiB |
| 422 | rejected |
the content failed the same checks the paste form runs; the message names the top signal. A body sent with encrypted: true that isn't a valid MARKYENC v2 envelope also answers rejected here, naming no signal |
| 429 | rate_limited |
too many keys from one network, or too many publishes from one address; Retry-After is set |
Routes
POST /api/v1/keys
Body optional: {"label": "…"} (64 characters at most). No authentication. Limited to 5 keys a day per network.
201 {"key": "fmrl_…", "prefix": "fmrl_ab12", "created_at": "…", "quota": {"publishes": 25, "period": "month"}}
The key is shown once. Keep it; only its hash is stored.
POST /api/v1/publish
Body: {"format": "html" | "md", "content": "…", "title": "…", "encrypted": false}. format may be left out and is detected from the content. title overrides the one taken from the content.
201 {"id": "…", "url": "https://fmrl.site/{id}", "raw_url": "https://fmrl.site/{id}/raw",
"manage_url": "https://fmrl.site/manage/{id}#k=…", "expires_at": "…", "status": "live" | "reviewing"}
url is the page. manage_url removes it from any browser; hand it to a person if they should be able to. raw_url is informational: the raw route answers only our viewer, so keep your own copy of the bytes. A reviewing status means a content check is still finishing; the page shows a short interstitial until it does. Publishing runs the paste form's checks with the human challenge skipped. Only an accepted publish counts against the quota.
Private pages. Send "encrypted": true with content set to a MARKYENC v2 envelope you sealed yourself: the text MARKYENC followed by {"v":2,"alg":"aes-256-gcm","kdf":"none"|"pbkdf2","salt":"…","nonce":"…","data":"…"}, AES-256-GCM with a 12-byte nonce, the 16-byte tag appended to data, fields in standard base64. With kdf none the key is 32 random bytes; hand readers url plus #p=<key as 43 base64url characters> — the server never sees the key and does not add the fragment. With kdf pbkdf2 the key is PBKDF2-HMAC-SHA256 over a passphrase, 600,000 iterations, 16-byte salt, and readers type the passphrase into the page. The plaintext must be a complete HTML document (render Markdown first); format must be html or left out, and title is ignored. The 2 MiB cap applies to the envelope, so about 1.4 MB of HTML fits. A private page has no title or preview, is not scanned, and is removed on a credible report without review. There is no key recovery.
GET /api/v1/docs/{id}
Any valid key may read any page the viewer would show.
200 {"id": "…", "url": "…", "status": "live" | "reviewing" | "pinned", "format": "html" | "md", "size": 1234,
"expires_at": "…" | null, "pinned": false, "cid": "bafy…"}
expires_at is null and pinned is true once someone has kept the page; cid is present only then.
DELETE /api/v1/docs/{id}
Owner key only: the key that published the page. 204 on success. Same effect as the manage link: the page answers 410 from now on, the bytes are deleted, and a kept page is unpinned if nothing else holds its CID. No refund.
GET /api/v1/me
200 {"prefix": "fmrl_ab12", "created_at": "…", "quota": {"publishes": {"used": 3, "limit": 25, "resets_at": "…"}}}
Limits
25 publishes a month per key, resetting on the first of the month (UTC). 5 keys a day per network. 2 MiB per page. Pages expire after seven days unless kept from the page itself. Content is subject to the acceptable use policy; anything the checks reject, or a report takes down, stays down.
Keeping a page, stashing bytes, and metered plans are not on the API yet.