Skip to content

SCIM Provisioning Admin Guide

import { Aside, Steps, Tabs, TabItem } from ‘@astrojs/starlight/components’;

SCIM 2.0 (System for Cross-domain Identity Management) automates user and group lifecycle management between your identity provider and Arbitex. This guide walks administrators through end-to-end setup for Okta and Microsoft Entra ID, explains attribute mapping configuration, and documents known limitations.

For the full SCIM API endpoint reference, see SCIM 2.0 Provisioning.


When SCIM is configured, your IdP becomes the authoritative source for user accounts and groups:

  • Automated provisioning — Users assigned to the Arbitex app in your IdP are automatically created in Arbitex
  • Real-time deprovisioning — Removing a user from the IdP immediately deactivates their Arbitex access
  • Group sync — IdP groups map to Arbitex groups, which drive policy rule enforcement
  • Attribute sync — Email, display name, and group membership stay in sync automatically

SCIM works alongside SSO (SAML or OIDC). SCIM provisions the account; SSO handles authentication. Both should be configured.

The Policy Engine evaluates user.groups conditions against the groups synced via SCIM:

User is member of "financial-analysts" in Okta
→ Okta syncs group to Arbitex via SCIM
→ Policy rule: IF user.groups contains "financial-analysts" → allow model gpt-4o
→ User gets access on next login

Group membership changes propagate on the IdP’s next sync cycle (Okta: up to 5 minutes; Entra ID: 40-minute incremental cycle, or on-demand sync).


Before configuring SCIM:

  1. You have admin access to your Arbitex organization
  2. SSO (SAML or OIDC) is configured and working — see SSO Configuration
  3. You have an Arbitex admin API key (found in Admin → API Keys)
  4. You have IdP admin permissions to create apps and configure provisioning

The SCIM token is a bearer secret that your IdP uses to authenticate to the Arbitex SCIM endpoint. Generate it using the admin API:

Terminal window
curl -X POST "https://api.arbitex.ai/v1/orgs/{org_id}/scim/token/rotate" \
-H "Authorization: Bearer $ARBITEX_ADMIN_API_KEY" \
-H "Content-Type: application/json"

Response:

{
"token": "scim_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"org_id": "8d4b2c1e-0a3f-4e9d-b7c6-1a2b3c4d5e6f",
"created_at": "2026-03-12T00:00:00Z"
}

Save the token securely in your password manager. You will enter it in your IdP in the next steps.

SCIM base URL: https://api.arbitex.ai/scim/v2


Arbitex is not in the Okta Integration Network (OIN) gallery as a pre-built app. Use the generic SCIM 2.0 (custom) app type instead.

  1. Create a new app integration

    In the Okta Admin Console, navigate to Applications → Applications → Create App Integration.

    Select:

    • Sign-in method: SAML 2.0 (or OIDC if you configured OIDC SSO for Arbitex)
    • App type: Web Application

    If you already have an Okta SSO app for Arbitex, you can add SCIM provisioning to the existing app (skip to step 3).

  2. Enable SCIM provisioning on the app

    In the Okta app settings, go to the General tab and find Provisioning. Click Edit and set the provisioning type to SCIM 2.0.

    If the Provisioning tab is not visible, go to General → App Settings → Edit and enable Enable SCIM provisioning.

  3. Configure SCIM connection settings

    Navigate to Provisioning → Integration.

    FieldValue
    SCIM connector base URLhttps://api.arbitex.ai/scim/v2
    Unique identifier field for usersuserName
    Supported provisioning actionsCheck: Push New Users, Push Profile Updates, Push Groups, Import New Users and Profile Updates, Import Groups
    Authentication ModeHTTP Header
    AuthorizationBearer <your-scim-token>

    Click Test Connector Configuration. Okta will perform a GET /scim/v2/Users?count=1 request. A successful response confirms connectivity.

  4. Configure attribute mappings (To App)

    Navigate to Provisioning → To App → Attribute Mappings.

    Set the following mappings:

    Okta AttributeSCIM AttributeMapping Type
    appuser.emailuserNameExpression
    appuser.emailemails[type eq "work"].valueExpression
    appuser.displayNamedisplayNameDirect
    appuser.firstNamename.givenNameDirect
    appuser.lastNamename.familyNameDirect
    appuser.loginexternalIdExpression

    Expression for userName and email:

    appuser.email

    Expression for externalId (uses the Okta user ID for stable correlation):

    appuser.getInternalProperty("id")
  5. Configure Group Push

    Navigate to Push Groups.

    Click Push Groups → Find groups by name and add each Okta group you want to sync to Arbitex. Alternatively, click Find groups by rule and create a rule to push all groups matching a naming convention (e.g., arbitex-*).

    Enable Push group memberships immediately if you want real-time group sync (instead of waiting for the scheduled sync cycle).

  6. Assign users to the app

    Navigate to Assignments. Assign either individual users or the groups whose members should be provisioned.

    Only users (or members of groups) assigned to the Arbitex app will be provisioned. Assigning a group automatically provisions all current members and future additions.

  7. Trigger initial sync

    Navigate to Provisioning → Import. Click Import Now to perform an initial import that creates Arbitex accounts for all assigned users.

    Monitor the Tasks view for any provisioning errors. Common errors on first sync:

    • 409 Conflict — A user with that email was already created manually in Arbitex
    • 400 Bad Request — Attribute mapping is missing a required field (usually userName)
EventSync behavior
User assigned to appProvisioned within 1–2 minutes
User removed from appDeprovisioned within 1–2 minutes
Group membership changeSynced on next scheduled cycle (up to 5 minutes) or on Group Push trigger
Profile update (email, name)Synced within 5 minutes if “Push Profile Updates” is enabled

Arbitex is available in the Microsoft Entra ID enterprise app gallery as Arbitex AI Gateway. You can also configure manually using the generic SCIM 2.0 app template.

  1. Add the enterprise application

    In the Azure Portal, navigate to Microsoft Entra ID → Enterprise Applications → New Application.

    Search for Arbitex AI Gateway and select it, or choose Create your own applicationIntegrate any other application you don’t find in the gallery (Non-gallery).

    Name the application (e.g., “Arbitex AI Gateway”) and click Create.

  2. Configure single sign-on (if not already done)

    Before configuring provisioning, ensure SSO is set up for the same application. SCIM requires users to be assigned to the app, and assignment is managed in one place.

    See SSO Configuration for the SAML setup steps.

  3. Configure automatic provisioning

    Navigate to the application → Provisioning → Get started.

    SettingValue
    Provisioning ModeAutomatic
    Tenant URLhttps://api.arbitex.ai/scim/v2
    Secret Token<your-scim-token> (without the Bearer prefix)

    Click Test Connection. Entra ID performs a GET /scim/v2/Users?count=1 to validate the connection. A success message confirms connectivity.

  4. Configure attribute mappings

    Navigate to Mappings → Provision Microsoft Entra ID Users.

    Verify or configure the following attribute mappings:

    Entra ID AttributeSCIM AttributeMatching Precedence
    userPrincipalNameuserNamePrimary (1)
    mailemails[type eq "work"].value
    displayNamedisplayName
    givenNamename.givenName
    surnamename.familyName
    objectIdexternalId
    IsSoftDeletedactive— (inverted: false → active false)

    Navigate to Mappings → Provision Microsoft Entra ID Groups.

    Verify or configure:

    Entra ID AttributeSCIM Attribute
    displayNamedisplayName
    objectIdexternalId
    membersmembers
  5. Set provisioning scope

    Navigate to Settings.

    SettingRecommended value
    ScopeSync only assigned users and groups
    Notification EmailYour IT admin email (receives provisioning error alerts)
    Send an email notification when a failure occursEnabled

    Setting scope to “Sync only assigned users and groups” ensures only users explicitly assigned to the app are provisioned. Using “Sync all users and groups” provisions your entire directory.

  6. Assign users and groups

    Navigate to Users and groups → Add user/group.

    Assign the groups (not individual users) that should be provisioned to Arbitex. All members of assigned groups will be provisioned.

    Assign the Arbitex Admins group if you have one — admin users need to be provisioned before they can access the admin panel.

  7. Start provisioning

    Navigate to Provisioning → Overview. Toggle provisioning to On.

    Click Provision on demand to provision a specific user immediately (useful for testing). Enter a user’s UPN, click Provision, and verify the result in the audit log.

    Entra will begin the initial sync cycle. Initial full sync can take 20–40 minutes for large directories.

  8. Monitor provisioning

    Navigate to Provisioning → Provisioning logs to see individual provisioning events.

    Filter by Status: Failure to identify any errors. Common errors:

    • InvalidLicense — The user exists in Arbitex but their account type doesn’t support SCIM-managed attributes
    • DuplicateTargetEntries — Duplicate email in Arbitex; resolve the conflict manually
EventSync behavior
Initial provisioning20–40 minutes
Incremental syncEvery 40 minutes (automatic)
On-demand syncImmediate (via “Provision on demand” or Graph API)
Group membership changeIncluded in next incremental sync cycle
User deprovisioningIncluded in next incremental sync (or on-demand)

SCIM AttributeArbitex FieldRequiredNotes
userNameemail + usernameYesUsed as primary identifier. Must be unique email.
emails[primary].valueemailNoUsed if userName is not an email format
displayNamedisplay_nameNoShown in admin UI and audit logs
name.givenNamefirst_nameNoStored but not required
name.familyNamelast_nameNoStored but not required
externalIdexternal_idNoIdP-side identifier for deduplication; strongly recommended
activeis_activeNofalse deactivates the account and revokes all active sessions

Recommendation: Always map externalId to the IdP’s stable user object ID (Okta: getInternalProperty("id"), Entra: objectId). This prevents duplicate account creation if a user’s email changes.

SCIM AttributeArbitex FieldNotes
displayNamenameGroup name — used in policy rule user.groups conditions
externalIdexternal_group_idIdP group object ID; used for SSO claim mapping
members[].valueGroup membershipArbitex user UUIDs (not IdP UUIDs)

The urn:arbitex:scim:schemas:extension:Group:2.0 extension allows group-level policy configuration from the IdP:

Extension AttributeTypeDescription
dlpPolicystringOverride DLP action: SKIP / BLOCK / CANCEL / REDACT
modelAccessstring arrayPermitted model IDs for group members
complianceBundlesstring arrayCompliance bundle IDs active for this group
quotaTokensintegerDaily token quota for group members

Example group with extension attributes:

{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:Group",
"urn:arbitex:scim:schemas:extension:Group:2.0"
],
"displayName": "data-science-team",
"externalId": "grp_ds_001",
"members": [
{ "value": "user-uuid-alice" },
{ "value": "user-uuid-bob" }
],
"urn:arbitex:scim:schemas:extension:Group:2.0": {
"modelAccess": ["gpt-4o", "claude-3-5-sonnet"],
"quotaTokens": 500000,
"complianceBundles": ["soc2-type2"]
}
}

The following are known limitations of the current SCIM implementation:

FeatureStatusWorkaround
PATCH /Users — nested path operationsNot supportedUse PUT /Users/{id} for full user replacement
Complex filter expressions (and, or, not)Not supportedFilters are limited to single-attribute equality; IdP handles complex filtering client-side
Bulk operations (/scim/v2/Bulk)Not supportedOperations are performed individually
SCIM /Me endpointNot supported
Sorting (sortBy, sortOrder)Not supportedResults are returned in insertion order
schema discovery via /scim/v2/ResourceTypesPartially supportedReturns Users and Groups only; extension schema not in ServiceProviderConfig
BehaviorCurrentImpact
Group delete propagationGroup DELETE removes the group but does not automatically remove users from the group policy contextUsers retain group-derived permissions until policy is re-evaluated on next request
Concurrent PATCH operationsNot atomicRapid sequential PATCH operations from the IdP (e.g., bulk member adds during initial sync) may result in eventual consistency rather than guaranteed ordering
User restore after soft-deletePATCH active: true re-activates; historical audit logs are preserved but conversation history access must be re-granted manually
Multi-value email attributesOnly primary: true email is used; secondary emails are ignored
Display name as usernamedisplayName is mapped to username (display field); userName (email) is the primary login identifierSome IdP configurations set displayName to a non-email value — ensure userName is always the email
IssueDetail
Nested group membershipEntra ID SCIM provisioning flattens nested groups — members of nested groups are NOT automatically provisioned. Assign all groups (including parent and child) directly to the app.
Group member limitEntra ID SCIM provisioning has a 1500-member limit per group in a single sync operation. Larger groups may require multiple sync cycles or manual member provisioning.
Provisioning scope changeChanging scope from “Sync assigned users and groups” to “Sync all” triggers a full re-sync which may take several hours for large directories.
IssueDetail
Okta Push Groups vs Import GroupsPush Groups is used to push Okta groups TO Arbitex; Import Groups imports Arbitex groups INTO Okta. For most setups, only Push Groups is needed. Enabling both can create sync loops if not configured carefully.
Profile masteringIf Okta is set as the profile master for the Arbitex app, Okta will override any manual attribute changes made in Arbitex. This is the intended behavior for SCIM.
App deactivation behaviorWhen the Arbitex app is deactivated in Okta (vs. a user being unassigned), Okta sends PATCH active: false for all provisioned users. This deactivates all users simultaneously.

Symptom: All SCIM operations return 401.

Causes and resolutions:

  1. Token was rotated — Check if the SCIM token was recently rotated. Run:

    Terminal window
    curl -X POST "https://api.arbitex.ai/v1/orgs/{org_id}/scim/token/rotate" \
    -H "Authorization: Bearer $ARBITEX_ADMIN_API_KEY"

    Update your IdP with the new token immediately.

  2. Incorrect Authorization header — Some IdPs include or omit the Bearer prefix incorrectly.

    • Okta: Enter the raw token in the “Authorization” field; Okta prepends Bearer automatically
    • Entra ID: Enter the raw token (without Bearer ) in the “Secret Token” field
  3. Org ID mismatch — Verify the SCIM token was generated for the correct org ID.

Symptom: First-time setup returns 403.

Cause: No SCIM token has been generated for the org.

Resolution: Run POST /v1/orgs/{org_id}/scim/token/rotate to create the initial token.

Symptom: User provisioning fails with 409 Conflict.

Cause: A user with the same userName (email) already exists in Arbitex, created before SCIM was configured.

Resolution options:

  1. Link existing user to SCIM (recommended) — Update the existing user’s externalId to match the IdP’s user object ID:

    Terminal window
    curl -X PATCH "https://api.arbitex.ai/scim/v2/Users/{arbitex_user_id}" \
    -H "Authorization: Bearer $SCIM_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"schemas":["urn:ietf:params:scim:api:messages:2.0:PatchOp"],"Operations":[{"op":"replace","path":"externalId","value":"idp-user-object-id"}]}'

    After linking, the IdP will find the existing user by externalId or userName and update rather than create.

  2. Delete and re-create — Delete the manually created user from Arbitex admin panel, then trigger a re-sync from the IdP.

Symptom: Groups are synced but some members are missing.

Checklist:

  • Are the missing users assigned to the Arbitex app? (Okta: check Assignments; Entra: check Users and groups)
  • Did the user provisioning succeed before the group PATCH was sent? Group PATCH uses Arbitex user UUIDs — if the user doesn’t exist yet, the member is silently skipped
  • For Entra ID: check for the 1500-member limit issue — look for “SkippedMember” entries in provisioning logs
  • Check the IdP provisioning log for group operations — look for PATCH operations with members path

Users deprovisioned but still able to log in

Section titled “Users deprovisioned but still able to log in”

Symptom: User’s is_active is false but they can still start new sessions.

Cause: Active sessions (JWT tokens) are not invalidated immediately when the user is deactivated via SCIM. Active sessions remain valid until they expire.

Resolution: To immediately invalidate all active sessions for a deactivated user:

Terminal window
# Force-invalidate all sessions for a user (admin API)
curl -X POST "https://api.arbitex.ai/api/admin/users/{user_id}/invalidate-sessions" \
-H "Authorization: Bearer $ARBITEX_ADMIN_API_KEY"

Session max lifetime is configurable in Admin → Organization → Session Policy. Default is 8 hours.

Symptom: Full sync has been running for hours with no errors but not all users are provisioned.

Recommendations:

  • For Entra ID: large directories (>10,000 users) can take 2–4 hours for initial full sync. This is normal; incremental syncs will be faster.
  • For Okta: check the “Tasks” view for any paused or failed operations
  • Verify rate limiting is not a factor — Arbitex enforces a per-org SCIM rate limit of 100 requests/second. Large syncs approaching this limit will be throttled (HTTP 429 responses)

Symptom: Some provisioning operations return 429.

Cause: The IdP is sending too many requests per second during bulk sync.

Response headers on 429:

Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1710000000

Resolution: Most IdPs automatically honor Retry-After. If your IdP does not, configure a rate limit or throttle setting in its provisioning connector. For Entra ID, this is handled automatically. For Okta, throttling is automatic.


After completing setup, verify provisioning is working:

Terminal window
# 1. Verify the SCIM endpoint is reachable and authenticated
curl "https://api.arbitex.ai/scim/v2/ServiceProviderConfig" \
-H "Authorization: Bearer $SCIM_TOKEN"
# 2. List provisioned users
curl "https://api.arbitex.ai/scim/v2/Users?count=5" \
-H "Authorization: Bearer $SCIM_TOKEN"
# 3. List provisioned groups
curl "https://api.arbitex.ai/scim/v2/Groups?count=5" \
-H "Authorization: Bearer $SCIM_TOKEN"
# 4. Verify a specific user was provisioned with correct attributes
curl "https://api.arbitex.ai/scim/v2/Users?filter=userName eq \"alice@example.com\"" \
-H "Authorization: Bearer $SCIM_TOKEN"

Expected output for step 4:

{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults": 1,
"Resources": [{
"id": "user-uuid-here",
"userName": "alice@example.com",
"displayName": "Alice Smith",
"active": true,
"externalId": "okta-user-object-id",
"meta": {
"resourceType": "User",
"created": "2026-03-12T00:00:00Z",
"lastModified": "2026-03-12T00:00:00Z"
}
}]
}

SCIM tokens do not expire automatically. Rotate them on your security rotation schedule (recommended: every 90 days, or immediately after personnel changes).

  1. Generate a new token (previous token remains active until step 3):

    Terminal window
    curl -X POST "https://api.arbitex.ai/v1/orgs/{org_id}/scim/token/rotate" \
    -H "Authorization: Bearer $ARBITEX_ADMIN_API_KEY"

    Copy the new raw token immediately.

  2. Update your IdP with the new token:

    • Okta: Provisioning → Integration → Edit → update Authorization field → Save → Test Connector Configuration
    • Entra ID: Provisioning → Edit provisioning → update Secret Token → Test Connection → Save
  3. Verify the new token works by triggering an on-demand sync of one user.

  4. Invalidation note: Token rotation immediately invalidates the previous token. If you have multiple IdPs (e.g., Okta AND Entra ID) both using SCIM, note that Arbitex only supports one active SCIM token per org. Both IdPs must be updated simultaneously.