Basalt docs
GitHub

Instance administration

The operator back office: every workspace on this instance with its metadata and plan, the plan assignment, and the append-only trail of operator actions. Metadata only — the operator of a hosted Basalt is a data processor and these routes cannot return page titles, page contents or comments. Session-only, and restricted to the operators named in BASALT_OPERATORS.

4 operations. Every path is relative to the instance origin; every response body is JSON unless stated. See API for authentication, errors and pagination.

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 (BASALT_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–100
  • q · string — length 1–200
  • plan · "free" | "team" | "business" | "self_hosted"

Response 200application/json

  • items · object[] — required
    • id · string (uuid) — required
    • slug · string — required, length 1–100
    • name · string — required
    • createdAt · string (date-time) — required
    • memberCount · integer — required, 0–9007199254740991
    • pageCount · integer — required, 0–9007199254740991
    • storageBytes · integer — required, 0–9007199254740991
    • plan · "free" | "team" | "business" | "self_hosted" — required
    • planSince · string (date-time) | null — required
    • lastActivityAt · string (date-time) | null — required
  • nextCursor · string | null — required

Response defaultapplication/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required

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 (BASALT_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 200application/json

  • id · string (uuid) — required
  • slug · string — required, length 1–100
  • name · string — required
  • createdAt · string (date-time) — required
  • memberCount · integer — required, 0–9007199254740991
  • pageCount · integer — required, 0–9007199254740991
  • storageBytes · integer — required, 0–9007199254740991
  • plan · "free" | "team" | "business" | "self_hosted" — required
  • planSince · string (date-time) | null — required
  • lastActivityAt · string (date-time) | null — required
  • membersByRole · object — required
    • owner · integer — required, 0–9007199254740991
    • admin · integer — required, 0–9007199254740991
    • member · integer — required, 0–9007199254740991
    • guest · integer — required, 0–9007199254740991
  • owners · object[] — required
    • userId · string (uuid) — required
    • name · string — required
    • email · string (email) — required
    • joinedAt · string (date-time) — required
  • attachmentCount · integer — required, 0–9007199254740991
  • limits · object — required
    • storageBytes · integer | null — required
    • fileSizeBytes · integer | null — required
    • members · integer | null — required
    • guests · integer | null — required
    • versionHistoryDays · integer | null — required
  • over · "storageBytes" | "fileSizeBytes" | "members" | "guests" | "versionHistoryDays"[] — required

Response defaultapplication/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required

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 (BASALT_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 bodyapplication/json, required

  • plan · "free" | "team" | "business" | "self_hosted" — required
  • note · string — length 0–500

Response 200application/json

  • id · string (uuid) — required
  • slug · string — required, length 1–100
  • name · string — required
  • createdAt · string (date-time) — required
  • memberCount · integer — required, 0–9007199254740991
  • pageCount · integer — required, 0–9007199254740991
  • storageBytes · integer — required, 0–9007199254740991
  • plan · "free" | "team" | "business" | "self_hosted" — required
  • planSince · string (date-time) | null — required
  • lastActivityAt · string (date-time) | null — required
  • membersByRole · object — required
    • owner · integer — required, 0–9007199254740991
    • admin · integer — required, 0–9007199254740991
    • member · integer — required, 0–9007199254740991
    • guest · integer — required, 0–9007199254740991
  • owners · object[] — required
    • userId · string (uuid) — required
    • name · string — required
    • email · string (email) — required
    • joinedAt · string (date-time) — required
  • attachmentCount · integer — required, 0–9007199254740991
  • limits · object — required
    • storageBytes · integer | null — required
    • fileSizeBytes · integer | null — required
    • members · integer | null — required
    • guests · integer | null — required
    • versionHistoryDays · integer | null — required
  • over · "storageBytes" | "fileSizeBytes" | "members" | "guests" | "versionHistoryDays"[] — required

Response defaultapplication/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required

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 (BASALT_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–100
  • workspaceId · string (uuid)

Response 200application/json

  • items · object[] — required
    • id · string (uuid) — required
    • at · string (date-time) — required
    • action · "workspace.plan.set" — required
    • operatorUserId · string (uuid) — required
    • operatorEmail · string (email) — required
    • workspaceId · string (uuid) | null — required
    • workspaceSlug · string | null — required
    • detail · object — required
  • nextCursor · string | null — required

Response defaultapplication/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required