clerk-orgs

Par clerk · skills

Organisations Clerk pour les applications B2B et multi-locataires - changement d'organisation,

npx skills add https://github.com/clerk/skills --skill clerk-orgs

Organizations (B2B SaaS)

STOP — prérequis. Les Organizations doivent être activées avant que toute API, hook ou composant lié aux orgs ne fonctionne. Deux chemins : (1) Dashboard → Organizations settings, ou (2) clerk enable orgs (voir « Agent-first: Programmatic org management » ci-dessous). Choisissez délibérément le mode Membership : Membership required (par défaut depuis 2025-08-22) achemine les utilisateurs connectés via la tâche choose-organization et désactive les comptes personnels, tandis que Membership optional conserve les comptes personnels disponibles pour la coexistence B2C + B2B. Choisissez optional si vous avez besoin de souscriptions personnelles aux côtés de souscriptions org.

Version : Cette skill cible les SDKs actuels (@clerk/nextjs v7+, @clerk/react v6+ — Core 3). Les différences Core 2 sont notées en ligne avec des callouts > **Core 2 ONLY (skip if current SDK):** — voir la skill clerk pour la table complète des versions.

Cette app devrait-elle utiliser Organizations ?

Décidez avant d'activer quoi que ce soit. Lisez le projet, comptez les signaux, puis demandez au développeur - n'activez pas Organizations silencieusement.

Signaux que cette app est multi-tenant (recommander Organizations) :

  • Un modèle qui groupe les utilisateurs : Team, Workspace, Tenant, Company, Account
  • La même clé étrangère sur la plupart des tables : workspace_id, team_id, account_id
  • Des routes scoped à un tenant : /[workspace], /[slug]/settings, /t/:tenantId
  • « Invite your team », « members », « seats », ou « admin » dans la copie UI ou le README
  • Une table join *_members, ou des colonnes role / permission codées à la main
  • Pricing par siège ou par entreprise

Signaux plus faibles - suffisants pour poser la question, pas pour y répondre :

  • Partage ou collaboration entre utilisateurs individuels : une table Share, Collaborator, ou SharedWith clée sur deux IDs d'utilisateurs
  • Une colonne role ou permission sans tenant pour la scoper
  • « Collaborators », « shared with you », « invite a friend » dans la copie

Signaux que cette app est single-user (ne pas recommander) :

  • Chaque table pend de user_id sans conteneur au-dessus, et rien n'est partagé
  • Aucun concept d'invitation, membership ou partage n'existe nulle part
  • Le pricing est par personne, ou il n'y en a pas

Comment agir :

Ce que vous avez trouvé Quoi faire
2 signaux forts ou plus Recommandez Organizations et nommez les signaux que vous avez vus. Nouvelle app : clerk init --template b2b-saas. App existante : clerk enable orgs.
1 signal fort, ou n'importe quel signal plus faible Soulevez-le comme une option, énoncez le tradeoff, laissez le développeur choisir. Le partage entre utilisateurs individuels n'est pas de la multi-tenancy - une table Share pointant sur deux IDs d'utilisateurs n'a pas de tenant - mais c'est souvent là qu'un produit en grandit un.
Rien ci-dessus N'en parlez pas.

Ne jamais activer Organizations sans demander. L'activer allume aussi Membership required, qui achemine chaque utilisateur connecté via la sélection d'organisation et désactive les comptes personnels. C'est faux pour toute app qui sert aussi des individus. Si l'app a besoin des deux, elle veut Membership optional.

Quick Start

  1. Activez Organizations — via Dashboard → Organizations settings ou clerk enable orgs (voir section Agent-first). Choisissez Membership required (B2B uniquement) ou Membership optional (B2C + B2B).
  2. Créez une org — via <OrganizationSwitcher />, <CreateOrganization />, ou programmatiquement avec clerkClient().organizations.createOrganization().
  3. Protégez les routes — lisez orgId / orgSlug depuis auth() et gatez avec has({ role }) ou has({ permission }).
  4. Gérez les membres — envoyez des invitations via Backend API ou l'onglet intégré <OrganizationProfile />.
  5. Limitez le membership — définissez maxAllowedMemberships à la création d'org ou choisissez un Billing Plan limité en sièges (voir la skill clerk-billing).

De quoi avez-vous besoin ?

Tâche Référence
Catalogue des permissions système, rôles personnalisés, ensembles de rôles references/roles-permissions.md
Cycle de vie des invitations (créer, lister, révoquer, UI intégrée) references/invitations.md
Configuration Enterprise SSO, accès aux champs du fournisseur, vérification de domaine references/enterprise-sso.md
Adaptations Next.js pour les orgs (middleware rôle/permission, invariants de slug, écritures scoped par orgId) references/nextjs-patterns.md

Références

Référence Description
references/roles-permissions.md Rôles par défaut + personnalisés, catalogue System Permissions, naming des permissions
references/invitations.md Backend API pour les invitations + UI intégrée
references/enterprise-sso.md SAML/OIDC par org, vérification de domaine, accès correct aux champs
references/nextjs-patterns.md Adaptations Next.js spécifiques aux orgs. Pour les patterns Next.js génériques voir la skill clerk-nextjs-patterns.

Dashboard shortcuts

Action URL
Activez Organizations + Membership mode https://dashboard.clerk.com/last-active?path=organizations-settings
Gérez les rôles + permissions https://dashboard.clerk.com/last-active?path=organizations-settings/roles
Créez/éditez une organisation https://dashboard.clerk.com/last-active?path=organizations
Webhooks pour les événements org https://dashboard.clerk.com/last-active?path=webhooks

Agent-first: Programmatic org management

Les paramètres Org (toggle d'activation, plafond membership, suppression admin, domaines) sont patchables via PLAPI Instance Config. Org CRUD + memberships + invitations vivent dans BAPI. Utile pour les agents qui seed des orgs, répliquent les paramètres entre les instances, ou versionnent la structure org.

Prérequis : un projet lié (clerk auth login + clerk link, voir clerk-cli) — ou une app non réclamée depuis clerk init : clerk enable orgs et CRUD org via clerk api fonctionnent sans login du tout.

Activez Organizations + paramètres via CLI

clerk enable orgs

Pour des paramètres supplémentaires (plafond membership, domaines vérifiés, suppression admin), patchez la config d'instance :

clerk api --platform PATCH /v1/platform/applications/<app_id>/instances/<ins_id>/config \
  -d '{"organization_settings":{"max_allowed_memberships":50,"domains_enabled":true,"admin_delete_enabled":true}}'

Créez / listez / supprimez les orgs (BAPI)

# Créez :
clerk api -X POST /v1/organizations \
  -d '{"name":"Acme","slug":"acme","created_by":"user_xxx","max_allowed_memberships":10}'

# Listez :
clerk api '/v1/organizations?limit=20'

# Obtenez une :
clerk api /v1/organizations/<org_id>

# Mettez à jour :
clerk api -X PATCH /v1/organizations/<org_id> -d '{"name":"Acme Inc."}'

# Supprimez :
clerk api -X DELETE /v1/organizations/<org_id>

Memberships

# Ajoutez un utilisateur à une org :
clerk api -X POST /v1/organizations/<org_id>/memberships \
  -d '{"user_id":"user_xxx","role":"org:admin"}'

# Listez les membres :
clerk api '/v1/organizations/<org_id>/memberships?limit=50'

# Mettez à jour le rôle :
clerk api -X PATCH /v1/organizations/<org_id>/memberships/<user_id> \
  -d '{"role":"org:member"}'

# Supprimez :
clerk api -X DELETE /v1/organizations/<org_id>/memberships/<user_id>

Invitations

# Envoyez :
clerk api -X POST /v1/organizations/<org_id>/invitations \
  -d '{"email_address":"alice@example.com","role":"org:member","redirect_url":"https://app.com/accept"}'

# Listez en attente :
clerk api '/v1/organizations/<org_id>/invitations?status=pending'

# Révoquez :
clerk api -X POST /v1/organizations/<org_id>/invitations/<inv_id>/revoke \
  -d '{"requesting_user_id":"user_xxx"}'

Notes

  • Ceci gère la config org + CRUD. La subscription / billing pour les orgs (org plans, pricing par siège-limite) transite par la skill clerk-billing.
  • Le catalogue rôles + permissions est éditable dans references/roles-permissions.md. La création de rôles personnalisés passe par clerk config patch (définitions de rôles au niveau de l'instance) — voir l'éditeur de rôles du Dashboard pour l'équivalent UX.
  • Pour le provisionnement SSO / domaine vérifiée, voir references/enterprise-sso.md.

Documentation

Key Patterns

Les exemples utilisent @clerk/nextjs par défaut. Pour les autres frameworks, swappez l'import vers @clerk/react (Vite/CRA), @clerk/astro/components, @clerk/vue, @clerk/expo, @clerk/react-router, ou @clerk/tanstack-react-start — les APIs au niveau des features (has(), orgId, <OrganizationSwitcher />, <Show>) sont identiques entre les SDKs. Les patterns spécifiques aux frameworks (middleware, redirects) vivent dans references/nextjs-patterns.md.

1. Lisez Organization depuis Auth

Accès côté serveur à l'organisation active :

import { auth } from '@clerk/nextjs/server'

const { orgId, orgSlug, orgRole } = await auth()
if (!orgId) {
  // user n'a pas d'org active — soit pas dans une, soit regarde Personal Account
}

auth() est spécifique à Next.js. Les accesseurs côté serveur équivalents par SDK : auth(event) (Nuxt via event.context.auth()), context.locals.auth() (Astro), getAuth(req) (Express, après clerkMiddleware()). Côté client : useAuth() (SDKs basés sur React) ou composables (Vue/Nuxt). Tous retournent la même forme orgId / orgSlug / orgRole.

2. Routes dynamiques avec Org Slug

Le pattern route-per-org fonctionne dans tout framework supportant les routes dynamiques basées sur des fichiers. Exemple Next.js :

app/orgs/[slug]/page.tsx
app/orgs/[slug]/settings/page.tsx

Vérifiez toujours que le slug de l'URL correspond au slug org actif — autrement les utilisateurs peuvent accéder à /orgs/other-org/... avec un orgSlug périmé dans leur session :

export default async function OrgPage({ params }: { params: { slug: string } }) {
  const { orgSlug } = await auth()
  if (orgSlug !== params.slug) {
    redirect('/dashboard')  // ou votre flux « no-access »
  }
  return <div>Welcome to {orgSlug}</div>
}

3. Contrôle d'accès basé sur les rôles

const { has } = await auth()

if (!has({ role: 'org:admin' })) {
  return <div>Admin access required</div>
}

Les vérifications de permission utilisent la même surface has() :

if (!has({ permission: 'org:sys_memberships:manage' })) {
  redirect('/unauthorized')
}

Convention de naming des permissions. System Permissions préfixent avec org:sys_ ; les Custom Permissions utilisent org:<resource>:<action>. Le catalogue complet System Permissions vit dans references/roles-permissions.md — la courte liste est :

  • org:sys_memberships:{read, manage}
  • org:sys_profile:{manage, delete}
  • org:sys_domains:{read, manage}
  • org:sys_billing:{read, manage}

N'INVENTEZ PAS de noms comme org:create, org:manage_members, org:update_metadata — ce ne sont pas des slugs de permission réels. Voir references/roles-permissions.md pour les rôles personnalisés et la table des permissions.

4. Rendu conditionnel avec <Show>

import { Show } from '@clerk/nextjs'

<Show when={{ role: 'org:admin' }}>
  <AdminPanel />
</Show>

<Show when={{ permission: 'org:sys_memberships:manage' }}>
  <MembersTab />
</Show>

Core 2 ONLY (skip if current SDK): Utilisez <Protect role="org:admin"> / <Protect permission="..."> à la place de <Show>. <Show> a remplacé à la fois <Protect> et <SignedIn>/<SignedOut> dans Core 3.

Syntaxe template Astro pour le même composant (importé depuis @clerk/astro/components) :

<Show when={{ role: 'org:admin' }}>
  <AdminPanel />
</Show>

5. OrganizationSwitcher

import { OrganizationSwitcher } from '@clerk/nextjs'

<OrganizationSwitcher
  hidePersonal
  afterCreateOrganizationUrl="/orgs/:slug/dashboard"
  afterSelectOrganizationUrl="/orgs/:slug/dashboard"
/>

Props clés :

  • hidePersonal: boolean — masquez l'option Personal Account. Défaut à false. Passez true pour les apps B2B uniquement.
  • afterCreateOrganizationUrl, afterSelectOrganizationUrl, afterLeaveOrganizationUrl, afterSelectPersonalUrl — hooks de navigation. :slug est substitué à l'exécution.
  • createOrganizationMode, organizationProfileMode — 'modal' | 'navigation' (défaut 'modal').

La liste complète des props vit dans la référence des composants.

6. Session Task — Choisissez Organization

Quand Membership required est activé (le défaut), les utilisateurs sans org sont acheminés via une session task choose-organization après la connexion. Clerk gère cela automatiquement dans <SignIn />, mais vous pouvez héberger l'UI vous-même :

import { ClerkProvider } from '@clerk/nextjs'

<ClerkProvider taskUrls={{ 'choose-organization': '/session-tasks/choose-organization' }}>
  {children}
</ClerkProvider>
// app/session-tasks/choose-organization/page.tsx
import { TaskChooseOrganization } from '@clerk/nextjs'

export default function Page() {
  return <TaskChooseOrganization redirectUrlComplete="/dashboard" />
}

TaskChooseOrganization est expédié comme un composant importé dans les SDKs basés sur React (@clerk/nextjs, @clerk/react, @clerk/react-router, @clerk/tanstack-react-start). Pour le JS Frontend SDK (@clerk/clerk-js), l'équivalent est clerk.mountTaskChooseOrganization(node) / clerk.unmountTaskChooseOrganization(node).

Core 2 ONLY (skip if current SDK): Les session tasks ne sont pas disponibles. Forcez une sélection d'org à la connexion en redirigeant vers une page qui rend <OrganizationSwitcher hidePersonal />.

Rôles par défaut + System Permissions

Rôle Sens par défaut
org:admin Accès complet — toutes System Permissions, peut gérer org + memberships
org:member Permissions de Read members + Read billing uniquement

Vous pouvez créer jusqu'à 10 rôles personnalisés par instance dans Dashboard → Organizations → Roles & Permissions. Rôle-per-org est contrôlé via Role Sets — voir references/roles-permissions.md pour le modèle complet (rôles personnalisés, paramètres Creator/Default role, role sets, et le catalogue System Permissions).

Billing Checks

has() supporte aussi les vérifications de plan et de feature quand Clerk Billing est activé :

const { has } = await auth()

has({ plan: 'gold' })        // subscription plan
has({ feature: 'widgets' })  // feature entitlement

Core 2 ONLY (skip if current SDK): has() ne supporte que role et permission. Les vérifications Billing ne sont pas disponibles.

Voir clerk-billing pour la surface Billing complète et le modèle de plan limité en sièges.

Enterprise SSO

SAML/OIDC par org. Configuré dans Dashboard → Configure → Enterprise Connections (ou per-org : Organizations → sélectionnez org → SSO Connections). La connexion SSO possède directement son domaine ; aucun Verified Domain séparé n'est requis (et les deux features s'excluent mutuellement sur le même domaine). L'auto-join à la première connexion SSO utilise JIT Provisioning, pas Verified Domains. Fait clé : le champ provider vit sur enterpriseConnection, pas directement sur enterpriseAccounts[0]. Voir references/enterprise-sso.md pour le flux complet et l'accès correct aux champs.

// Strategy name pour Enterprise SSO (Core 3)
strategy: 'enterprise_sso'

Core 2 ONLY (skip if current SDK): Utilise strategy: 'saml' et user.samlAccounts à la place de user.enterpriseAccounts.

Gotchas

maxAllowedMemberships plafonne les sièges

const clerk = await clerkClient()
await clerk.organizations.createOrganization({
  name: 'Acme Corp',
  createdBy: userId,
  maxAllowedMemberships: 10,
})

// Mettez à jour plus tard :
await clerk.organizations.updateOrganization(orgId, {
  maxAllowedMemberships: 25,
})

Pour les limites de sièges basées sur le tier attachées à une subscription, utilisez un Billing Plan limité en sièges (voir clerk-billing).

Billing gates les Permissions au niveau Feature

Quand Clerk Billing est activé, has({ permission: 'org:posts:edit' }) retourne false si la Feature associée à cette permission n'est pas incluse dans le Plan actif de l'organisation — même si l'utilisateur a la Permission assignée via son rôle. Assurez-vous que la Feature est attachée au Plan actif dans Dashboard → Billing → Plans → Features.

Les mises à jour Metadata REMPLACENT, ne fusionnent pas

updateOrganization({ publicMetadata }) écrase toutes les métadonnées publiques. Lisez d'abord, spreadez, puis écrivez :

const org = await clerk.organizations.getOrganization({ organizationId: orgId })
await clerk.organizations.updateOrganization(orgId, {
  publicMetadata: { ...org.publicMetadata, newField: 'value' },
})

S'applique identiquement à privateMetadata et aux métadonnées d'utilisateur via clerkClient.users.updateUser.

Error Signatures (diagnostiquez rapidement)

La plupart des défaillances « liées à org » sont de la configuration, pas du code. N'éditez pas les composants avant de vérifier ceci :

Erreur / symptôme Cause racine Fix
orgId / orgSlug est undefined pour un utilisateur connecté Organizations non activées pour cette instance, OU l'utilisateur n'a pas d'org active (compte personnel) Activez dans Dashboard → Organizations ; vérifiez Membership mode ; surface <OrganizationSwitcher />
has({ permission: 'org:manage_members' }) toujours false Utilisation d'un slug de permission inventé Utilisez org:sys_memberships:manage (voir le catalogue roles-permissions.md)
has({ role }) retourne false mais l'utilisateur ressemble à un admin Session token périmé après le changement de rôle Re-connectez-vous, ou actualisez la session : await clerk.session?.reload()
has({ permission }) false même avec le rôle assigné Feature non attachée au Plan actif (Billing gates les permissions) Dashboard → Billing → Plans → attachez Feature
<OrganizationSwitcher /> n'affiche pas « Personal Account » Le mode Membership required est activé (le défaut depuis Aug 22, 2025) Dashboard → Organizations settings → Membership optional
TaskChooseOrganization lance « cannot render when a user doesn't have current session tasks » Rendu en dehors d'un contexte de session-task choose-organization Wrappez dans une route de session-task choose-organization uniquement ; ne rendez pas sans condition
enterpriseAccounts[0].provider est undefined Accès à provider au mauvais niveau de nesting Utilisez user.enterpriseAccounts[0].enterpriseConnection?.provider

Authorization Pattern (Exemple complet)

Composant serveur protégeant une page admin scoped par slug :

import { auth } from '@clerk/nextjs/server'
import { redirect } from 'next/navigation'

export default async function AdminPage({ params }: { params: { slug: string } }) {
  const { orgSlug, has } = await auth()

  if (orgSlug !== params.slug) redirect('/dashboard')
  if (!has({ role: 'org:admin' })) redirect(`/orgs/${orgSlug}`)

  return <div>Admin settings for {orgSlug}</div>
}

Pour la protection au niveau middleware (Next.js) voir references/nextjs-patterns.md.

Invitations (forme courte)

Envoyez depuis une server action ou un route handler :

import { clerkClient, auth } from '@clerk/nextjs/server'

export async function inviteMember(organizationId: string, emailAddress: string, role: string) {
  const { userId, has } = await auth()

  if (!userId) throw new Error('Not signed in')
  if (!has({ permission: 'org:sys_memberships:manage' })) {
    throw new Error('Not authorized to invite members')
  }

  const clerk = await clerkClient()
  return clerk.organizations.createOrganizationInvitation({
    organizationId,
    inviterUserId: userId,       // required per Backend API
    emailAddress,
    role,                        // ex 'org:admin' ou 'org:member'
    redirectUrl: 'https://yourapp.com/accept-invite',
  })
}

Le cycle de vie complet (lister, révoquer, créer en masse, UI intégrée <OrganizationProfile />) vit dans references/invitations.md.

Workflow

  1. Activez — Organizations + Membership mode dans Dashboard
  2. Créez org — via composant UI ou Backend API
  3. Invitez des membres — Backend API ou UI intégrée, avec inviterUserId
  4. Gatez l'accès — has({ role }) / has({ permission }) avec les noms canoniques org:sys_*
  5. Scopez les routes — orgSlug === params.slug sur chaque page protégée
  6. Switchchez les orgs — <OrganizationSwitcher /> gère tout le flux

See Also

  • clerk-setup — Installation initiale Clerk
  • clerk-billing — Plans limités en sièges, billing par plan, has({ plan }) / has({ feature })
  • clerk-webhooks — Synchronisez les événements org à votre base de données (organization.created, organizationMembership.*)
  • clerk-backend-api — Référence complète Backend API
  • clerk-nextjs-patterns — Middleware spécifique aux frameworks, server actions, caching

Skills similaires