SCIM User Provisioning
If your organization manages people in Microsoft Entra ID (Azure AD), you can provision them into Ditio automatically instead of maintaining users in two places. Ditio supports the SCIM 2.0 protocol: when someone joins, changes details, or leaves in your directory, the change flows into Ditio on the next provisioning cycle.
What SCIM provisioning does
Section titled “What SCIM provisioning does”- Creates new Ditio accounts when people join your team or organization. New users get the base User role; give other roles in Ditio Web.
- Updates user profiles when changes are made in Entra ID
- Deactivates Ditio accounts when people leave the team or organization
Limitations
Section titled “Limitations”- Group provisioning is not supported
- Roles are not provisioned: Entra ID app roles don’t reach Ditio
- Write-back from Ditio to Entra ID is not supported
Prerequisites
Section titled “Prerequisites”- An Entra ID (Azure AD) tenant with an active subscription
- A Ditio company already registered
- Your Azure Tenant ID sent to Ditio — we need it to issue the token for your tenant
SCIM endpoint and token
Section titled “SCIM endpoint and token”The SCIM endpoint (the “Tenant URL” in Azure’s provisioning setup) is
https://integration.ditio.no/scim in production and https://core-api.ditio.dev/scim in test; see
Environments. Existing setups that use the
endpoint URL Ditio sent earlier keep working and need no change.
Send your Azure Tenant ID to [email protected]. Ditio issues a token for your tenant and sends it to you separately. You paste it into Azure as the Secret Token; there is nothing else to fetch or exchange.
Use the test endpoint to verify attribute mappings against test data before provisioning into production.
Setup in Azure
Section titled “Setup in Azure”- Sign in to the Azure Portal
- Go to Enterprise Applications → select your Ditio application
- Go to the Provisioning configuration page
- Set Provisioning Mode to Automatic
- Under Admin Credentials, enter the SCIM endpoint URL above as the Tenant URL and the token Ditio sent you as the Secret Token
For detailed instructions, see Microsoft’s guide: SCIM provisioning with Entra ID.
Attribute mapping
Section titled “Attribute mapping”Ensure the following attributes are mapped correctly in Entra ID.
Required attributes
Section titled “Required attributes”A user is created only when all three of these are present. If one is missing, provisioning fails for that user and the Azure provisioning log shows the error.
| Entra ID attribute | SCIM attribute | Description |
|---|---|---|
| userPrincipalName | userName | Email address |
| displayName (plus givenName, surname) | displayName (plus name.givenName, name.familyName) | The user’s name. Always map displayName: later updates take the name from it. A new user gets name.givenName + name.familyName when both are sent; otherwise displayName is split into first and last name at the first space |
| telephoneNumber, mobile | phoneNumbers[type eq "work"].value, phoneNumbers[type eq "mobile"].value | At least one phone number, which must be a valid phone number. See Phone numbers |
Also map active (Entra ID: Switch([IsSoftDeleted], ...)). It isn’t
required to create a user, but it is what enables and disables the user in
Ditio when they leave.
Phone numbers
Section titled “Phone numbers”Ditio reads the numbers by their primary flag, not by their type:
- The number marked
primarybecomes the user’s work mobile. - The first number not marked
primarybecomes the private mobile. - The work mobile is the user’s sign-in phone number in Ditio. If there is no primary number, the private mobile is used instead.
Optional attributes
Section titled “Optional attributes”| Entra ID attribute | SCIM attribute | Description |
|---|---|---|
| employeeId | urn:...:enterprise:2.0:User:employeeNumber | Employee number. Must be unique within the company when set |
| Extension attribute | urn:...:ditio:1.0:User:birthDate | Date of birth. When it is missing or not in a supported format, a new user gets the 1st of the current month as birth date |
| Extension attribute | urn:...:ditio:1.0:User:employmentStartDate | Employment start date. When missing, Ditio keeps the start date of the user’s current employment, or uses the 1st of the current month for a new user |
| jobTitle | title | Work title |
| department | urn:...:enterprise:2.0:User:department | Department on the employment |
| Extension attribute | urn:...:enterprise:2.0:User:costCenter | Sets “Main project number” on the employment |
| Extension attribute | urn:...:ditio:1.0:User:employmentEndDate | Employment end date (terminates the employment) |
| Extension attribute | urn:...:ditio:1.0:User:builderCard | Builder card ID |
| Extension attribute | urn:...:ditio:1.0:User:builderCardExpirationDate | Builder card expiration date |
| Extension attribute | urn:...:ditio:1.0:User:payrollConnected | true = hourly paid, false = fixed salary |
| Extension attribute | urn:...:ditio:1.0:User:workTimeArrangement | Work time arrangement table |
| Extension attribute | urn:...:enterprise:2.0:User:organization | Organization number (which Ditio company to create the user in) |
| Extension attribute | urn:...:ditio:1.0:User:tags | Assigns and removes tags, e.g. "Skilled worker:true,Apprentice:false". See Syncing tags |
| manager | urn:...:enterprise:2.0:User:manager.value | Sets the user’s nearest manager. The value must be the Entra Object ID of an existing Ditio user; the resolved user becomes the primary immediate supervisor. Existing non-primary supervisors are preserved. Removing the attribute does not clear an existing supervisor. |
Supported date formats
Section titled “Supported date formats”dd.MM.yyyy(e.g.15.01.2026)ddMMyyyy(e.g.15012026)yyyy-MM-dd(e.g.2026-01-15)
Syncing tags
Section titled “Syncing tags”The tags attribute assigns and removes Ditio tags on the user. Map it as a
single string attribute, not as a multi-valued one. The value is a
comma-separated list of TagName:true / TagName:false pairs:
Skilled worker:true,Apprentice:false,Safety course:true| Value | Effect |
|---|---|
TagName:true | Assigns the tag. No effect if the user already has an assignment of that tag, including one that has expired or has not started yet |
TagName:false | Removes every assignment of that tag from the user |
TagName or TagName: (no value) | Assigns the tag |
- Only the tags you list are changed. Tags that are not listed are left as they
are, and clearing or unmapping the attribute removes nothing — a tag stays
until you send it as
TagName:false. - Names are matched against the tags that already exist in the company, ignoring
case and spaces, so
Safety courseandsafetycourseare the same tag. - Only
true, in any casing, assigns. Every other value, including1, counts asfalse. - Avoid
:in tag names — it separates the name from the value. - Empty entries are ignored, so a trailing comma or a
,,from a sparse mapping expression costs you nothing.
Payroll tags
Section titled “Payroll tags”Payroll tags are assigned and removed through the same attribute. What SCIM cannot do is create one: on an assignment, if the name matches no existing tag it creates an ordinary tag instead, and the payroll effect silently never happens. Create the payroll tag in Ditio first, then map it.
Four constraints are worth knowing before you map one:
- The tag must exist in the company the user is provisioned into. The
organizationattribute routes each user into the company holding that organization number — your main company or one of its subsidiaries. A tag on the main company is not matched for a user in a subsidiary, so create the payroll tag in every company you provision into. - Assigning one does not trigger a recalculation. A tag assigned over SCIM carries no dates, so Ditio queues no recalculation of days that are already calculated, and the tag takes effect for days calculated from then on. Because it has no start date, an older day recalculated later for some other reason will pick the tag up as well. Assign the tag in Ditio, or through Employees API v5 with a from-date, when earlier days must be recalculated straight away.
TagName:falseremoves every assignment of that tag, including date-ranged assignments created through Employees API v5. Removing a date-ranged payroll assignment does queue a recalculation of the days it covered — up to 60 days back.- Do not manage the same tag from both surfaces.
PUT /api/v5/integration/employees/{employeeNumber}/tagsreplaces the employee’s whole tag list and removes SCIM-assigned tags it omits.
Tags assigned through SCIM carry no validity period. Use the Employees API v5 if you need a payroll tag with a from/to date.
Adding extension attributes in Entra ID
Section titled “Adding extension attributes in Entra ID”Extension attributes (for fields like birthDate and employmentStartDate)
can be added in two ways.
Option 1: use existing extension attributes
Set-ADUser -Identity "anyUser" -Add @{extensionAttribute4="myString"}Option 2: create custom extension attributes
See Microsoft’s guides:
Setting extension values:
Set-AzureADUserExtension ` -ObjectId {objectId} ` -ExtensionName extension_{your_extension_id}_BuilderCardId ` -ExtensionValue "Builder Card Value"Reading all user properties including extensions:
(Get-AzureADUser -ObjectId {objectId}).ToJson()Testing
Section titled “Testing”Test with individual users before running a full provisioning cycle:
- Go to Enterprise Applications → your Ditio app → Users and Groups
- Add a few test users
- Use Azure’s on-demand provisioning to provision them manually
- Verify the users appear correctly in Ditio
- Once confirmed, enable automatic provisioning for all users
Common issues
Section titled “Common issues”| Issue | Cause | Fix |
|---|---|---|
| Provisioning fails for a user | A required attribute is missing (userName, a name, or a phone number), or the phone number isn’t valid | Check the provisioning log in Azure; fill the attribute in Entra ID |
| Users get a placeholder birth date or start date | birthDate or employmentStartDate isn’t mapped, or isn’t in a supported format | Map the attribute and send one of the supported formats |
| Wrong number used to sign in | The sign-in number is the one marked primary, not the one with type work | See Phone numbers |
| Duplicate employee number | employeeId already in use in the Ditio company | Employee numbers must be unique within a company — correct the conflicting value |
| User lands in the wrong company | organization attribute doesn’t match a Ditio company organization number | Verify the organization number mapping with Ditio support |
| Nothing happens after setup | Provisioning cycle hasn’t run yet | Azure runs cycles roughly every 40 minutes; use on-demand provisioning to test immediately |
| A payroll tag has no effect | The name didn’t match an existing payroll tag (an ordinary tag was created instead); or the tag exists on the main company but the user was provisioned into a subsidiary; or the affected days were already calculated | Create the payroll tag in the company the user lands in, check the name in the mapping, and re-check days that were already calculated |
Alternatives
Section titled “Alternatives”- If your HR system (not Entra ID) is the master, use the Employees API v5 instead
- For sign-in (not provisioning) with Microsoft accounts, see Azure AD SSO
Related
Section titled “Related”- Azure AD SSO — single sign-on setup
- Employees API v5 — API-based employee management
- Users API (v4) — legacy user endpoints
- SCIM specification