Dashboard API¶
The customer-facing REST contract served by the §13 API binary —
conventions, the core-resource catalog, and an index to the feature pages' endpoints. Each
feature page owns its endpoints (same rule as tables); field lists derive from the
Database Schema; the OpenAPI spec is implementation-time output.
The Shuttle-facing API is separate (§25).
(Design record: specs/2026-07-11-dashboard-api-design.md.)
Conventions¶
- Base:
https://api.starform.io/api/v1· IDs are UUIDs · timestamps RFC3339. - URL shape — flat by UUID: reads/writes address resources directly (
/services/{id}); only list/create endpoints nest one level under the parent (/projects/{id}/services). - Auth: the Auth & Sessions cookies + required custom header on mutations (CSRF). Pre-launch API tokens (§39.2 #20) reuse these same endpoints with a bearer header.
- Errors — one envelope, always: the HTTP status plus
{"code": "env_protected", "message": "...", "detail": {...}}—codeis stable and machine-matchable;messageis human text. - Pagination: cursor-based —
?cursor=&limit=(default 50, max 100); responses carrynext_cursor(null at the end). - Permission column = minimum §15 role;
Developer*= Developer, blocked in protected environments (§15.5 — Admins pass). Enforcement is the §15.6 middleware, not the handler.
Core resources¶
Auth & identity — login/refresh/logout live on Auth & Sessions.
| Method | Path | Permission | Purpose |
|---|---|---|---|
| GET | /me |
any authed | Current user + workspace/project memberships |
Workspaces & members (anti-escalation, §15.3: no actor may grant/modify/remove a role ≥ their own; ownership moves only by Owner-initiated transfer)
| Method | Path | Permission | Purpose |
|---|---|---|---|
| POST | /workspaces |
any authed | Create workspace |
| GET | /workspaces |
any authed | List my workspaces |
| GET / PATCH | /workspaces/{id} |
Member / Admin | Read · rename, settings |
| DELETE | /workspaces/{id} |
Owner | Delete workspace |
| GET / POST | /workspaces/{id}/members |
Member / Admin | List · invite |
| PATCH / DELETE | /workspaces/{id}/members/{user_id} |
Admin | Change role · remove |
| POST | /workspaces/{id}/transfer-ownership |
Owner | Transfer the Owner role |
Projects & members
| Method | Path | Permission | Purpose |
|---|---|---|---|
| GET / POST | /workspaces/{id}/projects |
Member / WS Admin | List visible · create (carries default_region, FR-077) |
| GET / PATCH / DELETE | /projects/{id} |
Viewer / Proj Admin / Proj Admin | Read · settings · delete |
| GET / POST | /projects/{id}/members |
Viewer / Proj Admin | List · add |
| PATCH / DELETE | /projects/{id}/members/{user_id} |
Proj Admin | Change role · remove |
Environments (names RFC 1123 ≤30 chars, FR-070; production auto-created protected, §15.5)
| Method | Path | Permission | Purpose |
|---|---|---|---|
| GET / POST | /projects/{id}/environments |
Viewer / Proj Admin | List · create (is_ephemeral flag) |
| GET / PATCH / DELETE | /environments/{id} |
Viewer / Proj Admin / Proj Admin | Read · toggle is_protected · delete (tombstone) |
Observability reads (every query passes the FR-065 server-side tenant filter — no request field can widen scope)
| Method | Path | Permission | Purpose |
|---|---|---|---|
| GET | /service-environments/{id}/logs |
Viewer | ?since&until&level&q&limit; live=true → SSE tail (FR-049) |
| GET | /service-environments/{id}/metrics/{metric} |
Viewer | metric ∈ latency · rps · error_rate · throughput · cpu · memory · network; ?window&step |
Regions
| Method | Path | Permission | Purpose |
|---|---|---|---|
| GET | /regions |
any authed | Active regions (placement pickers) |
Feature endpoints — where they live¶
| Surface | Home |
|---|---|
| Services · service-environment instantiation (+ PUT payload) | Services & Environments |
| Builds · deployments · deploy/rollback (+ payload) | Starforge › Build Lifecycle |
| Databases (+ credentials, tier resize) | Managed Databases |
| Var Groups (+ entries payload, attachments) | Var Groups |
| Usage / billing | Billing |
| Login / refresh / logout | Auth & Sessions |
Deferred, noted for completeness: custom domains (§39.3 #27) · buckets (§39.3 #16) · API tokens (§39.2 #20) · rate limiting (§39.2 #23) · invoice endpoints (§39.1 #14).
Key payloads¶
{ "code": "env_protected",
"message": "Developers cannot deploy to protected environment 'production'.",
"detail": { "environment_id": "…", "required_role": "project_admin" } }
// SSE: one event per line
event: log
data: {"ts":"2026-07-11T14:03:02.114Z","level":"error","message":"connection refused","pod":"api-6f…"}
// non-live JSON: { "lines": [ {…} ], "next_cursor": "…" }
{ "metric": "latency", "unit": "ms",
"series": [ { "quantile": "p99", "points": [ ["2026-07-11T14:00:00Z", 182.4], … ] },
{ "quantile": "p95", "points": [ … ] } ] }
Cross-cutting semantics¶
- Async pattern: anything Shuttle must apply returns 202 + the resource carrying
status; the client polls the resource (or watches deploy state in Stardeck). No separate operations API at MVP. - Deletes are tombstones (§25.1/FR-073): DELETE returns 202; the entity shows
"status": "deleting"on GET until Shuttle confirms (§25.4), then 404s. - Permissions are middleware, not handler logic: each row's requirement maps to a §15.6 helper
(
RequireProjectDeveloper(),RequireEnvironmentWrite(), …);Developer*rows call the env-protection variant. - Reads never fan wider than the session: the observability endpoints go through the telemetry query broker (§13), which injects the FR-065 filter as bound parameters — the query params shape the window, never the tenant.
Cross-references
Auth flow + cookies → Auth & Sessions · roles and the protection flag → §15 · the tables behind every payload → Database Schema · the Shuttle-facing API → §25. Canonical map: Canonical Sources.