Groups
How SCIM 2.0 groups from your identity provider map to Pact roles and personas, including group-based role composition and persona overrides.
Groups
Pact treats your identity provider (IdP) as the source of truth for group membership. When you provision groups over SCIM 2.0, Pact composes each user's role (a privilege axis) and persona (a UI-shape axis) from the groups they belong to. The wire contract lives in api/routes/scim_groups.py; the composition logic is in core/scim_groups.py; the operator-facing mapping UI is served by api/routes/admin_scim_groups.py.
Group-based role composition
When a SCIM Group write adds or removes a member, Pact recomputes that user's effective role via recompute_user_role and writes it onto tenant_users.role. Roles compose by a fixed precedence:
owner > admin > manager > member > viewer
Groups elevate, they do not demote
Group membership can only raise a user's role. Removing a user from a group does not demote them — the explicit downgrade path is a SCIM Users PUT/PATCH on the user's role attribute (urn:company-scraper:scim:User:role). This prevents a group-sync race from silently stripping access.
The api role is outside the SCIM-assignable set and never participates in this comparison.
Persona mapping
Personas are a separate, non-privilege axis (sales_rep, sales_manager, marketer, dpo, cs_csm, revops, admin, and others in core/personas.py). recompute_user_persona writes the most-specific persona — the one attached to the most-recently-updated group the user belongs to — onto tenant_users.persona. Because personas are not a privilege axis, there is no strict precedence; two conflicting personas on one user signal an IdP misconfiguration and are logged rather than silently resolved.
For tenants whose IdP does not yet push the SCIM persona extension, infer_persona_from_name is a fallback that pattern-matches familiar group names (e.g. sales-reps → sales_rep). Prefer the explicit mapped_persona field whenever your IdP supplies it.
Operator surface
The IdP owns group membership, but you can review and override the persona each group confers from the admin UI:
GET /v1/admin/scim-groups — list groups + their mapped persona
PATCH /v1/admin/scim-groups/{group_id} — override mapped_persona
Both are owner/admin-only and tenant-scoped (tenant_id derived from the auth context, never the request body). Every PATCH writes a scim_group.persona.set audit row.
SCIM Groups wire endpoints
The bidirectional SCIM 2.0 Groups contract your IdP drives is under /scim/v2/Groups (list, read, create, replace, patch, delete), authenticated with a per-tenant bearer token. See SSO for how SCIM provisioning is enabled alongside SAML/OIDC.