{"schema_version":"1.0","name":"htmlbin","description":"Agent-first HTML hosting. Drop self-contained HTML, get a public URL.","home":"https://htmlbin.aesir.works","contact":"https://htmlbin.aesir.works/","onboarding":{"url":"https://htmlbin.aesir.works/api/onboard","content_types":["text/markdown","application/json"],"summary":"Single endpoint that returns the full agent protocol as markdown."},"auth":{"type":"device_code","start":"https://htmlbin.aesir.works/api/auth/start","poll":"https://htmlbin.aesir.works/api/auth/poll","verify_human_at":"https://htmlbin.aesir.works/verify","token_format":"Bearer hb_<base62>","token_lifetime":"non-expiring (revocable)"},"mcp":{"url":"https://htmlbin.aesir.works/mcp","transport":"streamable_http","auth":"Authorization: Bearer hb_<base62>","summary":"Same token as the REST API. GitHub sign-in happens once, when that token is minted. Send the header on every request, including initialize."},"skill":{"url":"https://htmlbin.aesir.works/.well-known/agent-skills/htmlbin/SKILL.md","summary":"Reference skill for the HTTP API and the MCP tools."},"capabilities":[{"id":"publish_html","description":"Upload self-contained HTML up to 2 MB; receive a permanent public URL. Creates v1. Returns the full Drop with status 201. Optional `metadata` (string→string, ≤10 keys) attaches owner-side tags for later lookup.","method":"POST","path":"/api/drops","accepts":["title","description?","html","passcode?","context?","metadata?"]},{"id":"update_html","description":"PUT mints a NEW version on the same slug — URL is preserved across iterations. `html` is required; title/description/metadata optional. Metadata replace semantics: omit → untouched, {} → cleared.","method":"PUT","path":"/api/drops/:slug","accepts":["html","title?","description?","context?","metadata?"]},{"id":"update_metadata","description":"PATCH updates title, description, and/or metadata without minting a new version. Returns 400 metadata_only_on_patch if `html` is included. Metadata replace semantics: omit → untouched, {} → cleared.","method":"PATCH","path":"/api/drops/:slug","accepts":["title?","description?","metadata?"]},{"id":"list_versions","method":"GET","path":"/api/drops/:slug/versions"},{"id":"get_version","description":"Includes the optional context recorded at that version.","method":"GET","path":"/api/drops/:slug/v/:n"},{"id":"delete_drop","description":"Returns 204 No Content.","method":"DELETE","path":"/api/drops/:slug"},{"id":"delete_version","description":"Delete a single version. Refused for the last remaining version; if the deleted version was the head, latest_version is recomputed. Returns the updated Drop.","method":"DELETE","path":"/api/drops/:slug/v/:n"},{"id":"list_my_drops","description":"Paginated list. Query params: page (default 1), pageSize (default 50, max 200), sortBy (created_at|updated_at|view_count), sortOrder (asc|desc). Also supports repeated `metadata.<key>=<value>` params for AND-filtering — the canonical lookup-then-mutate primitive for finding a drop you previously tagged (any tag combination — PR previews, session artifacts, client portfolios, document kinds, etc).","method":"GET","path":"/api/drops"},{"id":"lock_with_passcode","description":"Set, change, or remove a passcode (soft share gate, not encryption). Pass empty string to remove. Returns the updated Drop.","method":"POST","path":"/api/drops/:slug/passcode"},{"id":"whoami","description":"Returns user_id, created_at, drop_count, and current token info (id, label, created_at, last_used_at).","method":"GET","path":"/api/me"},{"id":"list_my_tokens","description":"List this user's tokens across machines, including revoked ones (revoked_at set). No plaintext.","method":"GET","path":"/api/tokens"},{"id":"revoke_token","description":"Revoke a specific token by its short id (first 12 hex chars of the hash). Returns 204 No Content.","method":"DELETE","path":"/api/tokens/:id"}],"error_shape":{"shape":"{ error: { code, message, details? } }","switch_on":"error.code","note":"All 4xx/5xx responses use this shape. Rate-limited responses (429) additionally carry a Retry-After header."},"naming_convention":"snake_case","cors":{"posture":"deny","note":"No CORS headers are set. Browser cross-origin XHR will fail by design. htmlbin is an agent-side API; call it from server-side scripts and agent runtimes, not from arbitrary third-party web pages."},"versioning":{"semantics":"Every PUT with a new html body increments the version. Slug + URL stay stable.","view_specific_version":"/p/<slug>?v=<n>","raw_specific_version":"/p/<slug>/raw?v=<n>"},"cross_machine_auth":{"method":"Run /api/auth/start on a new machine and sign in with the same GitHub account at /verify. We bind one htmlbin account per GitHub identity.","result":"Both devices share the same user_id with separate tokens."},"limits":{"max_html_bytes":2097152,"max_context_bytes":65536,"max_versions_per_drop":200,"writes_per_minute":60,"writes_per_day":500,"drops_per_account":500,"verification_ttl_seconds":600},"spec":{"openapi":"https://htmlbin.aesir.works/openapi.json","llms_txt":"https://htmlbin.aesir.works/llms.txt","agent_skills_index":"https://htmlbin.aesir.works/.well-known/agent-skills/index.json","api_catalog":"https://htmlbin.aesir.works/.well-known/api-catalog"},"license":"MIT"}