SCIM Integration Explained: How It Works and Why It Matters. A SCIM integration lets Okta or Entra ID create, update and deactivate accounts in your app over SCIM 2.0: who builds what, the real requests, and Keycloak 26.8.
A SCIM integration connects an identity provider such as Okta or Entra ID to an application over the SCIM 2.0 REST API, so that accounts are created, updated and deactivated automatically when people join, move or leave. The identity provider is the client, and the application exposes the /Users and /Groups endpoints that RFC 7644 defines, behind an authentication scheme the two sides agree on, such as an OAuth bearer token. Keycloak 26.8 ships this API as supported and on by default, switched on per realm.
SCIM stands for System for Cross-domain Identity Management. Its user and group data model is RFC 7643, the HTTP protocol that carries it is RFC 7644, and RFC 7642 section 3, “SCIM Use Cases”, lists the use cases behind both. If an enterprise customer or their Okta or Entra ID admin has asked whether your product “supports SCIM”, they are asking whether their directory can manage accounts in your app without anyone creating or removing them by hand. In this post we look at who builds which side of that integration, the requests that flow between the two, how Okta and Entra ID differ in what they send, and how to stand up Keycloak as a working SCIM service.
Key Takeaways
- A SCIM integration has two sides: the identity provider is the SCIM client, and your application (or Keycloak in front of it) is the SCIM service provider, as RFC 7643 section 1.2 defines the roles.
- In Okta’s documented flow, the connector checks whether the user exists with a filter on its unique identifier (
userNamein Okta’s example), creates the user withPOST /Users, and later sendsPUTorPATCHupdates, including the deactivation that setsactiveto false.- Okta’s SCIM 2.0 guide and Microsoft’s SCIM endpoint tutorial show different request bodies for the same deactivation, so test against each identity provider you intend to support.
- Keycloak 26.8 documents a per-realm SCIM API toggle (source) and a service account client with the
manage-usersrole (source) for calling the API.
What is a SCIM integration?
A SCIM integration is a standing connection from an identity provider to an application that keeps the application’s accounts in step with the directory. When an admin assigns someone to the app, the identity provider creates the account; when their name, email or team changes, it updates the account; and when they leave or are unassigned, it deactivates the account. Okta’s SCIM overview describes SCIM as “a defined schema for representing users and groups, and a RESTful API to run CRUD operations on those user and group resources”, where CRUD means create, read, update and delete.
The integration only moves account records, so it runs alongside your sign-in connection rather than replacing it. Single sign-on (SSO) with SAML or OpenID Connect (OIDC) answers “who is this person logging in right now”, while SCIM answers “which accounts should exist, with which attributes and groups”. SCIM lets the directory change an account even when the person never signs in again, which is exactly the offboarding case. Microsoft’s provisioning overview explains how provisioning complements SSO. See also SCIM vs SAML: What Each Does and When You Need Both and the SSO Implementation Guide: SAML and OIDC with Keycloak.
How is SCIM different from JIT provisioning?
Just-in-time (JIT) provisioning creates a user at first login, as our JIT versus SCIM comparison explains. That post also describes why a group change otherwise waits for the next login, and why removing users needs the directory to push changes, “which is the part JIT cannot do”. A SCIM integration is that push. Its timing is set by the identity provider: Microsoft’s tutorial, for example, says of Entra ID’s provisioning that “The process runs every 40 minutes”, according to Microsoft’s getting started steps. See also JIT Provisioning vs SCIM: When Is First Login Enough? and User Provisioning: How Accounts Get Created and Removed.
Who builds what in a SCIM integration?
RFC 7643 section 1.2 names two roles. The service provider is “an HTTP web application that provides identity information via the SCIM protocol”, and the client is “a website or application that uses the SCIM protocol to manage identity data maintained by the service provider”, which “initiates SCIM HTTP requests”. Okta’s overview puts it the same way: provisioning is “a set of actions between a SCIM client (like Okta) and the SCIM server (such as your cloud-based integration or service provider)”, according to Okta’s SCIM concepts page.
| Side | Who owns it | What they build or configure |
|---|---|---|
| SCIM client | The customer’s identity provider (such as Okta or Entra ID) and its admin | Assigns users and groups to your app, maps directory attributes to SCIM attributes, and holds the endpoint URL and credentials |
| Service provider | You, the application vendor | A /scim/v2 base URL with /Users and /Groups, authentication for the client, and the rules for what create, update and deactivate mean in your app |
| Sign-in | Both, through SAML or OIDC | The SSO connection that lets the provisioned accounts log in |
So when a customer asks for SCIM, the work on your side is the service provider: an HTTPS endpoint that accepts application/scim+json, answers searches and creates, applies PATCH operations and turns active: false into a real lockout. The customer’s admin does the client side in their identity provider’s console. If you would rather not write that endpoint yourself, you can put Keycloak in front of your app as the service provider, which the second half of this post sets up, and your app keeps reading users through OIDC or SAML.
| Component | Role when Keycloak is the service provider |
|---|---|
| Entra ID or Okta | The SCIM client. It decides which assigned users and groups to provision and sends the requests. |
| Keycloak | The SCIM service provider. It receives the requests and maintains the realm’s users and groups. |
| Your application | Authenticates against Keycloak with OIDC or SAML, as in our SSO implementation guide, and applies its own authorization to those identities. |
The Keycloak SCIM introduction (source) describes external systems such as identity governance platforms, HR systems or other identity providers using the API to provision and manage users in Keycloak. That is the inbound direction: Entra ID or Okta sends requests to Keycloak. Our Keycloak 26.8 SCIM post has a section on whether Keycloak SCIM does outbound provisioning in 26.8.
How do the requests of a SCIM integration flow?
A provisioning connector looks for an existing account, creates it when it finds none, and from then on updates that resource by the id your service assigned. Okta’s SCIM 2.0 protocol guide describes its lookup, creation and update requests, while Microsoft’s provisioning overview describes account creation, maintenance and removal. The examples below use fictional identities and short illustrative IDs, and omit the Authorization header for readability. Real requests carry credentials over HTTPS, an OAuth bearer token in the setups below. RFC 7644 section 2 does not define a SCIM-specific authentication scheme and lists bearer tokens alongside other methods, such as TLS client authentication.
The identity provider searches your app for the user by userName and gets an empty list (1), creates the account with POST /Users and keeps the returned id (2), sends PATCH requests to the user and its groups when the person changes teams (3), and sends PATCH with active set to false when they leave, which your app must turn into a blocked account (4).
A joiner triggers a lookup and account creation
Before creating an account, the connector searches on its matching attribute. Here we use a URL-encoded userName filter. RFC 7644 section 3.4.2 says that “a query that does not return any matches SHALL return success (HTTP status code 200) with “totalResults” set to a value of 0”, so an empty search is a list with no resources:
GET /scim/v2/Users?filter=userName%20eq%20%22alex%40example.com%22 HTTP/1.1
Host: app.example.com
Accept: application/scim+json
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults": 0,
"Resources": []
}
Because nothing matched, the connector creates the user with the core user schema:
POST /scim/v2/Users HTTP/1.1
Host: app.example.com
Content-Type: application/scim+json
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "alex@example.com",
"name": {"givenName": "Alex", "familyName": "Rivera"},
"active": true,
"emails": [{"value": "alex@example.com", "type": "work", "primary": true}]
}
The service provider assigns an id and returns it in the creation response with status 201 Created. The connector keeps that identifier for later updates: RFC 7643 section 3.1 says id “MUST be a stable, non-reassignable identifier”, so matching by ID avoids depending on an email address that may change. The same section says the value of externalId “is always issued by the provisioning client” and that the attribute is OPTIONAL.
A mover triggers attribute and membership updates
When Alex changes teams, the connector updates whichever profile attributes changed and adjusts group membership. A partial update changes only the named attribute without replacing the whole resource, following RFC 7644’s PATCH operation:
PATCH /scim/v2/Users/user-alex HTTP/1.1
Host: app.example.com
Content-Type: application/scim+json
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{"op": "replace", "path": "displayName", "value": "Alex Rivera (Platform)"}
]
}
For group membership, the connector can update the group’s members attribute with an add operation, as in RFC 7644’s PATCH add example, which adds a member to a group through the path members:
PATCH /scim/v2/Groups/group-platform HTTP/1.1
Host: app.example.com
Content-Type: application/scim+json
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{"op": "add", "path": "members", "value": [{"value": "user-alex"}]}
]
}
The old membership can be removed with a remove operation and a path such as members[value eq "user-alex"], the form RFC 7644’s removal example uses for a group member. Test both changes, so that a team move does not leave the old permissions behind.
A leaver triggers deactivation
Both Okta and Entra ID document deactivation through active: false. Microsoft’s endpoint requirements define soft deletion through active: false, with inactive users still returned by reads, and the RFC-style request looks like this:
PATCH /scim/v2/Users/user-alex HTTP/1.1
Host: app.example.com
Content-Type: application/scim+json
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{"op": "replace", "path": "active", "value": false}
]
}
The request changes the stored account. Test both a fresh login attempt and an application session that was already open, so you know how your application treats each. Include existing sessions and already-issued tokens in your offboarding tests, and read the protocol’s security considerations when you decide your application’s policy. See also SCIM Authentication: How to Harden Provisioning Endpoints.
What do Okta and Entra ID actually send?
The two identity providers’ documentation shows different request bodies for the same change, so test an endpoint against each one you intend to support. The differences below come from Okta’s SCIM 2.0 guide and Microsoft’s SCIM endpoint tutorial, as of November 2026.
| Behavior | Okta | Entra ID |
|---|---|---|
| Lookup before create | A filter on the integration’s unique identifier, such as filter=userName eq "..."; its example URL adds startIndex=1&count=100 |
“The attribute used for matching (which in this case is externalId) is configurable” in the attribute mappings, and by default users are “queried with their username and externalId” |
| Profile updates | PUT: “For any custom app integrations created using the App Integration Wizard (AIW), all updates to a user object are handled using a PUT method request”; new Okta Integration Network (OIN) integrations use PATCH for activating, deactivating and syncing the password, and PUT for all other updates |
PATCH: “Modify users or groups with PATCH requests” |
| Deactivation body | For new OIN integrations, {"op": "replace", "value": {"active": false}}, with no path (AIW integrations send a PUT) |
{"op": "Replace", "path": "active", "value": false}, with a capitalized op |
| User deletion | Never sends DELETE for users: “Okta doesn’t perform DELETE operations on user objects in your SCIM app” |
Documents DELETE /Users/{id} for deprovisioning, besides soft deletion with active: false |
| Group membership | PATCH with add and remove operations, or a replace that “replaces all the group members with the supplied object values”; AIW integrations use PUT /Groups/{groupID} |
PATCH with "op": "Add" or "op": "Remove" and path: members, expecting 204 No Content back |
| Bulk | Not mentioned in its SCIM 2.0 guide | “While we don’t support SCIM /Bulk today” |
For a new OIN integration, the Okta deactivation request looks like this, with the new value given as an object instead of a path:
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [{
"op": "replace",
"value": {"active": false}
}]
}
Microsoft’s “Disable User” example sends the same change with a path and a capitalized operation name, since “Microsoft Entra ID emits the values of op as Add, Replace, and Remove”:
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [{
"op": "Replace",
"path": "active",
"value": false
}]
}
The two bodies above differ in the case of op and in whether a path is given, so to accept both, an endpoint has to treat replace and Replace alike and accept a PATCH with no path whose value is an object of attributes. Okta also documents that “there’s no deprovisioning event sent for users that are suspended inside Okta”, in the Delete users section of its guide, so suspending a user in Okta does not by itself send your app a deactivation.
Authentication differs too. Microsoft’s authorization table for provisioning connectors in its section on authorization to provisioning connectors in the application gallery, marks the OAuth 2.0 client credentials grant and Workload Identity Federation “Supported for gallery apps and non-gallery apps”, and a long-lived bearer token “Supported for existing apps and non-gallery apps. Not supported for new apps.” Okta’s SCIM integration settings offer OAuth2 with a choice of the Client Credentials or Authorization Code grant. Because both list the client credentials grant, it is a reasonable option to plan for first, and then to confirm with each connector in testing.
Entra ID also does not provision the moment a change happens. Microsoft’s tutorial says “The initial cycle takes longer to perform than later syncs, which occur approximately every 40 minutes as long as the service is running”, according to Microsoft’s getting started steps. Measure offboarding from the directory change through to the application rather than treating the directory update as the finish line.
Identity management as a service, on open source
Skycloak is a managed identity platform in the same category as Auth0 or Okta, built on real upstream Keycloak. You get SSO, MFA, SCIM and audit logs without running the servers, and you can export and self-host whenever you want.
How do you set up a SCIM integration with Keycloak 26.8?
Keycloak 26.8.0 promotes the native SCIM API from preview to supported, according to the 26.8.0 release notes, and the 26.8.0 upgrading guide confirms the scim-api feature is enabled by default, so starting the server with --features=scim-api is no longer required. See also Keycloak Native SCIM API Preview in 26.7 and Keycloak 26.8 SCIM API: Supported and On by Default.
You need a realm on a 26.8 server, a hostname the identity provider can reach over HTTPS, admin access to the realm, and provisioning access in Entra ID or Okta. The configuration below uses the fictional hostname identity.example.com and realm workforce, so replace both with your own values wherever they appear, and use the realm’s SCIM base URL when configuring the audience mapper and the connector.
Enable the realm and create the service account
Enable each realm separately under Realm settings, General, SCIM API, then save. The realm configuration guide (source) documents both this toggle and the base path, and says “you must also enable SCIM for each realm individually”. For our example realm, the HTTPS base URL is:
https://identity.example.com/realms/workforce/scim/v2
The same realm configuration guide (source) lists /Users, /Groups, /ServiceProviderConfig, /ResourceTypes and /Schemas beneath that base URL.
Follow the endpoint access guide (source) to create a confidential client:
- Create
scim-provisionerunder Clients, with Client authentication and Service accounts roles enabled. - In its dedicated client scope, open Scope, turn Full Scope Allowed off and assign
realm-management’smanage-usersrole. - Assign
manage-usersunder Service account roles too, because, in the access guide’s words, “Roles included in the access token are the intersection of the client’s role scope mappings and the service account’s assigned roles. Both steps above are required.” (source) - Add an Audience mapper in that dedicated scope. Set Included Custom Audience to the full SCIM base URL and enable Add to access token, as the same guide describes.
The access guide (source) says “The minimum set of roles required for full access to the SCIM API is manage-users”. It also says the token’s audience “must match the SCIM API base URL exactly as seen by Keycloak, including the scheme, host, port, and path”, and that behind a reverse proxy you “use the frontend URL configured through the hostname settings”. Store the secret from Credentials in the connector’s protected field.
Configure token acquisition and check discovery
Use OAuth client credentials so the connector obtains bearer tokens without a user signing in. Microsoft and Okta document this option. The Keycloak token acquisition instructions (source) obtain the token with grant_type=client_credentials at the realm’s /protocol/openid-connect/token endpoint:
| Setting | Example value |
|---|---|
| SCIM base URL | https://identity.example.com/realms/workforce/scim/v2 |
| OAuth token endpoint | https://identity.example.com/realms/workforce/protocol/openid-connect/token |
| Client ID | scim-provisioner |
| Client secret | The value from the client’s Credentials tab |
| Grant type | client_credentials |
Before you assign anyone, fetch /ServiceProviderConfig with a token from that client. Keycloak’s 26.8.0 service provider configuration (source) says “The patch and filter capabilities are supported. List and search requests return at most 100 results per request”, and lists bulk, sort, etag and changePassword as not supported in this release. The same page says “The password attribute cannot be set or updated through SCIM.” Remove password synchronization from your connector’s mappings before the first sync, then confirm that the test user’s POST /Users succeeds and the account appears in the realm.
Connect Microsoft Entra ID
Follow Microsoft’s non-gallery app procedure and client credentials setup:
- Create a non-gallery enterprise application and open Provisioning.
- Under Admin Credentials, select OAuth2 Client Credentials Grant as the Authentication Method, then enter the Tenant URL, Token Endpoint, Client Identifier and Client Secret from the table above.
- Run Test Connection and save the configuration.
- Assign test users under Users and groups and scope provisioning to assigned users and groups. Review Attribute mapping, using
userNameas the user matching property. - Use Provision on-demand for the test user before starting routine provisioning.
See also SCIM Provisioning from Microsoft Entra ID to Keycloak 26.6+, and use the realm, service account and audience steps above for the Keycloak side.
Keep the initial mappings to attributes the realm supports. Keycloak’s default attribute mappings (source) map username, email, firstName, lastName, enabled and createdTimestamp to SCIM attributes, and locale “only when internationalization is enabled”. For additional attributes, create a User Profile attribute with a SCIM schema mapping, and note that, per the attribute permissions section (source), custom attributes “are only included in SCIM responses if they have admin view permissions” and “are only writable through SCIM if they have admin edit permissions”.
Connect Okta
On the Okta side, Okta’s SCIM integration settings describe selecting SCIM in the Provisioning field under the app’s General tab, then entering the SCIM connector base URL and the unique identifier field (userName here) under Provisioning, choosing OAuth2 with the Client Credentials grant type, and supplying the access token and authorization endpoints, client ID and secret. See also SCIM Integration Between Okta and Keycloak.
Under To App, Okta’s private integration guide describes the Create Users, Update User Attributes and Deactivate Users options. Enable those three, and leave Sync Password off because Keycloak’s service provider configuration (source) lists changePassword as not supported. The settings page also lists Push Groups, which you need if you want group resources and memberships synchronized rather than only app assignments. Assign a test user to the app, configure Group Push for a test group if needed, and inspect the resulting account and membership before expanding the assignment.
Where does a SCIM integration break?
When a sync misbehaves, compare the request, the response and the stored account. An empty successful search can hide a matching problem, so reproduce the connector’s exact filter against a known user.
| Symptom | What to check |
|---|---|
A fresh token gets 401 Unauthorized |
The endpoint access guide (source) says “Tokens without this audience are rejected with a 401 Unauthorized response.” Compare aud with the realm’s SCIM API base URL exactly as seen by Keycloak, including scheme, host, port and path. |
emails[type eq "work"] matches nothing |
The upstream report, opened on 26 June 2026 and open when retrieved on 4 November 2026, says “Filters such as emails[type eq "work"] and emails[primary eq true] return 0 results even when users exist with matching email entries.” Test userName or emails.value as the matching attribute instead. |
| Okta deactivation is rejected | Your endpoint probably expects a path. Okta’s guide sends replace with value: {"active": false} and no path. |
| Entra ID operations fail on a homegrown endpoint | Check whether it compares op case-sensitively: Microsoft’s examples send Replace, Add and Remove. |
The connector calls /Bulk |
Keycloak’s service provider configuration reports bulk.supported: false. Check that your connector can provision without Bulk operations. |
| An offboarded account still exists | Check its active value. Okta deactivates rather than deletes users, so the resource remaining is expected and your application has to honor active: false. |
The Keycloak filtering guide (source) gives examples such as userName eq "jdoe", emails.value eq "jdoe@example.com" and the bracket filter name[familyName eq "Smith"]. Test the specific attribute and filter your connector uses rather than assuming every bracket filter behaves the same way.
For the final acceptance test, assign one user, confirm the created account, change a mapped attribute, add and then remove a group membership, and unassign the user, checking the stored state after each step. Then repeat the run after the connector has had to fetch a fresh token, so you know renewal works too. If you are building the receiving API yourself rather than using Keycloak, see SCIM API: Building and Testing a SCIM 2.0 Endpoint.
Frequently asked questions
What is a SCIM integration in simple terms?
It is a connection that lets a company’s identity provider manage accounts in an application automatically. The identity provider sends standard SCIM 2.0 requests to the application’s /Users and /Groups endpoints to create, update and deactivate accounts, so nobody has to do it by hand when people join, change teams or leave.
What is the full form of SCIM?
SCIM stands for System for Cross-domain Identity Management. Its core schema specification standardizes how user and group records are represented so that different applications can exchange them, and RFC 7644 defines the HTTP protocol that carries them.
Does SCIM replace SAML or OIDC?
No. SCIM manages the account lifecycle, while SAML and OIDC handle authentication, so configure provisioning alongside your existing SSO connection. Our SCIM versus SAML comparison walks through what each protocol covers and where they meet.
Does SCIM provision users instantly?
Not always, because it depends on how the connector schedules its work. Microsoft says later Entra ID syncs “occur approximately every 40 minutes as long as the service is running”, after a longer initial cycle, according to Microsoft’s tutorial. Check your connector’s schedule before you promise an offboarding time.
Does Keycloak support SCIM provisioning?
Yes. The 26.8.0 release notes promote the SCIM API to supported, and the enabling SCIM page (source) gives each realm a base URL ending in /scim/v2, enabled per realm with the SCIM API toggle. External systems use it to provision and manage users, as its SCIM introduction (source) describes, and its service provider configuration (source) reports bulk operations, sorting, ETags and password changes as unsupported.
Is a SCIM group the same as a customer organization?
Do not assume they are interchangeable. The SCIM group schema defines a group with a display name and members, so decide separately how your application represents customers and isolates their access. If you are building a B2B product where each customer brings its own identity provider, see Federated Identity Management for B2B SaaS on Skycloak Organizations.
Test your SCIM integration before a customer connects
Before an enterprise customer points their identity provider at you, run the lifecycle above yourself. The free Skycloak SCIM Endpoint Tester builds SCIM provisioning requests as ready-to-run curl commands and checks pasted user or group JSON for SCIM 2.0 schema compliance, so you can send the Okta and Entra ID request shapes from this post to your endpoint and inspect the responses.
See also Using SCIM 2.0 with Skycloak (Managed Keycloak). If you would rather use Keycloak as the service provider than write your own endpoint, create a cluster as described in Creating the Cluster, which recommends the latest stable Keycloak version, and on a 26.8 server apply the realm and client setup above. The SCIM provisioning feature page lists SCIM 2.0 provisioning as included on all plans.
Sources
Version-specific Keycloak links refer to 26.8.0.
Retrieved on 4 November 2026.
- IETF, published September 2015 (per each RFC’s header): RFC 7642, Definitions, Overview, Concepts, and Requirements, RFC 7643, Core Schema and RFC 7644, Protocol.
- Keycloak, 26.8.0 Release Notes, 26.8.0 Upgrading Guide and 26.8.0 Server Administration Guide; the AsciiDoc sources of the SCIM chapter at the 26.8.0 tag (introduction, enabling, accessing endpoints, service provider configuration, attribute mapping, filtering); SCIM filtering and Bulk operations report, opened 26 June 2026 and open when retrieved.
- Microsoft, Automated app user provisioning and Develop and plan provisioning for a SCIM endpoint.
- Okta, SCIM concepts, Okta and SCIM Version 2.0, Add SCIM provisioning to app integrations and Add a private SCIM integration.




