{"openapi":"3.1.0","info":{"title":"Publee API","version":"1.0.0","summary":"Publish HTML as a shareable URL","description":"Publee turns HTML into a limited-share URL (https://<slug>.publee.site). Use POST /api/publish to publish a single HTML document or a static file tree. No account is required (anonymous sites expire in 7 days); authenticate with a self-serve API token (Bearer publee_live_..., issued at https://publee.app/tokens) to keep sites permanently, use private visibility, and update existing sites. An MCP server (Streamable HTTP) with the same capabilities is available at /api/mcp — see /.well-known/mcp.json.","contact":{"name":"Publee","email":"contact@publee.app","url":"https://publee.app"},"termsOfService":"https://publee.app/terms"},"servers":[{"url":"https://publee.app"}],"externalDocs":{"description":"Publee REST API docs","url":"https://publee.app/docs/api"},"paths":{"/api/publish":{"post":{"operationId":"publishSite","summary":"Publish HTML (or update an existing site)","description":"Publish a single HTML document (`html`) or a static file tree (`files`) and get back a shareable https://<slug>.publee.site URL. Works without authentication (site expires in 7 days and the response includes a claim URL). With a Bearer token the site is tied to your account; pass `slug` plus `overwrite: true` to replace the files of a site you own while keeping its URL.","security":[{},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishRequest"}}}},"responses":{"201":{"description":"Site published (or updated)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishResponse"}}}},"400":{"description":"Invalid input (missing files, bad slug, plan-gated option, ...)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Invalid or revoked Bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Plan limit reached (code: plan_limit_reached) — delete an unused site or upgrade","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"415":{"description":"Content-Type must be application/json","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited — retry later","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/slugs/check":{"get":{"operationId":"checkSlugAvailability","summary":"Check whether a custom slug is available","description":"Check whether `https://<slug>.publee.site` is still free. Custom slugs require a Pro plan or above at publish time; this check itself is public.","parameters":[{"name":"slug","in":"query","required":true,"description":"Candidate subdomain: 3-63 chars, lowercase letters/digits/hyphens","schema":{"type":"string","minLength":3,"maxLength":63}}],"responses":{"200":{"description":"Availability result (always 200; see `available`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlugCheckResponse"}}}}}}},"/api/mcp":{"post":{"operationId":"mcpJsonRpc","summary":"MCP endpoint (JSON-RPC 2.0, Streamable HTTP)","description":"Model Context Protocol server exposing the tools `publee_publish` and `publee_list_sites`. Speaks JSON-RPC 2.0 over stateless Streamable HTTP (no SSE). Authenticate with OAuth or a Bearer API token for permanent sites and site listing. Discovery manifest: /.well-known/mcp.json.","security":[{},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcRequest"}}}},"responses":{"200":{"description":"JSON-RPC response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}},"401":{"description":"Authentication required/expired. WWW-Authenticate points at the OAuth protected-resource metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Publee API token (`publee_live_...`). Self-serve issuance at https://publee.app/tokens — free plan included, no credit card required."}},"schemas":{"PublishRequest":{"type":"object","description":"Provide either `html` (single page) or `files` (static file tree).","properties":{"title":{"type":"string","description":"Site title. Defaults to the HTML <title> or 'Untitled site'."},"description":{"type":"string","maxLength":500,"description":"Optional description shown in the dashboard."},"slug":{"type":"string","minLength":3,"maxLength":63,"pattern":"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$","description":"Custom subdomain (https://<slug>.publee.site). Requires Pro plan or above; anonymous/free publishes always get a random slug."},"html":{"type":"string","description":"Full HTML document published as index.html. Use this OR `files`."},"files":{"type":"array","maxItems":1000,"description":"Static files. If no index.html exists but exactly one root-level HTML file does, it is promoted to index.html.","items":{"$ref":"#/components/schemas/PublishFile"}},"visibility":{"type":"string","enum":["private","members","workspace","password","public"],"default":"password","description":"Who can view the site. Anonymous publishers can use `password`/`public` only; `private` requires Pro, `members`/`workspace` require Team."},"password":{"type":"string","minLength":6,"description":"Viewer password. Required when visibility=password."},"spaMode":{"type":"boolean","default":false,"description":"Serve index.html for unmatched paths (SPA fallback)."},"noindex":{"type":"boolean","default":true,"description":"Keep search engines away. Setting false requires Pro plan or above."},"showBranding":{"type":"boolean","default":true,"description":"Show the Publee footer. Setting false requires Pro plan or above."},"memberEmails":{"type":"array","maxItems":100,"items":{"type":"string","format":"email"},"description":"Allowlist used when visibility=members."},"overwrite":{"type":"boolean","default":false,"description":"Replace the files of an existing site you own (requires `slug` and authentication). The URL stays the same; settings are not changed."}}},"PublishFile":{"type":"object","required":["path"],"properties":{"path":{"type":"string","description":"Relative path, e.g. index.html or css/style.css"},"content":{"type":"string","description":"UTF-8 text content (HTML/CSS/JS). Use this OR contentBase64."},"contentBase64":{"type":"string","contentEncoding":"base64","description":"Base64 content for binary files (images, fonts, ...)."},"mimeType":{"type":"string","description":"Optional MIME type override."}}},"PublishResponse":{"type":"object","required":["site"],"properties":{"site":{"$ref":"#/components/schemas/Site"},"claimToken":{"type":"string","description":"Anonymous publishes only: token to claim the site into an account."},"claimUrl":{"type":"string","format":"uri","description":"Anonymous publishes only: open after logging in to claim the site and lift the 7-day expiry."}}},"Site":{"type":"object","required":["id","slug","url","visibility","status"],"properties":{"id":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"description":{"type":["string","null"]},"visibility":{"type":"string","enum":["private","members","workspace","password","public"]},"status":{"type":"string","enum":["active","suspended","deleted"]},"hasPassword":{"type":"boolean"},"url":{"type":"string","format":"uri","description":"Public URL: https://<slug>.publee.site/"},"fileCount":{"type":"integer"},"totalBytes":{"type":"integer"},"spaMode":{"type":"boolean"},"noindex":{"type":"boolean"},"showBranding":{"type":"boolean"},"publishedAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time","description":"Auto-deletion time; null when the site is kept indefinitely (verified accounts on any plan)."}}},"SlugCheckResponse":{"type":"object","required":["available"],"properties":{"available":{"type":"boolean"},"slug":{"type":"string","description":"Normalized slug (present when available=true)"},"reason":{"type":"string","description":"Why the slug cannot be used (present when available=false)"}}},"ApiError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable message (Japanese)"},"code":{"type":"string","description":"Stable machine-readable error code","enum":["invalid_input","invalid_token","plan_limit_reached","unsupported_media_type","rate_limited","not_found","method_not_allowed","internal_error"]},"hint":{"type":"string","description":"How to resolve the error"},"docs":{"type":"string","format":"uri","description":"Related documentation URL"}}},"JsonRpcRequest":{"type":"object","required":["jsonrpc","method"],"properties":{"jsonrpc":{"type":"string","const":"2.0"},"id":{"type":["string","number","null"]},"method":{"type":"string","description":"initialize | ping | tools/list | tools/call | resources/list | prompts/list"},"params":{"type":"object"}}},"JsonRpcResponse":{"type":"object","required":["jsonrpc"],"properties":{"jsonrpc":{"type":"string","const":"2.0"},"id":{"type":["string","number","null"]},"result":{"type":"object"},"error":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"}}}}}}}}