Stitchflow
SAP SuccessFactors logo

SAP SuccessFactors User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 16, 2026

Summary and recommendation

The SAP SuccessFactors OData v2 API exposes user lifecycle operations under `/odata/v2/User` and, for Employee Central tenants, a separate entity model spanning `PerPerson`, `EmpEmployment`, and `EmpJob`.

Authentication requires OAuth 2.0 via SAML Bearer Assertion or X.509 mutual TLS-Basic Auth is deprecated and will be removed in November 2026.

The API user must hold explicit Role-Based Permission grants in Admin Center;

an OAuth token alone is insufficient without corresponding RBP assignments.

SCIM 2.0 is available via SAP Cloud Identity Services (Identity Provisioning Service) and is the recommended path for building an identity graph across enterprise systems, but it requires separate SAP Cloud Identity Services licensing, SSO as a prerequisite, and IPS transformation configuration for non-standard attribute mappings.

API quick reference

Has user APIYes
Auth methodOAuth 2.0 (SAML Bearer Assertion) or X.509 certificate; Basic Auth deprecated November 2026
Base URLOfficial docs
SCIM availableYes
SCIM plan requiredRequires SAP Cloud Identity Services (Identity Provisioning Service); available with Enterprise-tier SuccessFactors contracts. SSO prerequisite applies.

Authentication

Auth method: OAuth 2.0 (SAML Bearer Assertion) or X.509 certificate; Basic Auth deprecated November 2026

Setup steps

  1. Register an OAuth 2.0 client application in SuccessFactors Admin Center under 'Manage OAuth2 Client Applications'.
  2. Generate or upload an X.509 certificate (or use SAML assertion) for the client application.
  3. Obtain a SAML assertion from your IdP signed with the registered certificate.
  4. POST the SAML assertion to https:///oauth/token with grant_type=urn:ietf:params:oauth:grant-type:saml2-bearer to receive a Bearer access token.
  5. Include the Bearer token in the Authorization header for all subsequent API requests.
  6. For service-to-service (no user context), use the OAuth 2.0 client credentials flow with X.509 mutual TLS as Basic Auth is being retired.

Required scopes

Scope Description Required for
API permission roles (not OAuth scopes) SuccessFactors uses role-based permissions (RBP) rather than discrete OAuth scope strings. The API user must be granted specific Permission Roles in Admin Center. All API operations
Manage Users RBP permission allowing create, read, update, and deactivate of User records via OData API. User CRUD via /User entity
View Users RBP permission for read-only access to User and PerPerson entities. GET /User, GET /PerPerson
Manage Employee Central Required to read/write Employee Central entities such as EmpEmployment, EmpJob, and PerPersonal. Employee Central compound employee operations

User object / data model

Field Type Description On create On update Notes
userId String Unique identifier for the user; maps to login name. required immutable Primary key for the User entity.
username String Login username, often same as userId. required optional
firstName String User's first name. required optional
lastName String User's last name. required optional
email String Primary email address. required optional
status String Account status: 'active', 'inactive', 'suspended'. optional (defaults to active) optional Set to 'inactive' to deactivate; hard delete is generally not supported.
defaultLocale String User's preferred locale (e.g., en_US). optional optional
timeZone String User's time zone (e.g., US/Eastern). optional optional
department String Department name. optional optional
division String Division name. optional optional
title String Job title. optional optional
manager String (userId) userId of the user's direct manager. optional optional Navigation property; must reference a valid userId.
hr String (userId) userId of the assigned HR representative. optional optional
hireDate DateTime Employee hire date. optional optional Format: /Date(milliseconds)/
lastModified DateTime Timestamp of last modification. system-set system-set Read-only; useful for delta sync queries.
password String User password (write-only). optional optional Never returned in GET responses.
empId String Employee ID, distinct from userId. optional optional Used in Employee Central; links to PerPerson entity.
custom01–custom15 String Configurable custom fields on the User entity. optional optional Field labels and usage are tenant-configured.

Core endpoints

List Users

  • Method: GET
  • URL: https://<host>/odata/v2/User?$top=100&$skip=0&$select=userId,firstName,lastName,email,status
  • Watch out for: Default page size is 20; always specify $top up to 1000. Use $filter=lastModified gt datetime'...' for delta syncs.

Request example

GET /odata/v2/User?$top=100&$skip=0&$select=userId,firstName,lastName,email,status
Authorization: Bearer <token>
Accept: application/json

Response example

{
  "d": {
    "results": [
      {"userId":"jdoe","firstName":"Jane","lastName":"Doe","email":"jdoe@example.com","status":"active"}
    ]
  }
}

Get Single User

  • Method: GET
  • URL: https://<host>/odata/v2/User('<userId>')
  • Watch out for: userId is case-sensitive. Requesting a non-existent user returns HTTP 404.

Request example

GET /odata/v2/User('jdoe')?$select=userId,firstName,lastName,email,status
Authorization: Bearer <token>
Accept: application/json

Response example

{
  "d": {
    "userId":"jdoe",
    "firstName":"Jane",
    "lastName":"Doe",
    "email":"jdoe@example.com",
    "status":"active"
  }
}

Create User

  • Method: POST
  • URL: https://<host>/odata/v2/User
  • Watch out for: userId must be unique across the tenant. Employee Central hires require additional EmpEmployment and EmpJob entities created separately.

Request example

POST /odata/v2/User
Authorization: Bearer <token>
Content-Type: application/json

{"userId":"jdoe","firstName":"Jane","lastName":"Doe","email":"jdoe@example.com","status":"active"}

Response example

{
  "d": {
    "userId":"jdoe",
    "firstName":"Jane",
    "lastName":"Doe",
    "email":"jdoe@example.com",
    "status":"active"
  }
}

Update User (full/partial)

  • Method: PATCH
  • URL: https://<host>/odata/v2/User('<userId>')
  • Watch out for: OData v2 uses MERGE semantics for partial updates. Some clients must send X-HTTP-Method: MERGE header with a POST if PATCH is not supported.

Request example

PATCH /odata/v2/User('jdoe')
Authorization: Bearer <token>
Content-Type: application/json
X-HTTP-Method: MERGE

{"email":"jane.doe@example.com","title":"Senior Engineer"}

Response example

HTTP 204 No Content

Deactivate User

  • Method: PATCH
  • URL: https://<host>/odata/v2/User('<userId>')
  • Watch out for: Hard delete of users is not supported via the standard OData API. Set status to 'inactive' to deactivate. Purge requires Admin Center or a separate data retention process.

Request example

PATCH /odata/v2/User('jdoe')
Authorization: Bearer <token>
Content-Type: application/json
X-HTTP-Method: MERGE

{"status":"inactive"}

Response example

HTTP 204 No Content

Batch Request

  • Method: POST
  • URL: https://<host>/odata/v2/$batch
  • Watch out for: Batch requests reduce API call overhead. Maximum operations per batch are not publicly documented; SAP recommends keeping batches small to avoid timeouts.

Request example

POST /odata/v2/$batch
Authorization: Bearer <token>
Content-Type: multipart/mixed; boundary=batch_1

--batch_1
Content-Type: application/http
GET User('jdoe') HTTP/1.1
--batch_1--

Response example

HTTP 202 with multipart/mixed body containing individual operation responses.

Query Users (delta/filter)

  • Method: GET
  • URL: https://<host>/odata/v2/User?$filter=lastModified gt datetime'2024-01-01T00:00:00'&$top=1000
  • Watch out for: datetime filter format must match OData v2 syntax exactly. Timestamps are returned as /Date(ms)/ epoch format.

Request example

GET /odata/v2/User?$filter=lastModified%20gt%20datetime'2024-01-01T00:00:00'&$top=1000
Authorization: Bearer <token>
Accept: application/json

Response example

{
  "d": {
    "results": [
      {"userId":"jdoe","lastModified":"/Date(1704067200000)/","status":"active"}
    ]
  }
}

Get Compound Employee (Employee Central)

  • Method: GET
  • URL: https://<host>/odata/v2/PerPerson('<personIdExternal>')?$expand=employmentNav,personalInfoNav
  • Watch out for: Employee Central uses a separate entity model (PerPerson, EmpEmployment, EmpJob) from the basic User entity. Both must be managed for full employee lifecycle in EC-enabled tenants.

Request example

GET /odata/v2/PerPerson('EMP001')?$expand=employmentNav,personalInfoNav
Authorization: Bearer <token>
Accept: application/json

Response example

{
  "d": {
    "personIdExternal":"EMP001",
    "employmentNav":{"results":[{"userId":"jdoe","startDate":"/Date(1609459200000)/"}]},
    "personalInfoNav":{"results":[{"firstName":"Jane","lastName":"Doe"}]}
  }
}

Rate limits, pagination, and events

  • Rate limits: SAP SuccessFactors enforces API concurrency and throughput limits at the tenant level. Specific numeric limits are not publicly documented and vary by data center and contract. SAP recommends using batch requests ($batch) to reduce call volume.
  • Rate-limit headers: No
  • Retry-After header: No
  • Rate-limit notes: HTTP 429 or 503 responses may be returned when limits are exceeded. SAP documentation does not specify standard rate-limit response headers. Use exponential backoff and $batch to mitigate throttling.
  • Pagination method: offset
  • Default page size: 20
  • Max page size: 1000
  • Pagination pointer: $top / $skip
Plan Limit Concurrent
All tenants Not publicly specified; enforced per tenant by SAP operations 0
  • Webhooks available: Yes
  • Webhook notes: SAP SuccessFactors supports intelligent services (event-based notifications) via the Intelligent Services Center. Events can trigger outbound HTTP calls or integration flows via SAP Integration Suite. This is not a traditional webhook registration API but an event-driven integration framework.
  • Alternative event strategy: For near-real-time sync without Intelligent Services, use OData $filter on lastModified with scheduled polling. SAP Integration Suite (CPI) is the recommended middleware for event-driven integrations.
  • Webhook events: New Hire, Termination, Job Information Change, Personal Information Change, Position Change, Leave of Absence

SCIM API status

  • SCIM available: Yes

  • SCIM version: 2.0

  • Plan required: Requires SAP Cloud Identity Services (Identity Provisioning Service); available with Enterprise-tier SuccessFactors contracts. SSO prerequisite applies.

  • Endpoint: Provisioned dynamically by SAP Identity Provisioning Service (IPS); format: https://.accounts.ondemand.com/ips/service/scim/Users

  • Supported operations: GET /Users, GET /Users/{id}, POST /Users, PUT /Users/{id}, PATCH /Users/{id}, DELETE /Users/{id}, GET /Groups, POST /Groups, PATCH /Groups/{id}, DELETE /Groups/{id}

Limitations:

  • SCIM endpoint is managed by SAP IPS, not directly by SuccessFactors; endpoint URL is tenant-specific and not a fixed public URL.
  • Basic Authentication for IPS is deprecated as of November 2026; must migrate to X.509 certificate-based authentication.
  • Not all SuccessFactors custom fields are automatically mapped in SCIM; custom attribute mappings must be configured in IPS transformation rules.
  • Employee Central compound employee data (EmpJob, EmpEmployment) requires IPS transformation configuration beyond standard SCIM User schema.
  • SCIM DELETE maps to user deactivation (status=inactive), not hard delete.
  • SSO (SAML) must be configured as a prerequisite for IPS-based SCIM provisioning.

Common scenarios

Three integration patterns cover the majority of lifecycle use cases.

For new hire provisioning, POST to /odata/v2/User for the base record, then create EmpEmployment and EmpJob entities separately if the tenant runs Employee Central-attempting EC entity creation on a non-EC tenant returns an error, so tenant type must be confirmed before integration design.

For delta sync, filter on lastModified gt datetime'<timestamp>' with $top=1000 and paginate via $skip;

note that EC entity changes (EmpJob, EmpEmployment) carry their own lastModifiedDateTime fields and must be queried independently to build a complete identity graph.

For offboarding, PATCH status to inactive using MERGE semantics (send X-HTTP-Method: MERGE if the client does not support PATCH natively);

hard delete is not available via API, and setting status=inactive does not terminate active SSO sessions-IdP-side session revocation must be coordinated separately.

Provision a new hire from an external HR system

  1. Authenticate: POST SAML assertion to /oauth/token to obtain Bearer token.
  2. Check for existing user: GET /odata/v2/User('') – handle 404 as new user.
  3. Create basic user record: POST /odata/v2/User with required fields (userId, firstName, lastName, email, status=active).
  4. If Employee Central is enabled, create PerPerson, EmpEmployment, and EmpJob entities via separate POST calls with the same personIdExternal.
  5. Assign manager: PATCH /odata/v2/User('') with manager field set to manager's userId.
  6. Verify creation: GET /odata/v2/User('') and confirm status=active.

Watch out for: EC and non-EC tenants require different entity creation flows. Attempting to create EmpEmployment on a non-EC tenant will return an error. Confirm tenant type before integration design.

Delta sync of changed users for downstream system

  1. Store the timestamp of the last successful sync.
  2. Authenticate and GET /odata/v2/User?$filter=lastModified gt datetime''&$top=1000&$skip=0.
  3. Iterate pages using $skip until results array is empty.
  4. For each changed user, apply updates to the downstream system.
  5. Update stored sync timestamp to current time after successful processing.

Watch out for: lastModified reflects changes to the User entity only; changes to related EC entities (EmpJob, EmpEmployment) have their own lastModifiedDateTime fields and must be queried separately.

Deactivate a terminated employee

  1. Authenticate and obtain Bearer token.
  2. PATCH /odata/v2/User('') with body {"status":"inactive"} and X-HTTP-Method: MERGE header.
  3. If Employee Central is enabled, also terminate the EmpEmployment record by setting endDate via PATCH on the EmpEmployment entity.
  4. Confirm deactivation: GET /odata/v2/User('') and verify status=inactive.

Watch out for: Setting status=inactive does not automatically revoke SSO sessions or downstream system access. Coordinate with IdP session termination separately. Hard delete is not available via API.

Why building this yourself is a trap

The most common integration failure points are architectural, not syntactic. The dual entity model (User vs. PerPerson/EmpEmployment) means an integration built against one model will silently miss data from the other;

this is particularly acute for role, job, and employment data that lives exclusively in EC entities. Timestamp handling requires explicit OData v2 syntax (/Date(ms)/ epoch format on responses, datetime'...' literals in filters)-standard OData v4 or ISO 8601 assumptions will produce malformed queries or misparse responses. Rate limits are tenant-enforced and not publicly documented;

SAP returns HTTP 429 or 503 without standard retry headers, making exponential backoff and $batch usage non-optional for production workloads. Finally, the data center host is region-specific and not a universal endpoint, so hardcoding a base URL without tenant-region awareness will break cross-region deployments.

Automate SAP SuccessFactors 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.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 16, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

15Five logo

15Five

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

15Five uses a fixed role-based permission model with six predefined roles: Account Admin, HR Admin, Billing Admin, Group Admin, Manager, and Employee. No custom roles can be constructed. User management lives at Settings gear → People → Manage people p

1Password logo

1Password

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

1Password's admin console at my.1password.com covers the full user lifecycle — invitations, group assignments, vault access, suspension, and deletion — without any third-party tooling. Like every app that mixes role-based and resource-level permissions

8x8 logo

8x8

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

8x8 Admin Console supports full lifecycle user management — create, deactivate, and delete — across its X Series unified communications platform. Every app a user can access (8x8 Work desktop, mobile, web, Agent Workspace) is gated by license assignmen