IntegrationsSCIM — User Provisioning

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/v2

Two resource endpoints are implemented:

EndpointResource
/api/v1/scim/v2/UsersUser provisioning
/api/v1/scim/v2/GroupsGroup 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

  1. In EmbayLMS, go to Settings → User Management → SCIM Provisioning.
  2. Click Create token, enter a label (e.g. “Okta production”), and create it.
  3. 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.
  4. 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)

OperationMethodBehavior
List / filterGETLists active users; supports startIndex / count paging and the filter userName eq "user@example.com"
CreatePOSTProvisions a new user (or updates a match — see Matching)
ReadGET /Users/{id}Returns one user
ReplacePUTReplaces a user’s attributes
UpdatePATCHPartial update (e.g. set active)
DeactivateDELETE / active=falseDeactivates the user (see Deprovisioning)

User attribute mapping — SCIM attributes map to EmbayLMS user fields as follows:

SCIM attributeEmbayLMS fieldNotes
userNameEmailMust be a valid email address; used as the primary identifier
name.givenNameFirst nameDefaults to Unknown if omitted
name.familyNameLast nameDefaults to Unknown if omitted
activeAccount statustrue = active, false = deactivated (default true)
externalIdSCIM external IDThe IdP’s stable ID — used for matching on re-sync
roles[0].valueRoleFirst role value; one of learner, instructor, manager, group_admin, ecommerce_admin, admin, owner. Any other value falls back to learner. The retired author is still accepted and is provisioned as learner plus the “Content Author” custom role — see Role mapping.

Groups (/Groups)

OperationMethodBehavior
ListGETLists groups with members; supports startIndex / count paging
CreatePOSTProvisions a group with its members
ReplacePUTReplaces the group’s display name and full member list

Group attribute mapping:

SCIM attributeEmbayLMS fieldNotes
displayNameGroup nameRequired
externalIdSCIM external IDUsed for matching; auto-generated if the IdP omits it
members[].valueMember user IDsThe EmbayLMS user id of each member

Where provisioned groups show up, and linking them to LMS groups

Pushed groups are listed under Settings → User Management → SCIM → Provisioned groups: name, external ID, member count, the time of the last push, and the LMS group each one feeds (or Not linked). The list is read-only — the identity provider owns these rows.

A provisioned group does nothing in EmbayLMS until you link it to an LMS group:

  1. In Provisioned groups, click Link to LMS group on the row.
  2. Choose Create a new group named ”…” (the default), or Attach existing: … to feed a manual group you already use for course assignments or group admins.
  3. Click Link. Members are reconciled immediately and the confirmation says how many were added.

From then on, every push to /Groups reconciles the linked LMS group:

  • users in the pushed member list are added with the source Provisioned by SCIM;
  • SCIM-provisioned members no longer in the list are removed;
  • members added by other means (manually, Google Workspace, Microsoft Entra) are never touched;
  • a member ID that matches no EmbayLMS user yet is skipped until that user exists.

Linked groups carry a SCIM badge on the Groups page and on the group itself, and their Add member / Remove controls are disabled — membership is edited in the identity provider. Everything else about the group (course assignments, group admins, name, description) stays editable.

Unlink detaches the group: it keeps its members and becomes a normal manual group again. If the identity provider deletes the SCIM group, the LMS group likewise survives with its members.

RuleBehaviour
One LMS group per provisioned groupA provisioned group links to at most one LMS group, and a group is fed by at most one provisioned group
Only manual groupsA dynamic (rule-based) group cannot be linked — its members come from rules
PlanLinking needs the SCIM capability (Enterprise), like creating a token

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 externalId from 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

  1. 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.

  2. Enter the SCIM connector settings:

    Okta fieldValue
    SCIM connector base URLhttps://<your-tenant>.embaylms.com/api/v1/scim/v2
    Unique identifier field for usersuserName
    Authentication ModeHTTP Header
    AuthorizationBearer <token from Step 1>
    Supported provisioning actionsPush New Users, Push Profile Updates, Push Groups
  3. Click Test API Credentials — Okta calls GET /Users to verify the token.

  4. Under Provisioning → To App, enable Create Users, Update User Attributes, and Deactivate Users.

  5. Map Okta profile attributes to SCIM: userName → email, givenName, familyName, and (optionally) a roles mapping to one of learner, instructor, manager, group_admin, ecommerce_admin, admin, owner.

  6. 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

FieldWhereValue
SCIM base URLYour IdP’s SCIM apphttps://<your-tenant>.embaylms.com/api/v1/scim/v2
Bearer tokenSettings → User Management → SCIM Provisioning → Create tokenShown once; copy immediately
Unique user identifierYour IdPuserName (email)
Stable match keyYour IdPexternalId (strongly recommended)
Role attributeYour IdP roles[0].valuelearner | instructor | manager | group_admin | ecommerce_admin | admin | owner
PagingQuery stringstartIndex (1-based), count (max 200)
User filterQuery stringuserName eq "user@example.com"

Role mapping

Your IdP sends the EmbayLMS role in roles[0].value. The accepted values are the seven RBAC v2 personas:

ValueDescription
learnerConsumes assigned and self-selected learning
instructorFacilitates sessions and grades, scoped to their own courses
managerAssigns and monitors training for their direct reports
group_adminAdministers users and content within their assigned groups
ecommerce_adminManages the storefront, pricing, and orders
adminFull tenant administration
ownerTop of the tenant hierarchy

Any unrecognised value provisions the user as learner. This is deliberate — an IdP must never be able to provision a role EmbayLMS does not know about.

The retired author role

author was retired in RBAC v2 but is still accepted so existing IdP mappings keep working. A user provisioned with author is stored with primary role learner and granted the seeded “Content Author” custom role, which carries the same permissions the old author role had. The user keeps the capability; the primary role stops reporting a value that no longer exists.

Update your IdP mapping to instructor when convenient.


Troubleshooting

SymptomCause / fix
Test credentials / connection fails with 401The 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 tokenThe 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 (or a reactivation via active: true) returns 403 “Provisioning would exceed the plan limit of N active users”Your plan’s active-user seat cap is full (Free = 5 — the same limit POST /api/v1/users reports as 422). Deactivate a user or upgrade in Billing; the IdP retries on its next sync cycle. Deactivations and attribute-only updates are never gated (LMS-896).
Create user returns 409 ConflictA 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 roleThe roles[0].value was missing or not one of the roles in Role mapping; EmbayLMS falls back to learner rather than provisioning an unknown role. Fix the role mapping in your IdP. Note author also resolves to learner by design — see below.
Deactivated user still appears in reportsExpected — deactivation preserves the learning record. The user cannot log in. See Deprovisioning.
Duplicate accounts after an email changeThe 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.
A pushed group does not appear on the Groups pageExpected until you link it — see Where provisioned groups show up. Pushed groups are listed under SCIM → Provisioned groups.
Add member is missing on a group, or removing a member fails with “managed by SCIM”The group is fed by a provisioned group (SCIM badge). Change membership in the identity provider, or Unlink the group on the SCIM page to manage it by hand.
A linked group is missing some pushed membersThose member IDs match no EmbayLMS user yet (the IdP pushed the group before the users). They are added on the next push once the users exist; the link confirmation reports the skipped count.