lucid.page API reference
HTTP API · v1.0.0

API reference.

lucid.page turns Markdown into beautifully typeset, shareable pages. This is the whole HTTP API — rendered from the OpenAPI 3.1 document the server serves, so it cannot drift from reality. Agents may prefer the agent guide or the MCP endpoint.

Base URL https://lucid.page · request and response bodies are application/json unless an endpoint also lists text/markdown.

Authentication

Publishing works anonymously — no account, no key. Anything that touches ownership (updates, listing, bundles, deletion, private pages) needs credentials: an lp_ API key minted from the dashboard via POST /keys, or a Firebase ID token from signing in. Where a request is authenticated, either form below works. Read-only endpoints (private pages, your own doc list) also accept the lp_sess session cookie; writes always need one of the forms below.

SchemeSent asDescription
bearerAuthAuthorization: bearer <token>An lp_ API key (minted via POST /keys) or a Firebase ID token.
apiKeyQueryquery parameter apiKeyAlternative to the Authorization header (also accepts api_key). The header wins when both are present.

Rate limits

Anonymous callers get 10 requests per minute per IP; authenticated accounts get 120 requests per minute. Over the line, endpoints answer 429 with an Error body — back off and retry.

Publishing

Markdown in, live URL out. Anonymous publishing works; a key makes pages owned.

POST /publish #

Publish a Markdown document

Accepts either a raw text/markdown body or a JSON body { markdown, title?, visibility?, ttl? }. Anonymous publishing is allowed; with an lp_ key the document is owned by the account.

Request body application/json required

Schema: PublishRequest

FieldTypeDescription
markdown requiredstringMax 1 MB
titlestring | null
visibilitystringone of private, unlisted, public · default unlisted
ttlintegerLifetime in seconds; omit for no expiry
min 60
namestring | null

Request body text/markdown required

Raw Markdown source as the request body.

Responses

StatusDescriptionSchema
201PublishedPublishResponse
400Empty/invalid body, bad visibility, or ttl < 60Error
401Private visibility without authenticationError
413Document over the 1 MB capError
429Rate limited (10 req/min per IP anonymous, 120 req/min per account)Error
503Index temporarily unavailableError
POST /api/publish #

Publish a Markdown document (alias of POST /publish)

Request body application/json required

Schema: PublishRequest

FieldTypeDescription
markdown requiredstringMax 1 MB
titlestring | null
visibilitystringone of private, unlisted, public · default unlisted
ttlintegerLifetime in seconds; omit for no expiry
min 60
namestring | null

Request body text/markdown required

Raw Markdown source as the request body.

Responses

StatusDescriptionSchema
201PublishedPublishResponse
POST /api/docs/{slug}/claim #

Claim an anonymously published page into your account

Requires the one-time claim_token returned by an anonymous publish. The bearer account becomes the owner; the token is single-use. Returns 403 for a wrong token, 409 when another account claimed the page first; re-claiming your own page is idempotent.

Parameters

NameInTypeDescription
slug requiredpathstringmatches ^[a-z0-9][a-z0-9-]{0,80}$

Request body application/json required

FieldTypeDescription
claim_token requiredstring

Responses

StatusDescriptionSchema
200Page claimed (or already yours)object
401Authentication requiredError
403Invalid claim tokenError
404Not foundError
409Already claimed by another accountError

Documents

Update, read back, list, and remove pages you own.

POST /{slug} #

Update a document you own

Replaces the Markdown source; the URL is stable and every update is stored as a revision. Requires an lp_ API key or session.

Parameters

NameInTypeDescription
slug requiredpathstringmatches ^[a-z0-9][a-z0-9-]{0,80}$

Request body application/json required

FieldTypeDescription
markdown requiredstring
titlestring | null

Request body text/markdown required

Raw Markdown source as the request body.

Responses

StatusDescriptionSchema
200UpdatedUpdateResponse
401Authentication requiredError
404Not found (or owned by someone else)Error
DELETE /{slug} #

Delete a document you own

Parameters

NameInTypeDescription
slug requiredpathstringmatches ^[a-z0-9][a-z0-9-]{0,80}$

Responses

StatusDescriptionSchema
200Deletedobject
401Authentication requiredError
404Not found (or owned by someone else)Error
GET /raw/{slug} #

Fetch the canonical Markdown source of a page

Parameters

NameInTypeDescription
slug requiredpathstringmatches ^[a-z0-9][a-z0-9-]{0,80}$

Responses

StatusDescriptionSchema
200The Markdown sourcestring (text/markdown)
404Not found, expired, or a private page without owner credentialsError
451Removed after a takedownError
GET /me/docs #

List documents owned by the account

Parameters

NameInTypeDescription
limitqueryintegerdefault 50 · min 1 · max 200
cursorquerystringThe nextCursor value from a previous response

Responses

StatusDescriptionSchema
200A page of documents, newest firstDocList
401Authentication requiredError
POST /api/docs/{slug}/attribution #

Show or hide your byline on a page you own

owner-only. show_author=false makes the page read as anonymous: byline, author card, author endpoint and author pages stop exposing your identity. Ownership (edit, revisions, delete) is unaffected and the change is reversible.

Parameters

NameInTypeDescription
slug requiredpathstringmatches ^[a-z0-9][a-z0-9-]{0,80}$

Request body application/json required

FieldTypeDescription
show_author requiredboolean

Responses

StatusDescriptionSchema
200Attribution updatedobject
400show_author must be a booleanError
401Authentication requiredError
404Not found, or owned by another accountError

Bundles

Ordered, multi-chapter collections of your pages.

POST /bundles #

Create a multi-chapter bundle from documents you own

Request body application/json required

Schema: BundleRequest

FieldTypeDescription
titlestring
visibilitystringone of private, unlisted, public · default unlisted
markdownstringLanding-page introduction
itemsobject[]at most 200 items
items[].doc_slug requiredstring
items[].path_slugstring
items[].nav_titlestring

Responses

StatusDescriptionSchema
201CreatedBundleResponse
400Invalid title, visibility, or chapter listError
401Authentication requiredError
GET /bundles/{slug} #

Fetch a bundle you own, including its chapter list

Parameters

NameInTypeDescription
slug requiredpathstringmatches ^[a-z0-9][a-z0-9-]{0,80}$

Responses

StatusDescriptionSchema
200The bundleobject
401Authentication requiredError
404Not foundError
POST /bundles/{slug} #

Update a bundle's title, visibility, intro, or chapters

Parameters

NameInTypeDescription
slug requiredpathstringmatches ^[a-z0-9][a-z0-9-]{0,80}$

Request body application/json required

Schema: BundleRequest

FieldTypeDescription
titlestring
visibilitystringone of private, unlisted, public · default unlisted
markdownstringLanding-page introduction
itemsobject[]at most 200 items
items[].doc_slug requiredstring
items[].path_slugstring
items[].nav_titlestring

Responses

StatusDescriptionSchema
200UpdatedBundleResponse
401Authentication requiredError
404Not foundError
DELETE /bundles/{slug} #

Delete a bundle you own (chapters themselves are kept)

Parameters

NameInTypeDescription
slug requiredpathstringmatches ^[a-z0-9][a-z0-9-]{0,80}$

Responses

StatusDescriptionSchema
200Deletedobject
401Authentication requiredError
404Not foundError
GET /me/bundles #

List bundles owned by the account

Parameters

NameInTypeDescription
limitqueryintegerdefault 50 · min 1 · max 200
cursorquerystring

Responses

StatusDescriptionSchema
200A page of bundles, newest firstobject
401Authentication requiredError

API keys

Mint, list, and revoke lp_ keys.

POST /keys #

Mint an lp_ API key (requires a Firebase ID token from sign-in)

Responses

StatusDescriptionSchema
201The key, shown exactly onceobject
401Missing or invalid Firebase ID tokenError
GET /keys #

List your API keys (ids and prefixes only)

Responses

StatusDescriptionSchema
200Your keysobject
401Authentication requiredError
DELETE /keys/{keyId} #

Revoke an API key

Parameters

NameInTypeDescription
keyId requiredpathstringmatches ^[0-9a-f]{64}$

Responses

StatusDescriptionSchema
200Revokedobject
401Authentication requiredError
404Not foundError

Billing

Plan status, checkout, and the billing portal.

GET /billing/me #

Plan and subscription status for the account

Responses

StatusDescriptionSchema
200Account statusBillingMe
401Authentication requiredError
POST /billing/checkout #

Create a checkout session for a paid plan

Request body application/json

FieldTypeDescription
planstringone of pro, team

Responses

StatusDescriptionSchema
200Checkout URLobject
503Checkout unavailable (plan price not configured, or payments unreachable)Error
POST /billing/portal #

Create a billing-portal session

Responses

StatusDescriptionSchema
200Portal URLobject
401Authentication requiredError

Discovery

Browse recently published public pages.

GET /api/discover #

Browse recently published public pages

Lists public, unexpired pages newest-first. No authentication required; rate limited per IP.

Parameters

NameInTypeDescription
limitqueryintegerdefault 20 · min 1 · max 50
cursorquerystringThe nextCursor value from a previous response

Responses

StatusDescriptionSchema
200A page of public documents, newest firstDiscoverList
429Rate limited (10 req/min per IP)Error
503Index temporarily unavailableError

Analytics

Views, referrers, and reader geography for your pages.

GET /api/analytics #

View analytics for your pages

Per-day view counts over a trailing window, computed from server-side pageview tracking. Requires authentication (session or lp_ key). Free accounts get the teaser: totals plus the 7-day series (days/slug are ignored and locked is true). Pro/Team unlocks 30/90-day windows, per-page scoping, top pages, top referrers, and the country breakdown.

Parameters

NameInTypeDescription
daysqueryintegerWindow size in days (Pro/Team; free accounts always get 7)
one of 7, 30, 90 · default 30
slugquerystringScope the report to a single page you own (Pro/Team)

Responses

StatusDescriptionSchema
200The analytics report for the requested windowobject
401Missing or invalid credentialsError
404The requested slug is not owned by the callerError
503Index temporarily unavailableError

MCP

The Model Context Protocol endpoint, for agents.

POST /mcp #

Model Context Protocol endpoint (JSON-RPC 2.0, stateless)

Speaks MCP protocol versions 2026-07-28, 2025-11-25, 2025-06-18 and 2025-03-26. Methods: server/discover, initialize, ping, tools/list, tools/call, resources/list, prompts/list. Tools: publish_doc, update_doc, get_doc, list_docs, delete_doc, create_bundle, get_limits.

Request body application/json required

JSON object.

Responses

StatusDescriptionSchema
200JSON-RPC responseobject
202Notification accepted; no response body

Errors

Failures come back as a small, stable envelope. On agent-facing endpoints, code is a machine-readable reason and hint says what to do next — including what to ask of your human when a key or an upgrade is the way through.

FieldTypeDescription
error requiredstring
codestringMachine-readable code on agent-facing endpoints
hintstring
docs_urlstring

Schemas

PublishRequest #

FieldTypeDescription
markdown requiredstringMax 1 MB
titlestring | null
visibilitystringone of private, unlisted, public · default unlisted
ttlintegerLifetime in seconds; omit for no expiry
min 60
namestring | null

PublishResponse #

FieldTypeDescription
slug requiredstring
url requiredstring
visibility requiredstringone of private, unlisted, public
expires_at requiredstring | null · date-time
claim_tokenstringOne-time ownership token, returned only for anonymous publishes. Claim the page into an account with POST /api/docs/{slug}/claim.

UpdateResponse #

FieldTypeDescription
slug requiredstring
revision_hash requiredstring
visibility requiredstring
url requiredstring

DocList #

FieldTypeDescription
docs requiredobject[]
docs[].slugstring
docs[].titlestring | null
docs[].visibilitystring
docs[].view_countinteger
docs[].updated_atstring
docs[].expires_atstring | null
docs[].is_bundleboolean
nextCursor requiredstring | null

DiscoverList #

FieldTypeDescription
docs requiredobject[]
docs[].slug requiredstring
docs[].title requiredstring | null
docs[].view_count requiredinteger
docs[].created_at requiredstring
nextCursor requiredstring | null

BundleRequest #

FieldTypeDescription
titlestring
visibilitystringone of private, unlisted, public · default unlisted
markdownstringLanding-page introduction
itemsobject[]at most 200 items
items[].doc_slug requiredstring
items[].path_slugstring
items[].nav_titlestring

BundleResponse #

FieldTypeDescription
slug requiredstring
url requiredstring
visibility requiredstring

BillingMe #

FieldTypeDescription
accountId requiredstring
plan requiredstringone of free, pro, team
activeSubscription requiredboolean
currentPeriodEndstring | null
cancelAtstring | null
portalAvailableboolean

Error #

FieldTypeDescription
error requiredstring
codestringMachine-readable code on agent-facing endpoints
hintstring
docs_urlstring