Summary and recommendation
SAP Concur exposes user lifecycle management through two distinct API surfaces that must be used together.
Write operations (create, update, deactivate) are submitted exclusively via the asynchronous Bulk endpoint at POST /provisioning/v4/Bulk.
Read operations use the SCIM 2.0-compliant GET /profile/identity/v4/Users endpoint.
Attempting to POST or PATCH directly to the identity endpoint for writes will not work - the separation is architectural, not a versioning artifact.
Authentication uses OAuth 2.0 with scoped tokens.
Required write scopes are user.provision.write;
reads require user.provision.read plus identity.user.* scopes depending on which attribute classes are needed (core, sensitive, enterprise).
The OAuth token response includes a geolocation field specifying the correct datacenter-specific base URL - this value must be used for all subsequent calls.
Hardcoding a regional URL (e.g., us.api.concursolutions.com) will cause failures for tenants hosted in other regions.
SCIM provisioning requires an active SSO configuration on the Concur tenant.
This is a hard prerequisite - provisioning attempts against a tenant without SSO enabled will fail.
SCIM is available at the Enterprise tier only.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise (SSO prerequisite required; SCIM provisioning requires an active SSO configuration with a supported IdP such as Okta or Microsoft Entra ID) |
Authentication
Auth method: OAuth 2.0
Setup steps
- Register an application in the SAP Concur App Center or via the developer portal to obtain a client_id and client_secret.
- Request a Company-level JWT or use the Authorization Code grant to obtain an access token from https://us.api.concursolutions.com/oauth2/v0/token.
- Include the access token as a Bearer token in the Authorization header of all API requests.
- Ensure the registered app has the required scopes (e.g., user.provision.write, user.provision.read) assigned by the Concur administrator.
- For production, use the geolocation-specific base URL returned in the token response (e.g., eu.api.concursolutions.com for EU data centers).
Required scopes
| Scope | Description | Required for |
|---|---|---|
| user.provision.write | Allows creating and updating user records via the User Provisioning Service v4. | POST and PATCH user provisioning operations |
| user.provision.read | Allows reading user records via the User Provisioning Service v4. | GET user and list operations |
| identity.user.ids.read | Allows reading user identity IDs. | Resolving user UUIDs from login names or employee IDs |
| identity.user.core.read | Allows reading core identity attributes of users. | GET identity/profile operations |
| identity.user.coresensitive.read | Allows reading sensitive core identity attributes such as email and phone. | GET operations returning sensitive fields |
| identity.user.enterprise.read | Allows reading enterprise extension attributes (department, manager, employeeNumber). | GET operations returning enterprise schema fields |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | string (UUID) | Unique SAP Concur user UUID, system-generated. | system-assigned | immutable | Used as the resource identifier in all API paths. |
| userName | string | The user's login name, typically email format. Must be unique within the tenant. | required | optional | Maps to SCIM userName attribute. |
| active | boolean | Indicates whether the user account is active. | optional (defaults to true) | optional | Setting to false deactivates the user without deleting. |
| name.formatted | string | Full display name of the user. | optional | optional | SCIM name sub-attribute. |
| name.givenName | string | User's first name. | required | optional | |
| name.familyName | string | User's last name. | required | optional | |
| emails | array of objects | User email addresses. Each object has value, type, and primary fields. | required (work email) | optional | Primary work email is used for notifications. |
| phoneNumbers | array of objects | User phone numbers with value and type. | optional | optional | |
| preferredLanguage | string | IETF language tag for the user's preferred language (e.g., en-US). | optional | optional | |
| timezone | string | User's timezone in IANA format (e.g., America/New_York). | optional | optional | |
| urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeNumber | string | Employee number from HR system. | optional | optional | Part of SCIM Enterprise User extension schema. |
| urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:companyId | string (UUID) | SAP Concur company UUID the user belongs to. | required | immutable | Must match the authenticated company's UUID. |
| urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager | object | Manager reference with value (manager UUID) and displayName. | optional | optional | Manager must already exist as a Concur user. |
| urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department | string | User's department name. | optional | optional | |
| urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:costCenter | string | Cost center code for the user. | optional | optional | |
| urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:organization | string | Organization or company name. | optional | optional | |
| urn:ietf:params:scim:schemas:extension:concur:2.0:User:cashAdvanceAccountCode | string | Concur-specific extension: cash advance account code. | optional | optional | Part of the Concur custom extension schema. |
| urn:ietf:params:scim:schemas:extension:concur:2.0:User:reimbursementCurrency | string | ISO 4217 currency code for user reimbursement. | optional | optional | Concur extension schema field. |
Core endpoints
Create User
- Method: POST
- URL:
https://us.api.concursolutions.com/provisioning/v4/Bulk - Watch out for: User Provisioning v4 uses a Bulk endpoint and processes requests asynchronously. A 202 response does not confirm user creation; poll the job status endpoint to confirm completion.
Request example
POST /provisioning/v4/Bulk
Authorization: Bearer {token}
Content-Type: application/json
{
"schemas":["urn:ietf:params:scim:api:messages:2.0:BulkRequest"],
"Operations":[{"method":"POST","path":"/Users","data":{"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],"userName":"jdoe@example.com","name":{"givenName":"John","familyName":"Doe"},"active":true}}]
}
Response example
HTTP 202 Accepted
{
"schemas":["urn:ietf:params:scim:api:messages:2.0:BulkResponse"],
"Operations":[{"method":"POST","bulkId":"abc123","status":{"code":202}}]
}
Update User (PATCH)
- Method: POST
- URL:
https://us.api.concursolutions.com/provisioning/v4/Bulk - Watch out for: PATCH operations are also submitted via the Bulk endpoint, not a direct PATCH to /Users/{id}. Deactivation via active:false does not delete the user.
Request example
POST /provisioning/v4/Bulk
Authorization: Bearer {token}
{
"schemas":["urn:ietf:params:scim:api:messages:2.0:BulkRequest"],
"Operations":[{"method":"PATCH","path":"/Users/{userId}","data":{"schemas":["urn:ietf:params:scim:api:messages:2.0:PatchOp"],"Operations":[{"op":"replace","path":"active","value":false}]}}]
}
Response example
HTTP 202 Accepted
{
"Operations":[{"method":"PATCH","status":{"code":202}}]
}
Get Provisioning Job Status
- Method: GET
- URL:
https://us.api.concursolutions.com/provisioning/v4/provisions/{jobId}/status - Watch out for: Must poll this endpoint after submitting Bulk requests to confirm actual provisioning success or retrieve error details.
Request example
GET /provisioning/v4/provisions/{jobId}/status
Authorization: Bearer {token}
Response example
{
"status": "complete",
"jobId": "abc123",
"completedAt": "2024-01-15T10:30:00Z",
"resourcesSucceeded": 1,
"resourcesFailed": 0
}
List Users (SCIM)
- Method: GET
- URL:
https://us.api.concursolutions.com/profile/identity/v4/Users - Watch out for: Filter syntax follows SCIM 2.0 spec. The identity/v4 endpoint is read-oriented; writes must go through the provisioning/v4/Bulk endpoint.
Request example
GET /profile/identity/v4/Users?filter=userName+eq+"jdoe@example.com"&startIndex=1&count=100
Authorization: Bearer {token}
Response example
{
"schemas":["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults": 1,
"startIndex": 1,
"itemsPerPage": 100,
"Resources":[{"id":"uuid-here","userName":"jdoe@example.com","active":true}]
}
Get Single User (SCIM)
- Method: GET
- URL:
https://us.api.concursolutions.com/profile/identity/v4/Users/{userId} - Watch out for: The userId must be the SAP Concur internal UUID, not the employeeNumber or email. Use the List endpoint with a filter to resolve UUIDs first.
Request example
GET /profile/identity/v4/Users/{userId}
Authorization: Bearer {token}
Response example
{
"id": "uuid-here",
"userName": "jdoe@example.com",
"active": true,
"name": {"givenName":"John","familyName":"Doe"},
"emails":[{"value":"jdoe@example.com","type":"work","primary":true}]
}
Get SCIM Schemas
- Method: GET
- URL:
https://us.api.concursolutions.com/profile/identity/v4/Schemas - Watch out for: Use this endpoint to discover supported Concur extension schema URNs before constructing provisioning payloads.
Request example
GET /profile/identity/v4/Schemas
Authorization: Bearer {token}
Response example
{
"schemas":["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"Resources":[{"id":"urn:ietf:params:scim:schemas:core:2.0:User","name":"User"}]
}
Get Service Provider Config
- Method: GET
- URL:
https://us.api.concursolutions.com/profile/identity/v4/ServiceProviderConfig - Watch out for: Confirms bulk operation limits (maxOperations per request) and filter support. Check this before designing bulk provisioning batch sizes.
Request example
GET /profile/identity/v4/ServiceProviderConfig
Authorization: Bearer {token}
Response example
{
"schemas":["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"],
"bulk":{"supported":true,"maxOperations":100,"maxPayloadSize":1048576},
"filter":{"supported":true,"maxResults":1000}
}
OAuth 2.0 Token Request
- Method: POST
- URL:
https://us.api.concursolutions.com/oauth2/v0/token - Watch out for: The geolocation field in the token response specifies the correct base URL for subsequent API calls. Always use this value, not a hardcoded regional URL, to avoid cross-datacenter routing errors.
Request example
POST /oauth2/v0/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id={client_id}&client_secret={client_secret}
Response example
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600,
"geolocation": "https://us.api.concursolutions.com"
}
Rate limits, pagination, and events
Rate limits: SAP Concur does not publicly document specific numeric rate limits for the User Provisioning v4 or SCIM APIs in their developer documentation as of the knowledge cutoff.
Rate-limit headers: No
Retry-After header: No
Rate-limit notes: The developer docs note that bulk provisioning should be performed during off-peak hours and that the provisioning service processes requests asynchronously via a job queue. No explicit requests-per-minute or daily quota figures are published.
Pagination method: offset
Default page size: 100
Max page size: 1000
Pagination pointer: startIndex and count (SCIM standard parameters)
Webhooks available: No
Webhook notes: SAP Concur does not offer a general-purpose outbound webhook system for user lifecycle events. Event-driven integrations are handled via the Event Subscription Service (ESS), which is scoped to specific Concur product events (e.g., expense report submission) rather than user provisioning events.
Alternative event strategy: Use the Event Subscription Service (ESS) for supported product events. For user provisioning state, poll the provisioning job status endpoint after submitting Bulk operations.
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise (SSO prerequisite required; SCIM provisioning requires an active SSO configuration with a supported IdP such as Okta or Microsoft Entra ID)
Endpoint: https://us.api.concursolutions.com/provisioning/v4/Bulk (writes) and https://us.api.concursolutions.com/profile/identity/v4/Users (reads)
Supported operations: Create User (via Bulk POST), Update User (via Bulk PATCH), Deactivate User (via Bulk PATCH active:false), Get User by ID, List Users with SCIM filter, Get Schemas, Get ServiceProviderConfig
Limitations:
- All write operations (create, update, deactivate) must be submitted through the asynchronous Bulk endpoint; direct POST/PATCH to /Users/{id} is not supported for writes.
- Hard delete of users is not supported via the SCIM API; only deactivation (active:false) is available.
- Provisioning is asynchronous; callers must poll the job status endpoint to confirm outcomes.
- The geolocation-specific base URL from the OAuth token response must be used; hardcoded regional URLs may fail.
- SCIM provisioning requires SSO to be configured and active for the Concur tenant.
- Bulk request maxOperations limit applies per request (check ServiceProviderConfig for current value).
- Group management via SCIM is not fully documented as supported in the User Provisioning v4 API.
Common scenarios
Three scenarios cover the majority of identity graph maintenance operations against Concur's API.
Provisioning a new employee via an IdP (e.g., Okta): Configure the Concur SCIM app connector in Okta pointing to the Bulk endpoint with a Company-level Bearer token.
Okta submits a SCIM POST via Bulk on user assignment;
Concur returns HTTP 202.
Poll /provisioning/v4/provisions/{jobId}/status until status is 'complete', then verify the user record via GET /profile/identity/v4/Users filtered by userName.
A 202 response confirms job queuing only - not user creation.
Deactivating a terminated employee: Resolve the user's Concur UUID via GET /profile/identity/v4/Users?filter=userName+eq+"{email}".
Submit a Bulk PATCH with op:replace on active set to false.
Poll the jobId endpoint to confirm completion, then verify via GET /profile/identity/v4/Users/{userId}.
Hard delete is not available through the API;
only deactivation is supported, and historical expense and travel data is retained.
Updating profile attributes (department, manager, cost center): Resolve the target user's UUID via userName filter.
Construct a Bulk PATCH payload using SCIM PatchOp operations targeting the enterprise extension fields.
Manager updates require the manager's Concur UUID - not employeeNumber or email - resolved separately before constructing the payload.
Submit to POST /provisioning/v4/Bulk and poll the returned jobId for confirmation.
Provision a new employee from an IdP (e.g., Okta)
- Configure SCIM 2.0 provisioning in Okta using the Concur app connector, pointing to the Bulk endpoint with a Company-level Bearer token.
- Okta sends a SCIM POST via the Bulk endpoint when a user is assigned to the Concur app.
- Concur returns HTTP 202; Okta or your integration layer polls /provisioning/v4/provisions/{jobId}/status until status is 'complete'.
- Verify the user appears in /profile/identity/v4/Users with a filter on userName.
Watch out for: If SSO is not active on the Concur tenant, provisioning will fail silently or return an error. Confirm SSO is enabled before testing provisioning.
Deactivate a terminated employee
- Resolve the user's Concur UUID by calling GET /profile/identity/v4/Users?filter=userName+eq+"{email}".
- Submit a Bulk PATCH operation to /provisioning/v4/Bulk with op:replace on the active field set to false.
- Poll /provisioning/v4/provisions/{jobId}/status to confirm deactivation completed.
- Confirm the user's active field is false via GET /profile/identity/v4/Users/{userId}.
Watch out for: Deactivation does not delete the user or their historical expense/travel data. The account remains in Concur in an inactive state.
Update user profile attributes (department, manager, cost center)
- Resolve the user's Concur UUID via GET /profile/identity/v4/Users with a userName filter.
- Construct a Bulk PATCH payload targeting /Users/{userId} with SCIM PatchOp operations replacing the enterprise extension fields (department, manager.value, costCenter).
- Submit to POST /provisioning/v4/Bulk with Authorization: Bearer {token}.
- Poll the returned jobId at /provisioning/v4/provisions/{jobId}/status to confirm success.
Watch out for: Manager updates require the manager's Concur UUID (not employeeNumber or email). Resolve the manager's UUID separately before constructing the PATCH payload.
Why building this yourself is a trap
The asynchronous Bulk-only write pattern is the most common integration trap. Every create, update, and deactivation returns HTTP 202 immediately - callers that treat 202 as confirmation of success will build integrations that silently fail under error conditions. Polling /provisioning/v4/provisions/{jobId}/status is not optional;
it is the only way to confirm actual provisioning outcomes or retrieve structured error details.
The identity graph dependency surfaces in manager reference handling: the manager field in the enterprise extension requires the manager's internal Concur UUID. Any integration that passes employeeNumber or email for manager references will fail at provisioning time. UUID resolution must be built into the provisioning pipeline as a prerequisite step, not an afterthought.
Concur uses custom SCIM extension schemas (urn:ietf:params:scim:schemas:extension:concur:2.0:User) for Concur-specific fields. These URNs must be declared in the schemas array of every request payload; omitting them causes silent field drops or provisioning errors.
Use GET /profile/identity/v4/Schemas to discover current supported URNs before constructing payloads. SAP Concur does not publish numeric rate limits for the provisioning API; the developer documentation advises running bulk operations during off-peak hours, and the ServiceProviderConfig endpoint exposes the maxOperations limit per Bulk request.
Automate SAP Concur workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.