Summary and recommendation
SAP Sales Cloud exposes user management through an OData v2 API at https://<tenant>.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi.
The primary entity for user operations is EmployeeCollection.
Auth is OAuth 2.0 (SAML Bearer Assertion for delegated flows;
client credentials for machine-to-machine);
Basic Auth is deprecated for production.
All write operations - POST, PATCH - require a valid X-CSRF-Token fetched from a prior GET;
stale or missing tokens return HTTP 403.
The API is OData v2: response bodies are wrapped in {"d": {"results": [...]}} not top-level arrays.
Pagination uses $skip / $top with a default and maximum page size of 1000.
Rate limits are not publicly documented;
SAP recommends $batch to reduce call volume in bulk provisioning scenarios.
SCIM 2.0 provisioning is available but is never a direct endpoint on the tenant
it is always mediated through SAP Identity Provisioning Service (IPS), requires Enterprise tier, and must be enabled via SAP support ticket with SAML SSO configured as a prerequisite.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 (SAML Bearer Assertion) or Basic Authentication (deprecated for production) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise (SCIM provisioning requires SAP Identity Provisioning Service, which is included in SAP BTP enterprise agreements; SCIM is disabled by default and must be enabled via SAP support ticket) |
Authentication
Auth method: OAuth 2.0 (SAML Bearer Assertion) or Basic Authentication (deprecated for production)
Setup steps
- In SAP Sales Cloud, navigate to Administrator > OAuth2.0 Client Registration and register an OAuth client.
- Configure the OAuth client with the appropriate scopes and note the Client ID and Client Secret.
- For SAML Bearer flow: obtain a SAML assertion from your IdP and exchange it at the token endpoint: POST https://
.crm.ondemand.com/sap/bc/sec/oauth2/token. - For client credentials flow (machine-to-machine): POST to the same token endpoint with grant_type=client_credentials, client_id, and client_secret.
- Include the resulting Bearer token in the Authorization header for all API requests.
- For SCIM provisioning via SAP Identity Provisioning Service (IPS): configure SAP Sales Cloud as a target system in IPS, provide the tenant URL, admin credentials or OAuth details, and enable the connector.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| UIWC:CC_HOME | Basic access scope required for OData API calls in SAP Sales Cloud. | All OData API operations |
| Not formally named per-resource in public docs | SAP Sales Cloud OData API scopes are managed via Work Center access and business role assignments rather than named OAuth scopes. The OAuth client inherits the permissions of the associated technical user. | User read/write operations via OData |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| ObjectID | string | System-generated unique identifier for the employee/user record. | auto-generated | immutable | Used as the key in OData URLs. |
| EmployeeID | string | Business key / employee number. | required | optional | Must be unique within the tenant. |
| FirstName | string | User's first name. | required | optional | |
| LastName | string | User's last name. | required | optional | |
| string | Primary email address. | required | optional | Used for login and notifications. | |
| UserName | string | Login username for the SAP Sales Cloud tenant. | required | optional | Must be unique. Used as the SCIM userName attribute. |
| UserValidityStartDate | datetime | Date from which the user account is valid. | optional | optional | Edm.DateTime format. |
| UserValidityEndDate | datetime | Date on which the user account expires. | optional | optional | Set to deactivate/expire a user account. |
| EmployeeRoleCode | string | Role code assigned to the employee (e.g., sales rep, manager). | optional | optional | Mapped to business roles within SAP Sales Cloud. |
| Department | string | Department the user belongs to. | optional | optional | |
| ManagerID | string | ObjectID of the user's manager. | optional | optional | Used for org hierarchy. |
| MobilePhone | string | Mobile phone number. | optional | optional | |
| Phone | string | Business phone number. | optional | optional | |
| LanguageCode | string | Preferred language code (e.g., EN, DE). | optional | optional | ISO 639-1 language codes. |
| CountryCode | string | Country code for the user. | optional | optional | ISO 3166-1 alpha-2. |
| TimeZoneCode | string | User's time zone. | optional | optional | |
| UserLock | boolean | Indicates whether the user account is locked. | optional | optional | Set to true to lock/disable the user. |
| SalesOrganisationID | string | Sales organization the user is assigned to. | optional | optional |
Core endpoints
List Users (EmployeeCollection)
- Method: GET
- URL:
https://<tenant>.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi/EmployeeCollection - Watch out for: Default page size is 1000; use $top and $skip for pagination. Response is OData v2 JSON wrapped in d.results.
Request example
GET /sap/c4c/odata/v1/c4codataapi/EmployeeCollection?$top=100&$skip=0&$format=json
Authorization: Bearer <token>
Response example
{
"d": {
"results": [
{
"ObjectID": "00163E0A...",
"EmployeeID": "E001",
"FirstName": "Jane",
"LastName": "Doe",
"Email": "jane.doe@example.com",
"UserName": "jane.doe"
}
]
}
}
Get Single User
- Method: GET
- URL:
https://<tenant>.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi/EmployeeCollection('<ObjectID>') - Watch out for: ObjectID is the system key, not EmployeeID. Use $filter=EmployeeID eq 'E001' to look up by business key.
Request example
GET /sap/c4c/odata/v1/c4codataapi/EmployeeCollection('00163E0A...')?$format=json
Authorization: Bearer <token>
Response example
{
"d": {
"ObjectID": "00163E0A...",
"EmployeeID": "E001",
"FirstName": "Jane",
"LastName": "Doe",
"Email": "jane.doe@example.com"
}
}
Create User
- Method: POST
- URL:
https://<tenant>.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi/EmployeeCollection - Watch out for: A CSRF token (X-CSRF-Token) must be fetched via a GET request with X-CSRF-Token: Fetch header before any write operation.
Request example
POST /sap/c4c/odata/v1/c4codataapi/EmployeeCollection
Content-Type: application/json
Authorization: Bearer <token>
{"EmployeeID":"E002","FirstName":"John","LastName":"Smith","Email":"john.smith@example.com","UserName":"john.smith"}
Response example
{
"d": {
"ObjectID": "00163E0B...",
"EmployeeID": "E002",
"FirstName": "John",
"LastName": "Smith"
}
}
Update User (Merge/Patch)
- Method: PATCH
- URL:
https://<tenant>.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi/EmployeeCollection('<ObjectID>') - Watch out for: SAP OData v2 uses X-HTTP-Method: MERGE for partial updates in some clients. PATCH is supported but verify tenant OData version behavior. Always include X-CSRF-Token.
Request example
PATCH /sap/c4c/odata/v1/c4codataapi/EmployeeCollection('00163E0B...')
Content-Type: application/json
X-CSRF-Token: <token>
Authorization: Bearer <token>
{"MobilePhone":"+1-555-0100"}
Response example
HTTP 204 No Content
Deactivate / Lock User
- Method: PATCH
- URL:
https://<tenant>.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi/EmployeeCollection('<ObjectID>') - Watch out for: SAP Sales Cloud does not support hard-delete of user records via API in most configurations. Deactivation is done via UserLock=true or setting UserValidityEndDate to a past date.
Request example
PATCH /sap/c4c/odata/v1/c4codataapi/EmployeeCollection('00163E0B...')
Content-Type: application/json
X-CSRF-Token: <token>
Authorization: Bearer <token>
{"UserLock":true}
Response example
HTTP 204 No Content
Filter Users by Email
- Method: GET
- URL:
https://<tenant>.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi/EmployeeCollection?$filter=Email eq 'jane.doe@example.com'&$format=json - Watch out for: Not all fields are filterable. Refer to the API Business Hub metadata ($metadata) to confirm filterable properties.
Request example
GET /sap/c4c/odata/v1/c4codataapi/EmployeeCollection?$filter=Email%20eq%20'jane.doe@example.com'&$format=json
Authorization: Bearer <token>
Response example
{
"d": {
"results": [
{"ObjectID": "00163E0A...", "Email": "jane.doe@example.com"}
]
}
}
Fetch CSRF Token
- Method: GET
- URL:
https://<tenant>.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi - Watch out for: The CSRF token and session cookie must both be included in subsequent POST/PATCH/DELETE requests. Tokens expire with the session.
Request example
GET /sap/c4c/odata/v1/c4codataapi
X-CSRF-Token: Fetch
Authorization: Bearer <token>
Response example
HTTP 200 OK
X-CSRF-Token: <csrf-token-value>
Set-Cookie: sap-usercontext=...
Batch Request
- Method: POST
- URL:
https://<tenant>.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi/$batch - Watch out for: Use $batch to reduce API call volume. SAP recommends batch for bulk provisioning scenarios.
Request example
POST /sap/c4c/odata/v1/c4codataapi/$batch
Content-Type: multipart/mixed; boundary=batch_001
X-CSRF-Token: <token>
--batch_001
Content-Type: application/http
GET EmployeeCollection('00163E0A...')
--batch_001--
Response example
HTTP 202 Accepted
multipart/mixed response with individual operation results
Rate limits, pagination, and events
Rate limits: SAP does not publicly document specific rate limits for SAP Sales Cloud OData APIs. Throttling behavior is tenant-specific and managed at the infrastructure level.
Rate-limit headers: No
Retry-After header: No
Rate-limit notes: No publicly documented rate limit headers or Retry-After behavior found in official SAP Sales Cloud API documentation. SAP recommends batching OData requests using $batch to reduce call volume.
Pagination method: offset
Default page size: 1000
Max page size: 1000
Pagination pointer: $skip / $top
Webhooks available: No
Webhook notes: SAP Sales Cloud does not natively expose outbound webhooks for user lifecycle events in the traditional sense. Event-driven integration is achieved via SAP Integration Suite (Cloud Integration / Event Mesh) or by polling the OData API.
Alternative event strategy: Use SAP Integration Suite / SAP Cloud Integration to subscribe to SAP Sales Cloud business events and forward them to external systems. Alternatively, poll EmployeeCollection with $filter on ChangedOn timestamp for delta detection.
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise (SCIM provisioning requires SAP Identity Provisioning Service, which is included in SAP BTP enterprise agreements; SCIM is disabled by default and must be enabled via SAP support ticket)
Endpoint: SCIM endpoint is not a direct tenant URL; provisioning is mediated by SAP Identity Provisioning Service (IPS). The IPS connector for SAP Sales Cloud uses the OData API internally. The IPS admin console is at: https://ipsproxy
.accounts400.ondemand.com/ips/ Supported operations: Create User, Read User, Update User, Deactivate User, Read Groups (Business Roles), Assign User to Group
Limitations:
- SCIM is not exposed as a direct endpoint on the SAP Sales Cloud tenant; it is mediated through SAP Identity Provisioning Service (IPS).
- SCIM must be explicitly enabled by raising an SAP support ticket; it is disabled by default.
- SSO (SAML) must be configured as a prerequisite before SCIM provisioning can be activated.
- Hard delete of users is not supported; deprovisioning sets UserLock=true or adjusts validity dates.
- Group/role provisioning support depends on IPS connector version and may require additional configuration.
- Microsoft Entra ID (Azure AD) is the primary documented IdP for SCIM via IPS; other IdPs may require custom IPS configuration.
- The IPS connector maps SCIM attributes to SAP Sales Cloud OData fields; custom attribute mappings require IPS transformation rules.
Common scenarios
Three integration patterns cover the primary lifecycle operations.
For direct provisioning, POST to EmployeeCollection with EmployeeID, FirstName, LastName, Email, UserName, LanguageCode, and EmployeeRoleCode after obtaining a Bearer token and a fresh CSRF token.
Business Role assignment may not be fully automatable via OData in all tenant configurations - verify against your tenant's $metadata for role assignment sub-entities.
For automated lifecycle management at scale, the IPS-mediated SCIM path is the supported route: configure SAP Sales Cloud as a target system in the IPS admin console, map SCIM attributes to OData fields via IPS transformation rules, and connect your IdP (Microsoft Entra ID is the primary documented source).
Attribute mapping errors in IPS transformation rules are the most common failure point;
review IPS job logs on any provisioning failure.
For deactivation, PATCH EmployeeCollection('
hard delete is not supported via the OData API in standard configurations, and the record is retained for audit purposes.
When using IPS/SCIM, deprovisioning from the source IdP triggers the IPS job to set the lock flag automatically, keeping the identity graph consistent across connected systems.
Provision a new sales rep via OData API
- Authenticate: POST to OAuth token endpoint with client credentials or SAML Bearer to obtain Bearer token.
- Fetch CSRF token: GET /sap/c4c/odata/v1/c4codataapi with header X-CSRF-Token: Fetch; capture token and session cookie from response headers.
- Create employee: POST /sap/c4c/odata/v1/c4codataapi/EmployeeCollection with JSON body containing EmployeeID, FirstName, LastName, Email, UserName, LanguageCode, and EmployeeRoleCode.
- Verify creation: GET /sap/c4c/odata/v1/c4codataapi/EmployeeCollection?$filter=UserName eq 'new.user'&$format=json to confirm the record exists and capture ObjectID.
- Assign business role: Navigate to SAP Sales Cloud admin UI or use the BusinessRoleAssignment sub-entity if exposed in your tenant's $metadata.
Watch out for: Business role assignment may not be fully automatable via OData in all tenant configurations; verify $metadata for role assignment entities. CSRF token must be refreshed if the session expires.
Automate user lifecycle with SAP Identity Provisioning Service (SCIM)
- Raise an SAP support ticket to enable SCIM/IPS provisioning for your SAP Sales Cloud tenant.
- Configure SAML SSO between your IdP (e.g., Microsoft Entra ID) and SAP Sales Cloud as a prerequisite.
- In SAP IPS admin console, add SAP Sales Cloud as a target system and configure the connector with tenant URL, admin credentials or OAuth details.
- Configure attribute mappings in IPS transformation rules to map IdP user attributes (e.g., userName, emails, name) to SAP Sales Cloud OData fields.
- Add your IdP (e.g., Entra ID) as a source system in IPS and configure the SCIM provisioning job.
- Run a test provisioning job and verify users appear in SAP Sales Cloud EmployeeCollection.
- Enable scheduled provisioning jobs for ongoing sync.
Watch out for: IPS connector for SAP Sales Cloud uses the OData API internally, not a direct SCIM endpoint on the tenant. Attribute mapping errors in IPS transformation rules are a common failure point and require reviewing IPS job logs.
Deactivate a departed user
- Look up the user's ObjectID: GET /EmployeeCollection?$filter=Email eq 'departed@example.com'&$format=json.
- Fetch a fresh CSRF token via GET with X-CSRF-Token: Fetch header.
- Lock the account: PATCH /EmployeeCollection('
') with body {"UserLock": true} and headers X-CSRF-Token and session cookie. - Optionally set UserValidityEndDate to today's date for additional deactivation signal.
- Verify: GET the employee record and confirm UserLock is true.
Watch out for: Hard delete is not supported via the OData API in standard SAP Sales Cloud configurations. Locking the user prevents login but retains the record for audit/historical data purposes. If using IPS/SCIM, deprovisioning from the source IdP will trigger the IPS job to set the lock flag automatically.
Why building this yourself is a trap
Several non-obvious behaviors create integration risk. The ObjectID (system key) and EmployeeID (business key) are distinct - lookups by business key require $filter=EmployeeID eq 'E001'; passing EmployeeID where ObjectID is expected silently fails or returns no results.
Not all fields in EmployeeCollection are filterable; attempting $filter on a non-filterable field returns an error rather than an empty result set - always validate against the $metadata document. OAuth SAML Bearer Assertion flow requires a correctly configured trust relationship between the tenant and the IdP;
misconfigured trust produces 401 errors with minimal diagnostic detail. Tenant base URLs vary by data center region (e.g., .crm.ondemand.com, .crm.eu1.ondemand.com) - hardcoding the wrong base URL is a silent failure in multi-region deployments. The identity graph is only as current as the last IPS provisioning job run;
real-time sync requires polling EmployeeCollection with a $filter on the ChangedOn timestamp as a delta detection fallback, since SAP Sales Cloud does not natively expose outbound webhooks for user lifecycle events.
Automate SAP Sales Cloud 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.