Automatic provisioning with SCIM 2.0 Module
What it does — and what it does not
Vaks PM exposes a standard SCIM 2.0 endpoint that your directory drives. It supports users and groups, with full create, read, update, patch and delete. There is no scheduler on the Vaks PM side: every change is pushed by your directory on its own cycle, typically every 40 minutes for Entra.
- It provisions people, not permissions. New accounts land on the standard member role. Roles come from group mapping at sign-in, configured in the single sign-on guides, not from SCIM.
- It never hard-deletes. Deactivation suspends; deletion soft-deletes. Data is preserved either way.
- It ignores most attributes. Only email, name, active state, the external identifier and employee number are stored. Job title, department, manager, phone and the rest are accepted and silently dropped.
- Agent accounts are invisible to it. AI agent identities cannot be read or managed through SCIM.
Provisioning and licence seats
This is the single most surprising behaviour, so it is worth stating plainly before you start.
You have two ways to close that gap:
- Grant seats by hand in Admin → Users — workable when joiners are occasional.
- Use an automatic-licensing team — the recommended pattern. Mark a team as automatically licensed, then point a directory group at it. Anyone the directory adds to that group receives a seat; anyone removed has it released. Your licensed population then follows a group you manage in the directory.
Prerequisites
| Side | What you need |
|---|---|
| Directory | Rights to configure provisioning on an enterprise application. In Entra this needs an Entra ID P1 or P2 licence; automatic provisioning is not available on the free tier. |
| Vaks PM | The org:manage permission — an organization administrator. |
| Licence | A licence covering the SCIM feature. Without it the endpoint refuses every call and the token buttons are hidden. |
| Single sign-on | Configured and tested, per the warning above. |
Step 1 — Generate the token in Vaks PM
Open Admin → Integrations → Single sign-on and find the SCIM provisioning card below the provider list. It gives you the two values your directory needs.
Tenant URL — click to copy:
https://<your-host>/api/v1/scim/v2
Secret token — press Generate SCIM token. The secret is displayed once, under the notice "Copy this token now — it won't be shown again." Paste it straight into your directory.
If you run several organizations on separate subdomains, each one needs its own tenant URL, its own token and its own provisioning application — the tenant is identified by the hostname you call.
- Entra — the provisioning agent, part of on-premises application provisioning. Requires an Entra ID P1 or P2 licence, like cloud provisioning.
- Okta — On-Premises Provisioning (OPP), with the Okta Provisioning Agent on a Windows or Linux host.
Step 2 — In Microsoft Entra ID
- Go to Microsoft Entra admin center → Identity → Applications → Enterprise applications and open the application you created for single sign-on (or create a non-gallery application if provisioning is all you need).
- Open Provisioning → New configuration, or set Provisioning Mode to Automatic.
- Paste the Tenant URL and the Secret Token from step 1.
- Press Test Connection. It must succeed before you can save — this validates the token and the scope in one go.
- Under Users and groups, assign the people and groups to provision. Under Settings, set the provisioning scope, then set Provisioning Status to On.
Entra runs an initial full cycle, then incremental cycles roughly every 40 minutes. The first cycle can take a while on a large directory.
Step 2b — In Okta
- Open your Vaks PM application in the Okta admin console, go to the Provisioning tab and choose Configure API Integration.
- Tick Enable API integration. Enter the Base URL (the tenant URL from step 1) and the API Token (the secret token).
- Press Test API Credentials, then save.
- Under To App, enable Create Users, Update User Attributes and Deactivate Users.
- Assign users and groups, and push groups explicitly if you want teams created — Okta only sends groups you have added under Push Groups.
Step 3 — Attribute mappings
Only five attributes are stored. Everything else your directory sends is accepted and discarded, so trimming the mapping list is tidiness rather than necessity.
| SCIM attribute | Becomes | Notes |
|---|---|---|
userName | The account's email address | Required. Stored lower-cased. Map it to the user principal name or mail as you prefer, but keep it consistent with what single sign-on will present. |
externalId | The account key | Map this to the object ID, not to Entra's default. See the warning below. |
active | Account status | false suspends the account and releases its seat. |
name.formatted or displayName | Display name | Falls back to given name + surname, then the email. First and last names are not stored separately. |
employeeNumber optional | Employee identifier | From the enterprise extension. Not sent by default — add the mapping explicitly if you want it. Must be unique across the organization. |
externalId mapping. Entra maps it to mailNickname out of the box, which changes when a user is renamed — and when it changes, the directory stops recognising the account it created, then tries to create a duplicate and gets a conflict. Map externalId to objectId instead. This is also the value single sign-on matches on, so the two integrations converge on the same account.
Matching attributes — what can be filtered on
Before creating anyone, your directory asks whether the account already exists, by querying this endpoint with a filter. Entra lets you choose which attribute it matches on (Match objects using this attribute); Okta always matches on userName and offers no choice. If Entra is pointed at an attribute that cannot be filtered, every lookup fails.
| Resource | Filters accepted |
|---|---|
| Users | userName eq "…" · externalId eq "…" · emails[type eq "work"].value eq "…" · id eq "…" |
| Groups | displayName eq "…" · externalId eq "…" · id eq "…" |
Only equality on a single attribute is supported. Compound filters (and, or), the other comparison operators (ne, co, sw, gt…) and presence tests (pr) are refused with a 400. Attribute names may carry their schema prefix (urn:ietf:params:scim:schemas:core:2.0:User:userName) and are matched case-insensitively.
userName (or externalId) and clear the rest.
Step 4 — Groups and teams
A directory group becomes a Vaks PM team: the group's display name becomes the team name, and its members become team members. Teams created this way are matched on the group's external identifier, so renaming the group in the directory renames the team rather than creating a second one.
Two behaviours regularly cause a support ticket that turns out to be normal:
- A newly created group often arrives empty, filling in on a later cycle. The directory creates the group before it has provisioned all the members, and members it references that do not exist yet are dropped rather than rejected. This self-heals. If a group never fills, the cause is provisioning scope — the members are not assigned to the application.
- A team's local display label is never overwritten by the directory. Only the underlying name follows the group, so a label set inside Vaks PM survives.
Removing a group from the directory soft-deletes the team; its members keep their accounts. If the team had automatic licensing on, its members lose their seats unless another automatic-licensing team covers them.
Step 5 — Verify
After the first cycle completes, check three things:
- In your directory's provisioning log — users created, no recurring errors. Entra's log names each user and the outcome.
- In Admin → Users — the expected people are present. Remember they will have no seat yet unless an automatic-licensing team covers them.
- End to end — have one pilot user sign in through single sign-on. That proves the object identifier lines up between provisioning and federation, which is the single thing most likely to be wrong.
Then test a leaver: deactivate the pilot user in the directory, wait for a cycle, and confirm their Vaks PM account shows as suspended and their sessions are gone.
Deactivation and deletion
| Action in the directory | Effect in Vaks PM |
|---|---|
User deactivated (active: false) or unassigned from the application | Account suspended, licence seat released, and all active sessions revoked immediately. Data and history preserved. |
| User reactivated | Account active again, but the seat is not restored automatically — grant it again, or rely on an automatic-licensing team. |
| User deleted | Soft delete: the account is suspended and hidden, its data retained. |
| A previously deleted person is provisioned again | The original account is restored in place, with its history, time entries and comments intact. Convenient for a returning employee; worth knowing before you re-provision someone by accident. |
To erase someone's personal data rather than suspend them, use the anonymization function in the admin panel — provisioning has no equivalent, by design.
Troubleshooting
Error details returned to your directory are always in English, whatever your interface language — the caller is a machine and the text lands in its provisioning log. They appear verbatim there.
| Symptom | Cause & fix |
|---|---|
| Test connection fails · Missing SCIM token | No usable authorization header arrived. Re-paste the token; check for a stray space or line break. |
| Test connection fails · Invalid or revoked SCIM token | The token is unknown, revoked or expired. Generate a new one. Also check you are calling the right hostname — a token from one organization does not work on another's subdomain. |
| Token lacks the scim:provision scope | An ordinary API key was used. Use the Generate SCIM token button, which sets the scope for you. |
| 403 · This feature ("scim") requires an active license | The licence does not cover SCIM. Import one under Admin → Organization → License. |
| 400 · Filtering on '…' is not supported | The directory looked accounts up by an attribute this endpoint cannot filter on. In Entra, set Match objects using this attribute on userName or externalId only, and clear it everywhere else. See matching attributes for the accepted list. |
| 400 · The operator '…' is not supported | The filter used something other than a single eq comparison. Same fix: simplify the matching configuration. Sustained failures put an Entra provisioning job in quarantine; it resumes on its own once the configuration stops failing. |
| 409 · User already exists | An account already holds that email or external identifier. Usually the externalId mapping was changed after the first cycle, so the directory no longer recognises accounts it created. Restore the mapping to objectId. |
| 409 · userName (email) already used by another account | A rename would collide with a different account. Resolve the duplicate in the directory. |
| 409 on a team name | A team of that name already exists but was not created by provisioning. Rename one of the two. |
| Provisioning worked, then stopped entirely | The token expired. There is no grace period. |
| Users provisioned but cannot see anything | No licence seat. Expected — see provisioning and seats. |
| Groups arrive with no members | Normal on the first cycle. If it persists, the members are not in the provisioning scope. |
| The directory keeps creating duplicates | It is not finding existing accounts. Confirm externalId is mapped to objectId and that provisioning has not been reconfigured from scratch, which resets the directory's internal links. |
What is logged
Every provisioning write is audited, attributed to the administrator who generated the token, and visible under Admin → Security & Compliance → Audit log: account creation (flagged when it restored a previously deleted account), suspension, reactivation, deletion, and team membership changes. Membership entries record which members were requested, which were added, and which were dropped for not existing yet — the reliable place to confirm the empty-group behaviour above is benign.
Related: OIDC single sign-on · SAML single sign-on · all integrations.