Skip to content
Developer docs
Menu

Concepts

Organizations

How a sign-in picks the organization your app receives.

Every sign-in is for exactly one organization. Tokens carry it in the organization_id claim, so your app always knows whose data to show.

The organization claim

organization_id is a UUID, for example 00000000-0000-0000-0000-000000000000. It is in the ID token, the access token, the /userinfo response and every logout token that names a session. Use it as the tenant key for everything the person does in that session.

Single-organization apps

Most apps are registered to one organization: the one that owns the registration. Every token your app receives carries that organization.

If the owning organization is a customer organization, only its active members can sign in. Anyone else is sent back to your redirect URI with error=access_denied and error_description=organization_access_denied.

Multi-organization apps

An app that serves many organizations is registered as multi-organization. The mode is fixed when the app is registered. On each sign-in:

  • if the person belongs to one organization, it is selected automatically;
  • if they belong to several, SSO shows them a page where they choose one. The page is part of the sign-in; your app never links to it;
  • if they belong to none, the sign-in ends with error=access_denied and error_description=organization_access_denied.

Suggest an organization

Send organization_hint with an organization's UUID to preselect it on the chooser. SSO checks that the person belongs to it. A hint that isn't one of their organizations, or isn't a UUID in lowercase canonical form, is ignored without an error. The hint only preselects; the person still confirms.

Silent renewal

With prompt=none, SSO never shows a page. For a person with several organizations, a silent request succeeds only when organization_hint names an organization they already have a live session with in your app. Otherwise your redirect URI receives error=interaction_required and error_description=organization_selection_required; start an interactive sign-in instead.

Switching organization

To switch, start a new sign-in with organization_hint set to the organization the person wants. The new tokens carry the new organization_id and a new sid.

prompt=select_account is accepted. A person with more than one organization already chooses on every interactive sign-in, so it doesn't change what they see.

List a person's organizations

To build an organization switcher, call GET /api/v1/me/memberships with the person's access token. It lists their memberships, 20 per page by default (page and page_size, up to 100). For an app owned by a customer organization, the list only contains that organization.

JSON
{
  "data": [
    {
      "membership_id": "00000000-0000-0000-0000-000000000000",
      "org_id": "00000000-0000-0000-0000-000000000000",
      "org_name": "Example Org",
      "org_slug": "example-org",
      "org_status": "active",
      "status": "active"
    }
  ],
  "pagination": {
    "has_next": false,
    "has_previous": false,
    "page": 1,
    "page_size": 20,
    "total_items": 1,
    "total_pages": 1
  }
}

status is the membership's status and org_status the organization's. Suspended memberships and deleted organizations are listed too, so apply your own rule before you offer them.

The token decides the organization

Always take the organization from the validated token. Never trust an organization ID from your own UI, a URL or a hint.

Revoking a membership doesn't end a live session

Membership isn't re-checked on every request. /userinfo, /api/v1/me/memberships and introspection stay live for an app session that's already minted, purely on the token's status and expiry — even after the person's membership is revoked — until the app session or its SSO session ends.

Every new sign-in re-checks membership at consent, including a silent prompt=none renewal. A revoked member of a single-organization app, or a multi-organization member left with no organizations, gets error=access_denied and error_description=organization_access_denied on the redirect, and no new code is ever minted for them. A multi-organization member who still belongs to other organizations is instead signed in to one of them: automatically, even under prompt=none, when exactly one organization remains; with two or more remaining, prompt=none instead answers error=interaction_required and error_description=organization_selection_required, and an interactive request shows the picker. The code exchange checks membership again. If your app must react to a membership change before either of those runs, re-check the memberships list yourself. Always compare the new token's organization_id with the one you expected.

None of this applies to a single-organization app owned by the platform organization: membership is never checked for it, at sign-in or at exchange — it admits any identity.