Skip to content
Fermium

SCIM 2.0 reference

The SCIM provisioning surface for Enterprise workspaces. Endpoints, authentication, filter support, PATCH operations, and the error schema.

Fermium implements the subset of SCIM 2.0 (RFC 7643 and RFC 7644) that Microsoft Entra ID, Okta and similar provisioning clients use: Users keyed by userName, Groups with members, eq filters, and PATCH. SCIM is an Enterprise feature. The user-facing setup is in SSO and SCIM.

Base URL and authentication

https://fermium.app/scim/v2
Authorization: Bearer fsc_…

Tokens are minted by a workspace owner or admin with POST /api/scim/tokens (see below), shown once, and stored hashed. Every request carries one. A missing or invalid token gets a 401 with WWW-Authenticate: Bearer and a SCIM error body. A token of a workspace that is no longer on Enterprise stops working.

Every response, including errors, is application/scim+json.

Token management (app session)

These are ordinary /api endpoints authenticated with the app session, for the workspace settings page and scripts.

Method and path Body Returns
GET /api/scim/tokens {tokens: [{id, label, created_at, last_used_at, revoked}], available, plan, base_url}
POST /api/scim/tokens {"label": "Entra"} (optional) 201 with the token row plus token (once) and base_url; 402 below Enterprise
DELETE /api/scim/tokens/{id} {"revoked": id}
curl -b "$COOKIE" -H "Origin: https://fermium.app" -H "Content-Type: application/json" \
  -d '{"label":"Okta"}' https://fermium.app/api/scim/tokens

Minting and revoking are audited as scim.token_create and scim.token_revoke.

Discovery

Path Notes
GET /scim/v2/ServiceProviderConfig patch.supported: true, filter.supported: true with maxResults: 200, bulk, sort, etag and changePassword not supported, bearer token authentication
GET /scim/v2/ResourceTypes User and Group
GET /scim/v2/Schemas the core User schema (userName, externalId, displayName, name, emails, active) and Group schema (displayName, externalId, members)

Users

urn:ietf:params:scim:schemas:core:2.0:User. A user resource:

{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "id": "42",
  "externalId": "9b1c…",
  "userName": "bo@acme.com",
  "name": { "givenName": "Bo", "familyName": "", "formatted": "Bo" },
  "displayName": "Bo",
  "emails": [{ "value": "bo@acme.com", "primary": true, "type": "work" }],
  "active": true,
  "meta": { "resourceType": "User", "created": "2026-09-15T10:00:00Z", "lastModified": "2026-09-15T10:00:00Z", "location": "https://fermium.app/scim/v2/Users/42" }
}

id is Fermium's user id. userName must be an email address; it is the account's email. name is derived from the email and returned for compatibility; names, phone numbers, titles and the enterprise extension are accepted in requests and ignored.

Method and path Behaviour
GET /scim/v2/Users list, with filter, startIndex (1-based) and count (default 100, at most 200)
GET /scim/v2/Users/{id} by Fermium id or by externalId; a deactivated user remains readable so the client can reactivate them
POST /scim/v2/Users create; 201. A user who already exists in the workspace and is active is a 409 uniqueness. A deactivated or elsewhere-provisioned user with the same email is adopted into the workspace instead
PUT /scim/v2/Users/{id} replace userName, externalId and active
PATCH /scim/v2/Users/{id} apply operations (below)
DELETE /scim/v2/Users/{id} deprovision: same as active: false; 204

Created users get the member role and count against the plan's seat cap; a full workspace is a 409 with a detail naming the plan and the cap.

Deactivation (active: false, or DELETE) disables the account, removes it from the workspace's groups, and moves it to a personal workspace of its own. The seat is freed. The person's documents are kept under their account and are not deleted. Reactivation (active: true) re-enables the account and moves it back into the workspace if a seat is free.

Filters

One term of the form attribute op "value". Supported attributes and operators:

Resource Attributes Operators
Users userName, emails, emails.value, externalId, active eq, ne, co, sw, ew, pr; active takes eq true or eq false
Groups displayName, externalId eq, ne, co, sw, ew, pr

Text comparison is case-insensitive. A schema URN prefix on the attribute is accepted and stripped. Anything else (and, or, brackets, other attributes) is a 400 with scimType: invalidFilter.

curl -H "Authorization: Bearer $SCIM_TOKEN" \
  'https://fermium.app/scim/v2/Users?filter=userName%20eq%20%22bo%40acme.com%22'
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
  "totalResults": 1,
  "startIndex": 1,
  "itemsPerPage": 1,
  "Resources": [ { "…": "…" } ]
}

PATCH operations

urn:ietf:params:scim:api:messages:2.0:PatchOp with an Operations array. op is add, replace or remove; other values are a 400 invalidSyntax.

For users:

Path Effect
active activate or deactivate (see above)
userName, emails.value, emails[type eq "work"].value change the email; 409 uniqueness if another account has it
externalId set, or clear with remove
no path, object value (Okta style) each key is applied as above
anything else accepted and ignored
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [{ "op": "replace", "path": "active", "value": false }]
}

Groups

urn:ietf:params:scim:schemas:core:2.0:Group. A group resource:

{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
  "id": "5",
  "externalId": "e7a1…",
  "displayName": "Design",
  "members": [{ "value": "42", "display": "bo@acme.com", "$ref": "https://fermium.app/scim/v2/Users/42" }],
  "meta": { "resourceType": "Group", "created": "2026-09-15T10:00:00Z", "lastModified": "2026-09-15T10:00:00Z", "location": "https://fermium.app/scim/v2/Groups/5" }
}
Method and path Behaviour
GET /scim/v2/Groups list, with filter, startIndex, count
GET /scim/v2/Groups/{id} by Fermium id or externalId
POST /scim/v2/Groups create with displayName, optional externalId and members; 409 uniqueness if a group with that name exists
PUT /scim/v2/Groups/{id} replace name, external id and, when members is present, the whole membership
PATCH /scim/v2/Groups/{id} operations below
DELETE /scim/v2/Groups/{id} remove the group and every access grant that named it; 204

Groups created this way appear in Fermium with source: scim and a slug derived from the name, and work in access grants like groups made by hand. Members are referenced by Fermium user id, by externalId, or by email; a reference that is not a user in this workspace is a 400 invalidValue.

Group PATCH paths:

Path Effect
members with add add the listed users
members with remove and a value list, or members[value eq "42"] with remove remove those users
members with replace set the membership to the list
displayName rename
externalId set or clear

Errors

Every error is:

{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
  "status": "409",
  "scimType": "uniqueness",
  "detail": "a user with userName 'bo@acme.com' already exists"
}
Status scimType When
400 invalidSyntax body is not a JSON object, or an unknown PATCH op
400 invalidValue userName is not an email, a PATCH value has the wrong shape, an unknown group member
400 invalidFilter a filter outside the supported subset
401 missing or invalid bearer token, or the workspace is not on Enterprise
404 unknown user or group id
409 uniqueness duplicate userName or group name, or the workspace is at its member cap

Audit

Provisioning writes to the workspace audit log with the actor scim:<workspace-slug>: member.join and member.leave with via: scim, group.create and group.delete with via: scim.

Not supported

Bulk operations, sorting, ETags, password management, and the /Me endpoint. Filters with more than one term. Attributes other than those listed are accepted and ignored, as RFC 7643 allows.