# Administration API > Namespaces, backends, routing rules, members and keys — everything the console drives. Source: https://docs.stg.perdurance.dev/administration/ --- The console is the usual way to do all of this, and the CLI wraps the same routes. This page is the API underneath both, for anyone automating a deployment. There are two scopes. **Tenancy** routes live under `/{tenancy}/v1` and concern the organisation. **Namespace** routes live under `/{tenancy}/{namespace}/v1` and concern one isolated workspace inside it. ## Roles and what they carry | Role | Can | |---|---| | `user` | Find the tenancy's namespaces, and use the ones granted to them | | `admin` | Everything a user can, plus manage namespaces, backends, rules, members and keys | | `owner` | Everything an admin can, plus delete a namespace, delete the tenancy, and transfer ownership | A role is held in the tenancy. A **grant** is narrower: it gives one member a role inside one namespace, which is how a `user` comes to be able to use `prod` but not `staging`. ## Getting in | Route | Takes | Gives | |---|---|---| | `POST /v1/registrations` | A registration token, or an identity provider's | A new tenancy and its owner | | `POST /v1/sessions` | Email and password | A session key | | `POST /v1/sessions/exchange` | An identity provider's token | A session key | A registration token is a file in the deployment's secret mount, one per client. Registration is the only way a tenancy comes to exist, and the first account created owns it. Where the deployment signs people in through an identity provider, that provider's token is accepted in place of a registration token, and the owner's address is taken from it rather than from the body — a token whose address the provider has not confirmed is refused. Such an owner has no password on this service, because they do not sign in with one: they exchange the provider's token for a session at `POST /v1/sessions/exchange`, and everything below that point is the same for them as for anybody else. A deployment that runs no provider has neither route behaviour and is unchanged. ## Namespaces | Route | Scope | |---|---| | `GET /{tenancy}/v1/namespaces` | Any member | | `POST /{tenancy}/v1/namespaces` | Admin | | `DELETE /{tenancy}/v1/namespaces/{namespace}` | Owner | ```json { "slug": "prod", "name": "Production" } ``` The slug is the URL segment. `name` is for people, and defaults to the slug. ## Backends Created in a namespace, then optionally attached to others. | Route | Does | |---|---| | `POST /{tenancy}/{namespace}/v1/backends` | Create one here | | `GET /{tenancy}/{namespace}/v1/backends` | List the ones this namespace can route to | | `GET /{tenancy}/{namespace}/v1/backends/{backend}` | Show one | | `POST /{tenancy}/{namespace}/v1/backends/{backend}` | Attach an existing backend to this namespace | | `DELETE /{tenancy}/{namespace}/v1/backends/{backend}` | Detach it from this namespace | | `GET /{tenancy}/v1/backends` | List every backend in the tenancy | | `GET /{tenancy}/v1/backends/{backend}/namespaces` | Which namespaces use one | | `DELETE /{tenancy}/v1/backends/{backend}` | Delete it everywhere | ```json { "name": "anthropic-prod", "kind": "anthropic", "base_url": null, "credential": "sk-ant-…" } ``` `kind` is `openai_compat`, `anthropic` or `openrouter` — see [Routing](/routing). `base_url` may be omitted where the kind implies it. **`credential` is write-only**: it is encrypted before storage and never appears in any response, so `BackendView` carries `id`, `name`, `kind` and `base_url` and nothing else. Note the two deletes. Detaching removes a backend from one namespace; deleting removes it from the tenancy. ## Routing rules All namespace-scoped, all admin. | Route | Does | |---|---| | `POST /{tenancy}/{namespace}/v1/routing-rules` | Create | | `GET /{tenancy}/{namespace}/v1/routing-rules` | List | | `DELETE /{tenancy}/{namespace}/v1/routing-rules/{rule}` | Delete | ```json { "priority": 100, "pattern": "claude-*", "backend_id": "…", "model_rewrite": "claude-sonnet-4-20250514" } ``` Lowest priority wins. [Routing](/routing) has the semantics. ## Members and grants | Route | Does | |---|---| | `GET /{tenancy}/v1/users` | List members | | `GET /{tenancy}/v1/users/{user}` | One member, with the grants they hold | | `POST /{tenancy}/v1/users` | Invite one | | `POST /{tenancy}/v1/users/{user}/role` | Change their tenancy role | | `POST /{tenancy}/v1/users/{user}/password` | Set their password | | `DELETE /{tenancy}/v1/users/{user}` | Revoke them | | `GET /{tenancy}/v1/namespaces/{namespace}/grants` | Who may use this namespace | | `POST /{tenancy}/v1/namespaces/{namespace}/grants` | Grant a member a role in it | | `DELETE /{tenancy}/v1/namespaces/{namespace}/grants/{user}` | Take it back | An invited member given a password they did not choose must replace it before they can do anything else — that is the `403 password_change_required` in [Errors](/errors). ## Keys | Route | Does | |---|---| | `POST /{tenancy}/v1/me/keys` | Mint one for yourself | | `GET /{tenancy}/v1/me/keys` | List your own | | `DELETE /{tenancy}/v1/me/keys/{key}` | End one of your own | | `POST /{tenancy}/v1/users/{user}/keys` | Mint one for a member (admin) | | `GET /{tenancy}/v1/users/{user}/keys` | List theirs (admin) | | `DELETE /{tenancy}/v1/keys/{key}` | Revoke any key (admin) | Minting answers with the only copy of the secret: ```json { "id": "…", "name": "batch-worker", "prefix": "ab12cd34", "key": "sar_ab12cd34_…" } ``` Listing never does — `ApiKeyView` carries `id`, `name`, `prefix`, `created_at` and `revoked_at`. The secret is stored only as a keyed hash, so a lost key is replaced rather than recovered. The eight-character `prefix` is the public half, and it is what usage figures and log lines name a key by. ## Ownership | Route | Does | |---|---| | `POST /{tenancy}/v1/ownership` | Transfer the tenancy to another member | | `DELETE /{tenancy}/v1/tenancy` | Delete the tenancy | Both are the owner's alone.