control plane api
Projects
Projects are addressed by slug — the immutable, org-unique identity that travels on x-understudy-project. Names are display-only and freely renameable; deletion is soft and frees the slug for reuse.
Endpoints
| method & path | does |
|---|---|
GET /admin/v1/orgs/:org_id/projects | List live projects. Cursor-paginated (limit 1–100, default 20). |
POST /admin/v1/orgs/:org_id/projects | Create. Body: { "slug", "name" }. 409 if the slug is taken by a live project. |
POST /admin/v1/orgs/:org_id/projects/default | Idempotently ensure the default rehearsal project (and its main workload) exists; returns it either way. |
GET /admin/v1/orgs/:org_id/projects/:slug | Fetch one by slug. 404 if absent or soft-deleted. |
PATCH /admin/v1/orgs/:org_id/projects/:slug | Rename. Body: { "name" }. The slug itself is immutable. |
DELETE /admin/v1/orgs/:org_id/projects/:slug | Soft-delete: drops out of listings, stops resolving on the gateway, slug becomes reusable. Captures already written remain in storage. |
The project object
{
"id": "proj_...", // stable internal id (used in workload routes)
"org_id": "org_...",
"slug": "concierge", // immutable; the header identity
"name": "Concierge", // display only
"created_at": "2026-06-12T00:00:00Z",
"settings": {}, // reserved
"deleted_at": null
}Note the two identifiers: slug addresses projects in these URLs and in the scoping header; id is what workload and capture endpoints take. GET by slug is the bridge between them.