SCIM user provisioning
Your workspace speaks SCIM 2.0 (RFC 7643 and 7644) so your identity provider (IdP) can push user lifecycle events straight into OnTrackio: a new hire appears the moment HR adds them upstream, role and department changes flow within minutes, and a departure deactivates the account automatically. This guide wires up Okta or Microsoft Entra.
:::note Before you begin
- You're an administrator on the workspace. Minting the token and mapping groups both require an admin role.
- You're working against your own tenant subdomain —
<slug>.app.ontrackio.com. SCIM is per-tenant; the centralapp.ontrackio.comhost has no SCIM surface. - You have admin access to one IdP — Okta or Microsoft Entra — and the rights to add a provisioning app there.
- Connect one IdP per workspace. See One IdP per workspace before you wire a second. :::
How provisioning maps to the lifecycle
SCIM events from your IdP drive three flows in OnTrackio.
| Lifecycle event | SCIM operation | What happens in OnTrackio |
|---|---|---|
| Joiner | POST /Users | A user is created. IT can pre-assign hardware and license seats before day 1. |
| Mover | PATCH /Users/{id} | Department, job title, and manager update. Cost-allocation reports stay accurate. |
| Leaver | PATCH /Users/{id} with active: false | The account is deactivated, the hardware-return workflow fires, and seats are flagged for reclamation. The audit log records the timestamp. |
What the SCIM API exposes
The endpoints below are reached at https://<slug>.app.ontrackio.com/scim/v2. Your IdP
appends the resource paths itself, so you only ever paste the base URL.
| Resource | Verbs | Purpose |
|---|---|---|
/ServiceProviderConfig | GET | Capability discovery |
/Schemas, /Schemas/{id} | GET | User and group schemas |
/ResourceTypes, /ResourceTypes/{name} | GET | Advertises Users and Groups support |
/Users | GET, POST | List (paginated, filterable); create a joiner |
/Users/{id} | GET, PUT, PATCH, DELETE | Read; full replace; partial update (mover, leaver); soft-delete |
/Groups | GET, POST | List (filterable); create a group with members |
/Groups/{id} | GET, PUT, PATCH, DELETE | Read; replace members; add or remove members and rename; soft-delete |
Every request authenticates with a Bearer token — no session cookies, no CSRF token.
Responses use Content-Type: application/scim+json.
Step 1 — mint a SCIM token
The token is the credential your IdP uses to authenticate. Mint it in OnTrackio first, then paste it into the IdP in step 2.
- In the Admin console, open
/admin/api-tokens(also linked from Settings → API & webhooks). The page is titled API tokens. - In the Mint new token card, set Token name to something that records which IdP
it's for — for example
scim-oktaorscim-entra. - Set Owner to a service-account user, or yourself for testing. When the owner's account is deactivated the token stops working, so pick someone you'll keep active.
- Under Abilities (scope), tick only SCIM 2.0 endpoints (IdP joiner/mover/leaver push). Granting nothing else keeps the token least-privileged.
- Set Expires at (optional but recommended) to a date within a year, and plan to rotate it annually.
- Optionally, paste your IdP's egress CIDR ranges into IP allowlist (CIDR, optional).
A request from any other address gets a
401with no hint that the token was valid. - Select Mint token.
The plaintext token appears once in a green banner — This is the only chance to copy the
plaintext. We store only the SHA-256 hash; refreshing this page will hide it. Select
Copy and hold it for step 2. The IdP sends it as Authorization: Bearer <token>.
The plaintext is shown once and is never recoverable. If you lose it, revoke the token and mint a new one.
Step 2a — connect Okta
If you already have an Okta app for OnTrackio (the same tile users sign in through), add SCIM provisioning to that app so users see one tile and assignment lists line up. If you're SCIM-only, use the standalone SCIM 2.0 Test App (OAuth Bearer Token) template from the App Catalog instead — the steps from the connector panel onward are the same.
-
Open your OnTrackio app in Okta admin (Applications → Applications, then select the tile).
-
In General → App Settings → Edit, under Provisioning choose the SCIM radio, then select Save. A new Provisioning tab appears in the app.
-
On the Provisioning tab, go to Settings → Integration → Edit on the SCIM Connection panel and fill in the fields below.
Field Value SCIM connector base URL https://<slug>.app.ontrackio.com/scim/v2Unique identifier field for users userNameSupported provisioning actions Tick Import New Users, Push New Users, Push Profile Updates. Leave the group options unticked. Authentication Mode HTTP Header Authorization The plaintext token from step 1. Okta prepends Bearerfor you. -
Select Test Connector Configuration. Expect green ticks next to User Import, Import Profile Updates, Create Users, and Update User Attributes, and red marks next to Push Groups and Import Groups — OnTrackio doesn't accept group push from Okta, so that's correct.
-
Select Save.
-
Go to Provisioning → To App → Edit and enable the actions that drive each flow.
Action Flow SCIM call Create Users Joiner POST /UsersUpdate User Attributes Mover PATCH /Users/{id}Deactivate Users Leaver PATCH /Users/{id}withactive: falseSync Password — Leave off — OnTrackio is SSO-only and never accepts passwords over SCIM. -
Under Provisioning → To App → Attribute Mappings, map Okta profile attributes to OnTrackio's SCIM attributes.
Okta OnTrackio (SCIM) user.emailuserNameandemails[type eq "work"].valueuser.profile.firstNamename.givenNameuser.profile.lastNamename.familyNameuser.titletitleuser.departmenturn:ietf:params:scim:schemas:extension:enterprise:2.0:User:departmentuser.employeeNumberexternalIdandenterprise:User:employeeNumberuser.managerenterprise:User:manager.value -
On the Assignments tab, assign your test user. If you assigned the user before enabling SCIM, select Provision User from the row's menu to force the first push — Okta only auto-pushes assignments made after SCIM is wired.
Use user.profile.firstName and user.profile.lastName, not the shorter user.firstName
/ user.lastName. Okta's expression validator rejects the short forms with Invalid
property firstName in expression.
Step 2b — connect Microsoft Entra
-
In Microsoft Entra admin → Enterprise applications → New application → Create your own application, name it something like
OnTrackio ITAM (SCIM). -
In the app's Provisioning pane, set Provisioning Mode to Automatic.
-
Fill in Admin Credentials.
Field Value Tenant URL https://<slug>.app.ontrackio.com/scim/v2Secret Token The plaintext token from step 1 -
Select Test Connection. Entra calls
/ServiceProviderConfigand a probe filter on/Usersto verify the credentials. -
Under Mappings → Provision Microsoft Entra ID Users, map the attributes below.
Entra source OnTrackio target userPrincipalNameuserNameSwitch([IsSoftDeleted], , "False", "True", "True", "False")activegivenNamename.givenNamesurnamename.familyNamedisplayNamedisplayNamemailemails[type eq "work"].valueemployeeIdexternalIdjobTitletitledepartmenturn:ietf:params:scim:schemas:extension:enterprise:2.0:User:departmentmanagerenterprise:User:manager.value -
Select Start provisioning. Entra's first cycle reads the current OnTrackio directory; later cycles reconcile roughly every 40 minutes.
How groups map to roles
OnTrackio stores SCIM groups and their members as a first-class concept, but a SCIM
group is metadata, not a permission grant. Adding a user to a SCIM group named admin
does not give them the OnTrackio admin role. This is a deliberate safeguard: without it,
your IdP could grant super-admin to anyone by editing group membership, bypassing admin
review.
To turn IdP group membership into OnTrackio roles, map the groups explicitly.
- In the Admin console, go to Settings → Integrations and open the SCIM 2.0
Provisioning card (or open
/admin/scim/role-mappings). - In the Add mapping card, pick a SCIM group and the OnTrackio role to grant.
- Select Add mapping.
| Behaviour | Detail |
|---|---|
| Apply on save | Adding a mapping immediately grants the role to every current member of the SCIM group. |
| Apply on churn | When the IdP later adds members over SCIM, the grant applies automatically. |
| Revoke on removal | Removing a member from a mapped group revokes the role — but only for grants that came from SCIM. |
| Manual grants win | A role you assign on the Users page is marked manual and survives SCIM churn. Re-saving a user "locks" their roles this way. |
| Unmapped groups | An unmapped SCIM group grants nothing — there are no implicit role grants from an IdP-named group. |
SCIM groups are still useful beyond roles: reporting filters ("hardware assigned to the Sales group"), team-based automation, and reconciliation ("who's in this IdP group that OnTrackio thinks isn't?").
Filtering and pagination
Your IdP reads the directory with SCIM filters and a cursor. You rarely write these by hand, but the supported set determines what an IdP can ask for.
The full RFC 7644 operator set works on Users: eq, ne, co, sw, ew, pr, the
ordering operators gt / lt / ge / le, the combinators and / or / not, and
parentheses for grouping. For example, userName co "alice" and active eq true.
| Resource | Filterable attributes |
|---|---|
| Users | userName, email, externalId, active, displayName, title, name.givenName, name.familyName, name.formatted |
| Groups | displayName eq, externalId eq, displayName co only |
A filter on an attribute outside these lists returns 400 with scimType: invalidFilter.
Listings paginate with a standard cursor — GET /scim/v2/Users?startIndex=1&count=100.
startIndex is 1-indexed, count defaults to 100 and caps at 200, and the response
carries totalResults, startIndex, itemsPerPage, and Resources. If an IdP isn't
paginating a large directory, enable paging on its side — a single unpaged request over
thousands of users can time out.
How deactivation and deletion behave
| Operation | Effect |
|---|---|
PATCH active: false (leaver) | The account flips to inactive and an audit-log entry records the IdP-initiated deactivation with its timestamp. |
DELETE /Users/{id} | A soft delete. The user drops out of listings (a later GET returns 404), but the row is retained so hardware assignments, audit history, and GDPR records keep their references. |
| Re-provision after delete | A POST for a soft-deleted user restores and updates the record and returns 200, so a re-hire reuses the same identity. |
To erase a user's personal data entirely, use the GDPR erasure flow in the admin UI rather than SCIM DELETE. SCIM DELETE is intentionally reversible. See GDPR data subject requests.
One IdP per workspace
Connect only one IdP to a workspace's SCIM endpoint at a time. SCIM resource IDs are owned
by whichever IdP first provisions a user, and each IdP caches its own mapping of its users
to those IDs. If both Okta and Entra push to the same workspace, one can silently overwrite
the other's records when an ID is reused. The symptom is a user's userName, name, or
externalId appearing to change between cycles even though no admin touched them.
To migrate from one IdP to another, cut over in a window rather than running both:
- Turn provisioning off on the old IdP.
- Wait one full sync cycle (about 40 minutes) so in-flight writes drain.
- Turn provisioning on on the new IdP.
- Confirm on
/scim/v2/Usersthat every expected user is present with the correctexternalId. - Remove the old IdP's SCIM app once you trust the new one's coverage.
If you genuinely need two IdPs writing at once — usually a merger scenario — contact support. The supported path is a per-IdP workspace, or an identity hub (Okta Inbound Federation, Microsoft Cross-Tenant Sync) fanning out to one SCIM endpoint with consistent IDs.
Why some attributes are off-limits
OnTrackio's user schema deliberately omits roles, entitlements, and groups as
writable user attributes, and the service config doesn't advertise password change. Each
omission closes a path that would let an IdP unilaterally grant a role or push a password.
Role assignment stays in the admin UI or SAML role-mapping; passwords never travel over
SCIM. PATCH paths the product doesn't store (such as preferredLanguage or locale) are
accepted and silently ignored rather than rejected, so a long tail of optional IdP
attributes doesn't pause provisioning.
Troubleshooting
| Symptom | What to do |
|---|---|
Test connection fails with 401 | The token expired, was revoked, or its owner was deactivated — mint a fresh one. Check the header is Authorization: Bearer <token> with no extra whitespace. If you set an IP allowlist, confirm the IdP's egress IP is in it (it appears in the admin activity log around the failed attempt). |
Test connection fails with 404 | The base URL is wrong. It must end in /scim/v2 — no trailing slash, no /Users suffix. It must use your tenant subdomain, not the central app.ontrackio.com host. |
| Schema mismatch or attribute not found | The IdP is requesting an attribute OnTrackio doesn't advertise — commonly groups, roles, entitlements, or addresses. Check GET /scim/v2/Schemas and remove the mapping. |
| Provisioning paused after the first cycle | Okta and Entra both pause on repeated failures. Fix the root cause (usually a 401 or a malformed filter), then resume provisioning in the IdP. |
Filter rejected with invalidFilter | The filter uses an unsupported operator or attribute. Switch to a supported one from the tables above. |
| A user's details change between cycles on their own | Two IdPs are pushing to the same workspace. See One IdP per workspace. |