Docs/Tenant tokens
Organisations

Tenant tokens

POST /v1/orgs/{orgId}/tenants/{tenantId}/token, and what a Tenant token can call.

A Tenant token acts as one Tenant. Your backend exchanges its Organisation Key for one, then calls /v1/agents/* with it. Sessions, machines and events are the Tenant's, and the Organisation pays.

Exchange

Exchange an Organisation Key for a Tenant tokencURL
curl -s -X POST "$SAI_API_URL/v1/orgs/$ORG_ID/tenants/$TENANT_ID/token" \
  -H "Authorization: Bearer $SAI_ORG_KEY"

No body.

200 OK · Tenant tokenJSON
{
  "token": "eyJhbGciOiJSUzI1NiIs...",
  "expiresAt": 1790003600000,
  "tenantId": "Xk2p9QzR4tLm8vNa3bCd"
}

The token lasts one hour. expiresAt is Unix time in milliseconds. There is no refresh token: exchange again before it expires. Reuse a token for its hour rather than exchanging for every request.

The caller is the Organisation's own key, or a signed-in owner or admin of the Organisation (the console's Playground does this).

StatuserrorWhen
403session_requiredA personal API key, or any credential other than the two above.
403not_available_for_tenantA Tenant token. A Tenant cannot exchange for itself or another Tenant.
403tenant_disabledThe Tenant is disabled.
403organisation_disabledThe Organisation is disabled.
404not_foundNo such Organisation for this key, or no such Tenant in it.
429rate_limitedMore than 600 exchanges for the Organisation in an hour.
503unavailableTry again shortly.

Use it

Send the token in Authorization, like an API key:

Send a message as the Tenant
curl -s "$SAI_API_URL/v1/agents/message" \
  -H "Authorization: Bearer $TENANT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Open the calendar and read today’s appointments" }'

GET /v1/agents/auth shows who the token acts as:

200 OK · Who the token acts asJSON
{
  "ok": true,
  "userId": "Xk2p9QzR4tLm8vNa3bCd",
  "authType": "firebase",
  "tenantId": "Xk2p9QzR4tLm8vNa3bCd",
  "orgId": "org_3f9a1c0b7e2d4a6f8c10"
}

What a Tenant token can call

RoutePage
GET /v1/agents/authAuthentication
POST /v1/agents/messageMessage
GET /v1/agents/eventsEvents
POST /v1/agents/approveApprove
POST /v1/agents/abortAbort
POST /v1/agents/new-sessionNew session
GET /v1/agents/session, GET /v1/agents/sessions, GET /v1/agents/contextSessions
GET /v1/agents/machinesMachines
POST /v1/agents/machines/{machineId}/liveLive screen
GET /v1/agents/modelsModels

GET /v1/agents/machines lists the Tenant's enrolled machines. Pass one as machineId on /message.

Every other route answers 403:

403 Forbidden · not_available_for_tenantJSON
{ "error": "not_available_for_tenant", "message": "This route is not available for a Tenant." }

That includes turns, learn, judge, uploads, direct control, the account and keys, and /v1/orgs and /v1/tenants. A Tenant token cannot create or wake a cloud computer.

When a token stops working

A token is checked on every request, not only at the exchange:

StatuserrorWhen
401The token has expired. Exchange again.
401credential_revokedThe key that exchanged it was revoked, or the member who did is no longer an owner or admin.
403tenant_disabledThe Tenant was disabled.
403organisation_disabledThe Organisation was disabled.
404tenant_not_foundThe Tenant was deleted.

A revoked key or a removed member stops its tokens within a minute.