Prima Get an API key

PrimaChat

Workspaces

Which workspace a session's usage is billed to, chosen once by a person because nothing else is allowed to guess.

Live Built and working today on the surface this card describes.

What it does

A person can belong to several workspaces. The workspace is the billing attribution unit, and it is the first field hashed into the metering chain, so it has to be right the first time: there is no correcting it afterwards without invalidating the chain.

Neither the gateway nor the account service will pick one when the answer is ambiguous. Both answer 400 on purpose. These two routes are the missing half of that contract, the place where a person says which one.

Choosing a workspace mints a per-session gateway key scoped to it, named chat.prima.li (<first 8 of the session id>) and carrying only the ai scope. The key that the session was using before is retired, and what happened to it is reported rather than swallowed.

The browser never holds a credential. The chosen workspace id is written to the server-side session, and no cookie, header or field in any later request can influence it again.

Inputs and outputs

GET /api/workspaces {workspaces, selected, state, error}. ?refresh=1 forces a re-read of the membership list.
POST /api/workspaces/select {workspaceId} in. {ok, session, previousKey} back, where previousKey says what became of the session's earlier key.
403 unknown_workspace. Not a 404, because a 404 would additionally say the id does not exist anywhere, which is a fact about someone else's tenant.
503 / 502 / 409 directory_unavailable · mint_failed · anything else.

Configuration

Choosing and switching One route for both. Splitting them would mean two places that have to get the mint-then-retire order right.
Key label The workspace name is deliberately not in the key's label. Labels are readable by anyone who can list the account's keys, and a workspace name can be a client's name.
Refresh Not the default. The picker is reopened on every switch and a membership list does not change between two clicks. ?refresh=1 exists for the case where somebody was just added and is looking for it.

Limits

  • A workspace with no membership for this person cannot be chosen, and the account service checks membership again when the key is minted.
  • If the key cannot be issued, the choice is still recorded. The failure stays "the key could not be issued", which is true, instead of "pick a workspace", which would be a question with one possible answer.
  • Chat's /api/workspaces is not the account service's /api/workspaces. That one is tenant administration and is deliberately not reachable from the gateway host.

Example

choose a workspace

POST /api/workspaces/select
Content-Type: application/json

{"workspaceId": "…"}

Source of truth

  • prima-chat/server/routes/workspaces.ts (both routes, the status codes and the reasoning in the file header)
  • prima-chat/server/sessions.ts (per-session key label, mint-then-retire order)
  • prima-chat/server/identity.ts (`POST /api/developer/keys` with `scopes: ['ai']`)
  • prima-chat/server/gateway.ts (the session's key is attached server-side as `X-API-Key`, never as a Bearer header)