{"openapi":"3.1.0","info":{"title":"docbin API","version":"1.0.0","description":"Upload and version HTML or Markdown docs over HTTP. Everything the web UI and MCP server do, you can do with a `dbk_` API key, except managing keys - that is browser-only. Create a key under Settings > API keys; the secret is shown once."},"servers":[{"url":"https://docbin.app","description":"Production"}],"components":{"securitySchemes":{"Bearer":{"type":"http","scheme":"bearer","description":"API key as `dbk_...`"},"Session":{"type":"apiKey","in":"cookie","name":"db_session","description":"Browser session cookie. An API key cannot manage API keys."}},"schemas":{"UploadResult":{"type":"object","properties":{"url":{"type":"string","format":"uri","example":"https://docbin.app/you/release-notes"},"version":{"type":"integer","minimum":0,"exclusiveMinimum":true,"example":1}},"required":["url","version"]},"Error":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]},"Visibility":{"type":"string","enum":["private","unlisted","public"],"description":"Defaults to public when omitted.","example":"public"},"Theme":{"type":"string","enum":["paper","dark","terminal","book"],"description":"Pinned render theme. Omit to follow the viewer's light/dark preference. Unknown values unpin rather than error.","example":"terminal"},"Expires":{"type":"string","enum":["never","10m","1h","1d","1w","2w","1mo","6mo","1y"],"description":"Expiry measured from creation. Omit for the route default: account docs never expire; anonymous pastes are capped at 30 days, which this can only shorten.","example":"1w"},"UploadBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64,"description":"URL slug. Auto-derived from the content if omitted.","example":"release-notes"},"content":{"type":"string","minLength":1,"description":"Raw HTML, Markdown, or code body. Provide this OR files[].","example":"# Hello"},"contentType":{"type":"string","enum":["html","md","mdx","code"],"description":"mdx renders Markdown with JSX components in a sandbox. code covers source, plain text, and logs alike - highlighted when a language is given, plain otherwise. ('text' is accepted as a legacy alias of code.)","example":"md"},"language":{"type":"string","minLength":1,"maxLength":32,"description":"Syntax-highlight language hint for 'code' and 'text' content. Log-friendly grammars 'log' and 'accesslog' are included alongside the usual languages.","example":"rust"},"visibility":{"$ref":"#/components/schemas/Visibility"},"theme":{"$ref":"#/components/schemas/Theme"},"expires":{"$ref":"#/components/schemas/Expires"},"files":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":96},"content":{"type":"string"},"language":{"type":"string","minLength":1,"maxLength":32,"description":"Syntax-highlight language hint for 'code' and 'text' content. Log-friendly grammars 'log' and 'accesslog' are included alongside the usual languages.","example":"rust"}},"required":["name","content"]},"maxItems":50,"description":"Create a multi-file group; supersedes content/contentType. The entry file is auto-selected (index/readme/main, else first)."}},"required":["contentType"]},"ImportBody":{"type":"object","properties":{"url":{"type":"string","format":"uri","example":"https://gist.github.com/you/abc123"},"name":{"type":"string","example":"imported"},"visibility":{"type":"string","enum":["private","unlisted","public"]}},"required":["url"]},"NewVersionBody":{"type":"object","properties":{"content":{"type":"string","minLength":1,"example":"# Hello, again"},"contentType":{"type":"string","enum":["html","md","mdx","code"],"description":"mdx renders Markdown with JSX components in a sandbox. code covers source, plain text, and logs alike - highlighted when a language is given, plain otherwise. ('text' is accepted as a legacy alias of code.)","example":"md"},"language":{"type":"string","minLength":1,"maxLength":32,"description":"Syntax-highlight language hint for 'code' and 'text' content. Log-friendly grammars 'log' and 'accesslog' are included alongside the usual languages.","example":"rust"},"visibility":{"allOf":[{"$ref":"#/components/schemas/Visibility"},{"description":"Optional. Changes the doc's visibility if provided."}]},"theme":{"allOf":[{"$ref":"#/components/schemas/Theme"},{"description":"Optional. Re-pins the doc's render theme; omitted leaves it unchanged."}]},"expires":{"allOf":[{"$ref":"#/components/schemas/Expires"},{"description":"Optional. Resets the doc's expiry from now; omitted leaves it unchanged."}]}},"required":["content","contentType"]},"PublicFeed":{"type":"object","properties":{"docs":{"type":"array","items":{"type":"object","properties":{"handle":{"type":"string","example":"you"},"name":{"type":"string","example":"release-notes"},"title":{"type":"string","example":"Release notes"},"contentType":{"type":"string","example":"md"},"createdAt":{"type":"integer","example":1788289693351},"appUrl":{"type":"string","example":"https://docbin.app/you/release-notes"},"rawUrl":{"type":"string","example":"https://docbin.app/you/release-notes?raw=1"}},"required":["handle","name","title","contentType","createdAt","appUrl","rawUrl"]}},"next":{"type":"integer","nullable":true,"example":1788289693351}},"required":["docs","next"]},"DocMeta":{"type":"object","properties":{"handle":{"type":"string","example":"you"},"name":{"type":"string","example":"release-notes"},"latest":{"type":"integer","example":3},"versions":{"type":"array","items":{"type":"integer"},"example":[3,2,1]}},"required":["handle","name","latest","versions"]},"KeyCreated":{"type":"object","properties":{"id":{"type":"string","example":"k_8f3a2b"},"secret":{"type":"string","example":"dbk_live_9c2b1a4d..."},"label":{"type":"string","example":"ci"},"prefix":{"type":"string","example":"dbk_9c2b1a"}},"required":["id","secret","label","prefix"]},"KeyCreateBody":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":64,"example":"ci"}},"required":["label"]},"KeyInfo":{"type":"object","properties":{"id":{"type":"string","example":"k_8f3a2b"},"label":{"type":"string","example":"ci"},"prefix":{"type":"string","example":"dbk_9c2b1a"},"createdAt":{"type":"number","example":1736900000000},"lastUsedAt":{"type":"number","nullable":true,"example":1736986400000},"revokedAt":{"type":"number","nullable":true,"example":null}},"required":["id","label","prefix","createdAt","lastUsedAt","revokedAt"]},"ReportFiled":{"type":"object","properties":{"status":{"type":"string","example":"filed"}},"required":["status"]},"ReportBody":{"type":"object","properties":{"handle":{"type":"string","example":"someone"},"name":{"type":"string","example":"bad-doc"},"reason":{"type":"string","minLength":1,"maxLength":2000,"example":"spam"}},"required":["handle","name","reason"]}},"parameters":{}},"paths":{"/api/docs":{"post":{"tags":["Docs"],"summary":"Upload a doc","security":[{"Bearer":[]}],"description":"Create a new doc, or the first version under a name you choose. Returns the canonical URL under your handle.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadBody"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResult"}}}},"400":{"description":"Invalid body or bad name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Private doc limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/docs/paste":{"post":{"tags":["Docs"],"summary":"Paste from your terminal","security":[{"Bearer":[]}],"description":"POST a raw body and get the doc URL back as plain text - built for `curl --data-binary @file -H \"Authorization: Bearer <key>\" https://docbin.app/api/docs/paste`. Also accepts `multipart/form-data` with a `file` field (`curl -F file=@crash.log`), in which case the filename drives the name, type, and language unless overridden by query params. Tune with the query params; send `Accept: application/json` for a JSON response instead of text. Repeated multipart `file` fields create a multi-file group.","parameters":[{"schema":{"type":"string","example":"snippet"},"required":false,"name":"name","in":"query"},{"schema":{"type":"string","enum":["html","md","code"],"description":"code covers source, plain text, and logs; highlighted when language is given.","example":"code"},"required":false,"name":"type","in":"query"},{"schema":{"type":"string","example":"rust"},"required":false,"name":"language","in":"query"},{"schema":{"type":"string","enum":["private","unlisted","public"],"description":"Defaults to public when omitted."},"required":false,"name":"visibility","in":"query"},{"schema":{"$ref":"#/components/schemas/Theme"},"required":false,"name":"theme","in":"query"},{"schema":{"$ref":"#/components/schemas/Expires"},"required":false,"name":"expires","in":"query"}],"requestBody":{"content":{"text/plain":{"schema":{"type":"string","example":"fn main() { println!(\"hi\"); }"}}}},"responses":{"201":{"description":"Created - the URL as plain text","content":{"text/plain":{"schema":{"type":"string","example":"https://docbin.app/you/snippet"}}}},"400":{"description":"Empty body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Private doc limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/docs/paste/anon":{"post":{"tags":["Docs"],"summary":"Paste without an account","description":"Like `/api/docs/paste` but keyless - no authentication. The doc is owned by the reserved `anon` handle under a server-generated name (the `name` query param is ignored), is always unlisted (the `visibility` query param is ignored), expires automatically after 30 days, and is subject to a smaller body cap and a stricter per-IP rate limit. Also accepts `multipart/form-data` with a `file` field; the filename drives type and language unless overridden by query params. Send `Accept: application/json` for a JSON response instead of text.","parameters":[{"schema":{"type":"string","enum":["html","md","code"],"description":"code covers source, plain text, and logs; highlighted when language is given.","example":"code"},"required":false,"name":"type","in":"query"},{"schema":{"type":"string","example":"rust"},"required":false,"name":"language","in":"query"},{"schema":{"$ref":"#/components/schemas/Theme"},"required":false,"name":"theme","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/Expires"},{"description":"Anonymous docs always expire; this can only shorten the 30-day cap."}]},"required":false,"name":"expires","in":"query"}],"requestBody":{"content":{"text/plain":{"schema":{"type":"string","example":"[12:00:01] game started"}}}},"responses":{"201":{"description":"Created - the URL as plain text","content":{"text/plain":{"schema":{"type":"string","example":"https://docbin.app/anon/log-1780000000-a1b2c3"}}}},"400":{"description":"Empty body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/docs/import":{"post":{"tags":["Docs"],"summary":"Import from a URL","security":[{"Bearer":[]}],"description":"Fetch a public URL (or a GitHub gist) and store its body as a doc. The content type is inferred from the URL's extension. Only http(s) targets are allowed.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportBody"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResult"}}}},"400":{"description":"Invalid URL or unsupported scheme","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Private doc limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Could not fetch the source","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/docs/{handle}/{name}":{"post":{"tags":["Docs"],"summary":"Add a new version","security":[{"Bearer":[]}],"description":"Add a new immutable version to an existing doc you own. Old version URLs keep working. On an existing doc the visibility field is ignored - change visibility in the app. If no doc by that name exists this route creates one, and there the field applies, defaulting to public.","parameters":[{"schema":{"type":"string","example":"you"},"required":true,"name":"handle","in":"path"},{"schema":{"type":"string","example":"release-notes"},"required":true,"name":"name","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewVersionBody"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResult"}}}},"400":{"description":"Invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Docs"],"summary":"Doc metadata","description":"Fetch a doc's latest version number and full version list. Auth is optional - required only to read a private doc you own.","parameters":[{"schema":{"type":"string","example":"you"},"required":true,"name":"handle","in":"path"},{"schema":{"type":"string","example":"release-notes"},"required":true,"name":"name","in":"path"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocMeta"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Docs"],"summary":"Delete a doc","security":[{"Bearer":[]}],"description":"Permanently delete a doc you own and all of its versions. This cannot be undone.","parameters":[{"schema":{"type":"string","example":"you"},"required":true,"name":"handle","in":"path"},{"schema":{"type":"string","example":"release-notes"},"required":true,"name":"name","in":"path"}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/docs/public":{"get":{"tags":["Docs"],"summary":"Recent public docs","description":"Every public doc, newest first. No key needed. Page back by passing the last row's createdAt as ?before=. Private, unlisted, expired and burn-after-read docs are never listed.","parameters":[{"schema":{"type":"integer","nullable":true,"example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"example":1788289693351},"required":false,"name":"before","in":"query"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicFeed"}}}}}}},"/api/keys":{"post":{"tags":["API keys"],"summary":"Create an API key","security":[{"Session":[]}],"description":"Mint a new API key. The secret is returned once and never again - store it now. Browser session only: an API key cannot create API keys.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyCreateBody"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyCreated"}}}},"400":{"description":"Label required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"API key used - key management needs a browser session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["API keys"],"summary":"List API keys","security":[{"Session":[]}],"description":"List your keys. Secrets are never returned - only the prefix, label, and usage timestamps.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KeyInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"API key used - key management needs a browser session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/keys/{id}":{"delete":{"tags":["API keys"],"summary":"Revoke an API key","security":[{"Session":[]}],"description":"Revoke a key by id. Requests using it stop working immediately.","parameters":[{"schema":{"type":"string","example":"k_8f3a"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"Revoked"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"API key used - key management needs a browser session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/reports":{"post":{"tags":["Reports"],"summary":"Report a doc","description":"Flag a doc for review. No authentication required. Rate limited.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportBody"}}}},"responses":{"201":{"description":"Filed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportFiled"}}}},"400":{"description":"Invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Doc not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}