Instanzverwaltung
Das Backoffice des Betreibers: jeder Workspace dieser Instanz mit Metadaten und Tarif, die Tarifzuweisung, die fortschreibende Spur der Betreiberaktionen und der Posteingang der eingesendeten Meldungen (siehe Feedback). Bis auf diese eine Ausnahme ausschließlich Metadaten — der Betreiber einer gehosteten Instanz ist Auftragsverarbeiter, und diese Routen können weder Seitentitel noch Seiteninhalte noch Kommentare zurückgeben; eine Meldung ist hier nur deshalb lesbar, weil jemand sie geschrieben und an den Betreiber gesendet hat. Nur mit Sitzung, und beschränkt auf die in OPERATORS genannten Personen.
6 Operationen. Jeder Pfad ist relativ zum Ursprung der Instanz; jeder Antwortkörper ist JSON, sofern nicht anders vermerkt. Die Endpunktbeschreibungen unten stammen unverändert aus der Routentabelle des Servers und bleiben englisch. Authentifizierung, Fehler und seitenweise Abfrage stehen unter API.
GET /api/v1/admin/workspaces
Every workspace on this instance with its billing-relevant METADATA: name, slug, creation date, member count, live page count, stored bytes, plan and last activity. Newest first, keyset-paginated, optionally narrowed by name/slug or plan. Carries no page titles, no bodies and no comments — the operator is a processor, not a reader (GDPR Art. 28). Instance operator only (OPERATORS); everybody else gets 403, a workspace owner included. Session-only twice over: the route sits outside /workspaces/{workspaceId}, which tokens/scopes.ts classifies session-only, and the operator guard refuses a token-authenticated request outright.
listAdminWorkspaces · token scope: session only
Query parameters
cursor· string — length 1–∞limit· integer — 1–100q· string — length 1–200plan·"free"|"pro"|"business"|"enterprise"|"self_hosted"|"team"Show only workspaces on this plan. Deprecated spellings are accepted for compatibility and resolved before anything is stored or answered:teammeanspro, andself_hostedmeansenterprise— the deployment mode that id named was withdrawn (ADR-0024) and the id retired (ADR-0029 D1). Responses only ever spell the four live ids.
Response 200 — application/json
items· object[] — requiredid· string (uuid) — requiredslug· string — required, length 1–100name· string — requiredcreatedAt· string (date-time) — requiredmemberCount· integer — required, 0–9007199254740991pageCount· integer — required, 0–9007199254740991storageBytes· integer — required, 0–9007199254740991plan·"free"|"pro"|"business"|"enterprise"— requiredplanSince· string (date-time) | null — requiredlastActivityAt· string (date-time) | null — required
nextCursor· string | null — required
Response default — application/json
error· object — requiredcode·"bad_request"|"validation_failed"|"unauthorized"|"forbidden"|"not_found"|"conflict"|"rate_limited"|"internal"— requiredmessage· string — requiredreason· string — length 1–64
GET /api/v1/admin/workspaces/{targetWorkspaceId}
One workspace in detail: the list row plus memberships per role, the owners (the billing counterparties — owners only, never the whole roster), the attachment count, and the plan limits with whichever of them it currently exceeds. Metadata only. Instance operator only (OPERATORS); everybody else gets 403, a workspace owner included. Session-only twice over: the route sits outside /workspaces/{workspaceId}, which tokens/scopes.ts classifies session-only, and the operator guard refuses a token-authenticated request outright.
getAdminWorkspace · token scope: session only
Path parameters
targetWorkspaceId· string (uuid) — required
Response 200 — application/json
id· string (uuid) — requiredslug· string — required, length 1–100name· string — requiredcreatedAt· string (date-time) — requiredmemberCount· integer — required, 0–9007199254740991pageCount· integer — required, 0–9007199254740991storageBytes· integer — required, 0–9007199254740991plan·"free"|"pro"|"business"|"enterprise"— requiredplanSince· string (date-time) | null — requiredlastActivityAt· string (date-time) | null — requiredmembersByRole· object — requiredowner· integer — required, 0–9007199254740991admin· integer — required, 0–9007199254740991member· integer — required, 0–9007199254740991guest· integer — required, 0–9007199254740991
owners· object[] — requireduserId· string (uuid) — requiredname· string — requiredemail· string (email) — requiredjoinedAt· string (date-time) — required
attachmentCount· integer — required, 0–9007199254740991limits· object — requiredstorageBytes· integer | null — requiredfileSizeBytes· integer | null — requiredmembers· integer | null — requiredguests· integer | null — requiredversionHistoryDays· integer | null — required
over·"storageBytes"|"fileSizeBytes"|"members"|"guests"|"versionHistoryDays"[] — required
Response default — application/json
error· object — requiredcode·"bad_request"|"validation_failed"|"unauthorized"|"forbidden"|"not_found"|"conflict"|"rate_limited"|"internal"— requiredmessage· string — requiredreason· string — length 1–64
PUT /api/v1/admin/workspaces/{targetWorkspaceId}/plan
Assign a workspace's plan — the write that makes the billing work usable before payments are wired. Idempotent: re-assigning the plan a workspace already has changes nothing and records nothing. Every real change writes an append-only workspace.plan.set entry to the operator audit trail IN THE SAME TRANSACTION, so a plan change with no record of who made it cannot exist. A downgrade never removes, hides or locks anything — it only narrows what the next write may add. Instance operator only (OPERATORS); everybody else gets 403, a workspace owner included. Session-only twice over: the route sits outside /workspaces/{workspaceId}, which tokens/scopes.ts classifies session-only, and the operator guard refuses a token-authenticated request outright.
setAdminWorkspacePlan · token scope: session only
Path parameters
targetWorkspaceId· string (uuid) — required
Request body — application/json, required
plan·"free"|"pro"|"business"|"enterprise"|"self_hosted"|"team"— required The plan to assign. Deprecated spellings are accepted for compatibility and resolved before anything is stored or answered:teammeanspro, andself_hostedmeansenterprise— the deployment mode that id named was withdrawn (ADR-0024) and the id retired (ADR-0029 D1). Responses only ever spell the four live ids.note· string — length 0–500
Response 200 — application/json
id· string (uuid) — requiredslug· string — required, length 1–100name· string — requiredcreatedAt· string (date-time) — requiredmemberCount· integer — required, 0–9007199254740991pageCount· integer — required, 0–9007199254740991storageBytes· integer — required, 0–9007199254740991plan·"free"|"pro"|"business"|"enterprise"— requiredplanSince· string (date-time) | null — requiredlastActivityAt· string (date-time) | null — requiredmembersByRole· object — requiredowner· integer — required, 0–9007199254740991admin· integer — required, 0–9007199254740991member· integer — required, 0–9007199254740991guest· integer — required, 0–9007199254740991
owners· object[] — requireduserId· string (uuid) — requiredname· string — requiredemail· string (email) — requiredjoinedAt· string (date-time) — required
attachmentCount· integer — required, 0–9007199254740991limits· object — requiredstorageBytes· integer | null — requiredfileSizeBytes· integer | null — requiredmembers· integer | null — requiredguests· integer | null — requiredversionHistoryDays· integer | null — required
over·"storageBytes"|"fileSizeBytes"|"members"|"guests"|"versionHistoryDays"[] — required
Response default — application/json
error· object — requiredcode·"bad_request"|"validation_failed"|"unauthorized"|"forbidden"|"not_found"|"conflict"|"rate_limited"|"internal"— requiredmessage· string — requiredreason· string — length 1–64
GET /api/v1/admin/audit
The operator audit trail, newest first, optionally narrowed to one workspace. Append-only: a database trigger refuses UPDATE, DELETE and TRUNCATE, so an entry can be neither edited nor deleted by any code path, this one included. Instance operator only (OPERATORS); everybody else gets 403, a workspace owner included. Session-only twice over: the route sits outside /workspaces/{workspaceId}, which tokens/scopes.ts classifies session-only, and the operator guard refuses a token-authenticated request outright.
listAdminAudit · token scope: session only
Query parameters
cursor· string — length 1–∞limit· integer — 1–100workspaceId· string (uuid)
Response 200 — application/json
items· object[] — requiredid· string (uuid) — requiredat· string (date-time) — requiredaction·"workspace.plan.set"— requiredoperatorUserId· string (uuid) — requiredoperatorEmail· string (email) — requiredworkspaceId· string (uuid) | null — requiredworkspaceSlug· string | null — requireddetail· object — required
nextCursor· string | null — required
Response default — application/json
error· object — requiredcode·"bad_request"|"validation_failed"|"unauthorized"|"forbidden"|"not_found"|"conflict"|"rate_limited"|"internal"— requiredmessage· string — requiredreason· string — length 1–64
GET /api/v1/admin/feedback
The feedback inbox, newest first, optionally narrowed by status, kind or product. The one operator surface that holds user-written text, and it is not an exception to the metadata-only rule: a report is a message somebody composed and sent to the operator, having seen everything attached to it. Joins no workspace content and links nowhere into one. Instance operator only (OPERATORS); everybody else gets 403, a workspace owner included. Session-only twice over: the route sits outside /workspaces/{workspaceId}, which tokens/scopes.ts classifies session-only, and the operator guard refuses a token-authenticated request outright.
listAdminFeedback · token scope: session only
Query parameters
cursor· string — length 1–∞limit· integer — 1–100status·"open"|"closed"kind·"bug"|"idea"product·"basalt"|"lithic"|"tecto"
Response 200 — application/json
items· object[] — requiredid· string (uuid) — requiredat· string (date-time) — requiredkind·"bug"|"idea"— requiredstatus·"open"|"closed"— requiredmessage· string — requiredproduct·"basalt"|"lithic"|"tecto"| null — requiredreporter· object — requireduserId· string (uuid) — requiredname· string — requiredemail· string (email) — required
workspace· object | null — requiredid· string (uuid) — requiredname· string — required
context· object — requiredroute· string | null — requiredappVersion· string | null — requiredviewport· object | null — requiredwidth· integer — required, 0–100000height· integer — required, 0–100000
pointer·"coarse"|"fine"| null — requiredtrace· string | null — required
note· string | null — requiredclosedAt· string (date-time) | null — required
nextCursor· string | null — required
Response default — application/json
error· object — requiredcode·"bad_request"|"validation_failed"|"unauthorized"|"forbidden"|"not_found"|"conflict"|"rate_limited"|"internal"— requiredmessage· string — requiredreason· string — length 1–64
PATCH /api/v1/admin/feedback/{reportId}
Close or reopen a report, and/or set the operator's own note about it. The note is never shown to the sender. Instance operator only (OPERATORS); everybody else gets 403, a workspace owner included. Session-only twice over: the route sits outside /workspaces/{workspaceId}, which tokens/scopes.ts classifies session-only, and the operator guard refuses a token-authenticated request outright.
updateAdminFeedback · token scope: session only
Path parameters
reportId· string (uuid) — required
Request body — application/json, required
status·"open"|"closed"note· string | null
Response 200 — application/json
id· string (uuid) — requiredat· string (date-time) — requiredkind·"bug"|"idea"— requiredstatus·"open"|"closed"— requiredmessage· string — requiredproduct·"basalt"|"lithic"|"tecto"| null — requiredreporter· object — requireduserId· string (uuid) — requiredname· string — requiredemail· string (email) — required
workspace· object | null — requiredid· string (uuid) — requiredname· string — required
context· object — requiredroute· string | null — requiredappVersion· string | null — requiredviewport· object | null — requiredwidth· integer — required, 0–100000height· integer — required, 0–100000
pointer·"coarse"|"fine"| null — requiredtrace· string | null — required
note· string | null — requiredclosedAt· string (date-time) | null — required
Response default — application/json
error· object — requiredcode·"bad_request"|"validation_failed"|"unauthorized"|"forbidden"|"not_found"|"conflict"|"rate_limited"|"internal"— requiredmessage· string — requiredreason· string — length 1–64