switching clears the token & responses
No token
20

Switching between tenants

Sessions & devices

A user who belongs to more than one tenant changes which one their session acts in.

  1. GET /v1/me/memberships → tenants the user belongs to (default flagged)
  2. POST /v1/auth/switch-tenant → new accessToken scoped to the target tenant's roles; refresh rotated
GET/v1/me/membershipsauthList my memberships

Each row carries the tenant's name so a picker can show a human label instead of the opaque tenantId, plus isDefault and status.

ℹ︎Field guide — what each value means & where it comes fromno inputs

No request body, path params, or query params. Just a bearer-authenticated GET that lists the tenants the caller belongs to (the tenant picker); the tester supplies the access token and the gateway injects x-gateway-context, so you set nothing yourself. Returns 403 forbidden if the gateway context is missing or invalid.

Bearer token
GET https://api.kerja.team/v1/me/memberships
↩︎Response guide — what comes back & what each value means4 fields

200 OK. Body is the standard { data } envelope where data is an ARRAY of membership objects (one per tenant the user belongs to — the tenant picker). No notable response headers (no Set-Cookie / no Cache-Control beyond defaults).

data[].tenantIdUUIDalways
The tenant the user is a member of. Feed this into X-Tenant-Id (or the tenant-scoped login/switch flow) to act within that tenant.
data[].namestringalways
The tenant's organization name — a human-readable label so the tenant picker can show something friendlier than the opaque tenantId.
data[].isDefaultbooleanalways
Whether this tenant is the user's default selection. A UI can pre-select the tenant where true; exactly one membership is typically flagged true.
  • trueThis is the user's default tenant — pre-select it in the tenant picker.
  • falseNot the default tenant.
data[].statusenumalways
Status of the user's membership in this tenant. A client may disable picking a tenant whose membership is not active.
  • activeMembership is active — the user may operate within this tenant.
POST/v1/auth/switch-tenantauthSwitch active tenant

Needs a sid in the gateway context. The new accessToken (scoped to the TARGET tenant's roles, which may differ — admin in one, member in another) is auto-captured and the refresh token is rotated. 403 without an active membership in the target. Like refresh, this is a re-mint and never triggers a device challenge. THE SWITCH IS SESSION-WIDE, SO EVERY OPEN APP FOLLOWS IT (scenario 19): one session holds one tenant, so switching in app A repoints EVERY live chain under that session before rotating the caller's own — app B's next request carries a stale tenant_id and is refused, and B lands in the new tenant on its next renewal. Repointing all of them is not an optimisation: a chain left on the old tenant cannot see the repointed session under RLS and its app reports the session gone. THAT CONSEQUENCE MUST BE VISIBLE IN THE UI, NOT DISCOVERED AS A BUG — “switch tenant” moves every tab, so it is a deliberate, announced act; it is also why an app the caller holds only in ANOTHER tenant is absent from GET /v1/me/apps rather than silently switching the tenant for them when picked. The app the caller is IN survives the switch: the re-mint stays in the chain it just retired, so azp is carried across and the user is not bounced out of the product they were using.

Request body (JSON)
ℹ︎Field guide — what each value means & where it comes from1 field

Switches the active tenant for the current session and re-mints tokens scoped to the target tenant; rotates the refresh_token cookie. Requires an authenticated session (the tester supplies Authorization and the gateway injects x-gateway-context with the session id); the caller must already hold an active membership in the target tenant. Body has one field.

targetTenantIdbodystringrequiredfrom a response
The id of the tenant to switch into. The user does not type this freely; copy it from a prior tester response that lists the user's tenants/memberships (e.g. a tenant list or GET /v1/me membership entry). The user must hold an active membership in this tenant or the call returns 403. Must be non-empty.
Bearer token
POST https://api.kerja.team/v1/auth/switch-tenant
↩︎Response guide — what comes back & what each value means3 fields

200 OK with the standard { data } envelope; Cache-Control: no-store, and Set-Cookie: refresh_token (rotated) sets the new refresh cookie. The body re-mints an access token scoped to the target tenant's roles.

data.accessTokenJWTalways
Newly minted short-lived bearer token scoped to the target tenant's roles. Auto-captured by the tester as the active bearer token for subsequent calls.
data.tokenTypeenumalways
Token scheme to use in the Authorization header (Authorization: Bearer <accessToken>).
  • BearerSend the access token as 'Authorization: Bearer <accessToken>'.
data.expiresInintegeralways
Lifetime of the access token in seconds (e.g. 600 = 10 minutes). Client should refresh or switch before it elapses.