Skip to main content

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)
    • displayName, members
  • 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:
schemas
array
required
SCIM schema URIs (always ["urn:ietf:params:scim:schemas:core:2.0:User"])
userName
string
required
Unique username or email address
name
object
User’s name:
  • givenName: First name
  • familyName: Last name
  • formatted: Full name
emails
array
Email addresses (array of {value, primary} objects)
active
boolean
default:true
Whether user account is active
password
string
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:
201
Created
User created successfully
400
Bad Request
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"
}
409
Conflict
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:
user_id
string
required
Keycloak user ID (UUID)
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:
200
Success
User retrieved successfully
404
Not Found
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:
user_id
string
required
Keycloak user ID (UUID)
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:
200
Success
User updated successfully
400
Bad Request
Invalid SCIM schema
404
Not Found
User not found

PATCH /Users/

Update user with PATCH operations (SCIM 2.0). Supports partial updates using SCIM PATCH operations. Path Parameters:
user_id
string
required
Keycloak user ID (UUID)
Request Body:
schemas
array
required
["urn:ietf:params:scim:api:messages:2.0:PatchOp"]
Operations
array
required
Array of PATCH operations:
  • op: Operation type (replace, add, remove)
  • path: Attribute path (e.g., active, emails)
  • value: New value
Request Example:
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:
200
Success
User patched successfully
400
Bad Request
Invalid PATCH operation
404
Not Found
User not found

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:
user_id
string
required
Keycloak user ID (UUID)
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:
204
No Content
User deleted successfully
404
Not Found
User not found

GET /Users

List/search users (SCIM 2.0). Supports basic filtering and pagination. Query Parameters:
filter
string
SCIM filter expression (e.g., userName eq "alice@example.com")Supported filters:
  • userName eq "value"
  • email eq "value"
startIndex
integer
default:1
1-based start index for pagination (minimum: 1)
count
integer
default:100
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:
200
Success
Users retrieved successfully
400
Bad Request
Invalid filter or pagination parameters

Group Endpoints

POST /Groups

Create a new group (SCIM 2.0). Request Body:
schemas
array
required
["urn:ietf:params:scim:schemas:core:2.0:Group"]
displayName
string
required
Group name (e.g., “Engineering”, “Marketing”)
members
array
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:
201
Created
Group created successfully
400
Bad Request
Invalid SCIM schema
409
Conflict
Group already exists

GET /Groups/

Get group by ID (SCIM 2.0). Path Parameters:
group_id
string
required
Keycloak group ID (UUID)
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:
200
Success
Group retrieved successfully
404
Not Found
Group not found

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!