{"openapi":"3.0.3","info":{"title":"Tabularium API","description":"Tabularium — index, submit, and discover plugins.","version":"1.0.0","contact":{"name":"Tabularium"},"license":{"name":"Apache-2.0"}},"servers":[{"url":"https://registry.tabularis.dev","description":"Production"}],"tags":[{"name":"Plugins","description":"Browse and inspect indexed plugins."},{"name":"Auth","description":"OAuth + email/password sign-in."},{"name":"Submit","description":"Register a new plugin via OAuth."},{"name":"Webhooks","description":"Release-event ingestion."},{"name":"Requests","description":"Community-driven plugin wish list."},{"name":"Admin","description":"Provider instances, settings, users (admin only)."}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"},"cookieAuth":{"type":"apiKey","in":"cookie","name":"auth"}},"schemas":{}},"paths":{"/healthz/":{"get":{"tags":["Plugins"],"summary":"Health check","description":"Liveness/readiness probe. Returns `{ ok: true }` (200) when DB + cache + storage are reachable, `{ ok: false }` (503) otherwise. Intentionally terse to avoid leaking driver / setup-mode details to unauthenticated callers — admins should hit `/api/admin/diagnostics` for the full breakdown.","operationId":"healthz","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"}},"additionalProperties":false}}}},"503":{"description":"Response for status 503","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"}},"additionalProperties":false}}}}}}},"/api/plugins/":{"get":{"tags":["Plugins"],"summary":"List plugins","description":"Paginated catalog. Supports `search` (name/description/slug), `category` (exact), `tag` (exact), `featured=1`, and `sort=updated|new|name|featured`. Returns facets so the UI can render filter chips without an extra request. Public — no auth required.","operationId":"listPlugins","parameters":[{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"category","in":"query","required":false,"schema":{"type":"string"}},{"name":"tag","in":"query","required":false,"schema":{"type":"string"}},{"name":"kind","in":"query","required":false,"schema":{"type":"string"}},{"name":"featured","in":"query","required":false,"schema":{"type":"string"}},{"name":"verified","in":"query","required":false,"schema":{"type":"string"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["updated","new","name","featured"]}},{"name":"page","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["total","page","limit","plugins","facets"],"properties":{"total":{"type":"number"},"page":{"type":"number"},"limit":{"type":"number"},"plugins":{"type":"array","items":{"type":"object","required":["id","ownerId","providerInstanceId","name","description","author","repoUrl","homepage","latestVersion","status","category","tags","license","iconUrl","screenshots","documentationUrl","supportEmail","issuesUrl","featured","featuredOrder","verified","verifiedAt","extensions","downloads","manifestFetchedAt","createdAt","updatedAt"],"properties":{"id":{"type":"string"},"ownerId":{"type":"string"},"providerInstanceId":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"name":{"type":"string"},"description":{"type":"string"},"author":{"type":"string"},"repoUrl":{"type":"string"},"homepage":{"type":"string"},"latestVersion":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string","enum":["approved","pending","rejected"]},"category":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"tags":{"type":"array","items":{"type":"string"}},"license":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"iconUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"screenshots":{"type":"array","items":{"type":"object","required":["url","caption","alt"],"properties":{"url":{"type":"string"},"caption":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"alt":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}}},"documentationUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"supportEmail":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"issuesUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"featured":{"type":"boolean"},"featuredOrder":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"verified":{"type":"boolean"},"verifiedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"extensions":{"nullable":true,"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]},"downloads":{"type":"number"},"manifestFetchedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"createdAt":{"type":"number"},"updatedAt":{"type":"number"}}}},"facets":{"type":"object","required":["categories","kinds"],"properties":{"categories":{"type":"array","items":{"type":"object","required":["value","count"],"properties":{"value":{"type":"string"},"count":{"type":"number"}}}},"kinds":{"type":"array","items":{"type":"object","required":["key","label","count"],"properties":{"key":{"type":"string"},"label":{"type":"string"},"count":{"type":"number"}}}}}}}}}}}}}},"/api/plugins/{slug}/":{"get":{"tags":["Plugins"],"summary":"Get plugin detail","description":"Full plugin record including release history and the rendered README HTML (sanitized via DOMPurify, cached 10 min). Public — no auth required. Pass `?locale=` to request a localized README; falls back to base language, then `en`, then the first available.","operationId":"getPlugin","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"locale","in":"query","required":false,"schema":{"maxLength":10,"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["id","ownerId","providerInstanceId","name","description","author","repoUrl","homepage","latestVersion","status","category","tags","license","iconUrl","screenshots","documentationUrl","supportEmail","issuesUrl","featured","featuredOrder","verified","verifiedAt","extensions","downloads","manifestFetchedAt","readmeHtml","readmeLocale","readmeAvailableLocales","createdAt","updatedAt","releases"],"properties":{"id":{"type":"string"},"ownerId":{"type":"string"},"providerInstanceId":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"name":{"type":"string"},"description":{"type":"string"},"author":{"type":"string"},"repoUrl":{"type":"string"},"homepage":{"type":"string"},"latestVersion":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string","enum":["approved","pending","rejected"]},"category":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"tags":{"type":"array","items":{"type":"string"}},"license":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"iconUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"screenshots":{"type":"array","items":{"type":"object","required":["url","caption","alt"],"properties":{"url":{"type":"string"},"caption":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"alt":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}}},"documentationUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"supportEmail":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"issuesUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"featured":{"type":"boolean"},"featuredOrder":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"verified":{"type":"boolean"},"verifiedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"extensions":{"nullable":true,"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]},"downloads":{"type":"number"},"manifestFetchedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"readmeHtml":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"readmeLocale":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"readmeAvailableLocales":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"number"},"updatedAt":{"type":"number"},"releases":{"type":"array","items":{"type":"object","required":["id","pluginId","version","minRuntimeVersion","assets","createdAt","integrity"],"properties":{"id":{"type":"string"},"pluginId":{"type":"string"},"version":{"type":"string"},"minRuntimeVersion":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"assets":{"type":"object","patternProperties":{"^(.*)$":{"type":"object","required":["url"],"properties":{"url":{"type":"string"},"size":{"type":"number"},"sha256":{"type":"string"}}}}},"createdAt":{"type":"number"},"integrity":{"nullable":true,"anyOf":[{"type":"object","required":["jws","assets"],"properties":{"jws":{"type":"string"},"assets":{"type":"array","items":{"type":"object","required":["name","sha256","size","attestation_bundle"],"properties":{"name":{"type":"string"},"sha256":{"type":"string"},"size":{"type":"number"},"attestation_bundle":{}}}}}},{"type":"null"}]}}}}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Plugins"],"summary":"Delete plugin","description":"Removes the plugin and all of its releases from the registry. Requires auth and only the owner can perform it. The upstream repo and the webhook on the repo are left untouched.","operationId":"deletePlugin","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/plugins/{slug}/downloads/":{"get":{"tags":["Plugins"],"summary":"Aggregated download counts per version + platform","operationId":"getPluginDownloadStats","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["total","versions"],"properties":{"total":{"type":"number"},"versions":{"type":"array","items":{"type":"object","required":["version","total","platforms"],"properties":{"version":{"type":"string"},"total":{"type":"number"},"platforms":{"type":"object","patternProperties":{"^(.*)$":{"type":"number"}}}}}}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/plugins/{slug}/refresh-manifest/":{"post":{"tags":["Plugins"],"summary":"Re-fetch the .tabularium manifest (plugin owner)","description":"Pulls `.tabularium` (and the README) from the repo at the given ref (defaults to the latest released tag or HEAD). Authenticated plugin owners only; rate-limited to 1/min per plugin.","operationId":"authorRefreshPluginManifest","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ref":{"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"ref":{"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"ref":{"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","slug","source","ref"],"properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"source":{"type":"string"},"ref":{"type":"string"}}}}}},"401":{"description":"Response for status 401","content":{"application/json":{"schema":{"type":"object","required":["error","reauthFor"],"properties":{"error":{"type":"string"},"reauthFor":{"type":"string"}}}}}},"403":{"description":"Response for status 403","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"412":{"description":"Response for status 412","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"422":{"description":"Response for status 422","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/plugins/{slug}/latest/":{"get":{"tags":["Plugins"],"summary":"Resolve latest release for a platform","description":"Returns the matching asset for the requested platform. `os` and `arch` are optional — if missing, the User-Agent is inspected; if still unknown, falls back to `universal`. The full per-platform map is always included so clients can present alternatives.","operationId":"getLatestAsset","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"os","in":"query","required":false,"schema":{"description":"`linux`, `darwin`, or `win`.","type":"string"}},{"name":"arch","in":"query","required":false,"schema":{"description":"`x64` or `arm64`.","type":"string"}},{"name":"redirect","in":"query","required":false,"schema":{"description":"Pass `1` to 302-redirect to the download URL instead of returning JSON.","type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["version","min_runtime_version","platform","download_url","platforms"],"properties":{"version":{"type":"string"},"min_runtime_version":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"platform":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"download_url":{"type":"string"},"size":{"type":"number"},"sha256":{"type":"string"},"platforms":{"type":"object","patternProperties":{"^(.*)$":{"type":"object","required":["url"],"properties":{"url":{"type":"string"},"size":{"type":"number"},"sha256":{"type":"string"}}}}}}}}}},"302":{"description":"Response for status 302","content":{"text/plain":{"schema":{"type":"string"}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"platforms":{"type":"object","patternProperties":{"^(.*)$":{"type":"object","required":["url"],"properties":{"url":{"type":"string"},"size":{"type":"number"},"sha256":{"type":"string"}}}}}}}}}},"422":{"description":"Response for status 422","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"platforms":{"type":"object","patternProperties":{"^(.*)$":{"type":"object","required":["url"],"properties":{"url":{"type":"string"},"size":{"type":"number"},"sha256":{"type":"string"}}}}}}}}}}}}},"/api/plugins/{slug}/transfer/":{"post":{"tags":["Plugins"],"summary":"Initiate a plugin ownership transfer","description":"Owner-only. Creates a pending transfer the recipient must accept (or reject) within 7 days. Cancel anytime via `DELETE /auth/me/transfers/:id`. Only one pending transfer per plugin at a time.","operationId":"initiateTransfer","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["newOwnerId"],"properties":{"newOwnerId":{"type":"string"},"message":{"maxLength":500,"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["newOwnerId"],"properties":{"newOwnerId":{"type":"string"},"message":{"maxLength":500,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["newOwnerId"],"properties":{"newOwnerId":{"type":"string"},"message":{"maxLength":500,"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","transferId","pluginId","toUserId","expiresAt"],"properties":{"ok":{"type":"boolean"},"transferId":{"type":"string"},"pluginId":{"type":"string"},"toUserId":{"type":"string"},"expiresAt":{"type":"number"}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"403":{"description":"Response for status 403","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"409":{"description":"Response for status 409","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/plugins/{slug}/stats/":{"get":{"tags":["Plugins"],"summary":"Provider stats (stars / forks / last push) for a plugin","description":"Lazily fetched from the upstream provider and cached for 10 minutes. Returns null fields when the provider is unreachable or the repo is private.","operationId":"getPluginStats","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["stars","forks","watchers","lastPushAt","homepage"],"properties":{"stars":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"forks":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"watchers":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"lastPushAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"homepage":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"422":{"description":"Response for status 422","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/plugins/{slug}/rehash/":{"post":{"tags":["Plugins"],"summary":"Re-hash release assets (plugin owner)","description":"Re-fetches every asset of the given (or latest) release and updates `sha256` + `size`. Useful when the original webhook fetch raced asset upload. Set `force: true` to re-hash assets that already have a sha256. Authenticated plugin owners only; rate-limited to 1/min per plugin.","operationId":"authorRehashRelease","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"string"},"force":{"type":"boolean"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"version":{"type":"string"},"force":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"version":{"type":"string"},"force":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","slug","version","results"],"properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"version":{"type":"string"},"results":{"type":"object","patternProperties":{"^(.*)$":{"type":"object","properties":{"sha256":{"type":"string"},"size":{"type":"number"},"reason":{"type":"string"}}}}}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"401":{"description":"Response for status 401","content":{"application/json":{"schema":{"type":"object","required":["error","reauthFor"],"properties":{"error":{"type":"string"},"reauthFor":{"type":"string"}}}}}},"403":{"description":"Response for status 403","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"412":{"description":"Response for status 412","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"422":{"description":"Response for status 422","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/plugins/{slug}/releases/{version}/integrity/":{"get":{"tags":["Plugins"],"summary":"Asset integrity (sha256 + size) for a specific release","description":"Lookup endpoint for `sum.<host>`-style verifiers. When `release_assets` rows are present (post-ingest) the response includes the signed `jws` plus per-asset metadata (`name`, `sha256`, `size`, `attestation_bundle`). For legacy releases without per-asset rows yet, falls back to the platform-keyed JSON blob with `url`/`size`/`sha256`.","operationId":"getReleaseIntegrity","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"anyOf":[{"type":"object","required":["slug","version","jws","assets"],"properties":{"slug":{"type":"string"},"version":{"type":"string"},"jws":{"type":"string"},"assets":{"type":"array","items":{"type":"object","required":["name","sha256","size","attestation_bundle"],"properties":{"name":{"type":"string"},"sha256":{"type":"string"},"size":{"type":"number"},"attestation_bundle":{}}}}}},{"type":"object","required":["slug","version","assets"],"properties":{"slug":{"type":"string"},"version":{"type":"string"},"assets":{"type":"object","patternProperties":{"^(.*)$":{"type":"object","required":["url"],"properties":{"url":{"type":"string"},"size":{"type":"number"},"sha256":{"type":"string"}}}}}}}]}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/requests/":{"get":{"tags":["Requests"],"summary":"List plugin requests","description":"Browse the community wishlist of plugins users would like to see. Sorted by upvotes by default. Public — no auth required.","operationId":"listRequests","parameters":[{"name":"page","in":"query","required":false,"schema":{"description":"1-based page index. Default 1.","type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"description":"Items per page, max 100. Default 20.","type":"string"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["upvotes","recent"]}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["total","page","limit","requests"],"properties":{"total":{"type":"number"},"page":{"type":"number"},"limit":{"type":"number"},"requests":{"type":"array","items":{"type":"object","required":["id","slug","name","description","requesterId","upvotes","createdAt","claims","claimedByMe"],"properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"requesterId":{"type":"string"},"upvotes":{"type":"number"},"createdAt":{"type":"number"},"claims":{"type":"number"},"claimedByMe":{"type":"boolean"}}}}}}}}}}},"post":{"tags":["Requests"],"summary":"Create plugin request","description":"Add a new entry to the community wishlist. Requires auth. Slug must be unique across requests.","operationId":"createRequest","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["slug","name","description"],"properties":{"slug":{"pattern":"^[a-z0-9-]+$","minLength":1,"maxLength":80,"description":"URL-safe slug. Lowercase letters, digits, hyphens.","type":"string"},"name":{"minLength":1,"maxLength":120,"description":"Human-readable plugin name.","type":"string"},"description":{"minLength":1,"maxLength":2000,"description":"What the plugin should do.","type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["slug","name","description"],"properties":{"slug":{"pattern":"^[a-z0-9-]+$","minLength":1,"maxLength":80,"description":"URL-safe slug. Lowercase letters, digits, hyphens.","type":"string"},"name":{"minLength":1,"maxLength":120,"description":"Human-readable plugin name.","type":"string"},"description":{"minLength":1,"maxLength":2000,"description":"What the plugin should do.","type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["slug","name","description"],"properties":{"slug":{"pattern":"^[a-z0-9-]+$","minLength":1,"maxLength":80,"description":"URL-safe slug. Lowercase letters, digits, hyphens.","type":"string"},"name":{"minLength":1,"maxLength":120,"description":"Human-readable plugin name.","type":"string"},"description":{"minLength":1,"maxLength":2000,"description":"What the plugin should do.","type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["id","slug","name","description","requesterId"],"properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"requesterId":{"type":"string"}}}}}},"403":{"description":"Response for status 403","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"409":{"description":"Response for status 409","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/requests/{id}/upvote/":{"post":{"tags":["Requests"],"summary":"Toggle upvote on a request","description":"Toggles the current user's vote on the request: if not yet voted, adds a vote; if already voted, removes it. Returns the new total and the user's new vote state. Requires auth.","operationId":"toggleUpvote","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/requests/{id}/claim/":{"post":{"tags":["Requests"],"summary":"Toggle a claim on a plugin request","description":"Adds or removes the current user's claim on the request. Claims are non-exclusive — multiple users can claim the same request. They do not block plugin submission.","operationId":"toggleRequestClaim","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["claimed","claims"],"properties":{"claimed":{"type":"boolean"},"claims":{"type":"number"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/docs/plugin-development/":{"get":{"tags":["Plugins"],"summary":"Plugin developer docs (live, localized)","description":"Returns a structured PluginDocs document with the current core schema, global extensions, per-kind extensions, examples (YAML + JSON), and links to the OpenAPI spec. Generated on every request from the live registry state. Pass ?format=md for a single flat markdown blob (LLM-friendly).","operationId":"getPluginDocs","parameters":[{"name":"locale","in":"query","required":false,"schema":{"maxLength":16,"type":"string"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["md","markdown"]}}]}},"/api/kinds/":{"get":{"tags":["Plugins"],"summary":"List active plugin kinds","description":"Returns the registry-wide list of plugin kinds defined by the admin. Plugin authors mark a plugin's kind by adding the matching value to their `tags` field. Public — no auth required. A kind may opt in to a dedicated public catalogue page at `/c/:key` via `publicPageEnabled`; consumers treat the field as falsy when absent.","operationId":"listKinds","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["kinds"],"properties":{"kinds":{"type":"array","items":{"type":"object","required":["key","label","description"],"properties":{"key":{"type":"string"},"label":{"type":"string"},"description":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"publicPageEnabled":{"type":"boolean"},"publicPageCopy":{"nullable":true,"anyOf":[{"type":"object","required":["hero","intro"],"properties":{"hero":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"intro":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}},{"type":"null"}]}}}}}}}}}}}},"/api/admin/plugins/":{"get":{"tags":["Admin"],"summary":"List plugins (incl. non-approved)","operationId":"adminListPlugins","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["approved","pending","rejected"]}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["total","plugins"],"properties":{"total":{"type":"number"},"plugins":{"type":"array","items":{"type":"object","required":["id","ownerId","name","description","repoUrl","status","rejectionReason","latestVersion","featured","featuredOrder","verified","verifiedAt","verifiedBy","category","manifestFetchedAt","createdAt","updatedAt"],"properties":{"id":{"type":"string"},"ownerId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"repoUrl":{"type":"string"},"status":{"type":"string","enum":["approved","pending","rejected"]},"rejectionReason":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"latestVersion":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"featured":{"type":"boolean"},"featuredOrder":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"verified":{"type":"boolean"},"verifiedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"verifiedBy":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"category":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"manifestFetchedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"createdAt":{"type":"number"},"updatedAt":{"type":"number"}}}}}}}}}}}},"/api/admin/plugins/{id}/":{"patch":{"tags":["Admin"],"summary":"Update plugin (status / moderation / pin / categorize)","description":"Partial update. `status` toggles approval. `featured: true` pins to the landing grid (use `featuredOrder` to set position). `category`/`tags` are normally driven by the `.tabularium` manifest but can be overridden here.","operationId":"updatePlugin","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["approved","pending","rejected"]},"rejectionReason":{"maxLength":500,"type":"string"},"category":{"maxLength":40,"type":"string"},"tags":{"maxItems":16,"type":"array","items":{"maxLength":30,"type":"string"}},"featured":{"type":"boolean"},"featuredOrder":{"type":"number"},"ownerId":{"type":"string"},"verified":{"type":"boolean"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["approved","pending","rejected"]},"rejectionReason":{"maxLength":500,"type":"string"},"category":{"maxLength":40,"type":"string"},"tags":{"maxItems":16,"type":"array","items":{"maxLength":30,"type":"string"}},"featured":{"type":"boolean"},"featuredOrder":{"type":"number"},"ownerId":{"type":"string"},"verified":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["approved","pending","rejected"]},"rejectionReason":{"maxLength":500,"type":"string"},"category":{"maxLength":40,"type":"string"},"tags":{"maxItems":16,"type":"array","items":{"maxLength":30,"type":"string"}},"featured":{"type":"boolean"},"featuredOrder":{"type":"number"},"ownerId":{"type":"string"},"verified":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","id"],"properties":{"ok":{"type":"boolean"},"id":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Admin"],"summary":"Force-delete a plugin (admin override)","operationId":"adminDeletePlugin","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/admin/plugins/{id}/replay-webhook/":{"post":{"tags":["Admin"],"summary":"Manually replay the latest release ingest","description":"Re-fetches the upstream latest release via the owner's stored OAuth token and runs the same ingest path the webhook uses. Useful when the webhook never fired (initial setup) or upstream timed out. Skips signature verification.","operationId":"replayWebhook","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"},"version":{"type":"string"},"assets":{"type":"array","items":{"type":"string"}},"skipped":{"type":"boolean"},"reason":{"type":"string"}}}}}},"401":{"description":"Response for status 401","content":{"application/json":{"schema":{"type":"object","required":["error","reauthFor"],"properties":{"error":{"type":"string"},"reauthFor":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"412":{"description":"Response for status 412","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"422":{"description":"Response for status 422","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"423":{"description":"Response for status 423","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/plugins/{id}/refresh-manifest/":{"post":{"tags":["Admin"],"summary":"Re-fetch the .tabularium manifest from the repo","description":"Pulls `.tabularium` (and the README) from the repo at the given ref (defaults to the latest released tag or HEAD).","operationId":"refreshPluginManifest","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ref":{"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"ref":{"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"ref":{"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","slug","source","ref"],"properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"source":{"type":"string"},"ref":{"type":"string"}}}}}},"401":{"description":"Response for status 401","content":{"application/json":{"schema":{"type":"object","required":["error","reauthFor"],"properties":{"error":{"type":"string"},"reauthFor":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"412":{"description":"Response for status 412","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"422":{"description":"Response for status 422","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/plugins/{id}/rehash/":{"post":{"tags":["Admin"],"summary":"Re-hash release assets","description":"Re-fetches every asset of the given (or latest) release and updates `sha256` + `size`. Useful when the original webhook fetch failed. Set `force: true` to re-hash assets that already have a sha256.","operationId":"rehashRelease","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"string"},"force":{"type":"boolean"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"version":{"type":"string"},"force":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"version":{"type":"string"},"force":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","slug","version","results"],"properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"version":{"type":"string"},"results":{"type":"object","patternProperties":{"^(.*)$":{"type":"object","properties":{"sha256":{"type":"string"},"size":{"type":"number"},"reason":{"type":"string"}}}}}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/plugins/bulk/":{"post":{"tags":["Admin"],"summary":"Bulk-approve / reject / delete plugins","description":"Apply the same action to up to 100 plugins in one round-trip. `delete` cascades to releases. `reject` accepts an optional reason. Single audit-log entry per call.","operationId":"bulkPluginAction","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ids","action"],"properties":{"ids":{"maxItems":100,"type":"array","items":{"type":"string"}},"action":{"type":"string","enum":["approve","reject","delete"]},"rejectionReason":{"maxLength":500,"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["ids","action"],"properties":{"ids":{"maxItems":100,"type":"array","items":{"type":"string"}},"action":{"type":"string","enum":["approve","reject","delete"]},"rejectionReason":{"maxLength":500,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["ids","action"],"properties":{"ids":{"maxItems":100,"type":"array","items":{"type":"string"}},"action":{"type":"string","enum":["approve","reject","delete"]},"rejectionReason":{"maxLength":500,"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","action","affected","missing"],"properties":{"ok":{"type":"boolean"},"action":{"type":"string","enum":["approve","reject","delete"]},"affected":{"type":"number"},"missing":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/requests/{id}/":{"delete":{"tags":["Admin"],"summary":"Delete a plugin request","operationId":"adminDeleteRequest","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Response for status 204","content":{"type":"null"}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/docs/":{"get":{"tags":["Admin"],"summary":"Read docs config (intro/outro/sections)","operationId":"adminGetDocsConfig","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["config"],"properties":{"config":{"type":"object","required":["introMarkdown","introMarkdownTranslations","outroMarkdown","outroMarkdownTranslations","customSections"],"properties":{"introMarkdown":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"introMarkdownTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"outroMarkdown":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"outroMarkdownTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"customSections":{"type":"array","items":{}}}}}}}}}}},"put":{"tags":["Admin"],"summary":"Update intro / outro markdown","operationId":"adminUpdateDocsConfig","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"intro":{"type":"object","required":["body"],"properties":{"body":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"translations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}},"outro":{"type":"object","required":["body"],"properties":{"body":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"translations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"intro":{"type":"object","required":["body"],"properties":{"body":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"translations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}},"outro":{"type":"object","required":["body"],"properties":{"body":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"translations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"intro":{"type":"object","required":["body"],"properties":{"body":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"translations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}},"outro":{"type":"object","required":["body"],"properties":{"body":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"translations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["config"],"properties":{"config":{"type":"object","required":["introMarkdown","introMarkdownTranslations","outroMarkdown","outroMarkdownTranslations","customSections"],"properties":{"introMarkdown":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"introMarkdownTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"outroMarkdown":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"outroMarkdownTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"customSections":{"type":"array","items":{}}}}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/docs/sections/":{"get":{"tags":["Admin"],"summary":"List custom docs sections","operationId":"adminListDocsSections","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["sections"],"properties":{"sections":{"type":"array","items":{}}}}}}}}},"post":{"tags":["Admin"],"summary":"Create a custom docs section","operationId":"adminCreateDocsSection","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id","title","body","position"],"properties":{"id":{"minLength":1,"maxLength":60,"type":"string"},"title":{"nullable":true,"anyOf":[{"maxLength":200,"type":"string"},{"type":"null"}]},"titleTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"body":{"minLength":1,"type":"string"},"bodyTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"position":{"anyOf":[{"const":"page_top","type":"string"},{"const":"before_kinds","type":"string"},{"const":"after_kinds","type":"string"},{"const":"page_bottom","type":"string"},{"type":"object","required":["kind","slot"],"properties":{"kind":{"minLength":1,"maxLength":40,"type":"string"},"slot":{"anyOf":[{"const":"before","type":"string"},{"const":"after","type":"string"}]}}}]}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["id","title","body","position"],"properties":{"id":{"minLength":1,"maxLength":60,"type":"string"},"title":{"nullable":true,"anyOf":[{"maxLength":200,"type":"string"},{"type":"null"}]},"titleTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"body":{"minLength":1,"type":"string"},"bodyTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"position":{"anyOf":[{"const":"page_top","type":"string"},{"const":"before_kinds","type":"string"},{"const":"after_kinds","type":"string"},{"const":"page_bottom","type":"string"},{"type":"object","required":["kind","slot"],"properties":{"kind":{"minLength":1,"maxLength":40,"type":"string"},"slot":{"anyOf":[{"const":"before","type":"string"},{"const":"after","type":"string"}]}}}]}}}},"multipart/form-data":{"schema":{"type":"object","required":["id","title","body","position"],"properties":{"id":{"minLength":1,"maxLength":60,"type":"string"},"title":{"nullable":true,"anyOf":[{"maxLength":200,"type":"string"},{"type":"null"}]},"titleTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"body":{"minLength":1,"type":"string"},"bodyTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"position":{"anyOf":[{"const":"page_top","type":"string"},{"const":"before_kinds","type":"string"},{"const":"after_kinds","type":"string"},{"const":"page_bottom","type":"string"},{"type":"object","required":["kind","slot"],"properties":{"kind":{"minLength":1,"maxLength":40,"type":"string"},"slot":{"anyOf":[{"const":"before","type":"string"},{"const":"after","type":"string"}]}}}]}}}}}},"responses":{"201":{"description":"Response for status 201","content":{"application/json":{"schema":{"type":"object","required":["section"],"properties":{"section":{}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"409":{"description":"Response for status 409","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/docs/sections/{id}/":{"put":{"tags":["Admin"],"summary":"Update a custom docs section","operationId":"adminUpdateDocsSection","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id","title","body","position"],"properties":{"id":{"minLength":1,"maxLength":60,"type":"string"},"title":{"nullable":true,"anyOf":[{"maxLength":200,"type":"string"},{"type":"null"}]},"titleTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"body":{"minLength":1,"type":"string"},"bodyTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"position":{"anyOf":[{"const":"page_top","type":"string"},{"const":"before_kinds","type":"string"},{"const":"after_kinds","type":"string"},{"const":"page_bottom","type":"string"},{"type":"object","required":["kind","slot"],"properties":{"kind":{"minLength":1,"maxLength":40,"type":"string"},"slot":{"anyOf":[{"const":"before","type":"string"},{"const":"after","type":"string"}]}}}]}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["id","title","body","position"],"properties":{"id":{"minLength":1,"maxLength":60,"type":"string"},"title":{"nullable":true,"anyOf":[{"maxLength":200,"type":"string"},{"type":"null"}]},"titleTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"body":{"minLength":1,"type":"string"},"bodyTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"position":{"anyOf":[{"const":"page_top","type":"string"},{"const":"before_kinds","type":"string"},{"const":"after_kinds","type":"string"},{"const":"page_bottom","type":"string"},{"type":"object","required":["kind","slot"],"properties":{"kind":{"minLength":1,"maxLength":40,"type":"string"},"slot":{"anyOf":[{"const":"before","type":"string"},{"const":"after","type":"string"}]}}}]}}}},"multipart/form-data":{"schema":{"type":"object","required":["id","title","body","position"],"properties":{"id":{"minLength":1,"maxLength":60,"type":"string"},"title":{"nullable":true,"anyOf":[{"maxLength":200,"type":"string"},{"type":"null"}]},"titleTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"body":{"minLength":1,"type":"string"},"bodyTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"position":{"anyOf":[{"const":"page_top","type":"string"},{"const":"before_kinds","type":"string"},{"const":"after_kinds","type":"string"},{"const":"page_bottom","type":"string"},{"type":"object","required":["kind","slot"],"properties":{"kind":{"minLength":1,"maxLength":40,"type":"string"},"slot":{"anyOf":[{"const":"before","type":"string"},{"const":"after","type":"string"}]}}}]}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["section"],"properties":{"section":{}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Admin"],"summary":"Delete a custom docs section","operationId":"adminDeleteDocsSection","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Response for status 204","content":{"type":"null"}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/kinds/":{"get":{"tags":["Admin"],"summary":"List all plugin kinds","operationId":"adminListKinds","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["kinds"],"properties":{"kinds":{"type":"array","items":{"type":"object","required":["key","label","description"],"properties":{"key":{"type":"string"},"label":{"type":"string"},"labelTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"description":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"descriptionTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"extensionsSchema":{"nullable":true,"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]},"publicPageEnabled":{"type":"boolean"},"publicPageCopy":{"nullable":true,"anyOf":[{"type":"object","required":["hero","intro"],"properties":{"hero":{"nullable":true,"anyOf":[{"maxLength":80,"type":"string"},{"type":"null"}]},"heroTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]},"introTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}}}},{"type":"null"}]},"prosePre":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePreTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"prosePost":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePostTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"customExample":{"nullable":true,"anyOf":[{"type":"object","properties":{"yaml":{"type":"string"},"json":{"type":"string"}}},{"type":"null"}]}}}}}}}}}}},"post":{"tags":["Admin"],"summary":"Create a plugin kind","operationId":"adminCreateKind","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["key","label"],"properties":{"key":{"minLength":1,"maxLength":40,"type":"string"},"label":{"minLength":1,"maxLength":60,"type":"string"},"labelTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"description":{"nullable":true,"anyOf":[{"maxLength":280,"type":"string"},{"type":"null"}]},"descriptionTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"publicPageEnabled":{"type":"boolean"},"publicPageCopy":{"nullable":true,"anyOf":[{"type":"object","required":["hero","intro"],"properties":{"hero":{"nullable":true,"anyOf":[{"maxLength":80,"type":"string"},{"type":"null"}]},"heroTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]},"introTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}}}},{"type":"null"}]},"prosePre":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePreTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"prosePost":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePostTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"customExample":{"nullable":true,"anyOf":[{"type":"object","properties":{"yaml":{"type":"string"},"json":{"type":"string"}}},{"type":"null"}]}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["key","label"],"properties":{"key":{"minLength":1,"maxLength":40,"type":"string"},"label":{"minLength":1,"maxLength":60,"type":"string"},"labelTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"description":{"nullable":true,"anyOf":[{"maxLength":280,"type":"string"},{"type":"null"}]},"descriptionTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"publicPageEnabled":{"type":"boolean"},"publicPageCopy":{"nullable":true,"anyOf":[{"type":"object","required":["hero","intro"],"properties":{"hero":{"nullable":true,"anyOf":[{"maxLength":80,"type":"string"},{"type":"null"}]},"heroTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]},"introTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}}}},{"type":"null"}]},"prosePre":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePreTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"prosePost":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePostTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"customExample":{"nullable":true,"anyOf":[{"type":"object","properties":{"yaml":{"type":"string"},"json":{"type":"string"}}},{"type":"null"}]}}}},"multipart/form-data":{"schema":{"type":"object","required":["key","label"],"properties":{"key":{"minLength":1,"maxLength":40,"type":"string"},"label":{"minLength":1,"maxLength":60,"type":"string"},"labelTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"description":{"nullable":true,"anyOf":[{"maxLength":280,"type":"string"},{"type":"null"}]},"descriptionTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"publicPageEnabled":{"type":"boolean"},"publicPageCopy":{"nullable":true,"anyOf":[{"type":"object","required":["hero","intro"],"properties":{"hero":{"nullable":true,"anyOf":[{"maxLength":80,"type":"string"},{"type":"null"}]},"heroTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]},"introTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}}}},{"type":"null"}]},"prosePre":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePreTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"prosePost":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePostTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"customExample":{"nullable":true,"anyOf":[{"type":"object","properties":{"yaml":{"type":"string"},"json":{"type":"string"}}},{"type":"null"}]}}}}}},"responses":{"201":{"description":"Response for status 201","content":{"application/json":{"schema":{"type":"object","required":["kind"],"properties":{"kind":{"type":"object","required":["key","label","description"],"properties":{"key":{"type":"string"},"label":{"type":"string"},"labelTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"description":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"descriptionTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"extensionsSchema":{"nullable":true,"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]},"publicPageEnabled":{"type":"boolean"},"publicPageCopy":{"nullable":true,"anyOf":[{"type":"object","required":["hero","intro"],"properties":{"hero":{"nullable":true,"anyOf":[{"maxLength":80,"type":"string"},{"type":"null"}]},"heroTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]},"introTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}}}},{"type":"null"}]},"prosePre":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePreTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"prosePost":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePostTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"customExample":{"nullable":true,"anyOf":[{"type":"object","properties":{"yaml":{"type":"string"},"json":{"type":"string"}}},{"type":"null"}]}}}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"409":{"description":"Response for status 409","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/kinds/{key}/":{"get":{"tags":["Admin"],"summary":"Read one plugin kind","operationId":"adminGetKind","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["kind"],"properties":{"kind":{"type":"object","required":["key","label","description"],"properties":{"key":{"type":"string"},"label":{"type":"string"},"labelTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"description":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"descriptionTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"extensionsSchema":{"nullable":true,"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]},"publicPageEnabled":{"type":"boolean"},"publicPageCopy":{"nullable":true,"anyOf":[{"type":"object","required":["hero","intro"],"properties":{"hero":{"nullable":true,"anyOf":[{"maxLength":80,"type":"string"},{"type":"null"}]},"heroTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]},"introTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}}}},{"type":"null"}]},"prosePre":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePreTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"prosePost":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePostTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"customExample":{"nullable":true,"anyOf":[{"type":"object","properties":{"yaml":{"type":"string"},"json":{"type":"string"}}},{"type":"null"}]}}}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}},"put":{"tags":["Admin"],"summary":"Replace one plugin kind","operationId":"adminUpdateKind","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["key","label"],"properties":{"key":{"minLength":1,"maxLength":40,"type":"string"},"label":{"minLength":1,"maxLength":60,"type":"string"},"labelTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"description":{"nullable":true,"anyOf":[{"maxLength":280,"type":"string"},{"type":"null"}]},"descriptionTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"extensionsSchema":{"nullable":true,"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]},"publicPageEnabled":{"type":"boolean"},"publicPageCopy":{"nullable":true,"anyOf":[{"type":"object","required":["hero","intro"],"properties":{"hero":{"nullable":true,"anyOf":[{"maxLength":80,"type":"string"},{"type":"null"}]},"heroTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]},"introTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}}}},{"type":"null"}]},"prosePre":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePreTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"prosePost":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePostTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"customExample":{"nullable":true,"anyOf":[{"type":"object","properties":{"yaml":{"type":"string"},"json":{"type":"string"}}},{"type":"null"}]}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["key","label"],"properties":{"key":{"minLength":1,"maxLength":40,"type":"string"},"label":{"minLength":1,"maxLength":60,"type":"string"},"labelTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"description":{"nullable":true,"anyOf":[{"maxLength":280,"type":"string"},{"type":"null"}]},"descriptionTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"extensionsSchema":{"nullable":true,"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]},"publicPageEnabled":{"type":"boolean"},"publicPageCopy":{"nullable":true,"anyOf":[{"type":"object","required":["hero","intro"],"properties":{"hero":{"nullable":true,"anyOf":[{"maxLength":80,"type":"string"},{"type":"null"}]},"heroTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]},"introTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}}}},{"type":"null"}]},"prosePre":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePreTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"prosePost":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePostTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"customExample":{"nullable":true,"anyOf":[{"type":"object","properties":{"yaml":{"type":"string"},"json":{"type":"string"}}},{"type":"null"}]}}}},"multipart/form-data":{"schema":{"type":"object","required":["key","label"],"properties":{"key":{"minLength":1,"maxLength":40,"type":"string"},"label":{"minLength":1,"maxLength":60,"type":"string"},"labelTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"description":{"nullable":true,"anyOf":[{"maxLength":280,"type":"string"},{"type":"null"}]},"descriptionTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"extensionsSchema":{"nullable":true,"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]},"publicPageEnabled":{"type":"boolean"},"publicPageCopy":{"nullable":true,"anyOf":[{"type":"object","required":["hero","intro"],"properties":{"hero":{"nullable":true,"anyOf":[{"maxLength":80,"type":"string"},{"type":"null"}]},"heroTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]},"introTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}}}},{"type":"null"}]},"prosePre":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePreTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"prosePost":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePostTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"customExample":{"nullable":true,"anyOf":[{"type":"object","properties":{"yaml":{"type":"string"},"json":{"type":"string"}}},{"type":"null"}]}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["kind"],"properties":{"kind":{"type":"object","required":["key","label","description"],"properties":{"key":{"type":"string"},"label":{"type":"string"},"labelTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"description":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"descriptionTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"extensionsSchema":{"nullable":true,"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]},"publicPageEnabled":{"type":"boolean"},"publicPageCopy":{"nullable":true,"anyOf":[{"type":"object","required":["hero","intro"],"properties":{"hero":{"nullable":true,"anyOf":[{"maxLength":80,"type":"string"},{"type":"null"}]},"heroTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]},"introTranslations":{"type":"object","patternProperties":{"^(.*)$":{"maxLength":600,"type":"string"}}}}},{"type":"null"}]},"prosePre":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePreTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"prosePost":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"prosePostTranslations":{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"customExample":{"nullable":true,"anyOf":[{"type":"object","properties":{"yaml":{"type":"string"},"json":{"type":"string"}}},{"type":"null"}]}}}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"409":{"description":"Response for status 409","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Admin"],"summary":"Delete one plugin kind","operationId":"adminDeleteKind","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Response for status 204","content":{"type":"null"}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/pages/":{"get":{"tags":["Admin"],"summary":"List all markdown pages (grouped by slug)","operationId":"adminListPages","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["pages"],"properties":{"pages":{"type":"array","items":{"type":"object","required":["slug","locale","path","title","content","published","navOrder","showInFooter","translations","createdAt","updatedAt"],"properties":{"slug":{"type":"string"},"locale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"path":{"type":"string"},"title":{"type":"string"},"content":{"type":"string"},"published":{"type":"boolean"},"navOrder":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"showInFooter":{"type":"boolean"},"translations":{"type":"array","items":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}},"createdAt":{"type":"number"},"updatedAt":{"type":"number"}}}}}}}}}}},"post":{"tags":["Admin"],"summary":"Create a markdown page","description":"Creates the default-locale row unless `locale` is given. Pass `path` to mount at any non-reserved URL.","operationId":"createPage","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["slug","title","content"],"properties":{"slug":{"type":"string"},"locale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"path":{"type":"string"},"title":{"minLength":1,"maxLength":120,"type":"string"},"content":{"maxLength":200000,"type":"string"},"published":{"type":"boolean"},"navOrder":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"showInFooter":{"type":"boolean"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["slug","title","content"],"properties":{"slug":{"type":"string"},"locale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"path":{"type":"string"},"title":{"minLength":1,"maxLength":120,"type":"string"},"content":{"maxLength":200000,"type":"string"},"published":{"type":"boolean"},"navOrder":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"showInFooter":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","required":["slug","title","content"],"properties":{"slug":{"type":"string"},"locale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"path":{"type":"string"},"title":{"minLength":1,"maxLength":120,"type":"string"},"content":{"maxLength":200000,"type":"string"},"published":{"type":"boolean"},"navOrder":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"showInFooter":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","slug","path","locale"],"properties":{"ok":{"type":"boolean"},"slug":{"type":"string"},"path":{"type":"string"},"locale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"409":{"description":"Response for status 409","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/pages/preview/":{"post":{"tags":["Admin"],"summary":"Render markdown to sanitized HTML (admin live preview)","operationId":"previewPage","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["content"],"properties":{"content":{"maxLength":200000,"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["content"],"properties":{"content":{"maxLength":200000,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["content"],"properties":{"content":{"maxLength":200000,"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["html"],"properties":{"html":{"type":"string"}}}}}}}}},"/api/admin/pages/{slug}/":{"get":{"tags":["Admin"],"summary":"Get a markdown page (raw)","operationId":"adminGetPage","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"locale","in":"query","required":false,"schema":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["slug","locale","path","title","content","published","navOrder","showInFooter","createdAt","updatedAt"],"properties":{"slug":{"type":"string"},"locale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"path":{"type":"string"},"title":{"type":"string"},"content":{"type":"string"},"published":{"type":"boolean"},"navOrder":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"showInFooter":{"type":"boolean"},"createdAt":{"type":"number"},"updatedAt":{"type":"number"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}},"patch":{"tags":["Admin"],"summary":"Update a markdown page (creates the translation row if absent)","operationId":"updatePage","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"locale","in":"query","required":false,"schema":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"minLength":1,"maxLength":120,"type":"string"},"content":{"maxLength":200000,"type":"string"},"published":{"type":"boolean"},"path":{"type":"string"},"navOrder":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"showInFooter":{"type":"boolean"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"title":{"minLength":1,"maxLength":120,"type":"string"},"content":{"maxLength":200000,"type":"string"},"published":{"type":"boolean"},"path":{"type":"string"},"navOrder":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"showInFooter":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"title":{"minLength":1,"maxLength":120,"type":"string"},"content":{"maxLength":200000,"type":"string"},"published":{"type":"boolean"},"path":{"type":"string"},"navOrder":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"showInFooter":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"409":{"description":"Response for status 409","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Admin"],"summary":"Delete a markdown page (all locales) or one translation when `locale` is given","operationId":"deletePage","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"locale","in":"query","required":false,"schema":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}}]}},"/api/admin/pages/{slug}/translations":{"get":{"tags":["Admin"],"summary":"List existing translations for a page","operationId":"adminListPageTranslations","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["translations"],"properties":{"translations":{"type":"array","items":{"type":"object","required":["locale","title","updatedAt"],"properties":{"locale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"title":{"type":"string"},"updatedAt":{"type":"number"}}}}}}}}}}}},"/api/admin/setup/complete/":{"post":{"tags":["Admin"],"summary":"Mark the setup wizard as completed","description":"Sets `setup.completed_at` in settings. Frontend stops redirecting to /welcome after this.","operationId":"completeSetup","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"}}}}}}}}},"/api/admin/provider-instances/":{"get":{"tags":["Admin"],"summary":"List all provider instances (secrets redacted)","operationId":"listProviderInstances","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["instances"],"properties":{"instances":{"type":"array","items":{"type":"object","required":["id","kind","displayName","baseUrl","clientId","logoUrl","enabled","hasOAuthSecret","lastUsedAt"],"properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["github","gitlab","gitea"]},"displayName":{"type":"string"},"baseUrl":{"type":"string"},"clientId":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"},"hasOAuthSecret":{"type":"boolean"},"lastUsedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]}}}}}}}}}}},"post":{"tags":["Admin"],"summary":"Create a new provider instance","operationId":"createProviderInstance","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id","kind","displayName","baseUrl","clientId","clientSecret"],"properties":{"id":{"minLength":1,"maxLength":64,"type":"string"},"kind":{"type":"string","enum":["github","gitlab","gitea"]},"displayName":{"minLength":1,"maxLength":80,"type":"string"},"baseUrl":{"minLength":1,"type":"string"},"clientId":{"minLength":1,"type":"string"},"clientSecret":{"minLength":1,"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["id","kind","displayName","baseUrl","clientId","clientSecret"],"properties":{"id":{"minLength":1,"maxLength":64,"type":"string"},"kind":{"type":"string","enum":["github","gitlab","gitea"]},"displayName":{"minLength":1,"maxLength":80,"type":"string"},"baseUrl":{"minLength":1,"type":"string"},"clientId":{"minLength":1,"type":"string"},"clientSecret":{"minLength":1,"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","required":["id","kind","displayName","baseUrl","clientId","clientSecret"],"properties":{"id":{"minLength":1,"maxLength":64,"type":"string"},"kind":{"type":"string","enum":["github","gitlab","gitea"]},"displayName":{"minLength":1,"maxLength":80,"type":"string"},"baseUrl":{"minLength":1,"type":"string"},"clientId":{"minLength":1,"type":"string"},"clientSecret":{"minLength":1,"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"}}}}}},"responses":{"201":{"description":"Response for status 201","content":{"application/json":{"schema":{"type":"object","required":["id","kind","displayName","baseUrl","clientId","logoUrl","enabled","hasOAuthSecret","lastUsedAt"],"properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["github","gitlab","gitea"]},"displayName":{"type":"string"},"baseUrl":{"type":"string"},"clientId":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"},"hasOAuthSecret":{"type":"boolean"},"lastUsedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"409":{"description":"Response for status 409","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/provider-instances/{id}/":{"get":{"tags":["Admin"],"summary":"Get a single provider instance","operationId":"getProviderInstance","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["id","kind","displayName","baseUrl","clientId","logoUrl","enabled","hasOAuthSecret","lastUsedAt"],"properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["github","gitlab","gitea"]},"displayName":{"type":"string"},"baseUrl":{"type":"string"},"clientId":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"},"hasOAuthSecret":{"type":"boolean"},"lastUsedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}},"patch":{"tags":["Admin"],"summary":"Partially update a provider instance","operationId":"patchProviderInstance","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["github","gitlab","gitea"]},"displayName":{"type":"string"},"baseUrl":{"type":"string"},"clientId":{"type":"string"},"clientSecret":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["github","gitlab","gitea"]},"displayName":{"type":"string"},"baseUrl":{"type":"string"},"clientId":{"type":"string"},"clientSecret":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["github","gitlab","gitea"]},"displayName":{"type":"string"},"baseUrl":{"type":"string"},"clientId":{"type":"string"},"clientSecret":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["id","kind","displayName","baseUrl","clientId","logoUrl","enabled","hasOAuthSecret","lastUsedAt"],"properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["github","gitlab","gitea"]},"displayName":{"type":"string"},"baseUrl":{"type":"string"},"clientId":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"},"hasOAuthSecret":{"type":"boolean"},"lastUsedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Admin"],"summary":"Delete a provider instance","operationId":"deleteProviderInstance","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/admin/provider-instances/{id}/test-oauth/":{"post":{"tags":["Admin"],"summary":"Probe the OAuth authorize endpoint for a provider instance","description":"Issues a GET (manual redirect, 5s timeout) against the provider-kind specific authorize URL. A 302/400 typically means \"configured correctly\". 5xx or fetch errors are reported as not-ok. Records an audit entry with the outcome.","operationId":"testProviderOAuth","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","status"],"properties":{"ok":{"type":"boolean"},"status":{"type":"number"},"error":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/provider-instances/{id}/logo/":{"post":{"tags":["Admin"],"summary":"Upload a logo for a provider instance","description":"Multipart upload. Field name must be `file`. Accepts png/jpg/webp/svg up to 512 KB. Storage backend is set in `/admin/infra/storage`. The instance row is updated to reference the new URL (includes a cache-buster query param).","operationId":"uploadProviderLogo","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["file"],"properties":{"file":{"default":"File","type":"string","format":"binary"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["file"],"properties":{"file":{"default":"File","type":"string","format":"binary"}}}},"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"default":"File","type":"string","format":"binary"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","logoUrl","storedAt"],"properties":{"ok":{"type":"boolean"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"storedAt":{"type":"string"}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"413":{"description":"Response for status 413","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"415":{"description":"Response for status 415","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"500":{"description":"Response for status 500","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/provider-instances/bulk/":{"post":{"tags":["Admin"],"summary":"Bulk enable / disable provider instances","description":"Flip `enabled` on up to 100 provider instances in one round-trip. Unknown ids are returned in `missing` and silently skipped. Single audit-log entry per call.","operationId":"bulkProviderInstanceAction","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ids","action"],"properties":{"ids":{"maxItems":100,"type":"array","items":{"type":"string"}},"action":{"type":"string","enum":["enable","disable"]}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["ids","action"],"properties":{"ids":{"maxItems":100,"type":"array","items":{"type":"string"}},"action":{"type":"string","enum":["enable","disable"]}}}},"multipart/form-data":{"schema":{"type":"object","required":["ids","action"],"properties":{"ids":{"maxItems":100,"type":"array","items":{"type":"string"}},"action":{"type":"string","enum":["enable","disable"]}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","action","affected","missing"],"properties":{"ok":{"type":"boolean"},"action":{"type":"string","enum":["enable","disable"]},"affected":{"type":"number"},"missing":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/settings/":{"get":{"tags":["Admin"],"summary":"List all settings (encrypted values masked)","operationId":"listSettings","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["settings"],"properties":{"settings":{"type":"array","items":{"type":"object","required":["key","value","encrypted","updatedAt"],"properties":{"key":{"type":"string"},"value":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"encrypted":{"type":"boolean"},"updatedAt":{"type":"number"}}}}}}}}}}}},"/api/admin/settings/{key}/":{"get":{"tags":["Admin"],"summary":"Get a single setting (secrets redacted)","operationId":"getSetting","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["key","value","encrypted"],"properties":{"key":{"type":"string"},"value":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"encrypted":{"type":"boolean"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}},"put":{"tags":["Admin"],"summary":"Create or update a setting","description":"Keys ending in `.client_secret`, `.password`, or `.token` are stored encrypted automatically.","operationId":"putSetting","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["value"],"properties":{"value":{"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["value"],"properties":{"value":{"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["value"],"properties":{"value":{"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"}}}}}}}},"delete":{"tags":["Admin"],"summary":"Delete a setting","operationId":"deleteSetting","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/admin/features/":{"get":{"tags":["Admin"],"summary":"Get feature flags","operationId":"adminGetFeatures","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["submissionsEnabled","requestsEnabled"],"properties":{"submissionsEnabled":{"type":"boolean"},"requestsEnabled":{"type":"boolean"}}}}}}}},"patch":{"tags":["Admin"],"summary":"Toggle feature flags","operationId":"adminUpdateFeatures","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"submissionsEnabled":{"type":"boolean"},"requestsEnabled":{"type":"boolean"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"submissionsEnabled":{"type":"boolean"},"requestsEnabled":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"submissionsEnabled":{"type":"boolean"},"requestsEnabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["submissionsEnabled","requestsEnabled"],"properties":{"submissionsEnabled":{"type":"boolean"},"requestsEnabled":{"type":"boolean"}}}}}}}}},"/api/admin/users/":{"get":{"tags":["Admin"],"summary":"List users (most recent first)","operationId":"listUsers","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["users"],"properties":{"users":{"type":"array","items":{"type":"object","required":["id","displayName","email","role","createdAt"],"properties":{"id":{"type":"string"},"displayName":{"type":"string"},"email":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"role":{"type":"string","enum":["user","admin"]},"createdAt":{"type":"number"}}}}}}}}}}}},"/api/admin/users/{id}/":{"get":{"tags":["Admin"],"summary":"Get a single user","operationId":"getUser","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["id","displayName","email","role","createdAt"],"properties":{"id":{"type":"string"},"displayName":{"type":"string"},"email":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"role":{"type":"string","enum":["user","admin"]},"createdAt":{"type":"number"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}},"patch":{"tags":["Admin"],"summary":"Update a user (role or displayName)","operationId":"patchUser","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["user","admin"]},"displayName":{"minLength":1,"maxLength":60,"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["user","admin"]},"displayName":{"minLength":1,"maxLength":60,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["user","admin"]},"displayName":{"minLength":1,"maxLength":60,"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["id","displayName","email","role","createdAt"],"properties":{"id":{"type":"string"},"displayName":{"type":"string"},"email":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"role":{"type":"string","enum":["user","admin"]},"createdAt":{"type":"number"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"409":{"description":"Response for status 409","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/audit/":{"get":{"tags":["Admin"],"summary":"List audit log entries","operationId":"listAudit","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"action","in":"query","required":false,"schema":{"type":"string"}},{"name":"actorId","in":"query","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["total","page","limit","entries"],"properties":{"total":{"type":"number"},"page":{"type":"number"},"limit":{"type":"number"},"entries":{"type":"array","items":{"type":"object","required":["id","actorId","actorName","action","target","meta","ip","createdAt"],"properties":{"id":{"type":"string"},"actorId":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"actorName":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"action":{"type":"string"},"target":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"meta":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"ip":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"number"}}}}}}}}}}}},"/api/admin/diagnostics/":{"get":{"tags":["Admin"],"summary":"System diagnostics","description":"Admin-only deep health/diagnostics. Returns driver names, mode, uptime, and per-check failure detail. The public `/healthz` returns only `{ ok }` to avoid leaking infrastructure details.","operationId":"adminDiagnostics","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","uptimeSeconds","mode","checks"],"properties":{"ok":{"type":"boolean"},"uptimeSeconds":{"type":"number"},"mode":{"type":"string"},"checks":{"type":"object","required":["db","cache","storage"],"properties":{"db":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"},"detail":{"type":"string"}}},"cache":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"},"detail":{"type":"string"}}},"storage":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"},"detail":{"type":"string"}}}}}}}}}}}}},"/api/admin/instance/":{"get":{"tags":["Admin"],"summary":"Get instance-wide settings","operationId":"getInstanceSettings","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["requireApproval","rateLimits","manifest","assetSizeCapBytes"],"properties":{"requireApproval":{"type":"boolean"},"rateLimits":{"type":"array","items":{"type":"object","required":["id","limit","windowSeconds","defaultLimit","defaultWindowSeconds"],"properties":{"id":{"type":"string"},"limit":{"type":"number"},"windowSeconds":{"type":"number"},"defaultLimit":{"type":"number"},"defaultWindowSeconds":{"type":"number"}}}},"manifest":{"type":"object","required":["allowedFiles","defaultFiles","schemaUrl","filesOverridden","schemaUrlOverridden","requireKind","requireKindAvailable","configuredKindKeys"],"properties":{"allowedFiles":{"type":"array","items":{"type":"string"}},"defaultFiles":{"type":"array","items":{"type":"string"}},"schemaUrl":{"type":"string"},"filesOverridden":{"type":"boolean"},"schemaUrlOverridden":{"type":"boolean"},"requireKind":{"type":"boolean"},"requireKindAvailable":{"type":"boolean"},"configuredKindKeys":{"type":"array","items":{"type":"string"}}}},"assetSizeCapBytes":{"type":"number"}}}}}}}},"put":{"tags":["Admin"],"summary":"Update instance-wide settings","description":"Toggle approval mode, adjust per-bucket rate limits, and configure the manifest filename + schema URL. Pass `reset: true` to clear a rate-limit override; pass `null` or an empty string for the manifest fields to revert to defaults.","operationId":"updateInstanceSettings","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"requireApproval":{"type":"boolean"},"rateLimits":{"type":"array","items":{"type":"object","required":["id","limit","windowSeconds"],"properties":{"id":{"type":"string"},"limit":{"type":"number"},"windowSeconds":{"type":"number"},"reset":{"type":"boolean"}}}},"manifestAllowedFiles":{"nullable":true,"anyOf":[{"maxItems":12,"type":"array","items":{"maxLength":60,"type":"string"}},{"type":"null"}]},"manifestSchemaUrl":{"nullable":true,"anyOf":[{"maxLength":500,"type":"string"},{"type":"null"}]},"manifestRequireKind":{"type":"boolean"},"assetSizeCapBytes":{"type":"number"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"requireApproval":{"type":"boolean"},"rateLimits":{"type":"array","items":{"type":"object","required":["id","limit","windowSeconds"],"properties":{"id":{"type":"string"},"limit":{"type":"number"},"windowSeconds":{"type":"number"},"reset":{"type":"boolean"}}}},"manifestAllowedFiles":{"nullable":true,"anyOf":[{"maxItems":12,"type":"array","items":{"maxLength":60,"type":"string"}},{"type":"null"}]},"manifestSchemaUrl":{"nullable":true,"anyOf":[{"maxLength":500,"type":"string"},{"type":"null"}]},"manifestRequireKind":{"type":"boolean"},"assetSizeCapBytes":{"type":"number"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"requireApproval":{"type":"boolean"},"rateLimits":{"type":"array","items":{"type":"object","required":["id","limit","windowSeconds"],"properties":{"id":{"type":"string"},"limit":{"type":"number"},"windowSeconds":{"type":"number"},"reset":{"type":"boolean"}}}},"manifestAllowedFiles":{"nullable":true,"anyOf":[{"maxItems":12,"type":"array","items":{"maxLength":60,"type":"string"}},{"type":"null"}]},"manifestSchemaUrl":{"nullable":true,"anyOf":[{"maxLength":500,"type":"string"},{"type":"null"}]},"manifestRequireKind":{"type":"boolean"},"assetSizeCapBytes":{"type":"number"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/instance/app-schemes/":{"get":{"tags":["Admin"],"summary":"List configured app URL schemes","operationId":"listAppUrlSchemes","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["schemes"],"properties":{"schemes":{"type":"array","items":{"type":"object","required":["name","scheme"],"properties":{"name":{"minLength":1,"type":"string"},"scheme":{"pattern":"^[a-z][a-z0-9+.-]*$","type":"string"},"kinds":{"type":"array","items":{"minLength":1,"type":"string"}}}}}}}}}}}},"put":{"tags":["Admin"],"summary":"Replace the full list of app URL schemes","description":"Overwrites the stored app_url_schemes setting with the provided list. Each scheme needs a name + URL scheme (lowercase, starts with a letter). Optional `kinds` filter restricts the scheme to specific plugin kinds.","operationId":"replaceAppUrlSchemes","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["schemes"],"properties":{"schemes":{"type":"array","items":{"type":"object","required":["name","scheme"],"properties":{"name":{"minLength":1,"type":"string"},"scheme":{"pattern":"^[a-z][a-z0-9+.-]*$","type":"string"},"kinds":{"type":"array","items":{"minLength":1,"type":"string"}}}}}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["schemes"],"properties":{"schemes":{"type":"array","items":{"type":"object","required":["name","scheme"],"properties":{"name":{"minLength":1,"type":"string"},"scheme":{"pattern":"^[a-z][a-z0-9+.-]*$","type":"string"},"kinds":{"type":"array","items":{"minLength":1,"type":"string"}}}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["schemes"],"properties":{"schemes":{"type":"array","items":{"type":"object","required":["name","scheme"],"properties":{"name":{"minLength":1,"type":"string"},"scheme":{"pattern":"^[a-z][a-z0-9+.-]*$","type":"string"},"kinds":{"type":"array","items":{"minLength":1,"type":"string"}}}}}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","schemes"],"properties":{"ok":{"type":"boolean"},"schemes":{"type":"array","items":{"type":"object","required":["name","scheme"],"properties":{"name":{"minLength":1,"type":"string"},"scheme":{"pattern":"^[a-z][a-z0-9+.-]*$","type":"string"},"kinds":{"type":"array","items":{"minLength":1,"type":"string"}}}}}}}}}}}}},"/api/admin/instance/security/":{"get":{"tags":["Admin"],"summary":"Get current + previous registry signing keys","operationId":"getInstanceSecurity","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["current","previous"],"properties":{"current":{"type":"object","required":["kty","crv","x","use","alg","kid"],"properties":{"kty":{"const":"OKP","type":"string"},"crv":{"const":"Ed25519","type":"string"},"x":{"type":"string"},"use":{"const":"sig","type":"string"},"alg":{"const":"EdDSA","type":"string"},"kid":{"type":"string"},"created_at":{"type":"number"},"rotated_at":{"type":"number"}}},"previous":{"nullable":true,"anyOf":[{"type":"object","required":["kty","crv","x","use","alg","kid"],"properties":{"kty":{"const":"OKP","type":"string"},"crv":{"const":"Ed25519","type":"string"},"x":{"type":"string"},"use":{"const":"sig","type":"string"},"alg":{"const":"EdDSA","type":"string"},"kid":{"type":"string"},"created_at":{"type":"number"},"rotated_at":{"type":"number"}}},{"type":"null"}]}}}}}}}}},"/api/admin/instance/security/rotate":{"post":{"tags":["Admin"],"summary":"Rotate the registry signing key","description":"Generates a new Ed25519 keypair, demotes the current key to .previous (overwriting any earlier previous), and records an audit entry.","operationId":"rotateInstanceSigningKey","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","oldKid","newKid"],"properties":{"ok":{"type":"boolean"},"oldKid":{"type":"string"},"newKid":{"type":"string"}}}}}}}}},"/api/admin/branding/":{"get":{"tags":["Admin"],"summary":"Get current + default branding (with translation maps)","operationId":"getAdminBranding","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["current","defaults"],"properties":{"current":{"type":"object","allOf":[{"type":"object","required":["name","tagline","primaryHex","accentHex","successHex","logoUrl","faviconUrl","footerText","analyticsScript","allowIndexing"],"properties":{"name":{"type":"string"},"tagline":{"type":"string"},"primaryHex":{"type":"string"},"accentHex":{"type":"string"},"successHex":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"faviconUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"footerText":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"analyticsScript":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"allowIndexing":{"type":"boolean"}}},{"type":"object","required":["taglineTranslations","footerTextTranslations"],"properties":{"taglineTranslations":{"type":"object","properties":{"en":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"de":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"es":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"fr":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"it":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"zh-CN":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}},"footerTextTranslations":{"type":"object","properties":{"en":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"de":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"es":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"fr":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"it":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"zh-CN":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}}}}]},"defaults":{"type":"object","required":["name","tagline","primaryHex","accentHex","successHex","logoUrl","faviconUrl","footerText","analyticsScript","allowIndexing"],"properties":{"name":{"type":"string"},"tagline":{"type":"string"},"primaryHex":{"type":"string"},"accentHex":{"type":"string"},"successHex":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"faviconUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"footerText":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"analyticsScript":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"allowIndexing":{"type":"boolean"}}}}}}}}}},"put":{"tags":["Admin"],"summary":"Update branding (whitelabel)","description":"Partial update. `tagline` and `footerText` set the default-locale value; `taglineTranslations` / `footerTextTranslations` set per-locale overrides (pass null/empty to clear).","operationId":"updateBranding","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":60,"type":"string"},"tagline":{"maxLength":200,"type":"string"},"primaryHex":{"type":"string"},"accentHex":{"type":"string"},"successHex":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"faviconUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"footerText":{"nullable":true,"anyOf":[{"maxLength":1000,"type":"string"},{"type":"null"}]},"analyticsScript":{"nullable":true,"anyOf":[{"maxLength":4000,"type":"string"},{"type":"null"}]},"allowIndexing":{"type":"boolean"},"taglineTranslations":{"type":"object","properties":{"en":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"de":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"es":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"fr":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"it":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"zh-CN":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}},"footerTextTranslations":{"type":"object","properties":{"en":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"de":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"es":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"fr":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"it":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"zh-CN":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":60,"type":"string"},"tagline":{"maxLength":200,"type":"string"},"primaryHex":{"type":"string"},"accentHex":{"type":"string"},"successHex":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"faviconUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"footerText":{"nullable":true,"anyOf":[{"maxLength":1000,"type":"string"},{"type":"null"}]},"analyticsScript":{"nullable":true,"anyOf":[{"maxLength":4000,"type":"string"},{"type":"null"}]},"allowIndexing":{"type":"boolean"},"taglineTranslations":{"type":"object","properties":{"en":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"de":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"es":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"fr":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"it":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"zh-CN":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}},"footerTextTranslations":{"type":"object","properties":{"en":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"de":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"es":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"fr":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"it":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"zh-CN":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":60,"type":"string"},"tagline":{"maxLength":200,"type":"string"},"primaryHex":{"type":"string"},"accentHex":{"type":"string"},"successHex":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"faviconUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"footerText":{"nullable":true,"anyOf":[{"maxLength":1000,"type":"string"},{"type":"null"}]},"analyticsScript":{"nullable":true,"anyOf":[{"maxLength":4000,"type":"string"},{"type":"null"}]},"allowIndexing":{"type":"boolean"},"taglineTranslations":{"type":"object","properties":{"en":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"de":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"es":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"fr":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"it":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"zh-CN":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}},"footerTextTranslations":{"type":"object","properties":{"en":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"de":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"es":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"fr":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"it":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"zh-CN":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","branding"],"properties":{"ok":{"type":"boolean"},"branding":{"type":"object","allOf":[{"type":"object","required":["name","tagline","primaryHex","accentHex","successHex","logoUrl","faviconUrl","footerText","analyticsScript","allowIndexing"],"properties":{"name":{"type":"string"},"tagline":{"type":"string"},"primaryHex":{"type":"string"},"accentHex":{"type":"string"},"successHex":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"faviconUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"footerText":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"analyticsScript":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"allowIndexing":{"type":"boolean"}}},{"type":"object","required":["taglineTranslations","footerTextTranslations"],"properties":{"taglineTranslations":{"type":"object","properties":{"en":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"de":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"es":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"fr":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"it":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"zh-CN":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}},"footerTextTranslations":{"type":"object","properties":{"en":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"de":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"es":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"fr":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"it":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"zh-CN":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}}}}]}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/branding/logo/":{"post":{"tags":["Admin"],"summary":"Upload a branding logo","description":"Multipart upload. Field name must be `file`. Accepts png/jpg/webp/svg up to 512 KB. Storage backend is set in `/admin/infra/storage`. The branding `logo_url` setting is updated to reference the new URL (includes a cache-buster query param).","operationId":"uploadBrandingLogo","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["file"],"properties":{"file":{"default":"File","type":"string","format":"binary"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["file"],"properties":{"file":{"default":"File","type":"string","format":"binary"}}}},"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"default":"File","type":"string","format":"binary"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","logoUrl"],"properties":{"ok":{"type":"boolean"},"logoUrl":{"type":"string"}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"413":{"description":"Response for status 413","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"415":{"description":"Response for status 415","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"500":{"description":"Response for status 500","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/branding/favicon/":{"post":{"tags":["Admin"],"summary":"Upload a branding favicon","description":"Multipart upload. Field name must be `file`. Accepts png/jpg/webp/svg/ico up to 256 KB. Storage backend is set in `/admin/infra/storage`. The branding `favicon_url` setting is updated to reference the new URL (includes a cache-buster query param).","operationId":"uploadBrandingFavicon","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["file"],"properties":{"file":{"default":"File","type":"string","format":"binary"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["file"],"properties":{"file":{"default":"File","type":"string","format":"binary"}}}},"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"default":"File","type":"string","format":"binary"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","faviconUrl"],"properties":{"ok":{"type":"boolean"},"faviconUrl":{"type":"string"}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"413":{"description":"Response for status 413","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"415":{"description":"Response for status 415","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"500":{"description":"Response for status 500","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/infra/storage/":{"get":{"tags":["Admin"],"summary":"Get storage driver state","operationId":"getInfraStorage","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["driver","configuredDriver","s3"],"properties":{"driver":{"type":"string","enum":["off","disk","s3"]},"configuredDriver":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"s3":{"type":"object","required":["bucket","region","endpoint","publicBaseUrl","accessKeyConfigured","secretKeyConfigured"],"properties":{"bucket":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"region":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"endpoint":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"publicBaseUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"accessKeyConfigured":{"type":"boolean"},"secretKeyConfigured":{"type":"boolean"}}}}}}}}}},"put":{"tags":["Admin"],"summary":"Update storage driver (hot-swap)","description":"Pass an empty string for any s3 field to clear it. Access / secret keys are stored encrypted. `disk` writes under `data/uploads/` and serves via `/uploads/`. `s3` uses `Bun.S3Client` (compatible with AWS, MinIO, R2).","operationId":"updateInfraStorage","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["driver"],"properties":{"driver":{"type":"string","enum":["off","disk","s3"]},"s3":{"type":"object","properties":{"bucket":{"type":"string"},"region":{"type":"string"},"endpoint":{"type":"string"},"publicBaseUrl":{"type":"string"},"accessKey":{"type":"string"},"secretKey":{"type":"string"}}}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["driver"],"properties":{"driver":{"type":"string","enum":["off","disk","s3"]},"s3":{"type":"object","properties":{"bucket":{"type":"string"},"region":{"type":"string"},"endpoint":{"type":"string"},"publicBaseUrl":{"type":"string"},"accessKey":{"type":"string"},"secretKey":{"type":"string"}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["driver"],"properties":{"driver":{"type":"string","enum":["off","disk","s3"]},"s3":{"type":"object","properties":{"bucket":{"type":"string"},"region":{"type":"string"},"endpoint":{"type":"string"},"publicBaseUrl":{"type":"string"},"accessKey":{"type":"string"},"secretKey":{"type":"string"}}}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","driver"],"properties":{"ok":{"type":"boolean"},"driver":{"type":"string","enum":["off","disk","s3"]}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"500":{"description":"Response for status 500","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/infra/cache/":{"get":{"tags":["Admin"],"summary":"Get current cache driver state","operationId":"getInfraCache","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["driver","configuredDriver","defaultDriver","redisUrlConfigured","redisUrlMasked"],"properties":{"driver":{"type":"string","enum":["off","memory","redis"]},"configuredDriver":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"defaultDriver":{"type":"string","enum":["off","memory","redis"]},"redisUrlConfigured":{"type":"boolean"},"redisUrlMasked":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}}}}}}},"put":{"tags":["Admin"],"summary":"Update cache driver (hot-swap, no restart)","description":"Persists the driver choice (and optional redisUrl) and immediately reconfigures the running cache. redisUrl is stored encrypted. Pass empty string to clear the override.","operationId":"updateInfraCache","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["driver"],"properties":{"driver":{"type":"string","enum":["off","memory","redis"]},"redisUrl":{"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["driver"],"properties":{"driver":{"type":"string","enum":["off","memory","redis"]},"redisUrl":{"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["driver"],"properties":{"driver":{"type":"string","enum":["off","memory","redis"]},"redisUrl":{"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","driver"],"properties":{"ok":{"type":"boolean"},"driver":{"type":"string","enum":["off","memory","redis"]}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"500":{"description":"Response for status 500","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/home-copy/":{"get":{"tags":["Admin"],"summary":"Get home-page copy overrides (admin view)","operationId":"adminGetHomeCopy","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["eyebrow","features"],"properties":{"eyebrow":{"type":"object","required":["enabled","text"],"properties":{"enabled":{"type":"boolean"},"text":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"features":{"type":"object","required":["enabled","dropin","providers","release"],"properties":{"enabled":{"type":"boolean"},"dropin":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"providers":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"release":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}}}}}}}}}}},"put":{"tags":["Admin"],"summary":"Update home-page copy overrides","description":"Replaces the entire home_copy blob. Empty strings clear the override for that locale.","operationId":"adminUpdateHomeCopy","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eyebrow","features"],"properties":{"eyebrow":{"type":"object","required":["enabled","text"],"properties":{"enabled":{"type":"boolean"},"text":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"features":{"type":"object","required":["enabled","dropin","providers","release"],"properties":{"enabled":{"type":"boolean"},"dropin":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"providers":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"release":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}}}}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["eyebrow","features"],"properties":{"eyebrow":{"type":"object","required":["enabled","text"],"properties":{"enabled":{"type":"boolean"},"text":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"features":{"type":"object","required":["enabled","dropin","providers","release"],"properties":{"enabled":{"type":"boolean"},"dropin":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"providers":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"release":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["eyebrow","features"],"properties":{"eyebrow":{"type":"object","required":["enabled","text"],"properties":{"enabled":{"type":"boolean"},"text":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"features":{"type":"object","required":["enabled","dropin","providers","release"],"properties":{"enabled":{"type":"boolean"},"dropin":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"providers":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"release":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}}}}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["eyebrow","features"],"properties":{"eyebrow":{"type":"object","required":["enabled","text"],"properties":{"enabled":{"type":"boolean"},"text":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"features":{"type":"object","required":["enabled","dropin","providers","release"],"properties":{"enabled":{"type":"boolean"},"dropin":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"providers":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"release":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}}}}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/tokens/":{"get":{"tags":["Admin"],"summary":"List the caller’s admin API tokens","operationId":"listAdminTokens","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["tokens"],"properties":{"tokens":{"type":"array","items":{"type":"object","required":["id","name","prefix","scopes","expiresAt","lastUsedAt","createdAt","revokedAt"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"nullable":true,"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"expiresAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"lastUsedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"createdAt":{"type":"number"},"revokedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]}}}}}}}}}}},"post":{"tags":["Admin"],"summary":"Create a long-lived admin API token (M2M)","description":"Returns the plaintext token once. Store it immediately — the registry only persists a sha256 hash and cannot show the token again. Use it as `Authorization: Bearer <token>` against any /api/admin/* endpoint.","operationId":"createAdminToken","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":80,"type":"string"},"scopes":{"nullable":true,"anyOf":[{"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":40,"type":"string"}},{"type":"null"}]},"expiresAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":80,"type":"string"},"scopes":{"nullable":true,"anyOf":[{"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":40,"type":"string"}},{"type":"null"}]},"expiresAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]}}}},"multipart/form-data":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":80,"type":"string"},"scopes":{"nullable":true,"anyOf":[{"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":40,"type":"string"}},{"type":"null"}]},"expiresAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["token","row"],"properties":{"token":{"type":"string"},"row":{"type":"object","required":["id","name","prefix","scopes","expiresAt","lastUsedAt","createdAt","revokedAt"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"nullable":true,"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"expiresAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"lastUsedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"createdAt":{"type":"number"},"revokedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]}}}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/tokens/{id}/":{"delete":{"tags":["Admin"],"summary":"Revoke an admin API token","operationId":"revokeAdminToken","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"const":true,"type":"boolean"}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"403":{"description":"Response for status 403","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/manifest/extensions/":{"get":{"tags":["Admin"],"summary":"Read the current manifest extensions delta + merged schema","operationId":"getManifestExtensions","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["extensions","mergedSchema"],"properties":{"extensions":{"type":"object","patternProperties":{"^(.*)$":{}}},"mergedSchema":{}}}}}}}},"put":{"tags":["Admin"],"summary":"Replace the manifest extensions delta","description":"The delta is a flat record of `{ propertyName: JSONSchemaNode }`. The registry wraps it into an object schema and merges it with the locked core. Pass `null` or `{}` to clear.","operationId":"updateManifestExtensions","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["extensions"],"properties":{"extensions":{"nullable":true,"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["extensions"],"properties":{"extensions":{"nullable":true,"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}},"multipart/form-data":{"schema":{"type":"object","required":["extensions"],"properties":{"extensions":{"nullable":true,"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/admin/auth/email-recovery/":{"get":{"tags":["Admin"],"summary":"Read email-recovery state for the current admin","operationId":"getAdminEmailRecovery","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["persist","hasCredentials","email"],"properties":{"persist":{"type":"boolean"},"hasCredentials":{"type":"boolean"},"email":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}}}}}}},"put":{"tags":["Admin"],"summary":"Update email-recovery state","description":"Toggle whether rootCredentials survives the first OAuth link, and/or rotate the bootstrap email + password for the calling admin.","operationId":"updateAdminEmailRecovery","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"persist":{"type":"boolean"},"email":{"minLength":1,"maxLength":254,"type":"string"},"password":{"minLength":1,"maxLength":200,"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"persist":{"type":"boolean"},"email":{"minLength":1,"maxLength":254,"type":"string"},"password":{"minLength":1,"maxLength":200,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"persist":{"type":"boolean"},"email":{"minLength":1,"maxLength":254,"type":"string"},"password":{"minLength":1,"maxLength":200,"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["persist","hasCredentials","email"],"properties":{"persist":{"type":"boolean"},"hasCredentials":{"type":"boolean"},"email":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Admin"],"summary":"Delete the rootCredentials row for the current admin","operationId":"deleteAdminEmailRecovery","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["persist","hasCredentials","email"],"properties":{"persist":{"type":"boolean"},"hasCredentials":{"type":"boolean"},"email":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}}}}}}}},"/api/admin/i18n/":{"get":{"tags":["Admin"],"summary":"Get i18n config","operationId":"adminGetI18n","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["defaultLocale","enabledLocales","availableLocales"],"properties":{"defaultLocale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"enabledLocales":{"type":"array","items":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}},"availableLocales":{"type":"array","items":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}}}}}}}}},"patch":{"tags":["Admin"],"summary":"Update i18n config","operationId":"adminUpdateI18n","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"defaultLocale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"enabledLocales":{"type":"array","items":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"defaultLocale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"enabledLocales":{"type":"array","items":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"defaultLocale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"enabledLocales":{"type":"array","items":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["defaultLocale","enabledLocales","availableLocales"],"properties":{"defaultLocale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"enabledLocales":{"type":"array","items":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}},"availableLocales":{"type":"array","items":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/pages/":{"get":{"tags":["Plugins"],"summary":"List published markdown pages","operationId":"listPages","parameters":[{"name":"locale","in":"query","required":false,"schema":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["pages"],"properties":{"pages":{"type":"array","items":{"type":"object","required":["slug","locale","path","title","navOrder","showInFooter","updatedAt"],"properties":{"slug":{"type":"string"},"locale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"path":{"type":"string"},"title":{"type":"string"},"navOrder":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"showInFooter":{"type":"boolean"},"updatedAt":{"type":"number"}}}}}}}}}}}},"/api/pages/by-path":{"get":{"tags":["Plugins"],"summary":"Resolve a published page by its public path","description":"Returns sanitized HTML in the requested locale, with fallback to the default locale.","operationId":"getPageByPath","parameters":[{"name":"path","in":"query","required":false,"schema":{"type":"string"}},{"name":"locale","in":"query","required":false,"schema":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["slug","locale","path","title","html","updatedAt"],"properties":{"slug":{"type":"string"},"locale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"path":{"type":"string"},"title":{"type":"string"},"html":{"type":"string"},"updatedAt":{"type":"number"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/pages/{slug}/":{"get":{"tags":["Plugins"],"summary":"Render a published markdown page","description":"Returns sanitized HTML for a page in the requested locale, falling back to the default locale if missing.","operationId":"getPage","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"locale","in":"query","required":false,"schema":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["slug","locale","title","html","updatedAt"],"properties":{"slug":{"type":"string"},"locale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"title":{"type":"string"},"html":{"type":"string"},"updatedAt":{"type":"number"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/features/":{"get":{"tags":["Plugins"],"summary":"Get public feature flags (submissions/requests on/off)","operationId":"getFeatures","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["submissionsEnabled","requestsEnabled"],"properties":{"submissionsEnabled":{"type":"boolean"},"requestsEnabled":{"type":"boolean"}}}}}}}}},"/api/init/test-db/":{"post":{"tags":["Auth"],"summary":"Probe a database URL during install","description":"Tries to connect to the given URL and run SELECT 1. Reports the detected dialect either way. Bootstrap-only.","operationId":"initTestDb","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"minLength":1,"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["url"],"properties":{"url":{"minLength":1,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["url"],"properties":{"url":{"minLength":1,"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"anyOf":[{"type":"object","required":["ok","dialect"],"properties":{"ok":{"const":true,"type":"boolean"},"dialect":{"anyOf":[{"const":"sqlite","type":"string"},{"const":"pg","type":"string"},{"const":"mysql","type":"string"}]}}},{"type":"object","required":["ok","dialect","error"],"properties":{"ok":{"const":false,"type":"boolean"},"dialect":{"anyOf":[{"const":"sqlite","type":"string"},{"const":"pg","type":"string"},{"const":"mysql","type":"string"}]},"error":{"type":"string"}}}]}}}},"401":{"description":"Response for status 401","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/init/status/":{"get":{"tags":["Auth"],"summary":"Check whether the registry needs first-time setup","operationId":"initStatus","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["requiresInit","setupCompleted","emailRecoveryAvailable","mode"],"properties":{"requiresInit":{"type":"boolean"},"setupCompleted":{"type":"boolean"},"emailRecoveryAvailable":{"type":"boolean"},"mode":{"type":"string","enum":["setup","normal"]}}}}}}}}},"/api/init/defaults/":{"get":{"tags":["Auth"],"summary":"Env values to prefill the install wizard","operationId":"initDefaults","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["database","baseUrl","webBaseUrl","providers"],"properties":{"database":{"type":"object","required":["url"],"properties":{"url":{"type":"string"}}},"baseUrl":{"type":"string"},"webBaseUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"providers":{"type":"object","required":["github","gitlab","codeberg"],"properties":{"github":{"type":"object","required":["clientId","clientSecretSet"],"properties":{"clientId":{"type":"string"},"clientSecretSet":{"type":"boolean"}}},"gitlab":{"type":"object","required":["clientId","clientSecretSet"],"properties":{"clientId":{"type":"string"},"clientSecretSet":{"type":"boolean"}}},"codeberg":{"type":"object","required":["clientId","clientSecretSet"],"properties":{"clientId":{"type":"string"},"clientSecretSet":{"type":"boolean"}}}}}}}}}}}}},"/api/init/complete/":{"post":{"tags":["Auth"],"summary":"Finalize install: connect DB, migrate, promote bootstrap admin, exit","operationId":"initComplete","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["database"],"properties":{"database":{"type":"object","required":["url"],"properties":{"url":{"minLength":1,"type":"string"}}}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["database"],"properties":{"database":{"type":"object","required":["url"],"properties":{"url":{"minLength":1,"type":"string"}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["database"],"properties":{"database":{"type":"object","required":["url"],"properties":{"url":{"minLength":1,"type":"string"}}}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"detail":{"type":"string"}}}}}},"410":{"description":"Response for status 410","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"500":{"description":"Response for status 500","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"detail":{"type":"string"}}}}}}}}},"/api/init/login/":{"post":{"tags":["Auth"],"summary":"Bootstrap sign-in for the install wizard","operationId":"initLogin","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"minLength":3,"maxLength":254,"type":"string"},"password":{"minLength":1,"maxLength":256,"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"minLength":3,"maxLength":254,"type":"string"},"password":{"minLength":1,"maxLength":256,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"minLength":3,"maxLength":254,"type":"string"},"password":{"minLength":1,"maxLength":256,"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"}}}}}},"401":{"description":"Response for status 401","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"410":{"description":"Response for status 410","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/instance/info/":{"get":{"tags":["Plugins"],"summary":"Public instance info (app URL schemes, etc)","description":"Public read-only endpoint. Clients (frontend, third-party browsers) consume this on boot to learn which desktop apps can handle \"Open in App\" deep-links for plugin installation.","operationId":"getInstanceInfo","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["appUrlSchemes"],"properties":{"appUrlSchemes":{"type":"array","items":{"type":"object","required":["name","scheme"],"properties":{"name":{"type":"string"},"scheme":{"type":"string"},"kinds":{"type":"array","items":{"type":"string"}}}}}}}}}}}}},"/api/publish/{slug}/":{"post":{"tags":["Publish"],"summary":"Publish a release from a CI / CLI using a publisher token","description":"Token-authenticated counterpart to the webhook ingest path. First push of an unknown slug auto-claims it (requires publish:* scope and a repoUrl). Subsequent pushes update the plugin's latest release. Manifest is validated; asset URLs are hashed in the background.","operationId":"publishRelease","security":[{"publisherToken":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["manifest","manifestSource","version","assets"],"properties":{"manifest":{"minLength":1,"maxLength":65536,"type":"string"},"manifestSource":{"type":"string","enum":["tabularium.yaml","tabularium.json"]},"version":{"minLength":1,"maxLength":40,"pattern":"^v?\\d+\\.\\d+\\.\\d+(?:[-+][A-Za-z0-9.-]+)?$","type":"string"},"assets":{"maxItems":32,"type":"array","items":{"type":"object","required":["name","url"],"properties":{"name":{"minLength":1,"maxLength":255,"type":"string"},"url":{"minLength":1,"maxLength":1000,"type":"string"},"sha256":{"pattern":"^[0-9a-f]{64}$","type":"string"},"size":{"type":"number"}}}},"repoUrl":{"minLength":1,"maxLength":500,"type":"string"},"attestation":{}},"additionalProperties":false}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["manifest","manifestSource","version","assets"],"properties":{"manifest":{"minLength":1,"maxLength":65536,"type":"string"},"manifestSource":{"type":"string","enum":["tabularium.yaml","tabularium.json"]},"version":{"minLength":1,"maxLength":40,"pattern":"^v?\\d+\\.\\d+\\.\\d+(?:[-+][A-Za-z0-9.-]+)?$","type":"string"},"assets":{"maxItems":32,"type":"array","items":{"type":"object","required":["name","url"],"properties":{"name":{"minLength":1,"maxLength":255,"type":"string"},"url":{"minLength":1,"maxLength":1000,"type":"string"},"sha256":{"pattern":"^[0-9a-f]{64}$","type":"string"},"size":{"type":"number"}}}},"repoUrl":{"minLength":1,"maxLength":500,"type":"string"},"attestation":{}},"additionalProperties":false}},"multipart/form-data":{"schema":{"type":"object","required":["manifest","manifestSource","version","assets"],"properties":{"manifest":{"minLength":1,"maxLength":65536,"type":"string"},"manifestSource":{"type":"string","enum":["tabularium.yaml","tabularium.json"]},"version":{"minLength":1,"maxLength":40,"pattern":"^v?\\d+\\.\\d+\\.\\d+(?:[-+][A-Za-z0-9.-]+)?$","type":"string"},"assets":{"maxItems":32,"type":"array","items":{"type":"object","required":["name","url"],"properties":{"name":{"minLength":1,"maxLength":255,"type":"string"},"url":{"minLength":1,"maxLength":1000,"type":"string"},"sha256":{"pattern":"^[0-9a-f]{64}$","type":"string"},"size":{"type":"number"}}}},"repoUrl":{"minLength":1,"maxLength":500,"type":"string"},"attestation":{}},"additionalProperties":false}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["slug","version","claimed"],"properties":{"slug":{"type":"string"},"version":{"type":"string"},"claimed":{"type":"boolean"}},"additionalProperties":false}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}},"additionalProperties":false}}}},"403":{"description":"Response for status 403","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}},"additionalProperties":false}}}},"409":{"description":"Response for status 409","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}},"additionalProperties":false}}}},"422":{"description":"Response for status 422","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"errors":{"type":"array","items":{}}},"additionalProperties":false}}}}}}},"/api/webhooks/release/":{"post":{"tags":["Webhooks"],"summary":"Release event ingestion","description":"GitHub / GitLab / Gitea (and Forgejo) post releases here. Signature scheme is picked per plugin's linked provider instance kind.","operationId":"releaseWebhook"}},"/api/branding/":{"get":{"tags":["Plugins"],"summary":"Get instance branding (whitelabel)","description":"Public read-only endpoint. Pass `?locale=` to fetch the tagline/footer in a specific language (falls back to default locale).","operationId":"getBranding","parameters":[{"name":"locale","in":"query","required":false,"schema":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["name","tagline","primaryHex","accentHex","successHex","logoUrl","faviconUrl","footerText","analyticsScript","allowIndexing"],"properties":{"name":{"type":"string"},"tagline":{"type":"string"},"primaryHex":{"type":"string"},"accentHex":{"type":"string"},"successHex":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"faviconUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"footerText":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"analyticsScript":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"allowIndexing":{"type":"boolean"}}}}}}}}},"/api/submit/repos/":{"get":{"tags":["Submit"],"summary":"List submittable repos across linked identities","operationId":"listSubmittableRepos","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["groups"],"properties":{"groups":{"type":"array","items":{"type":"object","required":["identityId","providerInstanceId","providerDisplayName","providerKind","username","repos","error","reauthUrl"],"properties":{"identityId":{"type":"string"},"providerInstanceId":{"type":"string"},"providerDisplayName":{"type":"string"},"providerKind":{"type":"string","enum":["github","gitlab","gitea"]},"username":{"type":"string"},"repos":{"type":"array","items":{"type":"object","required":["identityId","providerInstanceId","fullName","owner","name","url","description","isPrivate"],"properties":{"identityId":{"type":"string"},"providerInstanceId":{"type":"string"},"fullName":{"type":"string"},"owner":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"description":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"isPrivate":{"type":"boolean"}}}},"error":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"reauthUrl":{"type":"string"}}}}}}}}}}}},"/api/submit/preview/":{"post":{"tags":["Submit"],"summary":"Dry-run a submission: fetch + validate the manifest, no writes","description":"Resolves the latest release + .tabularium for the given repo and returns a preview the frontend can render before the user confirms. No database writes. Useful so the submit page can show name/description/kind/screenshots from the manifest instead of asking the user to type them again.","operationId":"submitPreview","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["repoUrl"],"properties":{"repoUrl":{"minLength":1,"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["repoUrl"],"properties":{"repoUrl":{"minLength":1,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["repoUrl"],"properties":{"repoUrl":{"minLength":1,"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"anyOf":[{"type":"object","required":["ok","slug","slugTaken","repoUrl","providerInstanceId","providerKind","fromManifest","version","preview"],"properties":{"ok":{"const":true,"type":"boolean"},"slug":{"type":"string"},"slugTaken":{"type":"boolean"},"repoUrl":{"type":"string"},"providerInstanceId":{"type":"string"},"providerKind":{"type":"string"},"fromManifest":{"const":true,"type":"boolean"},"version":{"type":"string"},"preview":{"type":"object","required":["name","description","category","kind","tags","license","icon","screenshots","homepage","documentationUrl","minRuntimeVersion","source","readmeLocales"],"properties":{"name":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"category":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"tags":{"type":"array","items":{"type":"string"}},"license":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"icon":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"screenshots":{"type":"array","items":{"type":"object","required":["url","caption","alt"],"properties":{"url":{"type":"string"},"caption":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"alt":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}}},"homepage":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"documentationUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"minRuntimeVersion":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"source":{"type":"string"},"readmeLocales":{"type":"array","items":{"type":"string"}}}}}},{"type":"object","required":["ok","slug","slugTaken","repoUrl","providerInstanceId","providerKind","fromManifest","suggestedName","message"],"properties":{"ok":{"const":true,"type":"boolean"},"slug":{"type":"string"},"slugTaken":{"type":"boolean"},"repoUrl":{"type":"string"},"providerInstanceId":{"type":"string"},"providerKind":{"type":"string"},"fromManifest":{"const":false,"type":"boolean"},"suggestedName":{"type":"string"},"message":{"type":"string"},"validationErrors":{"type":"array","items":{}}}}]}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"const":false,"type":"boolean"},"error":{"type":"string"}}}}}},"401":{"description":"Response for status 401","content":{"application/json":{"schema":{"type":"object","required":["ok","error","reauthFor"],"properties":{"ok":{"const":false,"type":"boolean"},"error":{"type":"string"},"reauthFor":{"type":"string"}}}}}},"403":{"description":"Response for status 403","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"const":false,"type":"boolean"},"error":{"type":"string"}}}}}},"412":{"description":"Response for status 412","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"const":false,"type":"boolean"},"error":{"type":"string"}}}}}},"500":{"description":"Response for status 500","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"const":false,"type":"boolean"},"error":{"type":"string"}}}}}}}}},"/api/submit/oauth/":{"post":{"tags":["Submit"],"summary":"Submit plugin via OAuth-verified ownership","operationId":"submitOAuth","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["repoUrl"],"properties":{"repoUrl":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["repoUrl"],"properties":{"repoUrl":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["repoUrl"],"properties":{"repoUrl":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["slug","status","webhookSecret","webhookUrl","webhookInstalled","webhookSetupReason","repoUrl","providerInstanceId","providerKind"],"properties":{"slug":{"type":"string"},"status":{"type":"string","enum":["approved","pending","rejected"]},"webhookSecret":{"type":"string"},"webhookUrl":{"type":"string"},"webhookInstalled":{"type":"boolean"},"webhookSetupReason":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"repoUrl":{"type":"string"},"providerInstanceId":{"type":"string"},"providerKind":{"type":"string"}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"401":{"description":"Response for status 401","content":{"application/json":{"schema":{"type":"object","required":["error","reauthFor"],"properties":{"error":{"type":"string"},"reauthFor":{"type":"string"}}}}}},"403":{"description":"Response for status 403","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"409":{"description":"Response for status 409","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"412":{"description":"Response for status 412","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/home-copy/":{"get":{"tags":["Plugins"],"summary":"Get home-page copy overrides","description":"Public read-only endpoint. Returns the admin-configured eyebrow + feature-section overrides. Empty translation maps mean \"fall back to the built-in copy\".","operationId":"getHomeCopy","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["eyebrow","features"],"properties":{"eyebrow":{"type":"object","required":["enabled","text"],"properties":{"enabled":{"type":"boolean"},"text":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"features":{"type":"object","required":["enabled","dropin","providers","release"],"properties":{"enabled":{"type":"boolean"},"dropin":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"providers":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}},"release":{"type":"object","required":["title","body"],"properties":{"title":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}},"body":{"type":"object","properties":{"en":{"type":"string"},"de":{"type":"string"},"es":{"type":"string"},"fr":{"type":"string"},"it":{"type":"string"},"zh-CN":{"type":"string"}}}}}}}}}}}}}}},"/api/manifest/":{"get":{"tags":["Plugins"],"summary":"Discoverable plugin manifest spec","description":"Public reference for plugin authors. Returns the TypeBox JSON Schema, an example, and the registry's active kind keys.","operationId":"getManifestSpec","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["description","paths","schema","example","kinds"],"properties":{"description":{"type":"string"},"paths":{"type":"array","items":{"type":"string"}},"schema":{},"example":{"type":"string"},"kinds":{"type":"array","items":{"type":"string"}}}}}}}}}},"/api/manifest/validate/":{"post":{"tags":["Plugins"],"summary":"Validate a manifest body against the registry schema","description":"Public validator for authoring tools / CI. Returns 200 + ok:false on validation failure so HTTP-status-branching tools always reach the body. 4xx is reserved for malformed requests and oversized bodies.","operationId":"validateManifest","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string"},"source":{"type":"string","enum":["tabularium.yaml","tabularium.json"]},"kind":{"maxLength":40,"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string"},"source":{"type":"string","enum":["tabularium.yaml","tabularium.json"]},"kind":{"maxLength":40,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string"},"source":{"type":"string","enum":["tabularium.yaml","tabularium.json"]},"kind":{"maxLength":40,"type":"string"}}}}}}}},"/api/auth/me/tokens/":{"get":{"tags":["Publisher tokens"],"summary":"List the caller's publisher tokens","operationId":"listMyPublisherTokens","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["tokens"],"properties":{"tokens":{"type":"array","items":{"type":"object","required":["id","name","prefix","scopes","expiresAt","lastUsedAt","createdAt","revokedAt"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"expiresAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"lastUsedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"createdAt":{"type":"number"},"revokedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]}}}}},"additionalProperties":false}}}}}},"post":{"tags":["Publisher tokens"],"summary":"Create a publisher API token","description":"Returns the plaintext token once. Store it immediately — the registry only persists a sha256 hash and cannot show the token again. Use it as `Authorization: Bearer <token>` against POST /api/publish/:slug.","operationId":"createMyPublisherToken","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","scopes"],"properties":{"name":{"minLength":1,"maxLength":80,"type":"string"},"scopes":{"minItems":1,"maxItems":32,"type":"array","items":{"minLength":1,"maxLength":80,"type":"string"}},"expiresAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["name","scopes"],"properties":{"name":{"minLength":1,"maxLength":80,"type":"string"},"scopes":{"minItems":1,"maxItems":32,"type":"array","items":{"minLength":1,"maxLength":80,"type":"string"}},"expiresAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]}}}},"multipart/form-data":{"schema":{"type":"object","required":["name","scopes"],"properties":{"name":{"minLength":1,"maxLength":80,"type":"string"},"scopes":{"minItems":1,"maxItems":32,"type":"array","items":{"minLength":1,"maxLength":80,"type":"string"}},"expiresAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["token","row"],"properties":{"token":{"type":"string"},"row":{"type":"object","required":["id","name","prefix","scopes","expiresAt","lastUsedAt","createdAt","revokedAt"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"expiresAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"lastUsedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"createdAt":{"type":"number"},"revokedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]}}}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/api/auth/me/tokens/{id}/":{"delete":{"tags":["Publisher tokens"],"summary":"Revoke a publisher token","operationId":"revokeMyPublisherToken","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/i18n/":{"get":{"tags":["Plugins"],"summary":"Get i18n config (default + enabled locales)","operationId":"getI18nConfig","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["defaultLocale","enabledLocales","availableLocales"],"properties":{"defaultLocale":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]},"enabledLocales":{"type":"array","items":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}},"availableLocales":{"type":"array","items":{"type":"string","enum":["en","de","es","fr","it","zh-CN"]}}}}}}}}}},"/auth/logout/":{"post":{"tags":["Auth"],"summary":"Revoke session + clear cookie","description":"Idempotent — flips the server-side session record to revoked and clears the auth cookie. Returns `{ ok: true }` regardless of prior auth state.","operationId":"logout","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"}}}}}}}}},"/auth/providers/":{"get":{"tags":["Auth"],"summary":"List enabled OAuth provider instances","operationId":"listProviders","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["providers"],"properties":{"providers":{"type":"array","items":{"type":"object","required":["id","kind","displayName","baseUrl","logoUrl"],"properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["github","gitlab","gitea"]},"displayName":{"type":"string"},"baseUrl":{"type":"string"},"logoUrl":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]}}}}}}}}}}}},"/auth/me/":{"get":{"tags":["Auth"],"summary":"Get current user with linked identities","operationId":"getMe","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["id","username","displayName","role","identities"],"properties":{"id":{"type":"string"},"username":{"type":"string"},"displayName":{"type":"string"},"role":{"type":"string","enum":["user","admin"]},"identities":{"type":"array","items":{"type":"object","required":["id","providerInstanceId","providerKind","providerDisplayName","username"],"properties":{"id":{"type":"string"},"providerInstanceId":{"type":"string"},"providerKind":{"type":"string"},"providerDisplayName":{"type":"string"},"username":{"type":"string"}}}}}}}}}}},"delete":{"tags":["Auth"],"summary":"Delete your account","description":"Cascade-deletes identities + root credentials and clears the session cookie. Refuses with 409 if you still own plugins — transfer or delete them first.","operationId":"deleteMe","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"}}}}}},"409":{"description":"Response for status 409","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/auth/me/identities/{id}":{"delete":{"tags":["Auth"],"summary":"Unlink an identity","operationId":"unlinkIdentity","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/auth/me/transfers/":{"get":{"tags":["Auth"],"summary":"List plugin transfers involving the current user","description":"Returns both incoming (you are the recipient) and outgoing (you initiated) transfers. Each row carries plugin + actor names already joined in.","operationId":"listTransfers","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["transfers"],"properties":{"transfers":{"type":"array","items":{"type":"object","required":["id","pluginId","pluginName","fromUserId","fromName","toUserId","toName","status","message","createdAt","expiresAt","respondedAt","direction"],"properties":{"id":{"type":"string"},"pluginId":{"type":"string"},"pluginName":{"type":"string"},"fromUserId":{"type":"string"},"fromName":{"type":"string"},"toUserId":{"type":"string"},"toName":{"type":"string"},"status":{"type":"string","enum":["pending","accepted","rejected","cancelled","expired"]},"message":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"number"},"expiresAt":{"type":"number"},"respondedAt":{"nullable":true,"anyOf":[{"type":"number"},{"type":"null"}]},"direction":{"type":"string","enum":["incoming","outgoing"]}}}}}}}}}}}},"/auth/me/transfers/{id}/":{"post":{"tags":["Auth"],"summary":"Respond to a transfer (accept / reject / cancel)","description":"Recipient: `action=accept` (moves ownership) or `action=reject`. Sender: `action=cancel`. Idempotent — once terminal, returns 409.","operationId":"respondTransfer","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["accept","reject","cancel"]}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["accept","reject","cancel"]}}}},"multipart/form-data":{"schema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["accept","reject","cancel"]}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","status"],"properties":{"ok":{"type":"boolean"},"status":{"type":"string","enum":["accepted","rejected","cancelled"]}}}}}},"403":{"description":"Response for status 403","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"409":{"description":"Response for status 409","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/auth/{instance}/":{"get":{"tags":["Auth"],"summary":"Start OAuth flow for a provider instance","description":"Redirects to the provider instance's authorize page. Instance IDs are configured by the admin (e.g. `github`, `gitlab`, `forgejo-acme`). Set `link=1` to attach this identity to the currently signed-in user.","operationId":"startOAuth","parameters":[{"name":"instance","in":"path","required":true,"schema":{"type":"string"}},{"name":"link","in":"query","required":false,"schema":{"type":"string"}},{"name":"return_to","in":"query","required":false,"schema":{"type":"string"}}]}},"/auth/{instance}/callback/":{"get":{"tags":["Auth"],"summary":"OAuth callback for a provider instance","operationId":"oauthCallback","parameters":[{"name":"instance","in":"path","required":true,"schema":{"type":"string"}},{"name":"code","in":"query","required":true,"schema":{"type":"string"}},{"name":"state","in":"query","required":true,"schema":{"type":"string"}}]}},"/auth/email/register/":{"post":{"tags":["Auth"],"summary":"Register the bootstrap admin account","description":"Allowed only while no admin exists. The first registered account becomes admin. Credentials are stored in `root_credentials` and deleted automatically the first time the admin links an OAuth identity.","operationId":"registerBootstrapAdmin","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password","displayName"],"properties":{"email":{"minLength":3,"maxLength":254,"format":"email","type":"string"},"password":{"minLength":8,"maxLength":256,"type":"string"},"displayName":{"minLength":1,"maxLength":60,"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["email","password","displayName"],"properties":{"email":{"minLength":3,"maxLength":254,"format":"email","type":"string"},"password":{"minLength":8,"maxLength":256,"type":"string"},"displayName":{"minLength":1,"maxLength":60,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["email","password","displayName"],"properties":{"email":{"minLength":3,"maxLength":254,"format":"email","type":"string"},"password":{"minLength":8,"maxLength":256,"type":"string"},"displayName":{"minLength":1,"maxLength":60,"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","user"],"properties":{"ok":{"type":"boolean"},"user":{"type":"object","required":["id","username","displayName","role","identities"],"properties":{"id":{"type":"string"},"username":{"type":"string"},"displayName":{"type":"string"},"role":{"type":"string","enum":["user","admin"]},"identities":{"type":"array","items":{}}}}}}}}},"400":{"description":"Response for status 400","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"403":{"description":"Response for status 403","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}},"409":{"description":"Response for status 409","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/auth/email/login/":{"post":{"tags":["Auth"],"summary":"Bootstrap admin email + password sign-in","description":"Recovery path for the initial admin. Closes permanently once the admin links any OAuth identity.","operationId":"emailLogin","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"minLength":3,"maxLength":254,"format":"email","type":"string"},"password":{"minLength":1,"maxLength":256,"type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"minLength":3,"maxLength":254,"format":"email","type":"string"},"password":{"minLength":1,"maxLength":256,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"minLength":3,"maxLength":254,"format":"email","type":"string"},"password":{"minLength":1,"maxLength":256,"type":"string"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","userId"],"properties":{"ok":{"type":"boolean"},"userId":{"type":"string"}}}}}},"401":{"description":"Response for status 401","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}}}},"/uploads/*":{"get":{"operationId":"getUploads*"}},"":{"get":{"operationId":"getIndex"}}}}