SCIM user provisioning
EmbayLMS supports SCIM 2.0 (System for Cross-domain Identity Management) so your identity provider — Okta, Microsoft Entra ID (Azure AD), or any SCIM-2.0 client — can create, update, and deactivate EmbayLMS user accounts automatically, keeping them in sync with your HR system.
When an employee is onboarded in your IdP, the IdP provisions an EmbayLMS account. When their attributes change (name, role), the IdP pushes the update. When they are offboarded, the IdP deactivates the account in EmbayLMS — the learning record is preserved, not deleted.
SCIM vs. SSO: SCIM manages accounts (provisioning). SAML/OIDC manages logins (authentication). They are independent — you can run either alone, but most organizations run SCIM alongside SAML or OIDC so accounts are both auto-created and SSO-enabled.
Base endpoint
SCIM is served per-tenant on your own subdomain. The base URL is:
https://<your-tenant>.embaylms.com/api/v1/scim/v2Two resource endpoints are implemented:
| Endpoint | Resource |
|---|---|
/api/v1/scim/v2/Users | User provisioning |
/api/v1/scim/v2/Groups | Group provisioning |
All requests authenticate with a Bearer token (see Step 1) and the tenant is resolved automatically from the subdomain.
Step 1 — Generate a SCIM bearer token
- In EmbayLMS, go to Admin → Settings → User Management → SCIM.
- Click Create token, enter a label (e.g. “Okta production”), and create it.
- The token is shown only once in a reveal banner. Copy it immediately — it cannot be retrieved again. Only a short prefix is stored for identification.
- The page also shows the SCIM base path (
/api/v1/scim/v2) for reference.
You can create multiple tokens (e.g. one per IdP) and revoke any of them at any time from the same page; the list shows each token’s label, prefix, creation date, and last-used date. Revoking a token immediately stops provisioning for any IdP using it.
Tokens are stored as a SHA-256 hash. EmbayLMS never stores or displays the raw token after creation.
Step 2 — Supported operations and attributes
Users (/Users)
| Operation | Method | Behavior |
|---|---|---|
| List / filter | GET | Lists active users; supports startIndex / count paging and the filter userName eq "user@example.com" |
| Create | POST | Provisions a new user (or updates a match — see Matching) |
| Read | GET /Users/{id} | Returns one user |
| Replace | PUT | Replaces a user’s attributes |
| Update | PATCH | Partial update (e.g. set active) |
| Deactivate | DELETE / active=false | Deactivates the user (see Deprovisioning) |
User attribute mapping — SCIM attributes map to EmbayLMS user fields as follows:
| SCIM attribute | EmbayLMS field | Notes |
|---|---|---|
userName | Must be a valid email address; used as the primary identifier | |
name.givenName | First name | Defaults to Unknown if omitted |
name.familyName | Last name | Defaults to Unknown if omitted |
active | Account status | true = active, false = deactivated (default true) |
externalId | SCIM external ID | The IdP’s stable ID — used for matching on re-sync |
roles[0].value | Role | First role value; one of learner, author, manager, admin. Any other value falls back to learner |
Groups (/Groups)
| Operation | Method | Behavior |
|---|---|---|
| List | GET | Lists groups with members; supports startIndex / count paging |
| Create | POST | Provisions a group with its members |
| Replace | PUT | Replaces the group’s display name and full member list |
Group attribute mapping:
| SCIM attribute | EmbayLMS field | Notes |
|---|---|---|
displayName | Group name | Required |
externalId | SCIM external ID | Used for matching; auto-generated if the IdP omits it |
members[].value | Member user IDs | The EmbayLMS user id of each member |
Step 3 — Matching logic and deprovisioning
Matching (create vs. update). On a user POST, EmbayLMS first looks for an
existing user by externalId. If one is found, that user is updated in place
(email, name, role, active) rather than duplicated. If no externalId match
exists, it checks for an existing user with the same email — if that email is
already taken, the request returns 409 Conflict; otherwise a new user is
created. Groups follow the same pattern, matched by externalId.
Recommendation: always send a stable
externalIdfrom your IdP. It is the key that lets EmbayLMS recognize the same person across syncs (including after an email change), which prevents duplicate accounts.
Deprovisioning. EmbayLMS does not hard-delete users via SCIM. When your IdP
unassigns or deactivates a user, send active: false (via PATCH, PUT, or
DELETE). The account is deactivated: the user can no longer sign in, but
their completions, certificates, and transcript are retained for compliance.
Re-sending active: true reactivates the same account with its history intact.
Audit. Every SCIM create and update is written to the EmbayLMS audit log
(actor system, role scim) for SOC 2 evidence.
Step 4 — Worked example: configure Okta
-
In the Okta Admin Console, go to Applications → Browse App Catalog, add a SCIM 2.0 Test App (or your existing EmbayLMS SAML app if it supports provisioning), then open its Provisioning tab and click Configure API Integration.
-
Enter the SCIM connector settings:
Okta field Value SCIM connector base URL https://<your-tenant>.embaylms.com/api/v1/scim/v2Unique identifier field for users userNameAuthentication Mode HTTP Header Authorization Bearer <token from Step 1>Supported provisioning actions Push New Users, Push Profile Updates, Push Groups -
Click Test API Credentials — Okta calls
GET /Usersto verify the token. -
Under Provisioning → To App, enable Create Users, Update User Attributes, and Deactivate Users.
-
Map Okta profile attributes to SCIM:
userName→ email,givenName,familyName, and (optionally) arolesmapping to one oflearner,author,manager,admin. -
Assign people/groups to the app. Okta provisions them into EmbayLMS; unassigning a user sends
active: false, which deactivates the account.
Entra ID (Azure AD): the flow is equivalent — add the Enterprise application → Provisioning, set Provisioning Mode to Automatic, paste the same base URL as the Tenant URL and the token as the Secret Token, then Test Connection and start provisioning.
Configuration reference
| Field | Where | Value |
|---|---|---|
| SCIM base URL | Your IdP’s SCIM app | https://<your-tenant>.embaylms.com/api/v1/scim/v2 |
| Bearer token | Admin → Settings → User Management → SCIM → Create token | Shown once; copy immediately |
| Unique user identifier | Your IdP | userName (email) |
| Stable match key | Your IdP | externalId (strongly recommended) |
| Role attribute | Your IdP roles[0].value | learner | author | manager | admin |
| Paging | Query string | startIndex (1-based), count (max 200) |
| User filter | Query string | userName eq "user@example.com" |
Troubleshooting
| Symptom | Cause / fix |
|---|---|
| Test credentials / connection fails with 401 | The bearer token is wrong, revoked, or the tenant is not active. Re-check the Authorization header is Bearer <token> and generate a fresh token if needed. |
| 401 even with a valid-looking token | The request must hit your tenant subdomain (<your-tenant>.embaylms.com) — the tenant is resolved from the host. A bare apex/platform URL will not authenticate. |
| Create user returns 409 Conflict | A user with that userName (email) already exists but has a different/absent externalId. Set a matching externalId in your IdP so EmbayLMS updates the existing account instead of creating a new one. |
userName must be a valid email address (400) | userName must contain @. Map your IdP’s email attribute to userName. |
User provisioned as learner despite a different role | The roles[0].value was missing or not one of learner/author/manager/admin; EmbayLMS falls back to learner. Fix the role mapping in your IdP. |
| Deactivated user still appears in reports | Expected — deactivation preserves the learning record. The user cannot log in. See Deprovisioning. |
| Duplicate accounts after an email change | The IdP did not send a stable externalId, so the new email created a new account. Configure externalId mapping and merge the duplicate via the transcript tools in User Management. |