tabularium-sdk 0.1.0

Async Rust client for the Tabularium plugin registry HTTP API
Documentation
1
{"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.spitzli.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":"Pings the DB, cache, and storage adapters. Returns 503 if any check fails. Suitable for k8s/Docker liveness + readiness probes.","operationId":"healthz","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"type":"object","required":["ok","uptimeSeconds","checks"],"properties":{"ok":{"type":"boolean"},"uptimeSeconds":{"type":"number"},"checks":{"type":"object","patternProperties":{"^(.*)$":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"},"detail":{"type":"string"}}}}}},"additionalProperties":false}}}},"503":{"description":"Response for status 503","content":{"application/json":{"schema":{"type":"object","required":["ok","uptimeSeconds","checks"],"properties":{"ok":{"type":"boolean"},"uptimeSeconds":{"type":"number"},"checks":{"type":"object","patternProperties":{"^(.*)$":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"},"detail":{"type":"string"}}}}}},"additionalProperties":false}}}}}}},"/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"}}}}}},"additionalProperties":false}}}}}}},"/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"]}}},"additionalProperties":false}}}}}}},"/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"}}}}},"additionalProperties":false}}}}}}},"/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"}},"additionalProperties":false}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}},"additionalProperties":false}}}}}}},"/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/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/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"}}}}}}}},"additionalProperties":false}}}}}}},"/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"}},"additionalProperties":false}}}}}}},"/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/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":"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","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"}]},"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"}}}}}}},"additionalProperties":false}}}}}}},"/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","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"}]},"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"}]}}}}},"additionalProperties":false}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}},"additionalProperties":false}}}}}},"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}/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"}}}}}},"additionalProperties":false}}}},"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"}}}}}},"additionalProperties":false}}}},"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"}}}}}},"additionalProperties":false}}}}}}},"/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"}]}},"additionalProperties":false}}}},"404":{"description":"Response for status 404","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"}},"additionalProperties":false}}}}}}},"/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}/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"}}}}}}},"additionalProperties":false}}}},"404":{"description":"Response for status 404","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}},"additionalProperties":false}}}}}}},"/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}/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/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/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"],"properties":{"allowedFiles":{"type":"array","items":{"type":"string"}},"defaultFiles":{"type":"array","items":{"type":"string"}},"schemaUrl":{"type":"string"},"filesOverridden":{"type":"boolean"},"schemaUrlOverridden":{"type":"boolean"}}},"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"}]},"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"}]},"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"}]},"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/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/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/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/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/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/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/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/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"},"description":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"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"}]},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]}}},{"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"},"description":{"nullable":true,"anyOf":[{"maxLength":280,"type":"string"},{"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"}]},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]}}},{"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"},"description":{"nullable":true,"anyOf":[{"maxLength":280,"type":"string"},{"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"}]},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]}}},{"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"},"description":{"nullable":true,"anyOf":[{"maxLength":280,"type":"string"},{"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"}]},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]}}},{"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"},"description":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"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"}]},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]}}},{"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"},"description":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"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"}]},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]}}},{"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"},"description":{"nullable":true,"anyOf":[{"maxLength":280,"type":"string"},{"type":"null"}]},"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"}]},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]}}},{"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"},"description":{"nullable":true,"anyOf":[{"maxLength":280,"type":"string"},{"type":"null"}]},"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"}]},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]}}},{"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"},"description":{"nullable":true,"anyOf":[{"maxLength":280,"type":"string"},{"type":"null"}]},"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"}]},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]}}},{"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"},"description":{"nullable":true,"anyOf":[{"type":"string"},{"type":"null"}]},"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"}]},"intro":{"nullable":true,"anyOf":[{"maxLength":600,"type":"string"},{"type":"null"}]}}},{"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/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/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","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"}]},"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"}}}},"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"}}}},"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"}}}}}},"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/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/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/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/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/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}/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/{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/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/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"}},"additionalProperties":false}}}}}}},"/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/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/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/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/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/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"}}},"additionalProperties":false}}}}}}},"/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/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-]+$","description":"URL-safe slug. Lowercase letters, digits, hyphens.","type":"string"},"name":{"description":"Human-readable plugin name.","type":"string"},"description":{"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-]+$","description":"URL-safe slug. Lowercase letters, digits, hyphens.","type":"string"},"name":{"description":"Human-readable plugin name.","type":"string"},"description":{"description":"What the plugin should do.","type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["slug","name","description"],"properties":{"slug":{"pattern":"^[a-z0-9-]+$","description":"URL-safe slug. Lowercase letters, digits, hyphens.","type":"string"},"name":{"description":"Human-readable plugin name.","type":"string"},"description":{"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}/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/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/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"}}}}}}}}},"/auth/logout/":{"post":{"tags":["Auth"],"summary":"Clear the session cookie","description":"Idempotent — 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"}]}}}}},"additionalProperties":false}}}}}}},"/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"}}}}},"additionalProperties":false}}}}}},"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"]}}}}},"additionalProperties":false}}}}}}},"/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/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"}}}}}}}}},"/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/{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"}}]}},"/uploads/*":{"get":{"operationId":"getUploads*"}},"":{"get":{"operationId":"getIndex"}}}}