Your app's users are partitioned into workspaces (tenants). You pick a tenancy model for your org; nostackai enforces the isolation transparently and bakes the active workspace into each app JWT.
Tenancy types
- single — all app users share one default workspace. The first user bootstraps it; everyone after joins it automatically. Your app should never show a workspace prompt.
- multi — no default. Users arrive via an invite (which pre-assigns them) or create/join a workspace after signup. Full isolation per workspace. Moving from single to multi is allowed at any time; moving back to single is refused, because existing workspaces would become unreachable.
GET /org/settings?group=tenancy read tenancy_type
PUT /org/settings set single | multi How the workspace ends up in the token
Tenant resolution happens entirely at token exchange (GET /app/{orgCode}/token). The authorizer never resolves tenancy — by the time it runs, the workspace is already a claim in the JWT.
current=1.Detecting 'no workspace yet'
In multi tenancy, a JWT with no tenant_id is the signal to show your create-or-join screen. Two sign-ins can place a new user in a workspace straight away instead: a company email whose domain a workspace claimed for SSO, and a personal Gmail account that signed in through a Google client ID a workspace registered under Personal Google accounts (Gmail). Either way the user may still be pending until a workspace admin approves them, which also shows as a JWT with no tenant_id (and pending: true in the token response). While the user has no workspace they can call only the workspace endpoints (list, create, join, switch). Creating or joining returns a JWT that includes the workspace in the x-nostackai-token response header; store it and use it from then on.
Joining & switching (multi)
The platform exposes the workspace lifecycle as endpoints; your app builds its own UI on top. The common flows:
New user via invite acceptinvitation -> GET /token (JWT has tenant_id)
Existing user via invite sign in -> join with { invite_token } -> new token in the response
New user, no invite register -> sign in (no tenant_id) -> join with a code or create -> new token in the response
Switch workspace list user tenants -> switch-tenant -> new token in the response Switching returns a fresh token
Create, join and switch each set the workspace as current and return a JWT scoped to it in the x-nostackai-token response header. Replace your stored token with it. The previous token keeps working for the previous workspace until it expires.
Members, invitations and leaving
Once users are in workspaces, a few more calls let your app manage them. Workspace admins (a wildcard ACL) manage the workspace in their token: GET /tenant/members lists who is in it (with email, whether each is an admin, and a status), PUT /tenant/members with { user_id } approves someone who joined through the workspace's SSO and is waiting (their membership becomes approved and the workspace's default ACL is attached; an org admin can also approve from the console's Tenant Management → App Users), and DELETE /tenant/members?user_id= removes someone or declines a request (an org admin can do the same from Tenant Management → App Users → Remove); GET /tenant/invitations lists the invitations sent (pending, used or expired) and DELETE /tenant/invitations?id= withdraws one that was not used; GET /tenant/workspace and PUT /tenant/workspace read and rename it. Any signed-in user can POST /user/leave-tenant to leave one of their workspaces and GET /user/invitations to see invitations waiting for their email. A workspace always keeps an admin: removing or leaving as the last admin returns 409 until someone else is made admin.
Removal takes effect within about five minutes
Removing a member (or leaving) deletes their access and membership. A token they already hold stops giving them the workspace within a few minutes (the gateway's authorizer re-checks membership on every run, and its result is cached for up to five minutes), rather than at the token's own expiry. Leave returns a fresh token for the workspace the user is now in, or one with no workspace.