Overview
The SCIM (System for Cross-domain Identity Management) 2.0 API provides standardized endpoints for automated user and group provisioning. SCIM enables identity providers and HR systems to synchronize user accounts, manage lifecycle events, and maintain group memberships automatically.
v2.8.0 implements SCIM 2.0 protocol (RFC 7643 , RFC 7644 ) with Keycloak backend integration.
Use Cases
Identity Provider (IdP) Provisioning : Okta, Azure AD, OneLogin
HR System Integration : Workday, BambooHR, SAP SuccessFactors
User Lifecycle Automation : Onboarding, offboarding, role changes
Group Management : Automatic team/department synchronization
Directory Synchronization : Keep user data in sync across systems
SCIM 2.0 Standards Compliance
Supported Features
Backend Integration
✅ Core User Schema (RFC 7643 §4.1 )
userName, name, emails, active, password
✅ Core Group Schema (RFC 7643 §4.2 )
✅ CRUD Operations (RFC 7644 )
Create (POST), Read (GET), Update (PUT/PATCH), Delete
✅ Filtering & Pagination
Basic filters (userName eq, email eq)
Pagination (startIndex, count)
✅ PATCH Operations (RFC 7644 §3.5.2 )
replace, add (partial support)
Base URL
https://api.yourdomain.com/scim/v2
Authentication
All SCIM endpoints require authentication:
Authorization: Bearer {token} (service account or admin user)
Requires SCIM provisioning permissions
SCIM endpoints should be called by identity providers or automation systems, not end users. Configure appropriate access controls.
User Endpoints
POST /Users
Create a new user (SCIM 2.0).
Provisions user in Keycloak and syncs roles to OpenFGA.
Request Body :
SCIM schema URIs (always ["urn:ietf:params:scim:schemas:core:2.0:User"])
Unique username or email address
User’s name:
givenName: First name
familyName: Last name
formatted: Full name
Email addresses (array of {value, primary} objects)
Whether user account is active
User’s initial password (optional, can be set later)
Request Example :
curl -X POST https://api.yourdomain.com/scim/v2/Users \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "alice@example.com",
"name": {
"givenName": "Alice",
"familyName": "Smith",
"formatted": "Alice Smith"
},
"emails": [{
"value": "alice@example.com",
"primary": true
}],
"active": true,
"password": "SecurePassword123!"
}'
``` python
``` python Python
import httpx
response = httpx.post (
"https://api.yourdomain.com/scim/v2/Users" ,
headers = {
"Authorization" : f"Bearer {token}",
"Content-Type" : "application/scim+json"
},
json = {
"schemas" : [ "urn:ietf:params:scim:schemas:core:2.0:User" ],
"userName" : "alice@example.com",
"name" : {
"givenName" : "Alice",
"familyName" : "Smith",
"formatted" : "Alice Smith"
},
"emails" : [{ "value" : "alice@example.com", "primary": True}],
"active" : True,
"password" : "SecurePassword123!"
}
)
user = response.json ()
print ( f "Created user: {user['id']}" )
Response :
{
"schemas" : [ "urn:ietf:params:scim:schemas:core:2.0:User" ],
"id" : "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" ,
"userName" : "alice@example.com" ,
"name" : {
"givenName" : "Alice" ,
"familyName" : "Smith" ,
"formatted" : "Alice Smith"
},
"emails" : [
{
"value" : "alice@example.com" ,
"primary" : true
}
],
"active" : true ,
"meta" : {
"resourceType" : "User" ,
"created" : "2025-10-29T10:00:00Z" ,
"lastModified" : "2025-10-29T10:00:00Z"
}
}
Status Codes :
User created successfully
Invalid SCIM schema or missing required fields {
"schemas" : [ "urn:ietf:params:scim:api:messages:2.0:Error" ],
"status" : 400 ,
"detail" : "Invalid SCIM user schema" ,
"scimType" : "invalidValue"
}
User already exists {
"schemas" : [ "urn:ietf:params:scim:api:messages:2.0:Error" ],
"status" : 409 ,
"detail" : "User with userName 'alice@example.com' already exists" ,
"scimType" : "uniqueness"
}
GET /Users/
Get user by ID (SCIM 2.0).
Returns user in SCIM format.
Path Parameters :
Request Example :
curl https://api.yourdomain.com/scim/v2/Users/f81d4fae-7dec-11d0-a765-00a0c91e6bf6 \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
Response : Same as POST /Users response
Status Codes :
User retrieved successfully
User not found {
"schemas" : [ "urn:ietf:params:scim:api:messages:2.0:Error" ],
"status" : 404 ,
"detail" : "User f81d4fae-7dec-11d0-a765-00a0c91e6bf6 not found" ,
"scimType" : "notFound"
}
PUT /Users/
Replace user (SCIM 2.0 PUT).
Replaces entire user resource. All fields must be provided.
Path Parameters :
Request Body : Same as POST /Users
Request Example :
curl -X PUT https://api.yourdomain.com/scim/v2/Users/f81d4fae-7dec-11d0-a765-00a0c91e6bf6 \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "alice@example.com",
"name": {
"givenName": "Alice",
"familyName": "Smith-Johnson",
"formatted": "Alice Smith-Johnson"
},
"emails": [{
"value": "alice.johnson@example.com",
"primary": true
}],
"active": true
}'
Response : Same as POST /Users response
Status Codes :
User updated successfully
PATCH /Users/
Update user with PATCH operations (SCIM 2.0).
Supports partial updates using SCIM PATCH operations.
Path Parameters :
Request Body :
["urn:ietf:params:scim:api:messages:2.0:PatchOp"]
Array of PATCH operations:
op: Operation type (replace, add, remove)
path: Attribute path (e.g., active, emails)
value: New value
Request Example :
Deactivate User
Update Email
Python
curl -X PATCH https://api.yourdomain.com/scim/v2/Users/f81d4fae-7dec-11d0-a765-00a0c91e6bf6 \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "replace",
"path": "active",
"value": false
}
]
}'
Response : Same as POST /Users response
Status Codes :
User patched successfully
DELETE /Users/
Delete (deactivate) user (SCIM 2.0).
Deactivates user in Keycloak (sets enabled=false) and removes OpenFGA authorization tuples. User data is preserved for compliance.
Path Parameters :
Request Example :
curl -X DELETE https://api.yourdomain.com/scim/v2/Users/f81d4fae-7dec-11d0-a765-00a0c91e6bf6 \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
Response : No content (HTTP 204)
Soft Delete : Users are deactivated (not permanently deleted) to comply with audit and compliance requirements. Hard deletion requires direct Keycloak admin access.
Status Codes :
User deleted successfully
GET /Users
List/search users (SCIM 2.0).
Supports basic filtering and pagination.
Query Parameters :
SCIM filter expression (e.g., userName eq "alice@example.com") Supported filters :
userName eq "value"
email eq "value"
1-based start index for pagination (minimum: 1)
Number of results to return (1-1000)
Request Example :
curl 'https://api.yourdomain.com/scim/v2/Users?startIndex=1&count=100' \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
``` bash
``` bash Filter by UserName
curl 'https://api.yourdomain.com/scim/v2/Users?filter=userName%20eq%20%22alice@example.com%22' \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
Response :
{
"schemas" : [ "urn:ietf:params:scim:api:messages:2.0:ListResponse" ],
"totalResults" : 2 ,
"startIndex" : 1 ,
"itemsPerPage" : 2 ,
"Resources" : [
{
"schemas" : [ "urn:ietf:params:scim:schemas:core:2.0:User" ],
"id" : "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" ,
"userName" : "alice@example.com" ,
"name" : {
"givenName" : "Alice" ,
"familyName" : "Smith"
},
"emails" : [{ "value" : "alice@example.com" , "primary" : true }],
"active" : true
},
{
"schemas" : [ "urn:ietf:params:scim:schemas:core:2.0:User" ],
"id" : "9d3e2a1b-8c7f-4e6d-a5b4-3c2d1e0f9a8b" ,
"userName" : "bob@example.com" ,
"name" : {
"givenName" : "Bob" ,
"familyName" : "Johnson"
},
"emails" : [{ "value" : "bob@example.com" , "primary" : true }],
"active" : true
}
]
}
Status Codes :
Users retrieved successfully
Invalid filter or pagination parameters
Group Endpoints
POST /Groups
Create a new group (SCIM 2.0).
Request Body :
["urn:ietf:params:scim:schemas:core:2.0:Group"]
Group name (e.g., “Engineering”, “Marketing”)
Array of group members:
value: User ID (UUID)
display: Display name (optional)
Request Example :
curl -X POST https://api.yourdomain.com/scim/v2/Groups \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "Engineering",
"members": [
{
"value": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
"display": "Alice Smith"
},
{
"value": "9d3e2a1b-8c7f-4e6d-a5b4-3c2d1e0f9a8b",
"display": "Bob Johnson"
}
]
}'
Response :
{
"schemas" : [ "urn:ietf:params:scim:schemas:core:2.0:Group" ],
"id" : "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" ,
"displayName" : "Engineering" ,
"members" : [
{
"value" : "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" ,
"display" : "Alice Smith"
},
{
"value" : "9d3e2a1b-8c7f-4e6d-a5b4-3c2d1e0f9a8b" ,
"display" : "Bob Johnson"
}
],
"meta" : {
"resourceType" : "Group" ,
"created" : "2025-10-29T10:00:00Z"
}
}
Status Codes :
Group created successfully
GET /Groups/
Get group by ID (SCIM 2.0).
Path Parameters :
Request Example :
curl https://api.yourdomain.com/scim/v2/Groups/a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
Response : Same as POST /Groups response
Status Codes :
Group retrieved successfully
Identity Provider Integration Examples
Okta SCIM Integration
# Okta Application Settings
SCIM Base URL : https://api.yourdomain.com/scim/v2
Authentication : OAuth 2.0 Bearer Token
Supported Features :
- Push New Users
- Push Profile Updates
- Push Groups
- Import New Users and Profile Updates
Attribute Mappings :
userName : user.email
name.givenName : user.firstName
name.familyName : user.lastName
emails[0].value : user.email
active : user.status == "ACTIVE"
Azure AD SCIM Integration
# Azure AD Enterprise Application
$AppId = "<your-app-id>"
$TenantDomain = "https://api.yourdomain.com/scim/v2"
Set-AzureADApplication - ObjectId $AppId `
- IdentifierUris $TenantDomain `
- ReplyUrls " $TenantDomain /Users" , " $TenantDomain /Groups"
# Configure attribute mappings in Azure Portal
# Provisioning > Mappings > User Mappings
OneLogin SCIM Integration
{
"scim_base_url" : "https://api.yourdomain.com/scim/v2" ,
"authentication" : "Bearer Token" ,
"scim_version" : "2.0" ,
"user_provisioning" : true ,
"group_provisioning" : true ,
"attribute_mappings" : {
"userName" : "email" ,
"givenName" : "firstname" ,
"familyName" : "lastname"
}
}
SCIM Testing & Validation
Test User Provisioning Flow
# 1. Create user
USER_ID =$(curl -X POST https://api.yourdomain.com/scim/v2/Users \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "testuser@example.com",
"name": {"givenName": "Test", "familyName": "User"},
"emails": [{"value": "testuser@example.com", "primary": true}],
"active": true
}' | jq -r '.id' )
# 2. Get user
curl https://api.yourdomain.com/scim/v2/Users/$USER_ID \
-H "Authorization: Bearer $TOKEN"
# 3. Update user (deactivate)
curl -X PATCH https://api.yourdomain.com/scim/v2/Users/$USER_ID \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [{"op": "replace", "path": "active", "value": false}]
}'
# 4. Delete user
curl -X DELETE https://api.yourdomain.com/scim/v2/Users/$USER_ID \
-H "Authorization: Bearer $TOKEN"
Standards Compliant : Full SCIM 2.0 protocol implementation with Keycloak and OpenFGA integration!