Skip to main content

Custom roles and SCIM provisioning

Enterprise organizations can define custom roles as explicit permission sets, assign them organization-wide or as per-project overrides, and provision users and groups from their identity provider with SCIM 2.0. Built-in roles are fixed permission sets inside the same evaluator, so there is one authorization path.

Custom roles​

Custom roles contain explicit permissions such as datasets.list, datasets.get and models.list. The permission catalog is generated from the API authorization matrix: every operation is a permission, and adding an endpoint extends the catalog. Wildcards and unknown permission names are rejected with invalid_argument, naming the unknown strings. Built-in owner, admin, member and viewer names are reserved. The role administration endpoints are under /v1/orgs/{org_id}/roles.

Governance permissions​

The governance evidence operations are permissions like any other:

PermissionGrants
model_inventory.listThe organization model inventory (console sessions only).
models.governance.get, slots.governance.getReading a model's or a slot's governance fields.
models.governance.update, slots.governance.updateWriting them.
models.card.exportExporting a model's pinned card.
dossier_templates.listListing the dossier templates.
dossiers.create, dossiers.list, dossiers.get, dossiers.downloadAssembling, listing, reading and downloading dossiers.

A reviewer's custom role can, for example, hold model_inventory.list and the dossier permissions without the rest of admin.

Customer map permissions​

The customer map operations are permissions like any other. Applications are refused all of them.

PermissionBuilt-in floorAPI key scopeGrants
customer_map.getviewerreadReading the current customer map.
customer_map.settings.getviewerreadReading the map's update settings.
customer_map.editions.listviewerreadReading the history of the map.
customer_map.refreshmemberadminUpdate now: starting one new map on the map's fixed recipe. The caller chooses no columns, sample size or seed. Only one update runs at a time, and the map's cooldown applies.
customer_map.settings.putadminadminChanging how often and when the map updates, pausing it, the cooldown, the slot it follows and pinning one map.

customer_map.refresh is the only map write a member holds. Starting an export (exports.create) stays with admin. A custom role created before these permissions existed does not hold them; create or edit the role to add them.

Update now has limits of its own, because map updates meter nothing and never appear in usage. Apart from an Admin console session (one that holds customer_map.settings.put), every caller, every API key included, waits at least 15 minutes after the last successful update whatever the map's cooldown, waits a growing backoff (2, 4, 8 ... minutes, up to that cooldown) after updates that failed, and starts at most 24 updates in 24 hours for each map. An Admin may retry a failed update at once, but never inside the cooldown after a successful one.

Reading the map, its settings and its history shows only what the caller's purposes allow: a slot the caller may not use is left out, not refused, and so are its pin and next update time. Update now always works on the map's stored slot: the slot an Admin chose, and only it (if it stops qualifying, Update now is refused and scheduled updates fail visibly), or, when the server chooses, the stored slot while it still qualifies. A caller who may not use that slot's purpose is refused (purpose_not_allowed) before anything else is answered. Scheduled updates stop, waiting for an Admin, when the credential they run as may no longer use that purpose.

Assign an organization role through PUT /v1/orgs/{org_id}/members/{user_id}/role. A project override at PUT /v1/projects/{project_id}/members/{user_id}/role replaces the organization role for that project's operations; DELETE on the same path restores inheritance. Organization operations always use the organization role, and permission sets are never combined across scopes.

An administrator can only grant permissions they hold in that organization: creating, updating or assigning a role whose permissions exceed the actor's own returns permission_denied with the excess permissions in error.details. The same bound applies to whoever or whatever they act on. To change, downgrade, remove or deactivate a member, replace or edit a role, or revoke an invitation, the administrator must hold every permission that member, role or invitation holds now, as well as every permission of any role they would grant. A narrower administrator therefore cannot downgrade an administrator, remove one in two steps (downgrade, then remove), strip permissions from a role that more privileged members hold, or cancel an invitation it could not have issued; each is refused with permission_denied and the excess permissions. For a member this covers the organization role and every project override, and removing a project override must also keep the role it uncovers within the actor's permissions. Existing owner safeguards, including protection of the last owner, continue to apply; owner can never be a custom role, a SCIM default or a group mapping target. Operations such as deactivating an organization or deleting and restoring a project belong to the built-in owner role by default. Because a role can only contain permissions its author holds, only an owner can place them in a custom role; delegate them deliberately, as each role change is audited.

A role that is still referenced cannot be deleted: the request returns failed_precondition with organization and project assignment counts and any SCIM, invitation or SSO references. Permission changes apply to the next request.

Roles survive endpoint changes​

A role stores permission names, and a SCIM token stores the ceiling of the administrator who minted it as permission names. Both are evaluated against the permission catalog of the running release on every request. A release that adds an endpoint changes no existing grant: roles, SCIM tokens, invitations and SSO defaults created earlier keep working, and the new permission is available to roles created afterwards. A permission that a release removes grants nothing (only that permission is denied; the role's other grants keep working), and the role still reads, edits and assigns. Writing a role that names a permission the running release does not know is a client error (invalid_argument); a description-only edit leaves the stored names untouched.

Custom roles next to the v1 member, invitation and SSO schemas​

The v1 member, invitation and SSO schemas are frozen: Membership.role, MemberUpdateRequest.role, UserOrgMembership.role, Invite.role, InviteCreateRequest.role and SsoConfig/SsoConfigUpdate.default_role remain the closed Role, InviteRole and SsoDefaultRole enums, so existing API clients keep working in every organization. They accept only the built-in names. A member, pending invitation or SSO default that holds a custom role appears in those responses as viewer, the least-privileged built-in role, never as a broader one. The exact role is available through additive operations:

NeedOperation
Read a member's organization roleGET /v1/orgs/{org_id}/members/{user_id}/role
Read a member's project overrideGET /v1/projects/{project_id}/members/{user_id}/role
Assign or replace a rolePUT on the same two paths
Invite with a built-in or custom rolePOST /v1/orgs/{org_id}/role-invites
List invitations with exact rolesGET /v1/orgs/{org_id}/role-invites
Read or set the SSO just-in-time roleGET/PUT /v1/orgs/{org_id}/sso/default-role

Revoke an invitation with DELETE /v1/orgs/{org_id}/invites/{invite_id} and redeem it with POST /v1/invites:accept whichever operation created it. The full PUT /v1/orgs/{org_id}/sso replaces the whole configuration, including the default role, with its built-in value (member when omitted); set a custom default afterwards. Changing a member to a built-in role through PATCH /v1/orgs/{org_id}/members/{user_id} replaces a custom role.

Two consequences of the projection need care. Access-review and audit tooling that reads Membership.role lists custom-role holders as viewer whatever permissions their role grants, so point such tooling at GET /v1/orgs/{org_id}/members/{user_id}/role and the role endpoints. And a v1 read-modify-write cannot round-trip a custom role: reading a member and sending the displayed viewer back through PATCH (or sending a configuration back through the full PUT /v1/orgs/{org_id}/sso) is a change to the built-in role and replaces the custom one.

SCIM tokens​

An administrator mints or revokes the organization's SCIM token through /v1/orgs/{org_id}/scim-token. Token creation accepts default_role, which defaults to member and cannot be owner. Its tsc_ secret is shown once. Minting replaces the previous token; revoked credentials stop authenticating within five seconds. A token can never grant more than the administrator who minted it held at that moment (its permission ceiling): its grants are exactly that administrator's permissions, not a value the caller chooses, and its default role must be within them. As an organization-level credential, it keeps that ceiling if the administrator is later downgraded or removed: revoke or re-mint it to change it. Any administrator with the permission can revoke or replace the token whatever its ceiling, because revoking only removes access; the replacement carries the replacing administrator's grants, never the replaced token's. Session tokens and project API keys cannot authenticate SCIM requests, and a SCIM token cannot authenticate /v1 requests.

SCIM token creation accepts an optional Idempotency-Key. With a key, the server retains one successful issuance effect for 24 hours in the addressed organization. The first 201 response contains the bearer once; the receipt stores no bearer or response body. Every later authorized request with that key in that organization returns 400 failed_precondition whose error.details contains type: precondition_failure, reason: scim_token_already_issued, the issued token_id and issued_at, and no secret. A retry never mints, rotates or revokes a token and cannot restore a revoked bearer. After 24 hours, or with a new key, a request performs an explicit new issuance. This exception applies only to SCIM token creation; other POST idempotency behavior is unchanged.

SCIM 2.0 protocol​

SCIM is served at https://api.staging.tessera.zone/scim/v2 with Users, Groups and ServiceProviderConfig. Its contract is api/scim-openapi.yaml. Unlike the normal /v1 API, SCIM uses application/scim+json, standard camelCase field names such as userName, one-based startIndex pagination and RFC 7644 ListResponse and Error objects. It does not use the Tessera data/next_cursor or error envelopes.

{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status": "400",
"scimType": "invalidFilter",
"detail": "Unsupported filter attribute"
}

User filters support userName eq "dispatch@fleetwise.example" and externalId eq "idp-123". Usernames are email addresses unique within the organization; externalId round-trips unchanged. A duplicate provisioning request returns 409. active=false through PUT or PATCH, and DELETE on a user, soft-deactivate that organization's membership: the user's sessions are denied for that organization on their next request, and reactivation restores access. Identity and audit records are retained. Provisioning does not require SSO.

A token cannot change a member who holds more than it does. Deactivating or reactivating a user, or replacing the role of a group member, whose current permissions (the organization role and any project override) exceed the token's permission ceiling returns a SCIM 403 and changes nothing. De-provisioning an administrator therefore needs a token minted by an administrator whose permissions cover that member's; owners are never changed by SCIM.

Group mappings are administered through /v1/orgs/{org_id}/scim-group-mappings. Each group display name maps to one non-owner role. A mapped membership change applies that organization role; removal restores the previous applicable assignment. When two mapped groups conflict, the most recently applied assignment wins and a scim.group_conflict audit event records the conflict. Permission count never determines precedence.

The supported PATCH subset is replacing a user's active state and adding/removing group members. Unsupported operations return a SCIM 501 error without partial mutation. ServiceProviderConfig reports PATCH and filtering support; bulk operations, sorting, password changes and ETags are unsupported. Requests exceeding the per-token limit receive a SCIM 429 error with Retry-After.

Each token is bound to one organization. A resource outside that organization returns the same 404 as an absent resource. Role and SCIM mutations produce audit events (SCIM actions use the token ID as the actor); token plaintext is never included in them.

SCIM attribute handling​

SCIM attribute names are case-insensitive; responses use canonical SCIM names. Duplicate case variants are rejected. Returned read-only IDs and metadata may be sent back in POST/PUT and are ignored; unknown role, permission and organization attributes are rejected. Optional values may be cleared with null; empty arrays also clear multi-valued values.

On user creation, omitted active defaults to true. Explicit null clears active and grants no access. On replacement, both omitted and null active clear it; only explicit true reactivates membership. Unassigned active is returned as null. The supported active PATCH operation accepts true/false only; use PUT to clear.