Browse docs
SCIM Provisioning

Let your identity provider manage Frontelio accounts.

Point Okta or Microsoft Entra (Azure AD) at Frontelio over SCIM 2.0 and it creates, updates, and deactivates Frontelio accounts as people join and leave, and keeps their roles in step with your directory groups.

SCIM (System for Cross-domain Identity Management) is the standard your IdP already speaks to provision SaaS apps. Frontelio exposes a SCIM 2.0 endpoint your IdP calls directly — this page is for the person configuring that connection, not for building against the REST API (see the API Reference for that).

What SCIM manages

Your identity provider decides who has a Frontelio account, whether it is switched on, and which mapped roles the person holds. It does not decide where someone works or what their job is.

Set by your IdP

  • Creating the account, with the person's name, email address and, optionally, mobile number.
  • Renaming the person and changing their email address or mobile number.
  • Switching the account on and off — see Deactivating and deleting.
  • Roles, through groups you map to Frontelio roles — see Groups and roles.

Still set in Frontelio

  • Outlet. A new account gets the baseline Staff role at your company's earliest-created outlet. Move people to their real outlet under Staff.
  • Department, manager, job title and employee code. Your IdP may send these (title, and the enterprise extension's department, manager and employee number). Frontelio accepts them without error and ignores them.
  • Roles that are not mapped to a group, and custom roles. Only Frontelio's built-in roles can be mapped.

SCIM-created accounts have no password, and Frontelio does not send an invitation email. People sign in through your company's single sign-on, or set a password with the Forgot password option on the sign-in page.

Base URL

Give your IdP this as the SCIM base URL / tenant URL:

SCIM Base URL
https://api.frontelio.com/api/v1/scim/v2

Minting a SCIM token

A SCIM token is the same thing as a public-API key — just minted with the scim:read and scim:write scopes instead of the REST-resource ones. Go to Settings → Developer in the app:

  1. In the API keys card, click Create key. If you see a "Turn on the public developer API" prompt instead, flip that toggle first. It switches on the public developer API for your whole company, not only SCIM, and it needs to stay on: while it is off, the key list and the Revoke button are unavailable, so you could not revoke a lost SCIM key from the app. SCIM itself does not depend on the toggle, and a SCIM key keeps working while it is off. While it is on, a SCIM key can also read your outlet and employee lists through the public API, so keep the token in your IdP and nowhere else.
  2. Give it a name that identifies the IdP, e.g. "Okta SCIM" or "Entra SCIM".
  3. Select both the scim:read and scim:write scopes. With scim:read alone the IdP's connection test passes, but every create, update and delete is refused with a 403.
  4. Copy the key immediately — like every API key, the full value is shown once. If you lose it, revoke it and mint a new one, then paste the new value into your IdP. Keys created here do not expire, and revoking a key stops the IdP at once.

Paste that value into your IdP's SCIM bearer token field — the same field you'd use for any other SCIM-provisioned app. Frontelio reads the key from an Authorization: Bearer <key> header (an X-API-Key header also works). Some IdP fields send exactly what you type as the header value instead of adding Bearer themselves; if the connection test returns 401, enter the word Bearer, a space, then the key.

Okta setup

  1. In the Okta Admin Console, go to Applications → Browse App Catalog and add a SCIM 2.0 Test App (Header Auth) (or your Frontelio app integration, if you already have one).
  2. Under the app's Provisioning tab, click Configure API Integration and check Enable API integration.
  3. Set the Base URL to https://api.frontelio.com/api/v1/scim/v2 and the API Token to the SCIM key you minted above.
  4. Click Test API Credentials — Okta calls the SCIM endpoint to confirm the token authenticates before saving.
  5. Under Provisioning → To App, enable Create Users, Update User Attributes, and Deactivate Users. Deactivate Users is what switches a Frontelio account off when someone is unassigned or deactivated in Okta.
  6. Assign the people who should have Frontelio accounts, directly or through groups. Assigning a group to the app provisions its people; it does not send the group itself to Frontelio.
  7. To grant roles, add the groups that should map to Frontelio roles on the Push Groups tab. Frontelio learns about a group, and changes roles, only from a pushed group. Okta does not support using the same group for app assignment and Group Push, so keep a separate group for each job. Each pushed group's display name is what you map to a Frontelio role (see Map first, then push).

Microsoft Entra (Azure AD) setup

  1. In the Entra admin center, go to Enterprise applications and create a new application (or open your existing Frontelio one), then open its Provisioning blade.
  2. Set Provisioning Mode to Automatic.
  3. Under Admin Credentials, set the Tenant URL to https://api.frontelio.com/api/v1/scim/v2 and the Secret Token to the SCIM key you minted above.
  4. Click Test Connection — Entra confirms it can authenticate against the SCIM endpoint before you save.
  5. Under Mappings, keep both the users mapping and the groups mapping enabled: roles reach Frontelio only through groups. In the users mapping, make sure userName is the attribute Entra matches on (its Matching property), not externalId, and that it carries the person's email address. Groups are matched on displayName, which is Entra's default. See How accounts are matched.
  6. Under Settings, set Scope to "Sync only assigned users and groups", then assign the people and groups you want provisioned. Entra provisions the direct members of an assigned group, not the members of groups nested inside it. Turn Provisioning Status to On to start the sync.

How accounts are matched

Frontelio reads these attributes from a User. It accepts anything else your IdP sends without error and ignores it — including externalId, title, groups, roles and the enterprise extension. A User never carries a role into Frontelio; roles arrive only through groups.

AttributeWhat Frontelio does
userNameRequired to create an account. Used as the email address when it contains an @ and no email is supplied. Also the only attribute an IdP can look an account up by.
emailsThe primary entry, or the first, becomes the account email address if it contains an @.
phoneNumbersThe primary entry, or the first, becomes the mobile number, but only in international format: a + and 7 to 15 digits, ignoring spaces, hyphens and parentheses. A local number such as 055 566 6037 is dropped.
nameThe full name: name.formatted, else givenName and familyName, else displayName, else userName.
displayNameUsed for the full name when name gives none.
activetrue makes the account Active, false makes it Inactive. See Deactivating and deleting below.

An account needs an email address or a valid mobile number. A create request with neither is refused with a 400 (scimType invalidValue). This is what a create looks like, and what Frontelio answers:

POST /Users — request
{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "userName": "jane.doe@example.com",
  "name": { "givenName": "Jane", "familyName": "Doe" },
  "emails": [{ "value": "jane.doe@example.com", "primary": true }],
  "phoneNumbers": [{ "value": "+971 55 566 6037", "primary": true }],
  "externalId": "00u1abcd2efgh3ijk4l5",
  "title": "Barista",
  "active": true
}
201 Created — response
{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "id": "3f6c2a3e-8d41-4c57-9b0e-5a7d1e2c9f10",
  "userName": "jane.doe@example.com",
  "name": { "formatted": "Jane Doe" },
  "displayName": "Jane Doe",
  "emails": [{ "value": "jane.doe@example.com", "primary": true }],
  "phoneNumbers": [{ "value": "+971555666037", "primary": true }],
  "active": true,
  "meta": {
    "resourceType": "User",
    "created": "2026-09-20T08:15:00.000Z",
    "lastModified": "2026-09-20T08:15:00.000Z",
    "location": "/scim/v2/Users/3f6c2a3e-8d41-4c57-9b0e-5a7d1e2c9f10"
  }
}

The mobile number came back without spaces, and externalId and title are gone: they were ignored.

GET /Users lists every account that has not been deleted. That includes a system account named "API Integration", which Frontelio creates the first time an API key with a write scope is minted (a SCIM key counts). Do not assign, change or delete it.

Groups and roles

What Frontelio does with a group is controlled entirely by your company's group mappings, configured in Settings → Developer underneath the SCIM section. Only groups your IdP actually sends reach Frontelio: an Okta group pushed with Group Push, or an Entra group that is assigned to the app with the groups mapping enabled.

  • A mapping is a pair: an IdP group display name (must match exactly what your IdP sends, capitals included) → a built-in Frontelio role.
  • When a person is in a mapped group, they get that role in Frontelio, and lose it when they leave the group — see Who owns a mapped role.
  • A group with no mapping is accepted, so you can assign your Frontelio app to broad groups such as "All Employees" without every group needing a Frontelio-side meaning. Frontelio records it as a row marked Unmapped in Settings → Developer and never changes a role because of it. Accounts come from user provisioning, so an unmapped group never affects who has one.
  • Adding a mapping never changes anyone's role by itself. Roles change when your IdP next sends membership for the group.
  • Deleting a mapping (in Settings → Developer, or when your IdP deletes the group) removes only the mapping. Roles people already hold stay.

To add a mapping: open Settings → Developer, find the group name exactly as it appears in your IdP (Okta: Directory → Groups; Entra: Groups in the admin center), click Add mapping, and choose the Frontelio role it should grant.

Map first, then push

The reliable order is to create the mapping in Frontelio and then assign or push the group from your IdP. Any membership your IdP sends for a mapped group applies its role straight away.

If your IdP has already pushed the group, that works too. Frontelio holds the group as an Unmapped row; click Add mapping and enter the same name. Frontelio fills that row in and keeps its id, so your IdP's link to the group stays valid. Do not delete the Unmapped row first: its id goes with it, your IdP's stored copy of the group stops matching anything in Frontelio, and the membership changes it sends for that copy are accepted and ignored until the group is created again.

People who were already in the group get the role only when your IdP next sends the group's complete membership, for example after a manual re-push (Okta: Push Now on the Push Groups tab; Entra: Provision on demand for the group, or restart provisioning). Joiners and leavers after that are picked up as they happen. Frontelio does not read your IdP's member lists on its own.

Who owns a mapped role

Once a group is mapped, your IdP is the source of truth for who holds that role.

  • Adding or removing a member (a SCIM PATCH) grants or removes the role for that person only.
  • When your IdP sends a group's complete member list — by creating the group with a members array, or by replacing it with a PUT — Frontelio makes the role's holders match that list exactly. Anyone who holds the role and is not on the list loses it, including people you assigned by hand, and an empty list removes the role from everyone.
  • A request with no members array, such as a rename, does not touch roles.
  • GET /Groups reports every current holder of the role as a member of the group, whether or not your IdP put them there.
  • Members Frontelio has no account for are skipped without an error; the rest of the list still applies.

Deactivating and deleting

What happens when someone leaves depends on what your IdP sends.

  • Deactivate (active: false) is what Okta's Deactivate Users and an Entra disable or unassign send. The account becomes Inactive; it is not deleted. The person can no longer sign in, by password or by single sign-on, and a session already open stops working. Their door-access grants are revoked, and the audit log records the change as made by SCIM rather than by a person. Frontelio sends no webhook for it.
  • Reactivate (active: true) turns the account back on and restores sign-in. Door-access grants revoked at deactivation are not restored; issue them again. Because the IdP is the authority on active, an update that carries active: true also makes an account Active again if an administrator put it On leave or Suspended in Frontelio.
  • An account that is On leave or Suspended already reports active: false to your IdP, so a deactivate request leaves it exactly as it is.
  • Delete (SCIM DELETE) soft-deletes the account and answers 204. The account is Inactive and disappears from SCIM: later GET, PUT, PATCH and DELETE return 404 and lookups find nothing. The email address stays reserved, so creating the same address again is refused with a 409 (scimType uniqueness). A deleted person cannot be re-provisioned through SCIM, so use Deactivate rather than Delete unless you are sure they will not return. This is the only SCIM action that sends the user.terminated webhook.
  • The last owner or admin is protected. Frontelio refuses to deactivate or delete the last active TENANT_OWNER or COMPANY_ADMIN. The request fails with a 409 (scimType mutability), the account stays Active, and your IdP shows an error until the role is assigned to someone else.

Supported operations

Every route sits beneath the base URL and takes the SCIM key as a bearer token. Bodies use application/scim+json (plain application/json is accepted too), and errors come back in the standard SCIM error format.

MethodPathAuthDescription
GET/ServiceProviderConfigSCIM keyWhat this server supports. PATCH and filtering: yes. Bulk, sorting, ETags and password change: no.
GET/ResourceTypesSCIM keyThe two resource types, User and Group.
GET/SchemasSCIM keyThe attributes Frontelio reads for each.
GET/UsersSCIM keyList accounts that have not been deleted. Supports filter (userName eq only), startIndex and count.
GET/Users/{id}SCIM keyOne account.
POST/UsersSCIM keyCreate an account. Answers 201.
PUT/Users/{id}SCIM keyUpdate: applies the name, email, mobile number and active the body carries.
PATCH/Users/{id}SCIM keyChange name, email, mobile number or active.
DELETE/Users/{id}SCIM keySoft-delete the account. Answers 204.
GET/GroupsSCIM keyList groups. Supports filter (displayName eq only), excludedAttributes=members, startIndex and count.
GET/Groups/{id}SCIM keyOne group. Supports excludedAttributes=members.
POST/GroupsSCIM keyCreate a group. Answers 201. Adds an Unmapped row unless a mapping with that name already exists.
PUT/Groups/{id}SCIM keyRename the group and, for a mapped group whose body has a members array, set its complete member list.
PATCH/Groups/{id}SCIM keyRename the group, or add and remove members.
DELETE/Groups/{id}SCIM keyDelete the mapping only. Answers 204. Roles people hold stay.
  • Filters. Users: only userName eq "<value>". Groups: only displayName eq "<value>". The attribute name and eq are not case-sensitive; the value is matched exactly. Anything else returns 400 with scimType invalidFilter rather than an unfiltered list.
  • Paging. startIndex starts at 1. count defaults to 50 and is capped at 200.
  • PATCH on users. replace and add work on active, name, displayName, emails, phoneNumbers and userName. An operation with no path (Okta style) applies the active, name and email it carries. remove operations, filtered paths and other attributes are accepted and ignored.
  • PATCH on groups. replace on displayName, and add or remove on members, in both Okta's value-list form and Entra's members[value eq "<id>"] path form.
  • Not supported. Bulk operations, sorting, ETags and password changes.

Rate limits

SCIM routes have their own limits, separate from the general API limit. Each limit applies per route and per source IP address, not per company or per key:

  • Reads (GET): 60 requests per minute.
  • Writes (POST, PUT, PATCH, DELETE): 10 requests per minute.

Each write route has its own allowance, so creating accounts is capped at 10 a minute: a first sync that creates 500 accounts takes about 50 minutes. Over the limit, Frontelio answers 429 in the SCIM error format with a Retry-After header giving the seconds to wait. Your IdP should wait and retry, so a large first sync is slow rather than failed. Requests that fail authentication count towards the limit, and other SCIM traffic from the same IP address shares it.

Confirming it worked

Both IdPs have a built-in connection test (Okta's Test API Credentials, Entra's Test Connection) — run that first. Then assign a test person to the app: Okta provisions on assignment; in Entra use Provision on demand in the Provisioning blade, or wait for the next cycle (about 40 minutes). Confirm the account appears under Staff in Frontelio with the Staff role.

To test a role, map a test group in Settings → Developer first, then push it (Okta: add it on the Push Groups tab and use Push Now; Entra: assign the group, then Provision on demand) and check the person's roles.

Troubleshooting

What your IdP reportsWhat it usually means
401 UnauthorizedThe key is missing, mistyped, revoked or expired. If your IdP sends the token field as the whole header value, enter Bearer and a space before the key.
403 missing the required scopeThe key was not minted with both scim:read and scim:write.
403 plan does not include the required featureThe company is not on the Enterprise plan.
404 on every requestThe base URL is missing /api/v1.
400 invalidFilterThe IdP sent a filter Frontelio does not support. See Supported operations.
400 invalidValueuserName is missing, or the account has neither an email address nor a valid mobile number.
409 uniquenessAn account with that email address or mobile number already exists. Usually the IdP's userName and email differ, or the person was deleted earlier.
409 mutabilityOn create: the plan's active-user limit is reached. On deactivate or delete: the account is the last active owner or admin.
403 on an update, deactivate or deleteThe account belongs to someone with more authority than the person who created the key, for example an admin's key touching an owner. Use a key created by an owner, or change that account in Frontelio.
403 on a group membership changeThe person who created the key may no longer assign or remove the role the group is mapped to (demoted, deactivated or deleted), and nothing in the request was applied. Create a new key while signed in as someone who may grant the role, normally an owner.
400 Owner-tier and group-tier roles cannot be mapped to a SCIM groupThe mapping names an owner or group role. Those are granted in Frontelio, never through a group.
429 Too Many RequestsA rate limit was hit. The IdP should wait for the Retry-After delay and retry.

See also