Skip to content

Flatpage API and MCP server

Everything you can do in the dashboard's publish flow, from code or from an AI assistant: publish a page, a zipped site or a PDF, update it at the same link, list, rename and delete sites, and read views. On every plan, Free included.

Authentication

Create an API key in your dashboard under Settings. It's shown once; we store only a hash of it. Send it on every call:

Authorization: Bearer fpk_...

Claude, ChatGPT and other MCP clients don't need a key: they sign in with OAuth (see MCP server). Revoke a key or disconnect an app in Settings and it stops working at once.

Quick start

export FLATPAGE_API_KEY=fpk_...

curl -X POST "https://flatpage.app/v1/sites?linkName=spring-menu" \
  -H "Authorization: Bearer $FLATPAGE_API_KEY" \
  -H "Content-Type: text/html" \
  --data-binary @index.html

Answer when it's live (usually within a few seconds):

HTTP/1.1 201 Created
{
  "uploadId": "8d0c…",
  "status": "published",
  "site": {
    "id": "3f5e…",
    "slug": "spring-menu",
    "url": "https://spring-menu.flatpage.page",
    "kind": "html",
    "status": "active",
    "version": 1,
    …
  }
}

If processing takes longer than about 20 seconds you get 202 Accepted with "status": "processing"; poll GET /v1/uploads/{uploadId} every couple of seconds. Update the same site later with PUT /v1/sites/spring-menu and the same body.

Endpoints

Base URL https://flatpage.app/v1. {site} is a site's id or its link name. Bodies and answers are JSON unless noted.

GET/v1/me
Your email, plan and what you've used of it today.
GET/v1/sites
Your sites, newest first, with links and views.
POST/v1/sites
Publish a new site. Body: the file (text/html, application/zip, application/pdf) or JSON {"html", "linkName"}. ?linkName= names it.
GET/v1/sites/{site}
One site.
PUT/v1/sites/{site}
Replace a site's content with a new version. Same body as publishing. The link doesn't change.
PUT/v1/sites/{site}/link-name
Rename: {"linkName": "new-name"}. The old link redirects for 30 days.
GET/v1/sites/{site}/stats
Views per day for the last 30 days, this month and all time.
DELETE/v1/sites/{site}
Take a site offline and delete it.
GET/v1/link-names/{name}
Whether a link name is valid and free.
GET/v1/uploads/{uploadId}
A publish that answered 202: its status, and the site once it's live.
POST/v1/uploads
Files over 25 MB, step 1: {"fileName", "bytes", "siteId"?, "linkName"?} answers a putUrl.
POST/v1/uploads/{uploadId}/complete
Step 3, after you PUT the file to putUrl with the contentType it gave you.

Errors and limits

Errors answer with an HTTP status and {"error": "..."} in plain words you can show to a person:

  • 400 bad input, such as an invalid link name. 401 missing or revoked key. 403 over your plan's site limit. 404 no such site on your account. 409 link name taken. 413 file too big. 422 the upload was rejected (for example a zip without index.html, or a virus). 429 a rate limit.
  • Each API key, and each connected app, gets 200 calls a day, reset at midnight UTC.
  • Your plan's limits apply as in the dashboard: number of sites, file size and uploads per day (see pricing).
  • Bodies sent straight to POST /v1/sites can be up to 25 MB. Bigger files use the two-step upload.
  • Everything is checked like a dashboard upload: virus scan, phishing checks and the acceptable use policy.

MCP server

Remote MCP server (streamable HTTP) at https://flatpage.app/mcp. It signs in with OAuth 2.1 (dynamic client registration, PKCE), so it can be added as a custom connector in Claude and ChatGPT with no key. Setup steps for each app are in Publish from Claude and ChatGPT. Tools:

  • publish_html: html, link_name?: publish a page as a new site.
  • publish_file: file_name, content_base64, link_name?: publish a .zip, .pdf or .html (up to 25 MB).
  • update_site: site, then html or file_name + content_base64: new version, same link.
  • list_sites: Your sites with links, status and views.
  • get_site_stats: site: views for 30 days, this month, all time.
  • rename_site: site, link_name: change the link; the old one redirects.
  • delete_site: site, confirm: deletes only when confirm is the site's link name.

Each tool call counts as one API call. Clients that can't do OAuth can send an API key as a Bearer token instead.

Claude skill

A skill that teaches Claude to publish with Flatpage using the connector or this API: download the zip for claude.ai, or SKILL.md for Claude Code.

Big files: two-step upload

# 1. Ask for an upload URL
curl -X POST https://flatpage.app/v1/uploads -H "Authorization: Bearer $FLATPAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"fileName":"brochure.pdf","bytes":48000000,"linkName":"brochure"}'
# → {"uploadId":"…","putUrl":"https://…","contentType":"application/pdf",…}

# 2. PUT the file there with exactly that Content-Type
curl -X PUT "$PUT_URL" -H "Content-Type: application/pdf" --data-binary @brochure.pdf

# 3. Tell us it's there
curl -X POST https://flatpage.app/v1/uploads/$UPLOAD_ID/complete -H "Authorization: Bearer $FLATPAGE_API_KEY"

To replace an existing site this way, send its id or link name as siteId in step 1 instead of linkName.