{"openapi":"3.1.0","info":{"title":"BrewPage API","description":"Free instant hosting for HTML, Markdown, AI artifacts and files","version":"1.95.0"},"servers":[{"url":"https://brewpage.app/api/","description":"Generated server url"}],"tags":[{"name":"preview","description":"Per-content OpenGraph image (1200×630 PNG)"},{"name":"Short Links","description":"Short URL resolver for sharing"},{"name":"Owner Check","description":"Lightweight owner-token probe; never increments views or returns content"},{"name":"HTML","description":"HTML page hosting with markdown support"},{"name":"KV","description":"Key-Value store with up to 1000 keys per namespace"},{"name":"Sites","description":"Multi-file HTML site hosting via ZIP or folder upload"},{"name":"Owner Token","description":"Mints a fresh owner token client-side; stateless, persists nothing"},{"name":"SEO","description":"Search engine optimization endpoints"},{"name":"Gallery","description":"Browse public content from the 'public' namespace without password protection"},{"name":"JSON","description":"JSON document store with up to 10,000 docs per collection"},{"name":"Stats","description":"Platform-wide usage statistics"},{"name":"Namespace","description":"Fresh, collision-free namespace suggestions"},{"name":"preview","description":"OpenGraph metadata for social bots"},{"name":"Files","description":"File hosting up to 5 MB per file, 1000 files per namespace"},{"name":"Reports","description":"Abuse reports for hosted content"}],"paths":{"/api/sites/{ns}/{id}":{"get":{"tags":["Sites"],"summary":"Get site info","description":"Returns site metadata and file list. Requires owner token","operationId":"info","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token returned at creation","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Site info","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SiteInfoResponse"}}}},"403":{"description":"Missing or wrong owner token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SiteInfoResponse"}}}},"404":{"description":"Site not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SiteInfoResponse"}}}}}},"put":{"tags":["Sites"],"summary":"Update site","description":"Replaces the multi-file site bundle (ZIP archive or individual files with paths). Requires the owner token returned at creation. Password is preserved as-is (not changed in this operation).","operationId":"update","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"files","in":"query","description":"Individual files (alternative to archive)","required":false,"schema":{"type":"array","items":{"type":"string","format":"binary"}}},{"name":"paths","in":"query","description":"Relative paths for each file (must match files order)","required":false,"schema":{"type":"array","items":{"type":"string"}}},{"name":"tags","in":"query","description":"Comma-separated tags","required":false,"schema":{"type":"string"},"example":"demo,portfolio"},{"name":"ttl","in":"query","description":"Time to live in days (1-30). When omitted the existing expiry is preserved. Accepts '15', '15d' or '15 days'","required":false,"schema":{"type":"string"}},{"name":"entry","in":"query","description":"Entry file path override (default: auto-detect index.html)","required":false,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token returned at creation. Required for update","required":true,"schema":{"type":"string"}},{"name":"User-Agent","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"archive":{"type":"string","format":"binary","description":"ZIP archive containing site files"}}}}}},"responses":{"200":{"description":"Site updated","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SiteUpdateResponse"}}}},"400":{"description":"Invalid request, empty bundle, limits exceeded, or bad TTL","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SiteUpdateResponse"}}}},"403":{"description":"Missing or wrong owner token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SiteUpdateResponse"}}}},"404":{"description":"Site not found or expired","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SiteUpdateResponse"}}}},"410":{"description":"Site is no longer available","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SiteUpdateResponse"}}}},"415":{"description":"Unsupported file type in archive","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SiteUpdateResponse"}}}}}},"delete":{"tags":["Sites"],"summary":"Delete site","description":"Permanently removes the site and all its files from storage","operationId":"delete","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token returned at creation. Required for delete","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Site deleted"},"403":{"description":"Missing or wrong owner token"},"404":{"description":"Site not found"}}}},"/api/kv/{ns}/{id}/{key}":{"get":{"tags":["KV"],"summary":"Get key value","description":"Returns the value and last update timestamp for a specific key","operationId":"getKey","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Password","in":"header","description":"Access password via header","required":false,"schema":{"type":"string"}},{"name":"p","in":"query","description":"Access password via query param (alternative to X-Password header)","required":false,"schema":{"type":"string"}},{"name":"User-Agent","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token to bypass password protection when no password supplied","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Key value","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KvGetResponse"}}}},"403":{"description":"Wrong password","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KvGetResponse"}}}},"404":{"description":"Store or key not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KvGetResponse"}}}}}},"put":{"tags":["KV"],"summary":"Upsert key","description":"Creates or updates a key in the store; max 1000 keys per store","operationId":"upsertKey","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token returned at creation. Required for update and delete","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KvUpsertKeyRequest"}}},"required":true},"responses":{"200":{"description":"Key upserted","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KvUpsertKeyResponse"}}}},"403":{"description":"Missing or wrong owner token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KvUpsertKeyResponse"}}}},"404":{"description":"Store not found or expired","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KvUpsertKeyResponse"}}}},"409":{"description":"Key limit exceeded","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KvUpsertKeyResponse"}}}}}},"delete":{"tags":["KV"],"summary":"Delete key","description":"Removes a single key from the store; deleting the last key does not remove the store","operationId":"deleteKey","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token returned at creation. Required for update and delete","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Key deleted"},"403":{"description":"Missing or wrong owner token"},"404":{"description":"Store or key not found"}}}},"/api/json/{ns}/{id}":{"get":{"tags":["JSON"],"summary":"Get JSON document","description":"Returns raw JSON content with application/json content type","operationId":"getById","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Password","in":"header","description":"Access password via header","required":false,"schema":{"type":"string"}},{"name":"p","in":"query","description":"Access password via query param (alternative to X-Password header)","required":false,"schema":{"type":"string"}},{"name":"User-Agent","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token to bypass password protection when no password supplied","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"JSON content","content":{"*/*":{"schema":{"type":"string"}}}},"403":{"description":"Wrong password","content":{"*/*":{"schema":{"type":"string"}}}},"404":{"description":"Document not found or expired","content":{"*/*":{"schema":{"type":"string"}}}}}},"put":{"tags":["JSON"],"summary":"Update JSON document","description":"Replaces document content; requires the owner token returned at creation","operationId":"update_1","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token returned at creation. Required for update and delete","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"string"}}},"required":true},"responses":{"200":{"description":"Document updated","content":{"*/*":{"schema":{"$ref":"#/components/schemas/JsonUpdateResponse"}}}},"403":{"description":"Missing or wrong owner token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/JsonUpdateResponse"}}}},"404":{"description":"Document not found or expired","content":{"*/*":{"schema":{"$ref":"#/components/schemas/JsonUpdateResponse"}}}}}},"delete":{"tags":["JSON"],"summary":"Delete JSON document","description":"Permanently removes the document","operationId":"delete_1","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token returned at creation. Required for update and delete","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Document deleted"},"403":{"description":"Missing or wrong owner token"},"404":{"description":"Document not found or expired"}}}},"/api/html/{ns}/{id}":{"get":{"tags":["HTML"],"summary":"Get HTML page","description":"Returns rendered HTML content; password-protected pages require X-Password header or ?p= query param","operationId":"getById_1","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Password","in":"header","description":"Access password via header","required":false,"schema":{"type":"string"}},{"name":"p","in":"query","description":"Access password via query param (alternative to X-Password header)","required":false,"schema":{"type":"string"}},{"name":"User-Agent","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token to bypass password protection when no password supplied","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"HTML content","content":{"*/*":{"schema":{"type":"string"}}}},"403":{"description":"Wrong password","content":{"*/*":{"schema":{"type":"string"}}}},"404":{"description":"Page not found or expired","content":{"*/*":{"schema":{"type":"string"}}}}}},"put":{"tags":["HTML"],"summary":"Update HTML page","description":"Replaces page content; requires the owner token returned at creation. Also accepts raw text/* (HTML, Markdown, YAML, XML, CSV, code) bodies — see /llms-full.txt.","operationId":"update_2","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token returned at creation. Required for update and delete","required":true,"schema":{"type":"string"}},{"name":"format","in":"query","description":"Optional new content format on republish. Accepts 'html', 'markdown'/'md', or any code format (yaml, json, xml, csv, tsv, log, toml, ini, sql, sh, bat, env, javascript, typescript, tsx, jsx, css, properties, docker, txt). When omitted, the row's stored format is preserved. Body field 'format' on HtmlUpdateRequest takes precedence over the query param when both are present.","required":false,"schema":{"type":"string"}},{"name":"User-Agent","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HtmlUpdateRequest"}}},"required":true},"responses":{"200":{"description":"Page updated","content":{"*/*":{"schema":{"$ref":"#/components/schemas/HtmlUpdateResponse"}}}},"403":{"description":"Missing or wrong owner token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/HtmlUpdateResponse"}}}},"404":{"description":"Page not found or expired","content":{"*/*":{"schema":{"$ref":"#/components/schemas/HtmlUpdateResponse"}}}}}},"delete":{"tags":["HTML"],"summary":"Delete HTML page","description":"Permanently removes page and frees the short URL","operationId":"delete_2","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token returned at creation. Required for update and delete","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Page deleted"},"403":{"description":"Missing or wrong owner token"},"404":{"description":"Page not found or expired"}}}},"/api/sites":{"post":{"tags":["Sites"],"summary":"Upload site","description":"Upload a multi-file HTML site as a ZIP archive or as individual files with paths. Returns a link to the published site","operationId":"upload","parameters":[{"name":"files","in":"query","description":"Individual files (alternative to archive)","required":false,"schema":{"type":"array","items":{"type":"string","format":"binary"}}},{"name":"paths","in":"query","description":"Relative paths for each file (must match files order)","required":false,"schema":{"type":"array","items":{"type":"string"}}},{"name":"ns","in":"query","description":"Namespace. Default: public","required":false,"schema":{"type":"string","default":"public","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"tags","in":"query","description":"Comma-separated tags","required":false,"schema":{"type":"string"},"example":"demo,portfolio"},{"name":"ttl","in":"query","description":"Time to live in days (1-30, default 15). Site auto-deletes after expiry. Accepts '15', '15d' or '15 days'","required":false,"schema":{"type":"string"}},{"name":"entry","in":"query","description":"Entry file path override (default: auto-detect index.html)","required":false,"schema":{"type":"string"}},{"name":"X-Password","in":"header","description":"Access password","required":false,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Reuse existing owner token to group entities under one owner","required":false,"schema":{"type":"string"}},{"name":"User-Agent","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"archive":{"type":"string","format":"binary","description":"ZIP archive containing site files"}}}}}},"responses":{"201":{"description":"Site uploaded","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SiteUploadResponse"}}}},"400":{"description":"Invalid request, no HTML files, or limits exceeded","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SiteUploadResponse"}}}},"415":{"description":"Unsupported file type in archive","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SiteUploadResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SiteUploadResponse"}}}}}}},"/api/reports":{"post":{"tags":["Reports"],"summary":"Submit abuse report","description":"Records a public report about a resource hosted on brewpage.app for moderator review","operationId":"create","parameters":[{"name":"User-Agent","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportRequest"}}},"required":true},"responses":{"201":{"description":"Report accepted","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ReportResponse"}}}},"400":{"description":"Invalid request parameters","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ReportResponse"}}}}}}},"/api/kv":{"get":{"tags":["KV"],"summary":"List KV stores","description":"Returns all KV stores owned by the given token in the namespace. Returns empty list without token","operationId":"listStores","parameters":[{"name":"ns","in":"query","description":"Namespace","required":false,"schema":{"type":"string","default":"public","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"X-Owner-Token","in":"header","description":"Owner token to filter by ownership. Without token returns empty list","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Store list","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KvStoreListResponse"}}}}}}},"post":{"tags":["KV"],"summary":"Create KV store","description":"Creates a new store with an initial key-value pair and returns a shareable link. Reuse existing owner token to group entities under one owner","operationId":"createStore","parameters":[{"name":"ns","in":"query","description":"Namespace. Default: public. Pages in 'public' without password appear in gallery. Custom namespace is created automatically","required":false,"schema":{"type":"string","default":"public","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"tags","in":"query","description":"Comma-separated tags","required":false,"schema":{"type":"string"},"example":"demo,test"},{"name":"ttl","in":"query","description":"Time to live in days (1-30, default 15). Store auto-deletes after expiry. Accepts '15', '15d' or '15 days'","required":false,"schema":{"type":"string"}},{"name":"X-Password","in":"header","description":"Access password. Empty = public store visible in gallery. With password = hidden from gallery, viewers must enter password or pass ?p= in URL","required":false,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Reuse existing owner token to group entities under one owner. If omitted, a new token is generated","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KvCreateStoreRequest"}}},"required":true},"responses":{"201":{"description":"Store created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KvCreateStoreResponse"}}}},"400":{"description":"Invalid request parameters","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KvCreateStoreResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KvCreateStoreResponse"}}}}}}},"/api/json":{"get":{"tags":["JSON"],"summary":"List JSON documents","description":"Returns documents owned by the given token. Empty list without token","operationId":"list","parameters":[{"name":"ns","in":"query","description":"Namespace","required":false,"schema":{"type":"string","default":"public","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"X-Owner-Token","in":"header","description":"Owner token to filter by ownership. Without token returns empty list","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Document list","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/JsonListResponse"}}}}}}},"post":{"tags":["JSON"],"summary":"Create JSON document","description":"Stores any valid JSON and returns a shareable link. Reuse existing owner token to group entities under one owner","operationId":"create_1","parameters":[{"name":"ns","in":"query","description":"Namespace. Default: public. Pages in 'public' without password appear in gallery. Custom namespace is created automatically","required":false,"schema":{"type":"string","default":"public","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"tags","in":"query","description":"Comma-separated tags","required":false,"schema":{"type":"string"},"example":"demo,test"},{"name":"ttl","in":"query","description":"Time to live in days (1-30, default 15). Document auto-deletes after expiry. Accepts '15', '15d' or '15 days'","required":false,"schema":{"type":"string"}},{"name":"X-Password","in":"header","description":"Access password. Empty = public document visible in gallery. With password = hidden from gallery, viewers must enter password or pass ?p= in URL","required":false,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Reuse existing owner token to group entities under one owner. If omitted, a new token is generated","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"string"}}},"required":true},"responses":{"201":{"description":"Document created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/JsonCreateResponse"}}}},"400":{"description":"Invalid JSON or request parameters","content":{"*/*":{"schema":{"$ref":"#/components/schemas/JsonCreateResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"*/*":{"schema":{"$ref":"#/components/schemas/JsonCreateResponse"}}}}}}},"/api/html":{"post":{"tags":["HTML"],"summary":"Create HTML page","description":"Stores HTML or markdown content and returns a shareable link. Reuse existing owner token to group entities under one owner. Also accepts raw text/* (HTML, Markdown, YAML, XML, CSV, code) and application/octet-stream bodies — see /llms-full.txt.","operationId":"create_2","parameters":[{"name":"ns","in":"query","description":"Namespace. Default: public. Pages in 'public' without password appear in gallery. Custom namespace is created automatically","required":false,"schema":{"type":"string","default":"public","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"tags","in":"query","description":"Comma-separated tags","required":false,"schema":{"type":"string"},"example":"demo,test"},{"name":"ttl","in":"query","description":"Time to live in days (1-30, default 15). Page auto-deletes after expiry. Accepts '15', '15d' or '15 days'","required":false,"schema":{"type":"string"}},{"name":"format","in":"query","description":"Content format. 'html' (default) — stored verbatim. 'markdown' / 'md' — rendered to styled HTML via github-markdown-css. Code/text languages stored and served raw with format-specific MIME (text/csv, application/json, text/xml, text/plain) plus X-Content-Type-Options: nosniff: yaml, json, xml, csv, tsv, log, toml, ini, sql, sh, bat, env, javascript, typescript, tsx, jsx, css, properties, docker, txt","required":false,"schema":{"type":"string","default":"html"}},{"name":"X-Password","in":"header","description":"Access password. Empty = public page visible in gallery. With password = hidden from gallery, viewers must enter password or pass ?p= in URL","required":false,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Reuse existing owner token to group entities under one owner. If omitted, a new token is generated","required":false,"schema":{"type":"string"}},{"name":"User-Agent","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HtmlUploadRequest"}}},"required":true},"responses":{"201":{"description":"Page created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/HtmlUploadResponse"}}}},"400":{"description":"Invalid request parameters","content":{"*/*":{"schema":{"$ref":"#/components/schemas/HtmlUploadResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"*/*":{"schema":{"$ref":"#/components/schemas/HtmlUploadResponse"}}}}}}},"/api/files":{"get":{"tags":["Files"],"summary":"List files","description":"Returns files owned by the given token. Empty list without token","operationId":"list_1","parameters":[{"name":"ns","in":"query","description":"Namespace","required":false,"schema":{"type":"string","default":"public","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"X-Owner-Token","in":"header","description":"Owner token to filter by ownership. Without token returns empty list","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"File list","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FileListResponse"}}}}}}},"post":{"tags":["Files"],"summary":"Upload file","description":"Stores a file via multipart upload and returns a download link. Only safe file types accepted. Reuse existing owner token to group entities under one owner","operationId":"upload_1","parameters":[{"name":"ns","in":"query","description":"Namespace. Default: public. Pages in 'public' without password appear in gallery. Custom namespace is created automatically","required":false,"schema":{"type":"string","default":"public","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"tags","in":"query","description":"Comma-separated tags","required":false,"schema":{"type":"string"},"example":"demo,test"},{"name":"ttl","in":"query","description":"Time to live in days (1-30, default 15). File auto-deletes after expiry. Accepts '15', '15d' or '15 days'","required":false,"schema":{"type":"string"}},{"name":"X-Password","in":"header","description":"Access password. Empty = public file visible in gallery. With password = hidden from gallery, viewers must enter password or pass ?p= in URL","required":false,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Reuse existing owner token to group entities under one owner. If omitted, a new token is generated","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}}}},"responses":{"201":{"description":"File uploaded","content":{"*/*":{"schema":{"$ref":"#/components/schemas/FileUploadResponse"}}}},"400":{"description":"Invalid request or file too large","content":{"*/*":{"schema":{"$ref":"#/components/schemas/FileUploadResponse"}}}},"415":{"description":"Unsupported file type","content":{"*/*":{"schema":{"$ref":"#/components/schemas/FileUploadResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"*/*":{"schema":{"$ref":"#/components/schemas/FileUploadResponse"}}}}}}},"/{ns}/{id}":{"get":{"tags":["Short Links"],"operationId":"resolve","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Password","in":"header","description":"Access password for protected resources","required":false,"schema":{"type":"string"}},{"name":"p","in":"query","description":"Access password (query alternative to X-Password header)","required":false,"schema":{"type":"string"}},{"name":"dl","in":"query","description":"Force download as attachment","required":false,"schema":{"type":"boolean"}},{"name":"raw","in":"query","description":"Bypass SPA: stream raw bytes directly (used by <video src>, <iframe src> for PDF, embed cases)","required":false,"schema":{"type":"boolean"}},{"name":"inline","in":"query","description":"Multi-file site SPA-wrapper bypass: render the entry document as its own top-level page without CSP sandbox so user JS keeps same-origin access. Honored only for site-format resources.","required":false,"schema":{"type":"boolean"}},{"name":"Range","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Resolve","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token; when supplied, response carries X-Is-Owner: true on match","required":false,"schema":{"type":"string"}},{"name":"User-Agent","in":"header","required":false,"schema":{"type":"string"}},{"name":"Accept","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object"}}}}}}},"/{ns}/{id}/{sub}":{"get":{"tags":["Short Links"],"operationId":"resolveWithSub","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"sub","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Password","in":"header","description":"Access password for protected resources","required":false,"schema":{"type":"string"}},{"name":"p","in":"query","description":"Access password (query alternative to X-Password header)","required":false,"schema":{"type":"string"}},{"name":"dl","in":"query","description":"Force download as attachment","required":false,"schema":{"type":"boolean"}},{"name":"Range","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Resolve","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token; when supplied, response carries X-Is-Owner: true on match","required":false,"schema":{"type":"string"}},{"name":"User-Agent","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object"}}}}}}},"/{key}.txt":{"get":{"tags":["SEO"],"summary":"IndexNow key verification","description":"Serves the IndexNow verification key file for search engine crawlers","operationId":"serveKeyFile","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Key file content","content":{"text/plain":{"schema":{"type":"string"}}}},"404":{"description":"Unknown key","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/preview/{ns}/{id}.png":{"get":{"tags":["preview"],"summary":"Per-content OG image","description":"Returns 1200×630 PNG, cached, with ETag/If-None-Match support; falls back to /og-image.png?v=2 on any failure","operationId":"preview","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"PNG image bytes","content":{"image/png":{"schema":{"type":"object"}}}},"302":{"description":"Fallback to static og-image.png (flag off, no source, generation error, oversized)","content":{"image/png":{"schema":{"type":"object"}}}},"304":{"description":"Not modified (If-None-Match matched current ETag)","content":{"image/png":{"schema":{"type":"object"}}}},"429":{"description":"Per-IP rate limit exceeded","content":{"image/png":{"schema":{"type":"object"}}}}}}},"/preview-html/{ns}/{id}":{"get":{"tags":["preview"],"summary":"OpenGraph HTML stub","description":"Tiny HTML response with og:title/og:description/og:image meta tags for social-bot unfurls","operationId":"previewHtml","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"HTML stub with OG meta (or generic stub when resource is missing)","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/galleries":{"get":{"tags":["SEO"],"summary":"Flat HTML index of all current public publications","description":"Renders a minimal, bot-friendly anchor index of every current public site, page, image and video. Sorted by popularity (top 50 by views) then by recency. Underlying data is cached for ~2h.","operationId":"galleries","responses":{"200":{"description":"HTML index page","content":{"*/*":{"schema":{"type":"string"}}}}}}},"/api/{ns}/{id}/owner-check":{"get":{"tags":["Owner Check"],"summary":"Verify whether the supplied X-Owner-Token owns the resource","description":"Returns `{isOwner, type}`. Constant-time BCrypt match. 404 when the resource does not exist (or has expired). 200 with `isOwner:false` when the X-Owner-Token header is absent.","operationId":"ownerCheck","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token to verify (constant-time match)","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OwnerCheckResponse"}}}}}}},"/api/stats":{"get":{"tags":["Stats"],"summary":"Get platform stats","description":"Returns today's and all-time creation/view counts with per-type breakdown. Optional 'tz' query param (IANA id) controls the boundary of 'today'; defaults to UTC.","operationId":"getStats","parameters":[{"name":"tz","in":"query","description":"IANA timezone id for the 'today' boundary. Defaults to UTC. Example: Europe/Lisbon","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Platform statistics","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StatsResponse"}}}}}}},"/api/sites/{ns}/{id}/files/**":{"get":{"tags":["Sites"],"summary":"Serve site file","description":"Serves an individual file from the site with correct content type","operationId":"serveFile","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Password","in":"header","description":"Access password via header","required":false,"schema":{"type":"string"}},{"name":"p","in":"query","description":"Access password via query param","required":false,"schema":{"type":"string"}},{"name":"User-Agent","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token to bypass password protection when no password supplied","required":false,"schema":{"type":"string"}},{"name":"Range","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"File content","content":{"*/*":{"schema":{"type":"object"}}}},"403":{"description":"Wrong password","content":{"*/*":{"schema":{"type":"object"}}}},"404":{"description":"Site or file not found","content":{"*/*":{"schema":{"type":"object"}}}}}}},"/api/sitemap.xml":{"get":{"tags":["SEO"],"summary":"Dynamic XML sitemap","description":"Generates sitemap with static pages and all public gallery entries. Caddy should route /sitemap.xml to this endpoint","operationId":"sitemap","responses":{"200":{"description":"Sitemap XML","content":{"application/xml":{"schema":{"type":"string"}}}}}}},"/api/sitemap-index.xml":{"get":{"tags":["SEO"],"summary":"Top-level sitemap index","description":"sitemaps.org 0.9 sitemapindex pointing at /sitemap.xml and /sitemap-images.xml. Advertised in robots.txt so crawlers discover both children from a single URL. Caddy should route /sitemap-index.xml to this endpoint.","operationId":"sitemapIndex","responses":{"200":{"description":"Sitemap index XML","content":{"application/xml":{"schema":{"type":"string"}}}}}}},"/api/sitemap-images.xml":{"get":{"tags":["SEO"],"summary":"Google image-sitemap","description":"Per-content OG preview URLs (sitemap-image/1.1) for every qualifying public HTML/site publication. Reuses the publishable-items Caffeine cache slot shared with /sitemap.xml. Caddy should route /sitemap-images.xml to this endpoint.","operationId":"sitemapImages","responses":{"200":{"description":"Image sitemap XML","content":{"application/xml":{"schema":{"type":"string"}}}}}}},"/api/owner-token":{"get":{"tags":["Owner Token"],"summary":"Mint a fresh owner token and its derived owner id","description":"Returns `{token, ownerId}` where `token` is a 32-char base62 owner token and `ownerId` is its 64-char lowercase SHA-256 hex. Stateless: nothing is persisted. Send the returned token as `X-Owner-Token` when publishing to claim ownership.","operationId":"mintOwnerToken","responses":{"200":{"description":"Fresh owner token minted","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OwnerTokenResponse"}}}},"429":{"description":"Rate limit exceeded for this IP","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OwnerTokenResponse"}}}}}}},"/api/namespace/random":{"get":{"tags":["Namespace"],"summary":"Suggest a random namespace","description":"Returns a fresh `<word>-<word>-NN` namespace from the EFF Short Wordlist that does not collide with any existing resource. Pure read; no resource is allocated.","operationId":"random","responses":{"200":{"description":"Namespace suggested","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RandomNamespaceResponse"}}}},"503":{"description":"Namespace pool exhausted — retry budget hit consecutive collisions","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RandomNamespaceResponse"}}}}}}},"/api/kv/{ns}/{id}":{"get":{"tags":["KV"],"summary":"List keys","description":"Returns all key names and total count for a store","operationId":"listKeys","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Password","in":"header","description":"Access password via header","required":false,"schema":{"type":"string"}},{"name":"p","in":"query","description":"Access password via query param (alternative to X-Password header)","required":false,"schema":{"type":"string"}},{"name":"User-Agent","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token to bypass password protection when no password supplied","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Key list","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KvListKeysResponse"}}}},"403":{"description":"Wrong password","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KvListKeysResponse"}}}},"404":{"description":"Store not found or expired","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KvListKeysResponse"}}}}}},"delete":{"tags":["KV"],"summary":"Delete entire KV bucket (all keys under ns/id)","description":"Permanently removes all keys in the store; requires the owner token returned at creation","operationId":"deleteBucket","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token returned at creation. Required for delete","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Bucket deleted"},"403":{"description":"Missing or wrong owner token"},"404":{"description":"Store not found or expired"}}}},"/api/html/{ns}/{id}/source":{"get":{"tags":["HTML"],"summary":"Get HTML page raw source","description":"Returns the raw user-submitted content (pre-render, pre-sanitize) plus metadata for republish. Owner-only — requires the owner token returned at creation.","operationId":"getSource","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token returned at creation. Required for source access","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Raw source body and metadata","content":{"*/*":{"schema":{"type":"object"}}}},"400":{"description":"Owner token missing (code: OWNER_TOKEN_REQUIRED)","content":{"*/*":{"schema":{"type":"object"}}}},"403":{"description":"Wrong owner token","content":{"*/*":{"schema":{"type":"object"}}}},"404":{"description":"Page not found or expired","content":{"*/*":{"schema":{"type":"object"}}}}}}},"/api/html/{ns}/{id}/settings":{"get":{"tags":["HTML"],"summary":"Get HTML page settings","description":"Returns republish-prefill settings (namespace, ttlDays, hasPassword, showTopBar, format, title, filename, createdAt, expiresAt) without the content body. Owner-only — requires the owner token returned at creation.","operationId":"getSettings","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token returned at creation. Required for settings access","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Settings projection","content":{"*/*":{"schema":{"type":"object"}}}},"400":{"description":"Owner token missing (code: OWNER_TOKEN_REQUIRED)","content":{"*/*":{"schema":{"type":"object"}}}},"403":{"description":"Wrong owner token","content":{"*/*":{"schema":{"type":"object"}}}},"404":{"description":"Page not found or expired","content":{"*/*":{"schema":{"type":"object"}}}}}}},"/api/gallery":{"get":{"tags":["Gallery"],"summary":"Browse gallery","description":"Lists public pages (public namespace, no password) with optional case-insensitive search by title/tags. When `mine=true` and `X-Owner-Token` is supplied, results are restricted to the caller's own public publications.","operationId":"getGallery","parameters":[{"name":"q","in":"query","description":"Case-insensitive search by title or tags","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","description":"Page number (1-based)","required":false,"schema":{"type":"integer","format":"int32","default":1},"example":1},{"name":"size","in":"query","description":"Items per page (max 100)","required":false,"schema":{"type":"integer","format":"int32","default":20},"example":20},{"name":"sort","in":"query","description":"Sort order: 'date' (newest first, default) or 'views' (most viewed first)","required":false,"schema":{"type":"string","default":"date"},"example":"date"},{"name":"mine","in":"query","description":"When true, restrict results to the caller's owner_id (requires X-Owner-Token)","required":false,"schema":{"type":"boolean"}},{"name":"X-Owner-Token","in":"header","description":"Owner token; required when mine=true","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated gallery items","content":{"*/*":{"schema":{"$ref":"#/components/schemas/GalleryResponse"}}}}}}},"/api/files/{ns}/{id}":{"get":{"tags":["Files"],"summary":"Download file","description":"Returns file inline for previewable types (images, PDF, media) or as attachment. Add ?dl=1 to force download.","operationId":"download","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Password","in":"header","description":"Access password via header","required":false,"schema":{"type":"string"}},{"name":"p","in":"query","description":"Access password via query param (alternative to X-Password header)","required":false,"schema":{"type":"string"}},{"name":"dl","in":"query","description":"Force download as attachment","required":false,"schema":{"type":"boolean"}},{"name":"Range","in":"header","required":false,"schema":{"type":"string"}},{"name":"User-Agent","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token to bypass password protection when no password supplied","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"File content","content":{"*/*":{"schema":{"type":"object"}}}},"403":{"description":"Wrong password","content":{"*/*":{"schema":{"type":"object"}}}},"404":{"description":"File not found or expired","content":{"*/*":{"schema":{"type":"object"}}}}}},"delete":{"tags":["Files"],"summary":"Delete file","description":"Permanently removes the file from storage","operationId":"delete_3","parameters":[{"name":"ns","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,32}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Owner-Token","in":"header","description":"Owner token returned at creation. Required for update and delete","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"File deleted"},"403":{"description":"Missing or wrong owner token"},"404":{"description":"File not found or expired"}}}}},"components":{"schemas":{"AbuseResult":{"type":"object","properties":{"code":{"type":"string","enum":["success","duplicate","warned","failed"]},"action":{"type":"string","enum":["created","created_missing_assets","updated","merged","created_warned","unavailable","rate_limited","rejected"]},"reason":{"type":"string"},"extras":{"type":"object","additionalProperties":{}}}},"SiteUpdateResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique 10-character alphanumeric site ID"},"namespace":{"type":"string"},"link":{"type":"string","description":"Public URL to view the site"},"ownerLink":{"type":"string","description":"API URL for programmatic access (info, update, delete)"},"entryFile":{"type":"string","description":"Entry HTML file resolved from the upload (e.g. index.html)"},"fileCount":{"type":"integer","format":"int32"},"totalSizeBytes":{"type":"integer","format":"int64"},"expiresAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"type":"string"}},"result":{"$ref":"#/components/schemas/AbuseResult","description":"Abuse-v2 outcome envelope: code + action + optional reason."},"visibilityNotice":{"type":"string","description":"Human-readable notice describing the published item's visibility (public/gallery+indexed vs unlisted)."}}},"KvUpsertKeyRequest":{"type":"object","properties":{"value":{"type":"string","description":"New value for the key (max 1 MB)"}}},"KvUpsertKeyResponse":{"type":"object","properties":{"id":{"type":"string"},"namespace":{"type":"string"},"key":{"type":"string"},"sizeBytes":{"type":"integer","format":"int64"},"link":{"type":"string","description":"Public short URL for this key"},"ownerLink":{"type":"string","description":"API URL for programmatic access"},"visibilityNotice":{"type":"string","description":"Human-readable notice describing the published item's visibility (public/gallery+indexed vs unlisted)."}}},"JsonUpdateResponse":{"type":"object","properties":{"id":{"type":"string"},"namespace":{"type":"string"},"link":{"type":"string","description":"Public short URL for browser viewing"},"ownerLink":{"type":"string","description":"API URL for programmatic access"},"sizeBytes":{"type":"integer","format":"int64"},"updatedAt":{"type":"string","format":"date-time"},"visibilityNotice":{"type":"string","description":"Human-readable notice describing the published item's visibility (public/gallery+indexed vs unlisted)."}}},"HtmlUpdateRequest":{"type":"object","properties":{"content":{"type":"string","description":"New HTML content to replace existing page"},"format":{"type":"string","description":"Optional new content format on republish. Accepts 'html', 'markdown'/'md', or any code format (yaml, json, xml, csv, tsv, log, toml, ini, sql, sh, bat, env, javascript, typescript, tsx, jsx, css, properties, docker, txt). When omitted, the row's stored format is preserved. Takes precedence over the 'format' query param when both are present."}}},"HtmlUpdateResponse":{"type":"object","properties":{"id":{"type":"string"},"namespace":{"type":"string"},"link":{"type":"string","description":"Public short URL for browser viewing"},"ownerLink":{"type":"string","description":"API URL for programmatic access"},"expiresAt":{"type":"string","format":"date-time"},"sizeBytes":{"type":"integer","format":"int64"},"result":{"$ref":"#/components/schemas/AbuseResult","description":"Abuse-v2 outcome envelope: code + action + optional reason."},"visibilityNotice":{"type":"string","description":"Human-readable notice describing the published item's visibility (public/gallery+indexed vs unlisted)."}}},"SiteUploadResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique 10-character alphanumeric site ID"},"namespace":{"type":"string"},"entryFile":{"type":"string","description":"Entry HTML file resolved from the upload (e.g. index.html)"},"link":{"type":"string","description":"Public URL to view the site"},"ownerLink":{"type":"string","description":"API URL for programmatic access (info, delete)"},"fileCount":{"type":"integer","format":"int32"},"totalSizeBytes":{"type":"integer","format":"int64"},"expiresAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"type":"string"}},"ownerToken":{"type":"string","description":"Secret token required for info and delete operations. Store it safely -- cannot be recovered"},"result":{"$ref":"#/components/schemas/AbuseResult","description":"Abuse-v2 result envelope (code + action + optional reason). Created/Merged/CreatedWarned dispatched per silent-merge dedup decision."},"visibilityNotice":{"type":"string","description":"Human-readable notice describing the published item's visibility (public/gallery+indexed vs unlisted)."}}},"ReportRequest":{"type":"object","properties":{"reportedUrl":{"type":"string","description":"Full URL of the reported resource on brewpage.app or brewdata.app"},"category":{"type":"string","description":"Abuse category","enum":["cannot_delete","spam","phishing","malware","copyright","harassment","illegal","other"]},"description":{"type":"string","description":"Reporter description (10..5000 chars)"},"reporterEmail":{"type":"string","description":"Optional reporter email for follow-up"},"resourceNamespace":{"type":"string","description":"Optional namespace of the reported resource (e.g. 'public'). When supplied with resourceId takes priority over URL parsing."},"resourceId":{"type":"string","description":"Optional 10-character short ID of the reported resource. When supplied with resourceNamespace takes priority over URL parsing."}}},"ReportResponse":{"type":"object","properties":{"reportId":{"type":"string","description":"Unique 10-character alphanumeric report ID"},"receivedAt":{"type":"string","format":"date-time","description":"Server timestamp when the report was accepted"}}},"KvCreateStoreRequest":{"type":"object","properties":{"key":{"type":"string","description":"Initial key name"},"value":{"type":"string","description":"Value for the initial key (max 1 MB)"}}},"KvCreateStoreResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique 10-character alphanumeric store ID"},"namespace":{"type":"string"},"key":{"type":"string"},"sizeBytes":{"type":"integer","format":"int64"},"link":{"type":"string","description":"Public short URL for the initial key"},"ownerLink":{"type":"string","description":"API URL for programmatic access (upsert/delete)"},"expiresAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"type":"string"}},"ownerToken":{"type":"string","description":"Secret token required for upsert and delete operations. Store it safely -- cannot be recovered"},"visibilityNotice":{"type":"string","description":"Human-readable notice describing the published item's visibility (public/gallery+indexed vs unlisted)."}}},"JsonCreateResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique 10-character alphanumeric document ID"},"namespace":{"type":"string"},"link":{"type":"string","description":"Public short URL for browser viewing"},"ownerLink":{"type":"string","description":"API URL for programmatic access (update/delete)"},"sizeBytes":{"type":"integer","format":"int64"},"expiresAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"type":"string"}},"ownerToken":{"type":"string","description":"Secret token required for update and delete operations. Store it safely -- cannot be recovered"},"visibilityNotice":{"type":"string","description":"Human-readable notice describing the published item's visibility (public/gallery+indexed vs unlisted)."}}},"HtmlUploadRequest":{"type":"object","properties":{"content":{"type":"string","description":"HTML or markdown content to publish"},"filename":{"type":"string","description":"Optional original filename used as the tab title fallback and download basename. Trimmed; rejected if it contains path separators or control characters, length > 200, or bare name shorter than 4 chars"},"showTopBar":{"type":"boolean","description":"Per-content toggle for the frontend top toolbar. null = use global default (app.ui.show-top-bar-default)"}}},"HtmlUploadResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique 10-character alphanumeric page ID"},"namespace":{"type":"string"},"link":{"type":"string","description":"Public short URL for browser viewing"},"ownerLink":{"type":"string","description":"API URL for programmatic access (update/delete)"},"expiresAt":{"type":"string","format":"date-time"},"sizeBytes":{"type":"integer","format":"int64"},"tags":{"type":"array","items":{"type":"string"}},"ownerToken":{"type":"string","description":"Secret token required for update and delete operations. Store it safely -- cannot be recovered"},"result":{"$ref":"#/components/schemas/AbuseResult","description":"Abuse-v2 outcome envelope: code (success|duplicate|warned|failed) + action (created|updated|merged|created_warned|...) + optional reason. Present on every POST/PUT response."},"visibilityNotice":{"type":"string","description":"Human-readable notice describing the published item's visibility (public/gallery+indexed vs unlisted)."}}},"FileUploadResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique 10-character alphanumeric file ID"},"namespace":{"type":"string"},"filename":{"type":"string","description":"Original filename as uploaded"},"contentType":{"type":"string","description":"Detected MIME type (e.g. image/png)"},"sizeBytes":{"type":"integer","format":"int64"},"link":{"type":"string","description":"Public short URL for browser download"},"ownerLink":{"type":"string","description":"API URL for programmatic access (delete)"},"expiresAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"type":"string"}},"ownerToken":{"type":"string","description":"Secret token required for delete operation. Store it safely -- cannot be recovered"},"visibilityNotice":{"type":"string","description":"Human-readable notice describing the published item's visibility (public/gallery+indexed vs unlisted)."}}},"OwnerCheckResponse":{"type":"object","properties":{"type":{"type":"string"},"owner":{"type":"boolean"}}},"StatsResponse":{"type":"object","properties":{"createdToday":{"type":"integer","format":"int64","description":"Resources created in the last 24 hours"},"totalCreated":{"type":"integer","format":"int64","description":"All-time resource count"},"viewsToday":{"type":"integer","format":"int64","description":"Views in the last 24 hours"},"totalViews":{"type":"integer","format":"int64","description":"All-time view count"},"breakdown":{"type":"array","description":"Per-type breakdown (html, json, kv, file, site)","items":{"$ref":"#/components/schemas/TypeBreakdown"}},"createdTodayPublic":{"type":"integer","format":"int64","description":"Resources created today in the public namespace"},"createdTodayPrivate":{"type":"integer","format":"int64","description":"Resources created today in private namespaces"},"viewsTodayPublic":{"type":"integer","format":"int64","description":"Views today on resources in the public namespace"},"viewsTodayPrivate":{"type":"integer","format":"int64","description":"Views today on resources in private namespaces"},"totalCreatedPublic":{"type":"integer","format":"int64","description":"All-time resources created in the public namespace"},"totalCreatedPrivate":{"type":"integer","format":"int64","description":"All-time resources created in private namespaces"},"totalViewsPublic":{"type":"integer","format":"int64","description":"All-time views on resources in the public namespace"},"totalViewsPrivate":{"type":"integer","format":"int64","description":"All-time views on resources in private namespaces"},"deletedToday":{"type":"integer","format":"int64","description":"Resources deleted in the last 24 hours"},"deletedTodayPublic":{"type":"integer","format":"int64","description":"Resources deleted today in the public namespace"},"deletedTodayPrivate":{"type":"integer","format":"int64","description":"Resources deleted today in private namespaces"},"totalDeleted":{"type":"integer","format":"int64","description":"All-time deleted resource count"},"totalDeletedPublic":{"type":"integer","format":"int64","description":"All-time deleted resources in the public namespace"},"totalDeletedPrivate":{"type":"integer","format":"int64","description":"All-time deleted resources in private namespaces"}}},"TypeBreakdown":{"type":"object","properties":{"type":{"type":"string","description":"Resource type: html, json, kv, file, or site"},"today":{"type":"integer","format":"int64","description":"Created in the last 24 hours"},"total":{"type":"integer","format":"int64","description":"All-time count"},"todayPublic":{"type":"integer","format":"int64","description":"Created today in the public namespace"},"todayPrivate":{"type":"integer","format":"int64","description":"Created today in private namespaces"},"totalPublic":{"type":"integer","format":"int64","description":"All-time count in the public namespace"},"totalPrivate":{"type":"integer","format":"int64","description":"All-time count in private namespaces"},"todayDeleted":{"type":"integer","format":"int64","description":"Deleted in the last 24 hours"},"todayDeletedPublic":{"type":"integer","format":"int64","description":"Deleted today in the public namespace"},"todayDeletedPrivate":{"type":"integer","format":"int64","description":"Deleted today in private namespaces"},"totalDeleted":{"type":"integer","format":"int64","description":"All-time deleted count"},"totalDeletedPublic":{"type":"integer","format":"int64","description":"All-time deleted in the public namespace"},"totalDeletedPrivate":{"type":"integer","format":"int64","description":"All-time deleted in private namespaces"}}},"SiteFileInfo":{"type":"object","properties":{"path":{"type":"string","description":"Relative file path within the site"},"contentType":{"type":"string","description":"Detected MIME type"},"sizeBytes":{"type":"integer","format":"int64"}}},"SiteInfoResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique 10-character alphanumeric site ID"},"namespace":{"type":"string"},"entryFile":{"type":"string","description":"Entry HTML file resolved from the upload"},"fileCount":{"type":"integer","format":"int32"},"totalSizeBytes":{"type":"integer","format":"int64"},"files":{"type":"array","description":"List of all files in the site","items":{"$ref":"#/components/schemas/SiteFileInfo"}},"createdAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"views":{"type":"integer","format":"int64"},"tags":{"type":"array","items":{"type":"string"}}}},"OwnerTokenResponse":{"type":"object","properties":{"token":{"type":"string"},"ownerId":{"type":"string"}}},"RandomNamespaceResponse":{"type":"object","properties":{"namespace":{"type":"string"}}},"KvStoreListResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique 10-character alphanumeric store ID"},"keyCount":{"type":"integer","format":"int32","description":"Number of keys in the store"},"createdAt":{"type":"string","format":"date-time","description":"When the first key in the store was created"}}},"KvListKeysResponse":{"type":"object","properties":{"keys":{"type":"array","description":"All key names in the store","items":{"type":"string"}},"count":{"type":"integer","format":"int32","description":"Total number of keys"},"expiresAt":{"type":"string","format":"date-time","description":"When the store expires"},"views":{"type":"integer","format":"int64","description":"Aggregated views across all keys in this store"}}},"KvGetResponse":{"type":"object","properties":{"value":{"type":"string"},"updatedAt":{"type":"string","format":"date-time","description":"When the value was last written or updated"},"expiresAt":{"type":"string","format":"date-time","description":"When the value expires"},"views":{"type":"integer","format":"int64","description":"Total number of reads for this key (post-increment)"}}},"JsonListResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique 10-character alphanumeric document ID"},"content":{"type":"string"},"size":{"type":"integer","format":"int64"},"createdAt":{"type":"string","format":"date-time"}}},"GalleryItem":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"Resource type: html, json, kv, file, or site"},"title":{"type":"string","description":"Display title derived from content or filename"},"createdAt":{"type":"string","format":"date-time"},"views":{"type":"integer","format":"int64","description":"Total view count"},"visibility":{"type":"string","description":"Per-item visibility classification — public on default responses; my_public/my_public_password/my_private on mine=true","enum":["public","my_public","my_public_password","my_private"]},"namespace":{"type":"string","description":"Namespace the item lives in — 'public' on default responses; mine=true returns the real namespace (e.g. 'public', 'secret-ns') so the frontend can build correct /{ns}/{id} links"}}},"GalleryResponse":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/GalleryItem"}},"total":{"type":"integer","format":"int64","description":"Total number of matching items across all pages"},"page":{"type":"integer","format":"int32","description":"Current page number (1-based)"},"size":{"type":"integer","format":"int32","description":"Items per page"}}},"FileListResponse":{"type":"object","properties":{"id":{"type":"string","description":"Unique 10-character alphanumeric file ID"},"filename":{"type":"string","description":"Original filename as uploaded"},"contentType":{"type":"string","description":"Detected MIME type (e.g. image/png)"},"size":{"type":"integer","format":"int64"},"link":{"type":"string","description":"Public short URL for browser download"},"createdAt":{"type":"string","format":"date-time"}}}}}}