openapi: 3.1.0 info: title: BrewPage API description: "Free instant hosting for HTML, Markdown, AI artifacts and files" version: 1.33.3 servers: - url: https://brewpage.app 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: 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/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 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 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 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 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 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 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: $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_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: 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 5)" required: false schema: type: integer format: int32 - 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 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-365, default 5). Store auto-deletes\ \ after expiry" required: false schema: type: integer format: int32 - 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-365, default 5). Document auto-deletes\ \ after expiry" required: false schema: type: integer format: int32 - 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 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-365, default 5). Page auto-deletes after\ \ expiry" required: false schema: type: integer format: int32 - name: format in: query description: "Content format: 'html' (default), 'markdown', or 'md'. Markdown\ \ is rendered to styled HTML with github-markdown-css" 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 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-365, default 5). File auto-deletes after\ \ expiry" required: false schema: type: integer format: int32 - 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: 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 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: 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 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 /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}: 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" delete: tags: - Sites summary: Delete site description: Permanently removes the site and all its files from storage 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 delete required: true schema: type: string responses: "204": description: Site deleted "403": description: Missing or wrong owner token "404": description: Site not found /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 responses: "200": description: File content content: '*/*': schema: type: string format: byte "403": description: Wrong password content: '*/*': schema: type: string format: byte "404": description: Site or file not found content: '*/*': schema: type: string format: byte /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/namespace/random: get: tags: - Namespace summary: Suggest a random namespace description: Returns a fresh `--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 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/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 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: 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 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 HtmlUpdateRequest: type: object properties: content: type: string description: New HTML content to replace existing page 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 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 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 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 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 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 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 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, or file" 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 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