SCIM provisioning
Requires a license with the
scimfeature. See pricing.
SCIM provisioning lets your identity provider manage LyEve users for you. When someone joins, changes or leaves in Azure AD, Okta or another SCIM 2.0 directory, the directory calls LyEve and the change arrives without anyone opening the admin console. SCIM signs nobody in. Pair it with SAML single sign-on, so the people your directory provisions are the people who can sign in.
How it works
Section titled “How it works”| Part | What it is |
|---|---|
| Connection | A name, a bearer token and what a delete does. A super admin creates it in LyEve. |
| SCIM base URL | https://<your instance>/api/v1/scim/v2 on the Content API. The directory calls it with the token. |
| Account | A provisioned user gets a LyEve account at their first SAML sign-in through the linked provider, with editor when no other role is given. |
| Group | Stored with its members, so the directory can read it back. A group does not grant a role in LyEve. |
What you can rely on:
- Each connection authenticates with its own bearer token, and sees only the users and groups it created.
- A connection belongs to the tenant it was created in, and everything it provisions lands in that tenant.
- LyEve stores a hash of each token, never the token.
- A SCIM
passwordattribute is never stored.
Turn it on
Section titled “Turn it on”- Run with a license that carries
scim. See licensing and tiers. - Restart the instance.
- Create a connection. A super admin opens Access > SCIM provisioning in the admin console and chooses New connection, or uses the API below.
- In your identity provider, set the SCIM base URL to
https://lyeve.example.com/api/v1/scim/v2and the token to the one you chose.
Try it
Section titled “Try it”You need a super admin token in TOKEN. The
quickstart shows how to get one.
-
Create a connection with a long random token:
Terminal window SCIM_TOKEN=$(openssl rand -hex 32)curl -X POST http://localhost:3001/api/admin/scim/providers \-H "Authorization: Bearer $TOKEN" \-H "Content-Type: application/json" \-d '{"name": "azure-ad", "bearer_token": "'"$SCIM_TOKEN"'"}'{"bearer_token": "4dc4c30c8bb6...","provider": {"id": "47fe92cc-de5c-4c56-9b56-4e45fea8061c","name": "azure-ad","enabled": true,"tenant_id": "default","attribute_map": { "userName": "userName", "displayName": "displayName", "externalId": "externalId", "active": "active", "groups": "groups" },"deprovision_on_delete": true,"deprovision_action": "disable","created_at": "2026-10-01T09:30:00Z","updated_at": "2026-10-01T09:30:00Z"}}The answer is
201. This is the only time the token is returned. Copyprovider.idintoPROVIDER_ID. -
Read what the server supports. Discovery needs no token:
Terminal window curl http://localhost:3002/api/v1/scim/v2/ServiceProviderConfigThe answer uses
application/scim+jsonand says patch and filters are supported, with at most 200 results, and bulk, sort and password changes are not. -
Create a user the way a directory does:
Terminal window curl -X POST http://localhost:3002/api/v1/scim/v2/Users \-H "Authorization: Bearer $SCIM_TOKEN" \-H "Content-Type: application/json" \-d '{"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],"userName": "ada@example.com","active": true,"emails": [{"value": "ada@example.com", "primary": true}]}'The answer is
201with the SCIM user, itsidandmeta. -
Find the user by name:
Terminal window curl "http://localhost:3002/api/v1/scim/v2/Users?filter=userName%20eq%20%22ada%40example.com%22" \-H "Authorization: Bearer $SCIM_TOKEN"{"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],"totalResults": 1,"startIndex": 1,"itemsPerPage": 100,"Resources": [{"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],"id": "2ed58a06-1876-45d8-8eaf-a280ad86c9e3","userName": "ada@example.com","active": true}]} -
Call with the wrong token to see the refusal:
Terminal window curl http://localhost:3002/api/v1/scim/v2/Users \-H "Authorization: Bearer wrong"{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"], "detail": "invalid provider credentials", "status": 401, "scimType": "sensitive" }
Connection settings
Section titled “Connection settings”| Field | Meaning | Default |
|---|---|---|
name | A name for the connection | required |
bearer_token | The token the directory sends as Authorization: Bearer <token> | required |
saml_provider_id | The SAML provider the same directory signs users in through | none |
attribute_map | Which SCIM attributes feed userName, displayName, externalId, active and groups | each attribute maps to itself |
deprovision_action | What a SCIM delete does: disable marks the user inactive and is meant to clear the account's roles (see the caution above), delete removes the LyEve account. Any value other than disable deletes | disable |
With saml_provider_id set, the first SAML sign-in of a provisioned user
creates their LyEve account.
Rotate the token
Section titled “Rotate the token”curl -X POST http://localhost:3001/api/admin/scim/providers/$PROVIDER_ID/rotate \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"new_token": "'"$(openssl rand -hex 32)"'"}'The answer is {"bearer_token": "<the new token>"}. The old token stops
working at once, so update the identity provider right after.
What the directory calls
Section titled “What the directory calls”The directory authenticates every call except discovery with its bearer
token. Responses use the application/scim+json content type and the SCIM 2.0
shapes. Lists take filter, startIndex and count. count defaults to 100
and is capped at 200. Filters accept eq, ne, co, sw, ew, gt, ge,
lt, le and pr.
SCIM routes
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/scim/v2/ServiceProviderConfig | What this server supports. No token |
GET | /api/v1/scim/v2/Schemas | The resource schemas. No token |
GET | /api/v1/scim/v2/ResourceTypes | The resource types. No token |
GET | /api/v1/scim/v2/Users | List users |
POST | /api/v1/scim/v2/Users | Create a user. 201 |
GET | /api/v1/scim/v2/Users/{id} | Read a user |
PUT | /api/v1/scim/v2/Users/{id} | Replace a user |
PATCH | /api/v1/scim/v2/Users/{id} | Change part of a user |
DELETE | /api/v1/scim/v2/Users/{id} | Remove a user. 204 |
GET | /api/v1/scim/v2/Groups | List groups |
POST | /api/v1/scim/v2/Groups | Create a group |
GET | /api/v1/scim/v2/Groups/{id} | Read a group |
PUT | /api/v1/scim/v2/Groups/{id} | Replace a group |
PATCH | /api/v1/scim/v2/Groups/{id} | Add or remove members |
DELETE | /api/v1/scim/v2/Groups/{id} | Delete a group |
Manage connections
Section titled “Manage connections”These routes take a signed-in session. An admin token is refused on them with
403.
Connection routes
| Method | Path | Role | Purpose |
|---|---|---|---|
GET | /api/admin/scim/providers | admin | List connections, paginated |
POST | /api/admin/scim/providers | super_admin | Create a connection. 201 |
GET | /api/admin/scim/providers/{id} | admin | Read a connection |
DELETE | /api/admin/scim/providers/{id} | super_admin | Remove a connection. 204 |
POST | /api/admin/scim/providers/{id}/rotate | super_admin | Replace the token |
If you set LYEVE_PLUGINS, include scim in it.
Errors
Section titled “Errors”Errors on the SCIM routes come back in the SCIM error shape, with status and
detail. The 415 comes back as {"error": "..."}, because it is refused
before SCIM reads the request.
| Status | Message | Cause |
|---|---|---|
401 | invalid provider credentials | The directory sent no token or an unknown one. |
404 | user not found | The user does not exist, or another connection created it. |
415 | unsupported media type, expected application/json or multipart/form-data | The body was sent as application/scim+json or another type. |
Errors from the connection routes
| Status | Message | Cause |
|---|---|---|
400 | name is required or bearer_token is required | A connection field is missing. |
400 | new_token is required | A rotation had no new token. |
400 | invalid saml_provider_id | The id is not a UUID. |
404 | provider not found | No connection with that id in the tenant. |
404 | The requested endpoint does not exist. | The instance started without a license that carries scim. |
402 | payment_required | The license stopped carrying scim while the instance was running. |
Related
Section titled “Related”- Sign-in options: every way to sign in, side by side.
- SAML single sign-on: sign provisioned users in.
- Roles and permissions: what the
editorrole a new account gets may do.