6.5 KiB
Platform Administration
Cross-cutting admin operations that don't belong to a specific domain.
User Management
GET /admin/users → { "users": [...], "total": N }
POST /admin/users ← { "username", "password", "email", "role" }
PUT /admin/users/:id/role ← { "role": "admin|user" }
PUT /admin/users/:id/active ← { "is_active": false }
DELETE /admin/users/:id
POST /admin/users/:id/reset-password ← { "password": "..." }
POST /admin/users/:id/vault/reset → destroys user's UEK (BYOK keys lost)
PUT /admin/users/:id/role refuses to demote the last remaining admin
(returns 409). DELETE /admin/users/:id also refuses if the target is
the last admin, and destroys the user's vault before deleting the row.
Global Settings & Policies
Settings are key-value pairs in global_settings. Policies are
boolean flags that gate features.
GET /admin/settings → all settings
GET /admin/settings/:key → single setting
PUT /admin/settings/:key ← { "value": ... }
The handler auto-detects: if the value is a string and the key is a
known policy name, it writes to the policies table. Otherwise it
writes to global_config as JSON.
Public settings (non-admin, for FE bootstrapping):
GET /settings/public
{
"banner": { "enabled": true, "text": "DEVELOPMENT", "bg": "#007a33", "fg": "#ffffff", "position": "both" },
"branding": { "instance_name": "Switchboard", "logo_url": "...", "tagline": "..." },
"has_admin_prompt": true,
"storage_configured": true,
"paste_to_file_chars": 2000,
"policies": {
"allow_registration": "true",
"allow_user_byok": "true",
"allow_user_personas": "true",
"channel_retention_mode": "flexible"
}
}
Note: policy values are strings ("true" / "false"), not booleans.
Banner Configuration
The environment banner is stored as a single global_config entry
under the key "banner". It's a simple object:
{
"enabled": true,
"text": "DEVELOPMENT",
"position": "both|top|bottom",
"bg": "#007a33",
"fg": "#ffffff"
}
Set via PUT /admin/settings/banner with { "value": { ... } }.
The admin UI provides a color picker, position selector, and preset
dropdown. The Go template base layout reads the banner from
PageData and renders top/bottom strips with CSS custom properties.
Platform Stats
GET /admin/stats
{
"users": 42,
"channels": 156,
"messages": 12847
}
Storage & Vault
Storage status:
GET /admin/storage/status → { "backend", "healthy", "file_count", "total_bytes", ... }
GET /admin/storage/orphans → { "count": 3 }
POST /admin/storage/cleanup → removes orphaned blobs
GET /admin/storage/extraction → extraction queue status
Vault:
GET /admin/vault/status → { "encryption_key_set", "user_vaults_count", ... }
Audit Log
GET /admin/audit?page=1&per_page=50&action=user.create&actor_id=uuid&resource_type=...
GET /admin/audit/actions → { "actions": ["user.create", "policy.update", ...] }
The list endpoint returns a paginated envelope:
{
"data": [...],
"total": 128,
"page": 1,
"per_page": 50
}
Audit entry shape:
{
"id": "uuid",
"actor_id": "uuid",
"actor_name": "admin",
"action": "user.create",
"resource_type": "user",
"resource_id": "uuid",
"metadata": "{}",
"ip_address": "10.0.0.1",
"created_at": "..."
}
Usage & Pricing
GET /admin/usage → { "totals", "results" }
GET /admin/usage/teams/:id → { "results": [...] }
GET /admin/usage/users/:id → { "results": [...] }
GET /admin/pricing → [ ... ]
PUT /admin/pricing ← { "provider_config_id", "model_id", "input_per_m", "output_per_m" }
DELETE /admin/pricing/:provider_config_id/:model_id
GET /admin/pricing returns a bare array (no envelope).
PUT /admin/pricing accepts the full PricingEntry shape:
{
"provider_config_id": "uuid",
"model_id": "gpt-4o",
"input_per_m": 2.50,
"output_per_m": 10.00,
"cache_create_per_m": 1.25,
"cache_read_per_m": 0.50,
"currency": "USD"
}
Pricing cannot be set for personal BYOK providers (403).
Personal usage (non-admin):
GET /usage → { "totals", "results" }
Scoped to the user's BYOK provider usage only.
Roles (Platform)
GET /admin/roles → { "roles": [...] }
GET /admin/roles/:role
PUT /admin/roles/:role ← { "permissions": {...} }
POST /admin/roles/:role/test ← test role permissions
Email Test
POST /admin/notifications/test-email
No request body required. Sends a test email to the authenticated admin's own email address using the configured SMTP settings.
Archived Channels
GET /admin/channels/archived → { "channels": [...], "total", "page", "per_page", "retention_mode" }
DELETE /admin/channels/:id/purge → permanent delete (ignores retention)
Both endpoints require platform admin auth. GET accepts
?page=1&per_page=50 query parameters (max 100 per page).
DELETE verifies the channel is archived before purging and cleans
up associated file storage blobs.
Surfaces Admin
See surfaces.md for the full surface management API.
GET /admin/surfaces → all surfaces (including disabled)
GET /admin/surfaces/:id → surface with manifest
POST /admin/surfaces/install ← multipart .surface archive
PUT /admin/surfaces/:id/enable
PUT /admin/surfaces/:id/disable
DELETE /admin/surfaces/:id
Tasks Admin
See tasks.md for the full task management API.
GET /admin/tasks → all tasks across all users/teams
POST /admin/tasks/:id/run → force run
POST /admin/tasks/:id/kill → cancel active run
DELETE /admin/tasks/:id → delete task
Notifications Admin (v0.28.6)
POST /admin/notifications/broadcast ← { "title", "message", "level?" }
→ { "message": "broadcast sent", "count": N }
Sends a system.announcement notification to all active users.
Also emits a system.broadcast WebSocket event for real-time delivery.
level is optional (info | warning | critical, default info).
See notifications.md for full details.