Workspaces, members and principals
Membership, scoped keys and trusted end-user quotas.
Every inference key belongs to one org, workspace and principal. These bindings are immutable: mint a replacement key to change identity. Multiple keys for one principal share its RPM, TPM and concurrency limits. Request user, metadata, x-user-id, x-tm-principal-id and x-tm-workspace-id values do not authenticate a principal. Your trusted backend provisions an end-user principal and retains that principal's key; do not let an end user choose a different backend key. There is no per-request delegation, SSO or SCIM.
Existing keys and keys created through browser onboarding or the console use an org's Default workspace and shared _default service principal. This preserves existing access. It does not retroactively identify individual users of a shared legacy key.
Management authentication
Use a verified console session cookie. Inference bearer keys have no management rights. x-tm-org-id: <UUID> selects an org in which the session email must be an active member; it never grants access. Without the header, the caller's own org is preferred, then their oldest active membership. Query /api/identity/organizations to discover memberships. The console shows your default org; /console/byok and the playground take ?org=<UUID> to switch, and the other scoped identities are managed with the API below.
POSTs require Origin: https://app.routerplus.com (origin only) and Content-Type: application/json. Responses are not cacheable. No API sends an invitation or email when adding membership. Membership and member-principal emails use the same canonicalization as marketplace account emails (lowercase, plus-tag removal, and Gmail dot/domain normalization).
| Role | Read org configuration | Manage workspaces, principals, keys, connections, policies, limits | Manage members |
|---|---|---|---|
| owner | Yes | Yes | Yes |
| admin | Yes | Yes | No |
| viewer | Yes | No | No |
Roles apply across the whole org. There are no workspace-specific human roles in this version. The initial owner cannot be disabled, demoted or replaced through this API. Removing a membership removes that session's authority in this org without invalidating its memberships elsewhere.
Identity endpoints
All collection GETs return at most 500 entries; use returned UUIDs for subsequent writes. A create returns 201; an update, and a member write, 200.
| Request | JSON body / result |
|---|---|
GET /api/identity/organizations | {organizations:[{org_id,name,role}]} |
GET /api/identity/members | {members:[{email,role,disabled}]} |
POST /api/identity/members | {email,role:"admin"|"viewer",disabled?:boolean}; owner only, creates or updates |
GET /api/identity/workspaces | Workspace metadata and policy pointers |
POST /api/identity/workspaces | {name} → {workspace_id} |
POST /api/identity/workspaces/<id> | Any of {name,disabled,route_policy_id}; null clears its policy |
GET /api/identity/principals | Principal metadata |
POST /api/identity/principals | {workspace_id,kind:"service"|"end_user"|"member",subject,member_email?} → {principal_id} |
POST /api/identity/principals/<id> | {disabled:boolean} |
GET /api/identity/keys | Metadata only, including workspace/principal bindings |
POST /api/identity/keys | {principal_id,name,connection_id?} → {key_id,key}; raw key shown once; 300 RPM |
POST /api/identity/keys/<id> | {disabled:boolean} |
subject is an opaque stable backend identifier, unique within a workspace, limited to 160 characters. Leading _ is reserved. Use kind:member with an existing member's member_email to make membership revocation also stop their inference keys. An end_user or service principal is independent of human membership; disable it directly to revoke it.
Supplying connection_id while issuing a key checks that the connection is active and owned by the same org, then creates the key with that binding in the same transaction. The BYOK console uses this path so an interrupted setup never reveals a temporarily house-funded key. Omitting it preserves the original key-creation behavior for non-BYOK callers.
Disabling a workspace denies all its keys. Disabling a principal denies all its keys. Disabling a member denies their control access and their member principals. New dispatches observe the existing five-second revocation maximum; a previously dispatched stream can finish. A successful mutation response waits for publication of its Redis fence. If it returns 503, inspect state before retrying: SQL may have committed before fence publication.
Routes, limits and evidence
Effective authority is the intersection of org → workspace → key → request restrictions. Attach an immutable routing policy with POST /api/identity/workspaces/<id> and {route_policy_id:"..."}. This cannot grant a route denied by an org or key policy.
The /api/admission-limits API accepts scope:"workspace" and scope:"principal", with the corresponding UUID in subject. Defaults for each are 300 RPM, 1,000,000 TPM, eight concurrent requests. All org/key/model/platform/provider-pool limits also apply. These rate limits do not create separate workspace/principal monetary balances or spend caps; money remains org/key scoped. Configure outage_mode:"closed" when contractual limits require refusing work during counter failure.
GET /v1/usage and /v1/generation?id=... expose attempts belonging to the authenticated principal and workspace, including its other keys. Unknown or foreign generations return
- 404Org balance fields are
nullfor explicitly scoped principals; the default service principal retains those fields. The console provides org-wide accounting to authorized members. Attempt route evidence recordsworkspaceId,principalIdand up to three policy revisions.
Markdown source for agents: /docs/identity.md · index at /llms.txt