Authentication
Create, use, rotate, and revoke organization and workspace API keys, and distinguish machine credentials from CLI logins.
Create an API key for a script or MCP client, then copy it when shown. Oxagen stores a hash, so the raw key cannot be recovered.
- Workspace key. Open the workspace's Settings → API keys tab and create the key. It acts in that workspace only. A workspace Admin or an organization Owner can create one.
- Organization key. Open Organization → API keys (
/{org}/api-keys) and create the key. It acts at the organization level: it can create workspaces and read the organization's settings. It cannot read or change a workspace's runs, agents, skills, spend, or settings. Only an organization Owner can create one.
A key acts with its own role, never its creator's. A workspace key acts as a Member of its workspace. An organization key acts as an Org member, and an Org member holds no role in any workspace. So an organization key cannot list or read runs, transcripts, agents, skills, spend, or workspace settings, and it cannot edit or summarize a run. Each of those calls gets 403. To read inside one workspace, use a key from that workspace.
On Enterprise a key can act as a custom role of the same scope instead. A custom organization role can grant reads and writes in every workspace, so give an organization key such a role when one script must read across workspaces. No key acts as Owner or Admin, so a key cannot create, rotate, or revoke keys.
Keys use the ox_ prefix followed by a base64url-encoded random value. Keep them in a secret store or environment variable.
Use a key
POST /v1/{orgSlug}/{workspaceSlug}/agent/tools
Host: api.oxagen.sh
Authorization: Bearer ox_…
Content-Type: application/json
{}For MCP, send the same Bearer header to https://mcp.oxagen.sh/mcp. The key resolves the organization and workspace. Use the matching slugs or ids for API routes. A request whose URL names another organization or workspace gets 403 with error.reason key_scope_mismatch, and the message names both. An organization key can name any workspace in its own organization. Its Org member role still cannot read that workspace's runs, agents, skills, spend, or settings, so those calls get 403 with error.code forbidden.
Check a key
GET /v1/auth/whoami tells you whether Oxagen accepts a credential. It calls no capability and needs no org or workspace in the path.
curl "https://api.oxagen.sh/v1/auth/whoami" \
-H "Authorization: Bearer $OXAGEN_API_KEY"A valid key gets 200 with authenticated: true, apiKeyId, and the orgId and workspaceId the key belongs to. workspaceId is null for an organization key. userId is set for a CLI login and null for an ordinary key. A missing, malformed, invalid, or expired key gets 401.
Browser sessions
The app's own calls to the API use your sign-in session cookie instead of a key. The API applies your organization's sign-in policy to that session on every organization and workspace route, the same way the app does:
- With Require SSO on, a session that did not come from one of the organization's verified SSO providers gets 403 with
error.reasonsso_required. Owners are exempt. - When the organization requires two-factor authentication, an Owner who has not set it up by the end of the grace period gets 403 with
error.reasonmfa_required.
API keys do not carry a session, so these checks do not apply to them. Require SSO checks a key against its creator instead, as Single sign-on describes.
Key purposes
Not every ox_ credential has the same authority:
| Kind | Authority |
|---|---|
| Ordinary API key | Acts as Member of its workspace, or as Org member for an organization key, or as a custom role on Enterprise. An Org member holds no role in any workspace. Calls are attributed to its creator. There is no arbitrary capability allowlist for this key. |
CLI login, cli_session_v1 | Resolves to the person who authorized oxagen login. Authentication rechecks organization and workspace membership. |
Host key, tacho_host_v1 | The enrolled machine's own calls only: event ingestion, its policy bundle, command polling, a Git credential for its workspace's repositories, memory upload and recall, work order claims, local code graph builds, and certificate renewal |
Gateway key, tacho_gateway_v1 | MCP-exposed, nonmutating capabilities that are not high-sensitivity |
| Stella telemetry key | Operational telemetry ingestion only |
Machine-purpose checks apply before the plan-dependent IAM resolver. An unknown purpose is denied. purpose is a reserved scope key: create_api_key refuses a scope that sets it to any value, so callers cannot mint a host, gateway, or CLI-login key through the generic create call.
The agent registration credential has its own purpose and exchange flow. It is not a general API or MCP bearer credential. The credential for a mediated downstream connection is separate again: Oxagen holds that credential and uses it on the agent's behalf.
RBAC and roles explains the plan-dependent resolver. Do not assume a generic scope object creates a capability allowlist.
Rotate or revoke
Rotate or revoke a key on the page that lists it: the workspace's Settings → API keys tab, or Organization → API keys. Rotation issues a replacement and keeps the old key working for 24 hours, so a lost response does not cut the integration off. Put the new value in place, then confirm the new key to revoke the old one at once. If you do not confirm it, the old key stops working when its 24 hours end. Revocation causes subsequent authenticated requests with the old key to fail.
The API exposes the same three steps. An ordinary API key cannot call them, because no key acts as Owner or Admin. Call them from a signed-in session or an oxagen login CLI session:
POST /v1/{orgSlug}/{workspaceSlug}/api-keys/rotate
Authorization: Bearer ox_…
Content-Type: application/json
{ "keyPublicId": "aky_…" }The response carries the new key's publicId and rawKey, and a superseded block with the old key's publicId and the expiresAt when it stops working. Confirm the new key with its own public ID:
POST /v1/{orgSlug}/{workspaceSlug}/api-keys/rotate/acknowledge
Authorization: Bearer ox_…
Content-Type: application/json
{ "keyPublicId": "aky_…" }Revoke any key by its public ID. This route uses DELETE and takes a JSON body:
DELETE /v1/{orgSlug}/{workspaceSlug}/api-keys/revoke
Authorization: Bearer ox_…
Content-Type: application/json
{ "keyPublicId": "aky_…" }From a terminal, oxagen api-key rotate <key-id> prints the new secret. oxagen api-key acknowledge <key-id>, given the new key's ID, confirms it.
Inspect the key's expiry when creating or rotating it. Different provisioning flows set different lifetimes. Revocation, and confirming a rotation, writes api_key.revoked to the security record.
Store credentials
Keep keys out of source control, browser bundles, prompts, and shared terminal output. Revoke a key if an unauthorized person may have copied it. Use oxagen login for a person's terminal access and the enrollment flow for a registered agent's host.