SF HCM Odata API Dev
SF HCM Odata API Dev
2H 2020
Planned retirement of HTTP Basic Au We plan to retire HTTP Basic Authentica HTTP Basic Authentication (Deprecated)
thentication tion in favor of more secure authentica [page 11]
tion methods such as OAuth.
Return all upsert errors in a $batch You can now use the Upsert Behavior in a $batch ChangeSet
ChangeSet enableUpsertResponseExtensi
[page 107]
onInChangeset parameter to return
all upsert errors in a $batch ChangeSet. enableUpsertResponseExtensionInChan
geset [page 92]
1H 2020
Added new data centers DC41 and DC47 Information about DC41 and DC47 is About HXM Suite OData APIs [page 8]
added to the list of API servers.
Unsafe password options are deprecated Options that use username, user ID, and Creating and Editing Users Using an API
in Manage API Option Profiles e-mail as default passwords have been Option Profile [page 186]
deprecated in the Manage API Option
Profiles tool. You can no longer create API
option profiles using these password op
tions.
Added a new topic for $count Use $count to return the count number $count [page 55]
of entries identified by the resource path
in the URI.
Added a new topic for $inlinecount Use $inlinecount to return a list of $inlinecount [page 57]
entries and the count number identified
by the resource path in the URI.
Audit data purge in preview instances API audit logs in preview instances are Enabling API Audit Logs [page 182]
subject to the retention time defined by
SAP SuccessFactors. Audit data that
meets the criteria is automatically
purged.
Merged audit log topics into one task The OData API and SFAPI audit log topics Enabling API Audit Logs [page 182]
topic are now merged into one topic.
Removed user-based permission infor References to the legacy user-based per Permissions for API Center [page 177]
mation in API Center mission settings in API Center have been
removed.
Q4 2019
New topics for generating a self-signed X. A new topic is added to explain how to Creating a Self-Signed X.509 Certificate
509 certificate generate self-signed X.509 certificates [page 15]
for OData API authentication using
OAuth2.
Bind an OAuth2 client application to a You can now restrict the access of an Registering Your OAuth2 Client Applica
technical user OAuth2 client application to a technical tion [page 13]
user by enabling the Bind to Technical
User option when you register the appli
cation in Manage OAuth2 Client
Applications.
Timezone information for data centers Timezone information is now available for About HXM Suite OData APIs [page 8]
each data center.
New service limit for large MDF OData re We've added a service limit for MDF MDF OData API Operations [page 130]
quests OData requests that return large data
volumes to enhance the stability and per
formance of MDF OData API.
MDF upsert operations now return busi When you upsert an MDF OData entity, Edit Operations [page 155]
ness keys in response business keys are now available for suc
cessful and failed records if they're pro
vided in the request payload.
Snapshot-based pagination support in All MDF Foundation Objects now support Snapshot-Based Pagination [page 67]
MDF Foundation Objects snapshot-based pagination.
EntitySet information in metadata When you query an entity metadata us Retrieving Metadata [page 195]
ing Entity('EntityName'), the re
sponse now includes EntitySet of a navi
gation property.
MDF OData API documentation available The MDF OData API section contains in MDF OData API [page 111]
as part of the guide formation about enabling access to MDF
objects through OData API, opertions
specific to MDF entities, query and edit
parameters, as well as examples. Previ
ously, this information was only available
in the Implementing the Metadata
Framework (MDF) guide.
MDF OData API now supports all logical When you run snapshot-based pagina Snapshot-Based Pagination [page 67]
operators of $filter on the lastModi tion queries, you can now use previously
fiedDateTime property in snapshot- unsupported logical operators lt and le
based pagination queries. in $filter query options with lastMo
difiedDateTime property.
MDF OData API now supports multiple In MDF OData API, you can now use mul Snapshot-Based Pagination [page 67]
lastModifiedDateTime filter conditions tiple filters on lastModifiedDateTime for
and lastModifiedDateTime filters of navi entities in navigation. This applies to nav
gation properties in snapshot-based pag igation properties, MDF picklist entities,
ination queries. and entities in Valid When and Composite
associations. Previously, lastModifiedDa-
teTime filter on navigation properties or
multiple lastModifiedDateTime filters
weren’t supported for snapshot-based
pagination in MDF OData API.
Service limit for large $expand in snap We have set a 300,000 size limit for $ex Snapshot-Based Pagination [page 67]
shot-based pagination. pand operations in OData API. If the ex
panding size of your query exceeds the
limit, an error occurs and you’ll have to
tune your query to decrease the data vol
ume.
Added OData error message topics Two new topics have been added for Logon Errors [page 204]
OData API common errors and logon er
rors. Common OData Errors [page 200]
Added a new topic on how to create links You can use $links to create referen Working with Links in Effective-Dated En
for effective-dated records ces between two effective-dated records, tities [page 100]
or between effective-dated and non ef
fective-dated records.
New topics for MDF OData parameters New topics have been added for the fol recordStatus [page 135]
lowing MDF OData parameters:
workflowConfirmed [page 155]
● recordStatus
● workflowConfirmed
The Open Data Protocol (OData) is a standardized protocol for creating and consuming data APIs. OData builds on
core protocols like HTTP, and commonly accepted methodologies like REST. The result is a uniform way to expose
full-featured data APIs. OData provides both a standard for how to represent your data and a metadata method to
describe the structure of your data, and the operations available in your API. SuccessFactors OData API service is
based on OData V2.0.
The HXM Suite OData API is SuccessFactors Web Services API based on OData protocol intended to enable access
to data in the SuccessFactors system. The API is data oriented. This API provides methods for CRUD style access
(Create, Read, Update and Delete). The API is best used for frequent or real time requests for small amounts of
data. Large data requests are better handled by batch FTP processes. This OData API is used to configure entities.
Each SuccessFactors module can be accessed using its own set of entities.
SAP SuccessFactors offers the following OData API user guides on the Help Portal:
● The SAP SuccessFactors HXM Suite OData API: Developer Guide provides information for developers who wish
to use the OData API for integration, such as authorization setup, OData operations, available OData tools in
the API center, etc.
● The SAP SuccessFactors HXM Suite OData API: Reference Guide provides a complete list of entities available in
the current release with entity-specific details and use cases.
● The SAP SuccessFactors Employee Central OData API: Reference Guide provides a complete list of entities in
Employee Central available in the current release with entity-specific details and use cases.
Beta APIs
Beta APIs are API entities in beta release. SAP SuccessFactors provide new features with beta APIs for
demonstration and preview purposes. Beta APIs are subject to change and they are likely to have issues.
In the reference guides, a "(Beta)" suffix is added to the document title to indicate a beta API. In the OData API Data
Dictionary, beta APIs are not listed. To view the metadata of a beta API, use query https://<API-Server>/
odata/v2/beta/<Entity>/$metadata.
Note
Beta APIs are available in the /beta/ cateogry. The access to the beta category must be granted to your
company in Provisioning before you can use the APIs. Please contact SAP Cloud Support if you wish to have the
access.
The OData API feature is enabled for all instances by default, unless you manually turn it off in provisioning.
Your endpoint URLs for accessing the OData APIs depend on the data center hosting your SAP SuccessFactors
instance. Your SAP SuccessFactors support representative can tell you the data center location to use for your
instance.
Recommendation
We recommend using OAuth instead of HTTP Basic Authentication for accessing customer systems. Available
in scenarios in which the Event Connector is used to integrate platform systems with a customer system and
where the endpoint is a SOAP API.
Below is a list of data centers with location and time zone information:
This document explains the types of authentication used to access OData API, how to enable session reuse for
OData API access, and how to set exceptions for API login.
Basic Auth requires users to have admin access to the OData API and have valid accounts with usernames and
passwords. OAuth 2.0 lets all users log in regardless of whether they are SSO users, so long as they have a valid
token and the OAuth client is registered.
HTTP Basic Authentication (Basic Auth) allows you to access OData API by providing username, company ID, and
password information on an HTTP user agent, such as a web browser.
Caution
HTTP Basic Authentication will soon be retired. Please migrate to OAuth as soon as possible. For detailed
retirement plan, see the announcement in the SAP SuccessFactors Migrating Features guide.
To log in with Basic Auth, you must have the Allow Admin to Access OData API through Basic Authentication
permission.
● Username, company ID, and password are combined into a string as such: username@company
ID:password
● The resulting string literal is then encoded using Base64.
● The authentication method ("Basic") followed by a space is then put before the encoded string. For example,
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
HTTP Basic Authentication is generally considered less secure than authentication using OAuth 2.0. However, you
can increase the security by controlling the access using IP whitelisting.
Grant the required permissions to your role so that you can access OData APIs using HTTP Basic authentication.
Assign the Allow Admin to Access OData API through Basic Authentication permission under Administrator
Permissions Manage Integration Tools to the relevant role so that you can access OData APIs using Basic Auth.
You will not need to re-enter this information for subsequent queries until the browser is closed. If the user name
and password verification fails, you will need to open a new window and try again
Note
When you query in atom format in Firefox, the xml result will not be displayed, because Firefox regards atom
format as RSS content by default. You can check the result in JSON format or view the source code to check
the query result.
OAuth 2.0 lets all users log in regardless whether they’re SSO users. If you plan to use OAuth 2.0 for authentication,
you must set up the required permissions and then register your OAuth client application.
To be able to register your OAuth2 client application, you must have the Manage OAuth2 Client Applications
permission under Manage Integration Tools in Role-Based Permissions.
After you're granted the permission, you can use the Manage OAuth2 Client Applications tool to register and
manage client applications. The tool is available under Admin Center API Center .
Register your client application so that you can authenticate API users using OAuth2. After you register an
application, you'll get an exclusive API key for your application to access SAP SuccessFactors OData APIs.
Prerequisites
You have the required permissions to manage OAuth2 client applications in API Center. For more information, see
the Related Information section of this topic.
Procedure
2. Go to Admin Center API Center OAuth Configuration for OData and choose Register Client Application.
You can also access the tool by searching Manage OAuth2 Client Applications in Action Search.
3. On the new OAuth client registration screen, enter the following information:
Option Description
Company The name of your company. This value is prefilled based on the instance of
the company currently logged in.
Application URL (Required) A unique URL of the page that the client wants to display to the
end user. The page contains more information about the client application.
This is needed for 3-legged OAuth, however it isn’t currently supported.
Bind to Technical User (Optional) You can enable this option to restrict the access of the applica
tion to a technical user. A technical user is a special user created for inte
grating SAP SuccessFactors with other SAP products and solutions. Refer
to About Technical User for more information.
Technical User ID (Required if you enabled the Bind to Technical User option) Enter technical
user name of your instance. Contact your system administrator or SAP
Cloud Support if you don't know what the technical user name is.
X.509 Certificate (Required) The certificate corresponding to the private and public key used
in the OAuth 2.0 authentication process. In this flow, SAP SuccessFactors
require the public key and the client application has the private key. To reg
ister a client application, you must install the public key in SAP Success
Factors. If you supply that certificate, you must use the RSA-SHA1, RSA-
SHA2, or MD5 encryption type for authentication.
You can obtain an X.509 certificate from a trusted service provider such as
SAP Cloud Platform, or you can use a third-party tool to generate a self-
signed certificate. If neither option is avaiable, you can generate an X.509
Note
For better security, we recommend that you use a self-signed certifi-
cate or one from your trusted service provider.
Results
You’ve successfully registered your client application for OAuth2 authentication. An API key is generated and
assigned to your application. You can view the API key by choosing View on the registered application list.
You can also edit, disable, and delete an OAuth2 client registration.
Related Information
You can use tools such as OpenSSL to create a self-signed X.509 certificate.
Prerequisites
We recommend that you use OpenSSL to create the certificate. For Windows users, you can download the tool at
https://www.openssl.org . For Mac and Linux users, OpenSSL is available with the native command-line tools
such as Terminal.
Context
X.509 certificates are used in many Internet protocols, including TLS/SSL. An X.509 certificate consists of a public
key and a private key. The public key contains the identity information, such as a hostname, an organization, or an
individual. The public/private key pair is used to establish secure communication between your application and
SAP SuccessFactors.
Procedure
For Mac and Linux users, you can call OpenSSL directly in the command tool under the default path. For
Windows users, the entry point is the openssl binary, located in the installation folder, for example: C:
\Program Files\OpenSSL-Win64\bin\.
2. Follow the examples below to create an X.509 certificate:
$ openssl req -nodes -x509 -sha256 -newkey rsa:2048 -keyout private.pem -out
public.pem
$ openssl req -nodes -x509 -sha256 -newkey rsa:1024 -keyout private.pem -out
public.pem
$ openssl req -nodes -x509 -md5 -newkey rsa:1024 -keyout private.pem -out
public.pem
Note
private.pem and public.pem are the example names of the public/private key pair generated with this
command. You can change them to any names of your choice.
Option Description
Country Name Enter a two-letter country code of the entity to which the certificate is issued. A
country code represents a country or a region. Example: AU
State or Province Name Name of state or province of the entity to which the certificate is issued.
Locality Name Name of locality of the entity to which the certificate is issued.
Organization Unit Name The organization unit of the entity to which the certificate is issued.
Common Name The hostname or IP address for which the certificate is valid. The common name
(CN) represents the hostname of your application. It's technically represented by
the commonName field in the X.509 certificate. The common name doesn't in
clude any protocol, port number, or path. For example: www.bestrun.com
Results
A public/private key pair is generated and saved to the local drive with the names you specified in the command.
Caution
Only the public key is required when you register an OAuth2 client application in SAP SuccessFactors. The
private key must be kept secure under all circumstances. Do not share the private key with others. If you lose
the private key, you must create a new certificate.
-----BEGIN CERTIFICATE-----
MIIB9jCCAV+gAwIBAgIUKR82LgtkNBccdypYD26K87zZ+vYwDQYJKoZIhvcNAQEE
BQAwDTELMAkGA1UECwwCRVAwHhcNMTkwOTI2MDIwNDUyWhcNMTkxMDI2MDIwNDUy
WjANMQswCQYDVQQLDAJFUDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwKva
NZCOGcuY90/BudS+qQic+A31uM8mLtmI60R1iEjgEWGBCxSiDb2h8mQJiXwkul9W
ebaazP7hkqkdNoJgV/6NE7++GKyyS8fIhJgeWSb6EelMFhjQ0nZKzbZX5ms3I91n
twzkcHtKCQi/gi/Rouhlk/P/QVcrzSgHUHqJNy0CAwEAAaNTMFEwHQYDVR0OBBYE
FHHbgqnnhm3GAJ4gy2IuEDxpLye7MB8GA1UdIwQYMBaAFHHbgqnnhm3GAJ4gy2Iu
EDxpLye7MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAG5CoqcEy
15vUpj5VfJeR/DS70tPIinp/TCC9kRO/++TSnPbqVcfPr8vIyc4L3MPKjXFBsefE
vtfHGGucVtv5N1+4U/b9NxNFbuH2MP7W3swZ4WM72Na+W6iOhwesOr0p3IcOfxc3
RNCnagFmtbDFxAlPXQ0d+m+N5gxLRoCX1hE=
-----END CERTIFICATE-----
You can create an X.509 certificate in SAP SuccessFactors HXM Suite if you're unable to create a self-signed
certificate.
Context
Caution
We don’t recommend creating the X-509 certificate in API Center and downloading the private key. This
method is less secure compared with a self-signed certificate because downloading the private key increases
the risk of exposing it. This method should only be used if the client is unable to create a self-signed X.509
certificate.
Procedure
2. Go to Admin Center API Center OAuth Configuration for OData and choose Register Client Application.
You can also access the tool by searching Manage OAuth2 Client Applications in Action Search.
3. On the new OAuth client registration screen, choose Generate X.509 Certificate and enter the following
information:
Option Description
Common Name The hostname or IP address for which the certificate is valid.
The common name (CN) represents the hostname of your
application. It's technically represented by the common
Name field in the X.509 certificate. The common name
doesn't include any protocol, port number, or path. For ex
ample: www.bestrun.com
Organization Unit (Optional) The organization unit of the entity to which the
certificate is issued.
Validity The number of days for which you want the X.509 certificate
to be valid.
4. Choose Generate.
Results
A new X.509 certificate is generated and filled in the X.509 Certificate field on the new OAuth2 client registration
screen. Continue your registration in Registering Your OAuth2 Client Application [page 13] with this certificate.
Caution
Both the public key and private key are available to you in the generated certificate. You must save the private
key before you register your client application. Only the public key is available for viewing when the client
application is registered. The private key must be kept secure under all circumstances. Do not share the private
key with others. If you lose the private key, you create a new one.
You can view, modify, enable, disable, and delete an existing OAuth2 client application.
Procedure
2. Go to Admin Center API Center OAuth Configuration for OData . You can also access the tool by
searching Manage OAuth2 Client Applications in Action Search.
○ View: To view the registration information, including the API key assigned to your application.
Caution
If you modify the X.509 certificate, the system assigns a new API key to the application. Make sure you
update all your integrations with the new key.
SAP SuccessFactors OData API supports two-legged authentication for OAuth2. You can use our OData APIs to
generate access tokens for OAuth2 authentication.
Here is the list of APIs and the recommended order for using them:
Step 1 /oauth/idp Pass a private key to this API to generate a signed SAML asser
tion.
Note
This step is only required for SAML assertions generated in
API Center. A more secure approach is to use your own
trusted identity provider to generate the assertion.
Caution
The private key must be kept secure under all circumstances.
Don’t share the private key with others.
Step 2 /oauth/token Pass the signed SAML assertion to this API to generate a new ac
cess token. Once a token is generated, you can use it for authenti
cation.
Step 3 /odata/v2 Use the access token for authentication and access OData APIs.
Step 4 /oauth/validate Pass the access token to the API and verify if it’s still valid. A token
is only valid for 24 hours after it’s generated.
Related Information
Use API /oauth/idp to generate a Security Assertion Markup Language (SAML) assertion for OAuth2
authentication.
Context
Caution
To generate a SAML assertion using the /oauth/idp API, you’re required to provide an X.509 private key. We
recommend that you use your own trusted identity provider to generate a SAML assertion without the risk of
exposing your private key.
Example
URI https://<API-Server>/oauth/idp
Note
When the use_email value is set to "true", the subject nameId format in the
generated SAML assertion changes from nameid-format:unspecified to
nameid-format:emailAddress. An email address has to be mapped to a
unique user. If such email can be mapped to more than one userId, an HTTP 401
error "Unable to map [email protected] to a valid BizX User ID" will be returned.
Don’t set the values of use_username and use_email to "true" at the same
time.
A successful response returns a header with status code 200 OK and a Base64-encoded SAML assertion. You can
use this assertion to request an access token in the next step.
QE4pB4mXg7+JPzOiL6LetEhfpad5sCYajmf2lFALHjzMbkAUL7zVCsXh3IVCKRkC/89Lv7EESscH
7/xITBXyzX58k1f+WiIoEbDlf2ZWgDLJhmWeR2vPTl/
szeDF9FuUk0jbCPxBQTj8ufqTTnkCf7EM
pTdv+Ytqpz4EcDmYGYxbbQyp8+xezZ1CDMxENg==
</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
MIICDTCCAXagAwIBAgIETJj9LjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJVUzEbMBkGA1UE
ChMSU3VjY2Vzc2ZhY3RvcnMuY29tMQwwCgYDVQQLEwNPcHMxETAPBgNVBAMTCFNGIEFkbWluMB4X
DTEwMDkyMTE4NDUwMloXDTI1MDkxOTE4NDUwMlowSzELMAkGA1UEBhMCVVMxGzAZBgNVBAoTElN1
Y2Nlc3NmYWN0b3JeLmNvbTEMMAoGA1UECxMDT3BzMREwDwYDVQQDEwhTRiBBZG1pbjCBnzANBgkq
hkiG9w0BAQEFAAOQjQAwgYkCgYEArA9RLNnL9Pt6xynFfYfa8VXAXFDG9Y8xkgs3lhIOlsjqEYwb
SoghiqJIJvfYM45kx3aS7ZrN96tAR5uUupEsu/
GcS6ACxhfruW+BY6uw8v6/w2vXhBdfFjBoO+Ke
Lx4k3llleVgKsmNlf81okOXv1ree8wErfZ3ssnNxkuQgGB0CAwEAATANBgkqhkiG9w0BAQUFAAOB
gQBeBCSMFnY8TB6jtWoSP/lorBudhptgvO7/3r+l/
QK0hdk6CVv+VQmSilNPgWVgU9ktZGbNkZhw
IgwnqIQHAi6631ufkYQJB+48YUe1q/
pv6EWaeIwGvcGYSXZp/E/aGZPtceTIXFPfqOyHQoFtb0nq
MMFWoDhpXUHmlroyTc9sGg==
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
<saml2:Subject>
<saml2:NameID Format="urn:oasis:names:tc:SAML:
1.1:nameid-format:unspecified">UserABC</saml2:NameID>
<saml2:SubjectConfirmation
Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml2:SubjectConfirmationData
NotOnOrAfter="2018-09-10T12:30:19.690Z" Recipient="https://<hostname>/oauth/idp"/>
</saml2:SubjectConfirmation>
</saml2:Subject>
<saml2:Conditions NotBefore="2018-09-10T12:10:19.690Z"
NotOnOrAfter="2018-09-10T12:30:19.690Z">
<saml2:AudienceRestriction>
With a SAML assertion, you can now call API oauth/token to request an access token for authentication with the
API server.
Context
The API returns the token type, expiration time in seconds, and the token value you can use to authorize API
requests. An access token expires in 24 hours after it's generated.
Example
URI https://<API-Server>/oauth/token
Sample response:
{
"access_token": "eyJ0b2tlbkNvbnRlbnQ***ZMm5Tdz0ifQ==",
"token_type": "Bearer",
"expires_in": 86399
}
Context
An access token expires within 24 hours after it's generated. You can use this API to check if an access token is still
valid.
Example
A valid token returns status 200 OK in the header. The response body contains the access token, token type, and
expiry time in seconds. Example:
{
"access_token": "<Your Bearer token>",
"token_type": "Bearer",
"expires_in": 86312
}
Enable session reuse to reduce the number of sessions and optimize the performance of API calls.
Context
OData API is not stateful. By default, each OData call needs to go through authentication and authorization. In
reality, a user session is created for each API call. Creating user session is a resource-intensive process. Large
number of concurrent API calls will stress the server and drastically increase response time. To optimize
performance, we encourage you to reuse sessions whenever you can.
Procedure
The first successful login returns a header, Set-Cookie and a CSRF token named X-CSRF-Token. For example:
Note
As of 2019 Q2 Release, the path for OData sessions has changed from "/" to "/odata" to indicate that it's an
OData session. Do not reuse sessions between different types (OData API and SFAPI).
2. Store the value of the cookie and CSRF token in the authorization header as a HTTP Cookie (header field
names, Cookie and X-CSRF-Token) and use them in subsequent requests to reuse the session.
Cookie: JSESSIONID=2oeUIX5Glw14EZXU7fDWMQ**.ps4bsfapi52t
X-CSRF-Token: 97sHgXdBw6%2bCKGg3sWAahbT8ztI%3d
Results
If the cookie and token are passed correctly, then the login procedure will reuse the session.
If the CSRF token is invalid, you can still reuse the session by providing the correct login credentials of the same
user. Note that the login type must be the same as your first successful login.
● Because sessions use authentication information only once, subsequent requests are not aware of any
newer permission changes. Similar to UI login, you need to log out and log in again to allow the latest
permissions to take effect.
● An X-CSRF-Token is only valid within the session it was generated. Using the token in a non-matching
session will result in an error.
● To optimize server resource, a session is only deactivated when the timeout limit is reached.
You can validate a reused session with function import isValidSession. If a session is valid (value of name
property is true), you can reuse it for your subsequent requests.
URI https://<hostname>/odata/v2/isValidSession
{
"d": {
"name": true
}
}
Use this feature to set the maximum password age and login exceptions for individual users for OData API and
SFAPI access.
Context
Your SAP SuccessFactors HXM Suite administrator sets password policies for all users in the system, including the
timing for password expirations. However, you may want to set different expiration times for passwords for
integrations that are built against a specially designed API user account. You can set exceptions to the password
policy for your API users, and specify a different password expiration (maximum password age) policy. To maintain
security, users who have password policy exceptions are required to have an IP address restriction to connect to
the API server.
Follow the instructions below to set password policy and login exceptions for your API users.
1. Go to Admin Center Password & Login Policy Settings , and choose Set API login exceptions.... You can
also access the Password & Login Policy Settings tool from API Center Legacy SFAPI IP Whitelisting .
2. Click Add to create a new policy. In the pop-up dialog, enter the following:
Field Description
Username Enter the username for whom you want to apply the policy.
Maximum password age (days) Enter the number of days the password is valid for. To set a
password to never expire, enter -1.
Note
For security reasons, we do not recommend setting a
password to never expire.
Note
The maximum password age applies only when the user
is accessing from the IP addresses specified here.
● Permission to query
● Permission to edit
Permission to Query
There are three permission levels for querying OData API entities:
Operation level An operation-level permission check determines if a user has the right to access
the module to which the entities belong.
Row level A row-level permission check determines if the logged-in user has permission to
query the record in the query result set.
Field level A field-level permission check determines if the logged-in user has permission
to query a specific property in the query result.
Export permission is enabled and query entity permission has No row-level permission check is required. The user can query
no target population. all entities.
Export permission is enabled and query entity permission has Row-level permission check is required and only entities in the
a target population. target population are in the query result.
Export permission is disabled. Field-level permission check is performed and only fields that
the user has permission to access are returned in the query re
sult.
Permission to Edit
There are three permission levels for editing OData API entities:
Operation level An operation-level determines if user has the right to access the module to
which the entities belong.
Row level A row-level permission check determines if the logged-in user has permis
sion to edit the record in the query result set.
Field level A field-level permission check determines if the logged-in user has permis
sion to edit a specific property in the query result.
Import permission is disabled and the operation The operation can’t be executed.
type is Insert/Upsert.
Import permission is disabled and the operation A field-level permission check is performed and only fields that the user
type is Update/Merge. has permission to edit can be edited. Note that Entities that have a target
population also require a row-level permission check before field-level per
missions are checked.
The OData API is a standardized protocol for accessing a database. The Entities in the API are described in the
metadata document. Accessing the Entities is as simple as knowing the entity name, and then referencing the
entity through an HTTP call. For example, all SuccessFactors HCM Suite solutions have a User Entity that
represents a user account. To query all the users in a system, you type the URL https://<hostname>/odata/v2/
User A query can be more complex by including filtering, paging, sorting, and joining of entities (known as
expanding in OData).
OData protocol allows the service provider (SAP SuccessFactors HCM Suite) to decide which operations are
supported. In SAP SuccessFactors HCM Suite, the supported operations vary by entity. For example, the system
does not allow user accounts to be deleted. User accounts can be removed from use only by setting them as
inactive. Therefore the User entity does not support the delete operation.
This document does not cover the details of each entity behavior for all SAP SuccessFactors HCM Suite solutions.
Details for each entity are provided in a separate reference document. That document provides details about
operations, business meaning and business logic, and configuration details such as security and custom-field
configuration.
http://<hostname>/odata/v2/EntitySet[(KeyPredicate)][/NavPropSingle][/
NavPropCollection][/ComplexType][/Property]
● EntitySet: Name of the OData API entity. An entity set represents the resource exposed by the OData API.
● KeyPredicate: A predicate that identifies the key property of the entity. If an entity has a single key property,
the key predicate may only inlcude the value of the property. If the entity has a composite key, that is, the entity
has multiple key properties, the key predicate must include the key/value pairs to identify an entry.
Note that when a key predicate is present in a URI, the $filter query option will be disregarded.
● navPropSingle: The name of a navigation property defined by the entry associated with the prior path
segment. The navPropSingle must identify a single entity (that is, have a "to 1" relationship).
● NavPropCollection: Same as NavPropSingle except it must identify a collection of entries (that is, have a "to
many" relationship).
● ComplexType: The name of a property of the entity or complex type associated with the prior path segment.
● Property: The name of a property of the entity or complex type associated with the prior path segment.
http://<hostname>/odata/v2/User('1')/ Same as the URI above, but identifies the raw value of the
proxy('1')/hr/username/$value username property.
Related Information
URLs are sent over the Internet using the ASCII character set. If a URL contains characters outside the ASCII set,
the characters must be converted into a valid ASCII format. Percent encoding, also known as URL encoding, is a
mechanism that encodes special characters into ASCII characters that can be understood. It is used in SAP
SuccessFactors OData API to handle special characters that appear in the URI or request/response body. In this
topic, you'll find a list of special characters and when to encode them in OData API.
The characters allowed in a URI can be categorized into reserved characters and unreserved characters:
● Reserved characters are the characters for special use in defining the syntax of the URI, for example, the dollar
sign ("$") and the question mark ("?"). These special characters may need to be encoded under certain
circumstances if you don't want them to be used in their special role.
● Unreserved characters serve no special purposes. They do not need to be percent-encoded.
Percent encoding converts a special character into its corresponding ASCII value with a "%" followed by two
hexadecimal digits. Below is a list of special characters, the values after percent encoding, and how to use them:
/ %2F As is As is or encode
Depending on where the special characters appear, percent-encoding may or may not be needed when you
compose a URI. Following the rules listed in the table above, and refer to the following use cases as examples.
https://<hostname>/odata/v2/FOCostCenter?$filter=externalCode eq 'CC%23001'&
$select=externalCode,name&$format=JSON
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://<hostname>/odata/v2/
FOCostCenter(externalCode='CC#001',startDate=datetime'2013-01-01T00:00:00')",
"type": "SFOData.FOCostCenter"
},
"externalCode": "CC#001",
"name": "Special Character Test"
}
]
}
}
{
"__metadata":{
"uri":"FOCostCenter(externalCode='CC
%23001',startDate=datetime'2013-01-01T00:00:00')",
"type":"SFOData.FOCostCenter"
},
"name":"Special Character Test",
"status":"A"
}
When you query the entity, you need to encode the '%' into '%25' to filter the cost center in the result:
https://<hostname>/odata/v2/FOCostCenter?$filter=externalCode eq 'CC%2523001'&
$select=externalCode,name&$format=JSON
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://<hostname>/odata/v2/
FOCostCenter(externalCode='CC%23001',startDate=datetime'2013-01-01T00:00:00')",
"type": "SFOData.FOCostCenter"
{
"__metadata":{
"uri":"FOCostCenter(externalCode='McDonald''s',startDate=datetime'2013-01-01T00:00:0
0')",
"type":"SFOData.FOCostCenter"
},
"name":"Special Character Test",
"status":"A"
}
https://<hostname>/odata/v2/FOCostCenter?$filter=externalCode eq 'McDonald''s'&
$select=name,externalCode&$format=JSON
The SuccessFactors HCM Suite OData service supports the both Edm.DateTime and Edm.DateTimeOffsetf
ormats of the OData protocol. This support makes it easy for integration clients to convert date and time values to
different time zones as needed.
For DateTime format, the input value is in UTC time, and it is stored and retrieved from the database in the same
format.
Atom Format
Input: If the input value in Atom format is 2014-4-22T18:10:10, the OData server treats the input string as UTC time,
stores to the database directly.
JSON Format
Input: If the date is 2014-04-22T18:10:10, then convert it in milliseconds under UTC timezone. Time in milliseconds
is 1398190210000, so the input date in json format is /Date(1398190210000)/. When OData receives this value, it
converts it to 2014-04-22T18:10:10, and stores it in database.
Output: Since the date in database is 2014-4-22T18:10:10, when the date is retrieved, OData converts and formats
the time in milliseconds under UTC timezone. The retrieved value is /Date(1398190210000)/
Note
Some modules store the date and truncate the time. In this case, for the input string 2014-04-22T18:10:10,
the stored value would be 2014-04-22T00:00:00, and the retrieved value might be 2014-04-22T00:00:00.
In a HANA-based instance, incorrect date time format such as 2014-04-22 is also accepted and treated as
2014-04-22T00:00:00. In non-HANA instances, an error will occur.
The property lastModifiedWithTZ exposes handling of date and time at an entity level.
The following API call returns the most recent handling of date and time in Java data format:
https://<hostname>/odata/v2/User?$format=json&$select=lastModifiedWithTZ&
$filter=lastModifiedWithTZ ge datetime'2013-12-18T17:19:28'&$top=200
{
"__metadata": {
"uri": "https://<localhost>:443/odata/v2/User('eclark1')",
"type": "SFOData.User"
},
"lastModifiedWithTZ": "/Date(1392046843000-0480)/"
}
Atom Format
If input date is 2014-4-22T23:10:10+01:00, OData convert the input date to server time, because the input date
time zone is different from server time zone (input date is at GMT+1 time zone), the date is stored as
2014-4-22T18:10:10.
Input:For input date 2014-4-22T18:10:10 in milliseconds of UTC is 1398219010000, so the input date string in json
is "/Date(1398219010000-0240)/"
Output: If the date in the database is 2014-4-22T18:10:10, when retrieved, it is converted to milliseconds of UTC, /
Date(1398219010000-0240)/.
The property lastModifiedWithTZ exposes handling of date and time at an entity level.
The following API call returns the most recent handling of date and time in Java data format:
https://<hostname>/odata/v2/User?$format=json&$select=lastModifiedWithTZ&
$filter=lastModifiedWithTZ%20ge%20datetimeoffset%272013-12-18T17:19:28-07:00%27&
$top=200
Note
As of Release Q1 2019, DateTimeOffset values without timezone information are also accepted in query URLs
and edit request payloads. The value will be processed by the server as UTC time.
This topic explains the how query operations work in SuccessFactors HCM Suite OData API.
The query operation uses the GET HTTP method against a resource URI to retrive data. You can query a single entry
using a key predicate. You can refine query results with filters, sort them in a particular order, page large amount of
data, and limit data size by specifying which properties to be returned. You can also expand to related entities and
use date constraints to retrieve effective-dated entries.
Note
In an OData URI, a key predicate identifies the key property or key properties of the resource specified. If an entity
has a single key property, the key predicate may include only the value to be able to uniquely identify an entry. If an
entity has multiple key properties, that is, a composite key, the key predicate must include all key/value pairs to be
able to identify a unique entry.
https://<API-Server>/odata/v2/FormHeader('123')
https://<API-Server>/odata/v2/PicklistLabel(locale='en_US',optionId='123')
A URI with key predicate identifies only one entry from the specified OData resource. Therefore, when a key
predicate is present in a URI, the following system query options are ignored:
● $filter
● $top
● $skip
● $orderby
Related Information
System query options are query string parameters that can be used to control the order and amount of data
returned for the URI. All system query options start with the "$" character. You can join multiple options with the
"&" character. In this section, you'll learn how each system query options work and how they work together.
Related Information
Use the $orderby system query option to specify an expression for determining what values are used to order the
collection of entries identified by the URI.
The $orderby system query option only works when multiple entries are returned.
Example
Query all users in the system and sort the results by the username property:
https://<API-Server>/odata/v2/User?$orderby=username
Example
Query all users in the system and sort the results by the username property in ascending order:
https://<API-Server>/odata/v2/User?$orderby=username asc
Example
Query all users in the system and sort the results by the username property in descending order, and
subsequently sort the results by the username property of the user's HR:
https://<API-Server>/odata/v2/User?$orderby=username,hr/username desc
Note
The system query option sorts the top level entity only; child entities aren’t sorted. When using $orderby on a
1: many navigation properties, the order isn’t predicable.
Numbers of a string-type property are sorted in lexical order (lexicographic sorting) rather than by their numeric
values. For example, if you sort a collection of entries by a string-type ID field in ascending order, ID "123" comes
before ID "45".
Example
Query TodoEntryV2 and sort the results by the categoryId property in ascending order:
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://<API-Server>/odata/v2/TodoEntryV2(185114M)",
"type": "SFOData.TodoEntryV2"
},
"categoryId": "44"
},
{
Related Information
5.4.2.2 $top
This topic shows examples of the $top system query option used in a URI.
To learn about the OData System query options used in the example URIs in this section, visit www.odata.org, and
search for "OData Version 2.0 Uri Conventions".
https://<API-Server>/odata/v2/User?$top=5 The first 5 User entries are returned and the entries are
sorted using a schema determined by the OData service.
https://<API-Server>/odata/v2/User?$top=5& The first 5 User entries are returned in descending order and
$orderby=username desc sorted by the username property.
Related Information
5.4.2.3 $skip
This topic shows examples of the $skip system query option used in a URI.
To learn about the OData System query options used in the example URIs in this section, visit www.odata.org, and
search for "OData Version 2.0 Uri Conventions".
https://<API-Server>/odata/v2/User(1)/ The set of proxy Entries (associated with the User Entry
proxy?$skip=2 identified by key value 1) starting with the third User.
https://<API-Server>/odata/v2/User? The third and fourth User Entries from the collection of all
$skip=2&$top=2&$orderby=username User entities when the collection is sorted by username in
ascending order.
Related Information
5.4.2.4 $filter
This topic shows examples of the logical operators, arithmetic operators, grouping operators, and customized
operators including customized string functions that can be used with the $filter system query option.
You can use the $filter system query option in the URI to limit the number of results returned in an OData API
query. For example, the following URI filters the status field in the User entity to return all active users:
https://<API-Server>/odata/v2/User?$filter=status eq 't'
Note
● You can only filter top-level entities and child entities that have 1:1 relationships with their parents. When
you apply $filter on an entity that has one-to-many (1:M) relationship with its children, the child entities
are not filtered. The query returns all parents with full list of child entities even though only one child meets
the condition in the filter.
● When a key predicate is present in the URI, the $filter option is disregarded.
To learn more about the OData System query options used in the example URIs in this section, visit www.odata.org,
and search for "OData Version 2.0 Uri Conventions".
Logical Operators
eq Equal Finds a User entity whose hr/username is cgrant (hr is the navi
gation property): https://<API-Server>/odata/v2/User?
$filter=hr/username eq 'cgrant'. Finds a User Entity
whose username property is cgrant: https://<API-Server>/
odata/v2/User?$filter=username eq 'cgrant' .
or Logical or https://<API-Server>/odata/v2/PicklistLabel?
$filter=id le 3.5 or id gt 200
Note
There's a limit to the number of expressions you can use in a single query. This number varies from server to
server. When the limit is reached, an error occurs. To solve this problem, reduce the number of expressions in
your query.
Arithmetic Operators
Customized Operators
CUSTOMIZED OPERA
TOR DESCRIPTION EXAMPLE
Note
Due to the limit of number of expressions (1000)
in a list, OData API doesn’t accept an IN clause ex
ceeding 1000 expressions in $filter.
https://<API-Server>/odata/v2/User?
$filter=userId like ’cgrant’
$filter=userId eq 'cgrant’
https://<API-Server>/odata/v2/User?
$filter=userId like ’cgrant%’
$filter=startswith(userId, 'cgrant’)
https://<API-Server>/odata/v2/User?
$filter=userId like ’%cgrant’
This is equivalent to
$filter=endswith(userId, 'cgrant’)
https://<API-Server>/odata/v2/User?
$filter=userId like ’%cgrant%’
https://<API-Server>/odata/v2/User?
$filter=tolower(userId) like ’
%cgrant%’
https://<API-Server>/odata/v2/User?
$filter=toupper(userId) like ’
%cgrant%’
https://<API-Server>/odata/v2/User?
$filter=toupper(userId) like ’
%cgrant%’ or tolower(username) like
'%cgrant%’
https://<API-Server>/odata/v2/User?
$filter=toupper(userId) like ’
%cgrant%’ and tolower(username) like
'%cgrant%’
String Functions
In addition to operators, a set of functions is also defined for use with the $filter query option. The following
table lists the available functions.
Note
ISNULL or COALESCE operators are not defined. Instead, there’s a null literal that can be used in comparisons.
If your query contains a single quote, it must be escaped by another one if it appears with single-quoted string.
bool endswith(string p0, string p1) Finds all usernames that end with Futterkiste:
https://<API-Server>/odata/v2/User?
$filter=endswith(username, 'Futterkiste')
bool startswith(string p0, string p1) Finds all usernames that start with Alfr:
https://<API-Server>/odata/v2/User?
$filter=startswith(username, 'Alfr')
bool substringof(string p0, string p1) Case-insensitive search for username field:
https://<API-Server>/odata/v2/User?
$filter=substringof('futter',tolower(usern
ame)) eq true
string tolower(string p0) Compare username in lower case to the literal value
futterkiste :
https://<API-Server>/odata/v2/User?
$filter=tolower(username) eq 'futterkiste'
string toupper(string p0) Compare username in upper case to the literal value 'FUTTER
KISTE' :
https://<API-Server>/odata/v2/User?
$filter=toupper(username) eq 'FUTTERKISTE'
string trim(string p0) Trim leading and trailing whitespaces before comparing values:
https://<API-Server>/odata/v2/User?
$filter=trim(username) eq 'Futterkiste'
The $filter option can also be used in a query using a datetime parameter:
QUERY EXAMPLE
https://<API-Server>/odata/v2/User? The query returns last modified date in Java data format (the
$format=json&$select=lastModified& milliseconds since January 1, 1970, 00:00:00 GMT).
$filter=lastModified%20ge%20datetime
%272003-12-18T17:19:28%27&$top=200
Related Information
This topic shows examples of the $expand system query option used in a URI.
To learn about the OData System query options used in the example URIs in this section, visit www.odata.org, and
search for "OData Version 2.0 Uri Conventions".
EXAMPLE DESCRIPTION
Related Information
This topic shows examples of the $format system query option used in a URI and the Response received for each
one.
To learn about the OData System query options that will be used in the example URIs in this section, visit
www.odata.org, and search for "OData Version 2.0 Uri Conventions".
atom application/atom+xml
json application/json
https://<API-Server>/odata/v2/User?
<?xml version='1.0' encoding='utf-8'?>
$format=atom <feed xmlns="https://www.w3.org/2005/
Atom" xmlns:m="https://
schemas.microsoft.com/ado/2007/08/
dataservices/metadata" xmlns:d="https://
schemas.microsoft.com/ado/2007/08/
dataservices" xml:base="https://<API-
Server>/odata/v2/">
<title type="text">Picklist</title>
<id>https://<API-Server>/odata/v2/
Picklist</id>
<updated>2013-07-29T07:10:44Z</
updated>
<link rel="self" title="Picklist"
href="Picklist" />
<entry>
<id>https://<API-Server>/
odata/v2/
Picklist('CandidateStatus')</id>
<title type="text" />
<updated>2013-07-29T07:10:44Z</
updated>
<author>
<name />
</author>
<link rel="edit"
title="Picklist"
href="Picklist('CandidateStatus')" />
<link rel="https://
schemas.microsoft.com/ado/2007/08/
dataservices/related/picklistOptions"
type="application/atom+xml;type=entry"
title="picklistOptions"
href="Picklist('CandidateStatus')/
picklistOptions" />
<category
term="SFOData.Picklist" scheme="https://
schemas.microsoft.com/ado/2007/08/
dataservices/scheme" />
<content type="application/xml">
<m:properties>
<d:picklistId>CandidateStatus</
d:picklistId>
</m:properties>
</content>
</entry>
</feed>
https://<API-Server>/odata/v2/User?
d": {
$format=json "results": [{
"__metadata": {
"uri": "https://<API-
Server>/odata/v2/
Picklist('CandidateStatus')",
"type":
"SFOData.Picklist"
},
"picklistId":
"CandidateStatus",
"picklistOptions": {
"__deferred": {
"uri":
"Picklist('CandidateStatus')/
picklistOptions"
}
}
}]
}
}
Related Information
5.4.2.7 $select
You can use the $select system query option in a URI to specify a subset of properties to be returned by the
OData service.
The value of the $select query option is a list of comma-separated properties or navigation properties. By limiting
the number of fields returned, this query option can reduce the query data size and improve performance.
To learn about the OData System query options used in the example URIs in this section, visit www.odata.org, and
search for "OData Version 2.0 Uri Conventions".
https://<API-Server>/odata/v2/User? username property value and a link to the related proxy Entry
$select=username,proxy is returned for each User.
https://<API-Server>/odata/v2/User? All the properties of the Entries identified by the proxy and
$select=username,proxy&$expand=proxy/hr hr navigation properties are returned.
Related Information
5.4.2.8 $count
Use the $count system query option to identify the number of entries represented by the resource the URI.
The $count query option can be used standalone or with the $filter query option. When it's used to count
filtered entries, it must be placed before the $filter option.
● The following query returns the total number of users in the system:
https://<API-Server>/odata/v2/User/$count
● If you add a $filter to the same query, the $count option returns the number of filtered results:
https://<API-Server>/odata/v2/User/$count?$filter=status eq 'F'
In SAP SuccessFactors OData API, the $count option calculates the number of identified entries after whatever
business logic is implemented in the entity. Here are two examples to help you understand the design.
Example
The following query returns the number of all active users in the system by default:
https://<API-Server>/odata/v2/User/$count
If you want to count both active and inactive users, apply a filter option and call out the status explicitly in the
query:
Example
The following query returns position entries that are active on the present date by default:
https://<API-Server>/odata/v2/Position/$count
If you want the number of all position entries including historical and future positions, you need to apply a time
parameter to the query:
https://<API-Server>/odata/v2/Position/$count?toDate=9999-12-31
Related Information
5.4.2.9 $inlinecount
Use the $inlinecount system query option to include a count number of the entries identified by the resource
path section of the URI.
The $inlinecount calculates all entries identified by the resource path after applying any $filter query
options. The difference between $inlinecount and $count is that $count only returns a count number in the
response while $inlinecount returns both entries and the count number.
Parameter Values
Use Cases
The following query identifies a list of countries/regions with a count number at the end of the response:
https://<API-Server>/odata/v2/Country?$inlinecount=allpages
{
"d": {
"results": [
... ...
],
"__count": "251"
}
}
The following query identifies all users with last name Smith and the count number of the entries:
https://<API-Server>/odata/v2/User?$filter=lastName eq 'Smith'&$inlinecount=allpages
Sample Code
{
"d": {
"results": [
... ...
],
"__count": "8"
}
}
Related Information
Related Information
The following URIs are constructed differently but provide the same response:
http://<hostname>/odata/v2/User?$select=userId,username,hr&$format=json&$top=1
http://<hostname>/odata/v2/User?$select=userId,username,hr/manager/proxy&$format=json&
$top=1
Each URI identifies all User Entities. Instead of having the response contain all properties for each Entity, the
$select option ensures the response contains only the three properties specified in the URI. . You can specify a
simple property, a navigation property, or a nested-navigation property. For a simple property, the response
contains results. For a navigation property, the response is a deferred link , whatever the depth of the property.
Sample response:
The following example shows the $select and $expand options used together in a URI:
http://<hostname>/odata/v2/User?$select=userId,manager/hr/manager&$format=json&
$expand=manager/hr
The URI identifies all entities of a User Entity Set. The response is similar to the response from a URI that uses
nested-navigation properties with the $select option, but the $expand option can make a difference in the results
that appear in the response. For example:
Sample Response:
{
"__metadata" : {
http://<hostname>/odata/v2/User('asliva_upsert_JAM_200')/proxy?
$select=userId,manager/hr/manager&$format=json&$expand=manager/hr
In the response, the $expand option expands the selected navigation property. Unselected data columns do not
get expanded. Set entities of User/proxy are returned.
Sample Response:
[
{
"__metadata" : {
"uri" : "http://localhost:8080/odata/v2/User('asliva_upsert_JAM_197')", "type" :
"SFOData.User"
}, "userId" : "asliva_upsert_JAM_197", "manager" : null
},
{
"__metadata" : {
"uri" : "http://localhost:8080/odata/v2/User('asliva_upsert_JAM_198')", "type" :
"SFOData.User"
}, "userId" : "asliva_upsert_JAM_198", "manager" : null
},
{
"__metadata" : {
"uri" : "http://localhost:8080/odata/v2/
User('asliva_upsert_b11_112_inline_proxy')", "type" : "SFOData.User"
}, "userId" : "asliva_upsert_b11_112_inline_proxy", "manager" : null
},
{
"__metadata" : {
"uri" : "http://localhost:8080/odata/v2/
User('asliva_upsert_b11_112_inline_proxy_1')", "type" : "SFOData.User"
}, "userId" : "asliva_upsert_b11_112_inline_proxy_1", "manager" : null
}
https://<hostname>/odata/v2/User?$format=JSON&$select=userId,username,proxy/
userId,proxy/username&$expand=proxy&$filter=proxy/userId eq 'admin'
The response identifies a set of users who has a proxy with userId value admin. After the users are identified, the
response expands the proxy navigation property and retrieves the columns as specified in the $select option.
Note
In a 1:M navigation relation, $filter only works on the base object. Expanded data will not be filtered. In this
case, $filter=proxy/userId eq 'admin' serves as a search criteria to identify user 103004. In the
response, both proxies of the user are retrieved.
Sample Response:
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://<hostname>/odata/v2/User('103004')",
"type": "SFOData.User"
},
"userId": "103004",
"username": "vwagner",
"proxy": {
"results": [
{
"__metadata": {
"uri": "https://<hostname>/odata/v2/User('admin')",
"type": "SFOData.User"
},
"userId": "admin",
"username": "admin"
},
{
"__metadata": {
"uri": "https://<hostname>/odata/v2/User('sfadmin')",
"type": "SFOData.User"
},
"userId": "sfadmin",
"username": "sfadmin"
}
]
}
}
]
}
}
Related Information
Using the $top and $skip options together in a request URI creates a pagination query. The response is a subset
of the whole result, from $skip to $top. $skip indicates the starting row for the query. $top limits the size of the
query.
For example, if the URI uses $top=50, $skip=20, the response is a subset from numbers 21 to number 70 of the
whole result set.
When you use keywords $expand and $orderby in the same query, only the parent entities are sorted. Entities
expanded through 1:n navigation properties aren't sorted. For example, in the following query tries to expand and
sort the proxy users:
https://<hostname>/odata/v2/User?$format=JSON&$select=userId,username,proxy/
userId,proxy/username&$expand=proxy&$s=proxy/userId eq 'admin'&$orderby=proxy/
userId desc
Notice in the expanded results, the proxy users are not sorted in descending order by userId:
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://<hostname>/odata/v2/User('103004')",
"type": "SFOData.User"
},
"userId": "103004",
"username": "vwagner",
"proxy": {
"results": [
{
"__metadata": {
"uri": "https://<hostname>/odata/v2/User('admin')",
"type": "SFOData.User"
},
"userId": "admin",
"username": "admin"
},
{
"__metadata": {
"uri": "https://<hostname>/odata/v2/User('sfadmin')",
"type": "SFOData.User"
},
"userId": "sfadmin",
"username": "sfadmin"
}
]
}
}
]
Related Information
5.4.3 Pagination
The OData API provides several pagination options for query results.
A single OData HTTP GET request can return at most 1,000 records. This is reasonable because most HTTP clients
have a timeout limit of 2 - 5 minutes. They do not allow transactions to wait more than that. A request running
longer than the limit gets an HTTP timeout error. Therefore, it is often required to tune complex queries and lower
the data size for them to complete within the timeout restriction.
Pagination limits the maximum size of a query response to 1,000 records. The OData standard provides a
'__next' link in your query response if there are more results in the database.
SAP SuccessFactors OData API provides several pagination options for you to choose from. In this document, you'll
find out how each pagination mechanism works, what are their advantages and disadvantages, and where to use
which.
Restriction
Pagination only works for entity sets. Function imports do not support pagination.
Client-side pagination uses query options on the client side to create an offset that restricts the amount of data
returned from the server.
A client-side pagination query can be made using the following URI parameters:
● The $top parameter indicates the number of records to return in the batch. The default and maximum number
is 1,000 records.
● The $skip parameter indicates the number of records in the full data set to skip before fetching data.
For example, a query with $skip=2000&$top=500 returns the fifth page of data where the page size is 500.
Note
Client-side pagination doesn't guarantee that every page returns the exact number of records as you specified
in the $top parameter because there might be dirty data and post-business rule validations might apply. This
doesn't mean that the data is lost. The total number of records in the entire data set is not impacted.
Advantages Disadvantages
Related Information
SAP SuccessFactors offer two types of server pagination: cursor-based pagination and snapshot-based pagination.
You can specify the pagination method in the query by adding the parameter &paging=cursor or
&paging=snapshot.
Both types of pagination return a __next parameter at the end of each query response that contains a
$skiptoken value indicating the next page of data. Here are two examples of the _next link:
Caution
Do not change the URL in the __next link. A modified URL can lead to unexpected results.
In the Atom format query responses, the ampersand "&" is automatically encoded as "&". If you
choose Atom as the format of your query response and the __next URL contains an ampersand, the link
doesn't work because the server doesn't recognize "&". You must decode the link before opening it.
Alternatively, you can choose JSON as the response format.
"__next" : "https://<hostname>/odata/v2/User?$format=JSON&
$skiptoken=eyJzdGFydFJvdyI6MTAwMSwiZW5kUm93IjoyMDAxfQ%3D%3D"
Related Information
Cursor-based pagination maintains a database "cursor" on the server throughout pagination HTTP requests. The
cursor represents a pointer to the start of the next page in the full data set. This is enabled using the
Note
Currently, cursor-based pagination is only supported in Employee Central EmpJob entity, User entity, MDF
Generic Objects, and most EC Foundation Objects.
Advantages Disadvantages
● Cursor-based pagination is faster than client offset pagi ● Sorting is not supported. The $orderby parameter can
nation because the database query is only executed when not be used. An error will occur if it is attempted.
the first page is requested. Subsequent pages are fetched ● Filter is not supported for MDF-based entities. This in
from the location indicated by the $skiptoken value. cludes Position, custom Generic Objects, and entities for
● It will not suffer data loss and duplication issues exhibited MDF-based features such as Time, Benefits, CRM, etc. It
by client offset pagination. can only be used for full extracts and does not support
"last modified since" delta queries.
● It does not support going backwards in the data set, mak
ing it not the ideal choice for UI consumption where UI5
control binding may require scrolling backwards for data
not buffered in the control.
Related Information
Snapshot-based pagination is a server-side pagination that works by keeping a list of all business keys of the data
set on the server.
During the first page request, a "snapshot" or list of all the business keys is created after executing the query on the
base entity. Query option $filter is processed to filter the full data set and $orderby is executed to sort the full
data set. Subsequent pages are fetched by selecting the set of business keys and fetching each record based on
these keys.
You can enable snapshot-based pagination by adding parameter &paging=snapshot in a query. Here is an
example:
/odata/v2/PerPerson?paging=snapshot&$filter=lastModifiedDateTime ge
datetime'2001-07-01T17:19:28'&$orderby=personId
Advantages Disadvantages
● Snapshot-based pagination can improve the performance ● Potential first-page timeouts for complex queries. When
of complex queries with large data volumes. For simple you execute a query, a snapshot is created during the first
queries, consider other pagination options. page by executing the full query. Therefore, the request of
● It doesn’t suffer data loss and duplication issues exhibited the first page takes longer than subsequent pages. It can
by client offset pagination. lead to potential timeout errors for large data sets with
● It fully supports filtering with $filter and sorting with complex filtering, sorting, and expanding options.
Important Notes
Before you use snapshot-based pagination in your query, read the following important notes:
● Not all entities support snapshot-based pagination. If you attempt snapshot-based pagination on an entity that
doesn’t support the feature, the server automatically forces client offset pagination on the query. You can find
out which type of pagination is used in the following custom headers of the query response:
○ If snapshot-based pagination is successful: X-SF-Paging: snapshot
○ If client offset pagination is successful as a fallback: X-SF-Paging: offset
Note
The custom header information is only available for successful snapshot-based pagination and forced
client offset pagination when snapshot-based pagination isn’t supported.
● Enable session reuse if you choose snapshot-based pagination. It helps avoid potential errors caused by
switching servers during load balancing. For more information, see Enabling Session Reuse.
● Querying entities that have multiple business keys can be slow if they aren’t optimized for snapshot-based
pagination.
● Do not use &paging=snapshot together with $top or $skip.
● A snapshot is deleted after 15 minutes if the next page isn't accessed. In rare cases, a snapshot may be lost on
the cache server before the 15-minute limit is reached. In either situation, you need to request the entire
snapshot again.
We’ve optimized all MDF entity types for snapshot-based pagination. To be able to run snapshot-based pagination
queries on MDF entities, you must have the Admin access to MDF OData API permission under Metadata
Framework in RBP.
Although the snapshot-based pagination in MDF OData API is built based on the OData framework, it has its own
specific logic and behaviors:
● Filter ($filter) and sort ($orderby) can only be applied to the first query. Queries to fetch subsequent
pages can’t be filtered or sorted.
● You can use multiple filter conditions on lastModifiedDateTime of the base entity and expanded entities. This
applies to all types of navigation in MDF OData entities (excluding MDF Foundation Objects), such as picklist
fields and entities with Valid When and Composite associations. For example:
/odata/v2/Position?paging=snapshot&$filter=parentPosition/lastModifiedDateTime
le datetime'2001-07-01T17:19:28' or companyNav/lastModifiedDateTime ge
datetime'2001-07-01T17:19:28'
As of 1H 2020 Release, the following Employee Central entities have been optimized for snapshot-based
pagination:
● PerPersonal
● EmpJob
● EmpEmployment
● FOLocation
● FOPayGrade
● MDF Foundation Objects
The following table summarizes the limits we currently have for snapshot-based pagination:
Maximum number of consecutive "bad requests" within 30 mi 10 (Company user level)
nutes
5 (Entity level)
Expand Limit
The expand limit is intended to prevent excessive resource consumption and ensure better availability and
performance for all customers. The default limit for the number of expanded records returned in a snapshot-based
pagination query is 300,000. If this limit is reached, an error occurs.
This example query tries to expand a list of users' manager's direct reports:
/odata/v2/User?paging=snapshot&$filter=status eq 'T'&$expand=manager/
directReports&$select=manager/directReports/userId
Under the expand limit, the total number of managers or direct reports expanded by $expand=manager/
directReports must not exceed 300,000.
Note
If you wish to increase the size limit, contact SAP Cloud Support.
Snapshot-based pagination is intended for data sync and integration purposes where you pull data from the first
page to the last. However, it often occurs that a user repeatedly requests for the first page of a paginated dataset
and never reaches the subsequent pages. These requests are considered as bad requests because they waste
server resources and could lead to performance issues.
To protect servers against intended or unintended bad requests, we’ve introduced a protection mechanism to
regulate the usage of snapshot-based pagination. A quota is imposed on the company user level and the entity level
to restrict the number of bad requests that can be made within a given period of time:
● The company user-level limit counts the number of consecutive bad requests made on all entities.
● The entity level limit counts the number of consecutive bad requests made on a single entity.
If the limit is reached, the user is blocked for 30 minutes during which the server rejects all subsequent requests
and returns an error message in response.
Note
● Multiple bad requests on a single entity also count as one bad request on the company user level.
● Before the limit is reached, using $skiptoken in the __next parameter to request the subsequent pages
resets the count.
● If a user is blocked on the entity level, they still can access other entities.
● If a user is blocked on the company user level, they can't access any entities in this company.
Example
You have made three bad requests on three different entities a, b, and c, and another two bad requests on entity
d, the counts of consecutive bad requests are:
If you request for the second page of entity d now, the counts change to:
Related Information
As a developer, you can choose among the different types of pagination based on their advantages and
disadvantages. Here are our recommendations:
● Always use client offset pagination for UI consumptions. This is the only supported method on UI.
● Cursor-based pagination and snapshot-based pagination are recommended for integration use cases.
● Only use snapshot-based pagination for complex queries with large data volume, for example, a query that has
complex sorting, filtering, and expanding options.
● We recommend that you tune your queries when you use snapshot-based pagination. If you have problems
with first page performance for extremely large data sets, switch to cursor-based pagination if applicable.
Related Information
The default and maximum page size for a paginated query is 1000 items. You can customize the page size by
setting a different number with parameter customPageSize.
You can use customPageSize for both client and server paginations. Here are two examples:
https://<hostname>/odata/v2/Photo?$format=JSON&$top=500&$skip=200&customPageSize=50
https://<hostname>/odata/v2/Photo?$format=JSON&paging=snapshot&customPageSize=50
Note
The custom page size must be smaller than 1000. Page size larger than 1000 is ignored.
Related Information
You can use the following additonal keywords for effective-dated entities (such as most Employee central entities
and those MDF entities that are created effective-dated) :
asOfDate https://<API-Server>/odata/v2/EmpJob&
$format=json&asOfDate=2014-01-01
fromDate/toDate https://<API-Server>/odata/v2/EmpJob&
$format=json&fromDate=2014-01-01&toDate=2014-12-
31
The following rules are followed when an effective dated entity is queried:
1. If both the base entity and the navigation entity are effective-dated, then when expanding the navigation entity,
the effectiveStartDate of the base entity is used as the asOfDate of the navigation entity.
2. If the base entity is none effective-dated, TODAY is used as asOfDate when expanding the navigation entity.
3. For a Picklist field, the navigation entity is PicklistValue. When expanding a Picklist field, the effectiveStartDate/
effectiveEndDate of the Picklist entity are used because PicklistValue itself is not effective-dated.
4. If you specify asOfDate as a query option, all entities in all levels of the query use this date as asOfDate.
5. If you specify fromDate and toDate as query options, then only the top level entity is filtered by the fromDate
and toDate, all lower level entities follow rules 1, 2, and 3.
6. You can use either AsOfDate or fromDate/endDate in a query request, not both.
Related Information
This topic explains how edit operations work in SAP SuccessFactors HCM Suite OData API.
Edit operations include insert, update (replace/merge), upsert (insert/update), and delete.
The insert operation uses HTTP method POST to create an entry. In an insert request, you must specify the name of
the API in the URI.
Request:
Operation Insert
URI https://<API-Server>/odata/v2/User
Payload
{
"__metadata": {
"uri": "User('paulchris')"
},
"username": "paulchris",
"password": "pwd",
"hireDate": "/Date(978307200000)/",
"gender": "M",
"status": "active",
"userId": "paulchris",
"firstName": "Paul",
"lastName": "Chris",
"email":
"[email protected]",
"timeZone": "PST",
"manager": {
"__metadata": {
"uri": "User('kevinsmith')"
},
"username": "kevinsmith",
"password": "pwd",
"hireDate": "/
Date(978307200000)/",
"gender": "M",
"status": "active",
"userId": "kevinsmith",
"firstName": "Kevin",
"lastName": "Smith",
"email":
"[email protected]",
"department": "Retail Banking",
"timeZone": "PST"
}
}
Response:
{
"d": [
{
"key": "paulchris",
"status": "OK",
"editStatus": "INSERTED",
"message": null,
"index": 0,
"httpCode": 201,
To learn more about OData operations, visit www.odata.org, and search for "OData Version 2.0 Operations".
Related Information
5.5.2 Merge
In certain cases, you might want to do an incremental update without replacing all the content of a data entity. To
avoid overloading the PUT HTTP method, OData offers the merge operation to update a record. A merge request
updates only the properties provided in the request body, and leaves the data not mentioned in the request body in
its current state.
A merge operation request is delivered using the POST HTTP method with special header x-http-method, as
shown in the example below. In the request payload, you only include the fields you want to update.
URI https://<API-Server>/odata/v2/
User('ExistingUser')
For merging data that includes navigation properties, see the examples in Updating Links [page 97].
Related Information
5.5.3 Replace
The replace operation updates an existing record with the fields specified in the request payload and resets all
other fields to their default value. In a replace request, you use the PUT HTTP method and identify the record to be
updated by passing the key predicate in the request URI. Unlike the merge operation, you must provide all required
fields in the request payload.
The replace request returns status 204 (No Content) to indicate success. No response body is returned.
Operation Replace
URI https://<API-Server>/odata/v2/
User('ExistUser')
Related Information
5.5.4 Upsert
The upsert operation is a custom function import to update or insert records. If the record specified in payload
doesn't exist, the server inserts (creates) a new record; if a record exists, the upsert operation updates the record.
You can use upsert to batch insert/update and inline-edit records. You can invoke the upsert operation using the
HTTP method POST.
Remember
For effective-dated entities, the failure of upserting one time slice causes all other time slices of the same
external code to fail. See example 2 for more information.
Example 1: Create and Update Users in One Upsert Request [page 80]
Example 3: Get a Faster Upsert Response by Specifying Entity Names in URI [page 85]
Related Information
Depending on the request, the upsert operation returns the following values in the httpCode field of the response:
200 Successful.
{
"key": "newuser",
"status": "OK",
"editStatus":
"UPSERTED",
"message": null,
"index": 0,
"httpCode": 200,
"inlineResults":
null
}
{
"key": "cgrant",
"status": "OK",
"editStatus":
"UPDATED",
"message": null,
"index": 1,
"httpCode": 204,
"inlineResults": null
}
Related Information
Example 1: Create and Update Users in One Upsert Request [page 80]
Example 2: Upsert Multiple Records of an Effective Dated Entity [page 83]
Example 3: Get a Faster Upsert Response by Specifying Entity Names in URI [page 85]
Appendix of OData Errors [page 200]
The following code sample shows an upsert request that creates a new user, updates an existing user, and updates
the manager of the existing user inline.
Request
Operation Upsert
URI https://<API-Server>/odata/v2/upsert
Response
{
"d": [
{
"key": "NewUser",
"status": "OK",
"editStatus": "INSERTED",
"message": null,
"index": "0",
"httpCode": 201,
"inlineResults": null
},
{
"key": "ExistUser",
"status": "OK",
"editStatus": "UPDATED",
"message": null,
"index": "1",
"inlineResults": [
{
"results": [
{
"key": "OldManager",
"status": "OK",
"editStatus": "UPDATED",
"message": null,
"index": "0",
"httpCode": 204,
"inlineResults": null
}
],
"inlineProperty": "manager"
}
]
}
]
Related Information
You can create historical information of an effective-dated record by upserting multiple time slices of the same
external code in one request. Note that if one of these time slices fails, all other time slices of the same external
code fail.
Request
In this example, the date format in the first time slice is incorrect.
Operation Upsert
URI https://<API-Server>/odata/v2/upsert
"uri":"cust_TestObject(effectiveStartDate=datetime'2019-06-01T00:0
0:00',externalCode='SO-004')"
},
"externalCode":"SO-005",
"effectiveStartDate":"/Date(1559347ABC)/",
"cust_Category":"Cat A"
},
{
"__metadata":{
"uri":"cust_TestObject(effectiveStartDate=datetime'2019-07-01T00:0
0:00',externalCode='SO-004')"
},
"externalCode":"SO-005",
"effectiveStartDate":"/Date(1561939200000)/",
"cust_Category":"Cat B"
},
{
"__metadata":{
"uri":"cust_TestObject(effectiveStartDate=datetime'2019-08-01T00:0
0:00',externalCode='SO-004')"
},
"externalCode":"SO-005",
"effectiveStartDate":"/Date(1564617600000)/",
"cust_Category":"Cat C"
}]
Response
The response shows ERROR status for the first time slice and ACCEPTED for the other two. All three time slices
have failed.
{
"d": [
{
"key": null,
"status": "ERROR",
"editStatus": null,
"message": "Illegal DateTime value in JSON format: /Date(1559347ABC)/.
Please follow the standard format: \"/Date(<ticks>)/\", <ticks> = number of
milliseconds since midnight Jan 1, 1970. Example: \"/Date(1495746637000)/\". For
more information, see https://help.sap.com/viewer/d599f15995d348a1b45ba5603e2aba9b/
latest/en-US/971f5829b7aa4c9fab04c12f06838683.html.",
"index": 0,
"httpCode": 400,
"inlineResults": null
},
{
"key": "cust_TestObject/
effectiveStartDate=2019-07-01T00:00:00.000Z,cust_TestObject/externalCode=SO-004",
"status": "ACCEPTED",
"editStatus": null,
"message": null,
"index": 1,
"httpCode": 202,
"inlineResults": null
Related Information
When you start to make API calls, your first upsert operation tries to download the full set of OData metadata from
the server and stores it in cache. Subsequent API calls use the cached metadata until it's refreshed. Due to its large
size, downloading the full metadata can be time consuming. To get a faster response, you can explicitly include the
entities to be upserted in the URI so that only the metadata of these entities are downloaded.
To do this, you include the entity names in the URI, separated by comma.
Note
If you want to inline edit an entity, you also need to include it in the URI.
This example shows how to create an activity for a user. In the request URI, only the metadata of the necessary
entities are specified.
Request URI:
https://<API-Server>/odata/v2/User,Activity,ActivityStatus/upsert
Request Payload:
{
"__metadata":{
"uri":"Activity"
},
"activityName":"Test Activity",
"activityStatusNav":{
Related Information
5.5.5 Delete
The delete operation deletes an entry. In a delete request, you use the DELETE HTTP method and specify the entry
to be deleted by passing the key predicate in the request URI.
Note
In SAP SuccessFactors, there are hard deletes and soft deletes. A hard delete permanently removes an entry
from the database while a soft delete marks the entry as deleted. Soft deleted entries can be accessed with
certain tools, for example, an audit report.
In Employee Central, soft deleted entries can even be queried if the instance is configured as such. For more
information, see Expanded Entities: Handling Deleted Objects.
Operation Delete
URI https://<API-Server>/odata/v2/
TodoEntryV2(18234M)
The parameters in this section apply to all SAP SuccessFactors HCM Suite OData API entities unless otherwise
specified in the topic. For a list of MDF-specific parameters, see Query Parameters [page 132] and Insert and
Upsert Parameters.
Related Information
The apiOptionProfileID parameter allows you to specify an API option profile in your API call. With an API
option profile, additional processing parameters can be specified to control extra business logic when you perform
edit operations with the User entity.
Parameter Values
Value Description
User defined value The parameter uses the API option profile ID that you define in the
Manage API Option Profile tool in API Center. For more information, see
Creating and Editing Users Using an API Option Profile [page 186].
Use Case
For more information about using the apiOptionProfileID, see Creating and Editing Users Using an API Option
Profile [page 186].
5.5.6.2 processInactiveEmployees
processInactiveEmployees is an exclusive parameter for the User entity. You can use this parameter when edit
an inactive user.
Parameter Values
Value Description
Use Cases
By default, you can't edit users in statuses inactive (f) and inactive_external (F). To edit inactive users, you need to
either use the processInactiveEmployees parameter or an API option profile that allows eidting of inactive
users.
5.5.6.3 purgeType
purgeType is an optional parameter that determines whether an incremental or full update will be performed on
an entity.
Parameter Values
Value Description
incremental (default) The upsert operation purges and replaces only the data specified in the
request payload.
full or fullPurge The upsert operation purges all existing data of the entry and creates new
data specified in the request payload.
Caution
If there are multiple time slices for the given record, the upsert oper
ation with purgeType=full deletes all the time slices and creates
new ones specified in the request. If you want to keep a particular
time slice, make sure to include all of its data in the request payload.
If some time slices are missing, then import engine will delete them.
This type of incremental purge is available only for MDF entities and the
User entity.
Since a full purge erases all existing data of an entry and creates new data, it follows the mandatory fields
governance of the entity. Whether an entry exists or not in the database, you must provide all required fields in your
strictTransactionIsolate is an upsert parameter for determining if partial upsert applies during multiple-
record upserts.
Parameter Values
Caution
The strictTransactionIsolate parameter is used to ensure data integrity during upsert. Using the
parameter could result in performance degradation, the extent of which depends on the single record
processing time and the batch size.
Value Description
false (default) Standard upsert behavior: one failed record during upsert
causes all records to fail.
Use Case
Parameter Values
Value Description
false (default) When upsert errors occur in a batch ChangeSet, only the first
error is returned.
true When upsert errors occur in a batch ChangeSet, all errors are
returned.
Use Case
In this example, the batch request contains an upsert operation that tries to create two users. In the upsert
payload, neither user ID matches the key, so the upsert operation fails.
Request Payload
--batch_202010071357
Content-Type: multipart/mixed; boundary=changeset_202010071357
Content-Length: 100
--changeset_202010071357
Content-Transfer-Encoding: binary
Content-Type: application/http
POST upsert HTTP/1.1
Content-Type: application/json;charset=utf-8
accept:application/json
[
{
"__metadata": {
"uri": "User('user1')"
},
"username": "content1",
"password": "content1",
"hireDate": "/Date(978307200000)/",
"status": "active",
"userId": "content1",
"firstName": "content1",
"lastName": "[email protected]",
"department": "test",
"timeZone": "PST"
},
{
"__metadata": {
"uri": "User('user2')"
},
"username": "content2",
"password": "content2",
"hireDate": "/Date(978307200000)/",
--batch_202010071357--
Compare the following two responses when the parameter is set to different values.
--batch_a89da8c8-82e1-4171-ac21-e69e474666e1
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 500 Internal Server Error
Content-Type: application/json;charset=utf-8
DataServiceVersion: 1.0
X-SF-Record-Count-Recursive: 0
Content-Length: 265
{
"error": {
"code": "COE_GENERAL_SERVER_FAILURE",
"message": {
"lang": "en-US",
"value": "ChangeSet index 1 - Key property (User/userId) doesn't match
the key in the __metadata uri; Key property (User/userId) doesn't match the key in
the __metadata uri"
}
}
}
--batch_a89da8c8-82e1-4171-ac21-e69e474666e1--
--batch_fe845a0f-d7be-42dd-8cdb-bc4096abef2f
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 500 Internal Server Error
Content-Type: application/json;charset=utf-8
DataServiceVersion: 1.0
X-SF-Record-Count-Recursive: 0
Content-Length: 446
{
"error": {
"code": "COE_GENERAL_SERVER_FAILURE",
"message": {
"lang": "en-US",
"value": "ChangeSet index 1 - Upsert error"
},
"innererror": {
"errordetails": [
{
"code": "User/userId=user1",
"message": "Key property (User/userId) doesn't match the key in
the __metadata uri",
"severity": "",
"target": ""
},
{
"code": "User/userId=user2",
Related Information
You can use the $links system option to query, create, edit, and delete associations between OData entities.
The $links operation is a system-defined OData option that can be used to address associations between OData
entities. An association is a relationship between one entity and another. The basic rules for addressing
associations are shown in the following figure:
Note
Key predicate of the navigation property can be required, optional, or empty depending on the type of request
and association.
For example, the URI below can be used to query a users manager:
https://<API-Server>/odata/v2/User('cgrant')/$links/manager
https://<API-Server>/odata/v2/User('cgrant')/$links/matrixManager
The response returns the URI of the navigation property manager, not the entry itself:
https://<API-Server>/odata/v2/User('cgrant')/$links/matrixManager
Example response:
{
"d": {
"results": [
{
"uri": "https://<API-Server>/odata/v2/User('manager1')"
},
{
"uri": "https://<API-Server>/odata/v2/User('manager2')"
}
]
}
}
Related Information
You can create associations between entities by referencing one entity during the creation or modification of
another, or by explicitly issuing a POST request against the URL of the $links option. The request must have the
new URI in the request body following the appropriate format for stand-alone $links options in XML or JSON.
The example below adds a new matrix manager by creating a link between the user and the manager through
navigation property matrixManager:
URI https://<API-Server>/odata/v2/
User('cgrant')/$links/matrixManager
Payload ATOM:
<uri xmlns="http://
schemas.microsoft.com/ado/2007/08/
dataservices">
https://
apisalesdemo4.successfactors.com/v2/
User('EYtest02')
</uri>
JSON:
{
"uri":"User('manager2')"
}
Different rules apply when you create links for MDF entities and non-MDF entities.
The following rules apply when you create links between non-MDF entities:
● A new link is always inserted for a 1:n association. If the same link already exists, the POST operation returns
204 No Content without data change.
● The server always performs a replace operation for a 1:1 relationship. For example, if you create a manager link
for a user who already has a manager, the new manager replaces the old manager.
The following rules apply when you create links between MDF entities (including MDF Foundation Objects):
● You can't create links for MDF Generic Objects with composite type associations.
● A new link is always inserted for a 1:n association. If the same link already exists, the POST operation returns
204 No Content without data change.
Related Information
Replacing Links
You can replace links for 1:1 or 1:n asociations using the PUT HTTP method.
● For 1:n associations, you must provide the key in the URI and replace it with the new entry in the request
payload:
URI https://<API-server>/odata/v2/
User('cgrant')/$links/
matrixManager('manager1')
Payload {
"uri":"User('manager3')"
}
This reqeust replaces manager1 of user cgrant with manager3. Note that the user's other matrix manager
links aren’t impacted.
● For 1:1 associations, you don't provide any key in the URI. You provide only the new link in the request payload.
This entry replaces the existing link.
Payload {
"uri":"User('manager1')"
}
Note
For non-MDF entities, you can also use POST to replace a 1:1 link. It gets you the same result as PUT. For
MDF entities, POST can only be used when there's no existing link.
Merging Links
You can use POST requests to add a new link to a 1:n relation. When you add a new link, it’s merged with the existing
links.
The example below adds a new matrix manager manager3 to user cgrant.
URI https://<API-server>/odata/v2/
User('cgrant')/$links/matrixManager
Payload
{
"uri":"User('manager3')"
}
Related Information
Using a DELETE request with the $links option, you can remove the association between two entries. The
business key of the source and target entry must be provided in the URI. Upon success, the operation returns
status 204 No Content. No response body is returned.
For example, the following request deletes the link between user cgrant and its matrix manager:
URI https://<API-Server>/odata/v2/
User('cgrant')/$links/
matrixManager('manager1')
The following rules apply when you delete links of non-MDF entities:
Rules for Deleting Links of MDF Entities (Including MDF FOundation Objects)
The following rules apply when you delete links of MDF entities, including MDF Foundation Objects:
Related Information
Special rules follow when you create and edit links for effective-dated entities, such as MDF entities.
● Between effective-dated records, such as Valid When associations, a Picklist field, and Generic Object (GO)
field.
● From an effective-dated record to a noneffective-dated record with a Valid When association
● From a noneffective-dated record to an effective-dated record with a Valid When association
When you create a link between two effective-dated records using $links, the following rules apply:
● The effectiveStartDate of the source timeslice must be later than the effectiveStartDate of the earliest
timeslice in the target record. Otherwise, an error occurs. (See Example 1)
● You can only create links for source timeslices that are subsets of the target record. The target timeslice
doesn't necessarily have to match the source timeslice. The link is created between the source timeslice and
matching target timeslices. (See examples 2 and 3)
Example 1
In this example, you try to link a source entry to a target whose earliest timeslice has an effectiveStartDate later
than the source record. It leads to an error.
URI https://<API-Server>/odata/v2/
cust_SourceObject(effectiveStartDate=datet
ime'2019-06-01T00:00:00',externalCode='SO-
001')/$links/cust_TargetNav
Payload
<uri xmlns="http://
schemas.microsoft.com/ado/2007/08/
dataservices">
https://<API-Server>/odata/v2/
cust_TargetObject(effectiveStartDate=dat
etime'2019-07-01T00:00:00',externalCode=
'TO-001')
</uri>
{
"error": {
"code": "COE_GENERAL_BAD_REQUEST",
"message": {
"lang": "en-US",
"value": "[COE0018]Invalid reference '(effectiveStartDate=2019-07-01,
externalCode=TO-001)' for valid when association 'cust_TargetNav'."
}
}
}
Example 2
For the same objects, if you choose a different timeslice in the source entry that has a later effectiveStartDate, the
link is created.
URI https://<API-Server>/odata/v2/
cust_SourceObject(effectiveStartDate=datet
ime'2019-07-01T00:00:00',externalCode='SO-
001')/$links/cust_TargetNav
Payload
<uri xmlns="http://
schemas.microsoft.com/ado/2007/08/
dataservices">
https://<API-Server>/odata/v2/
cust_TargetObject(effectiveStartDate=dat
etime'2019-06-15T00:00:00',externalCode=
'TO-001')
</uri>
Example 3
If the source timeslice is a subset of the target record, you can create links to any timeslice in the target. The link is
created between the source timeslice and the matching target timeslices.
URI https://<API-Server>/odata/v2/
cust_SourceObject(effectiveStartDate=datet
ime'2019-07-01T00:00:00',externalCode='SO-
001')/$links/cust_TargetNav
Payload
<uri xmlns="http://
schemas.microsoft.com/ado/2007/08/
dataservices">
https://<API-Server>/odata/v2/
cust_TargetObject(effectiveStartDate=dat
etime'2019-05-01T00:00:00',externalCode=
'TO-001')
</uri>
You can delete links between effective-dated records with $link. To delete a link, specify the target key predicate in
the URI:
URI https://<API-Server>/odata/v2/
cust_SourceObject(effectiveStartDate=datet
ime'2019-07-01T00:00:00',externalCode='SO-
001')/$links/
cust_TargetNav(effectiveStartDate=datetime
'2019-05-01T00:00:00',externalCode='TO-001
')
{
"error": {
"code": "COE_GENERAL_BAD_REQUEST",
"message": {
"lang": "en-US",
"value": "[COE0018]Required field 'cust_TargetObject' cannot be
deleted."
}
}
}
You can create links between an effective-dated record and a non-effective-dated record, and vice versa. There are
no constraints on effective dates when you create such links.
Related Information
The OData protocol has introduced the batch operation that can combine multiple requests into a single request.
An OData batch request is represented as a multipart MIME v1.0 message, a standard format allowing the
representation of multiple parts, each of which may have a different content type, within a single request.
Batch requests are submitted as a single POST request to the /odata/v2/$batch URI. The request must include
a content-type header specifying a content type of “multipart/mixed” and a boundary specification. This is an
example for a batch request header:
Sample Code
POST /odata/v2/$batch
HOST: localhost
Content-Type: multipart/mixed; boundary=batch_36522ad7-
fc75-4b56-8c71-56071383e77b
The batch boundary must follow the pattern [a-zA-Z0-9_\\-\\.'\\+]{1,70} and it must not contain any
special character in ( ) < > @ , ; : / “ [ ] ? =.
Note
The maximum number of requests allowed in a batch request is 180. An error occurs if this number is
exceeded.
In a batch request, each ChangeSet and query request is represented as a distinct MIME part and is separated by
the boundary maker defined in Content-Type. This is an example of a batch request body that includes these
operations:
Sample Code
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: application/http
Content-Transfer-Encoding:binary
GET User('admin') HTTP/1.1
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: multipart/mixed; boundary=changeset_77162fcd-b8da-41ac-
a9f8-9357efbbd621
Batch Processing
Request parts in a batch are processed sequentially and independently of each other. A request part can be a query
reqeust or a ChangeSet. The OData batch process follows these rules:
● All operations in a single ChangeSet unit follow the all-or-nothing rule. Failure of a single unit causes the entire
ChangeSet to fail. No partial changes are applied.
● Upsert operations in a ChangeSet also follow the all-or-nothing rule. If multiple records are processed in an
upsert operation within a ChangeSet, the failure of one single record causes all other records to fail.
● Each ChangeSet is processed independently. The failure of one ChangeSet doesn't affect others.
If there’s an insert request in the ChangeSet and the MIME part representing that request includes a Content-ID
header, then the new entity that is created by the insert operation can be referenced by subsequent requests within
that ChangeSet by referring to the Content-ID value prefixed with a “$” character:
Sample Code
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: multipart/mixed; boundary=changeset_77162fcd-b8da-41ac-
a9f8-9357efbbd621
--changeset_77162fcd-b8da-41ac-a9f8-9357efbbd621
Content-Type: application/http
Content-Transfer-Encoding: binary
POST User HTTP/1.1
Content-Type: application/atom+xml;type=entry
Content-ID: 1
<AtomPub representation of a new User>
--changeset_77162fcd-b8da-41ac-a9f8-9357efbbd621
Content-Type: application/http
Content-Transfer-Encoding: binary
POST $1 /Manager HTTP/1.1
Content-Type: application/atom+xml;type=entry
<AtomPub representation of a new user manager>
The body of a batch response contains a response for each Query and ChangeSet request that makes up the batch
request. The order of responses matches the order of requests. Each response includes a Content-Type header
with a value of "application/http", and a and a Content-Transfer-Encoding MIME header with a value of “binary”.
Sample Code
-batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 200 Ok
Content-Type: application/atom+xml;type=entry
Content-Length: ###
<AtomPub representation of the User entity with EntityKey admin>
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: multipart/mixed; boundary=changeset_77162fcd-b8da-41ac-
a9f8-9357efbbd621
Content-Length: ###
--changeset_77162fcd-b8da-41ac-a9f8-9357efbbd621
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 201 Created
Content-Type: application/atom+xml;type=entry
Location: http://localhost/odata/V2/User('abc')
Content-Length: ###
<AtomPub representation of a new User entity>
--changeset_77162fcd-b8da-41ac-a9f8-9357efbbd621
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 204 No Content
--changeset_77162fcd-b8da-41ac-a9f8-9357efbbd621--
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 404 Not Found
Content-Type: application/xml
Content-Length: ###
<Error message>
--batch_36522ad7-fc75-4b56-8c71-56071383e77b--
Note
For a successful ChangeSet request, the response lists all the request responses included in the ChangeSet. For
example, if a ChangeSet has three requests, then the response contains three responses in the ChangeSet
body. However, if the changeset fails, then only one response entry, which is the error in the problematic
request is displayed. The ChangeSet response is then a non-ChangeSet response because it doesn’t have the
ChangeSet body.
Related Information
http://www.odata.org/documentation/odata-version-3-0/batch-processing/
Upsert operations in a $batch ChangeSet follow the "all-or-nothing" rule. That means, if one record fails to upsert,
all other changes will roll back.
Caution
By default, if errors occur to an upsert operation in a $batch ChangeSet, only the first error is returned no matter
how many entities are in the upsert payload. However, you can use parameter
enableUpsertResponseExtensionInChangeset to control whether all error messages of an upsert operation
in a ChangeSet are returned. To return all upsert error messages, set the parameter to true in the request.
{
"error": {
"code": "COE_GENERAL_SERVER_FAILURE",
"message": {
"lang": "en-US",
"value": "ChangeSet index 1 - Upsert error"
},
"innererror": {
"errordetails": [
{
"code": "cust_MDF35137/
effectiveStartDate=2000-01-01T00:00:00.000Z,cust_MDF35137/externalCode=P1",
"message": "Invalid value 'DummyValue' for the Generic Object
field 'cust_gof'.;Invalid Picklist value: 'DummyValue'.;Invalid User ID: 'abc123'.",
"severity": "error",
"target": ""
},
{
Related Information
Examples of $batch requests are listed. Please strictly follow the format in the examples including the new lines
and spaces.
An upsert request is a special kind of the update request. It should be included in a ChangeSet.
Sample Code
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Length: 100
Content-Type: multipart/mixed; boundary=changeset_77162fcd-b8da-41ac-
a9f8-9357efbbd621
--changeset_77162fcd-b8da-41ac-a9f8-9357efbbd621
Content-Transfer-Encoding: binary
Content-Type: application/http
POST upsert HTTP/1.1
Content-Type: application/json;charset=utf-8
accept:application/json
{
"__metadata": {
"uri": "User('content1')"
--batch_36522ad7-fc75-4b56-8c71-56071383e77b--
Sample Code
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: application/http
Content-Transfer-Encoding:binary
GET User('admin')?$format=json HTTP/1.1
Content-Type: application/atom+xml;type=entry
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Length: 100
Content-Type: multipart/mixed; boundary=changeset_77162fcd-b8da-41ac-
a9f8-9357efbbd621
--changeset_77162fcd-b8da-41ac-a9f8-9357efbbd621
Content-Transfer-Encoding: binary
Content-Type: application/http
POST upsert HTTP/1.1
Content-Type: application/json;charset=utf-8
accept:application/json
{
"__metadata": {
"uri": "User('content1')"
},
"username": "content1",
--batch_36522ad7-fc75-4b56-8c71-56071383e77b--
Related Information
This section provides information about MDF OData API entities including MDF Generic Object entities and MDF
Foundation Object entities.
When you create a new MDF generic object (GO), you can choose whether you want to expose it to OData API.
Once exposed, you can access the object through OData API calls. In this section, we'll introduce what you need to
do to expose an MDF object, how to query and edit data through API calls, and what rules you need to follow when
accessing MDF OData APIs.
In addition to GO entities, you'll also learn how MDF Foundation Object entities work. MDF Foundation Objects are
Employee Central Foundation Objects which have been migrated to MDF and exposed to OData as OData API
entities. Many MDF Foundation Objects adopt the same querying and editing behaviors as MDF Generic Objects.
For more information, see MDF Foundation Objects.
Before you proceed, make sure you have the basic knowledge of OData API and you're already familiar with
configuring an MDF object definition:
● OData Operations
● Configuring the Object Definition
You can expose an MDF generic object to OData API by setting the API Visibility field when you create or edit the
object definition.
Prerequisites
MDF is enabled for your company and you have the corresponding permissions to configure object definitions. For
more information, see the Enabling MDF section of the Implementing the Metadata Framework (MDF) guide.
Context
The OData API exposes the object definition, field definition, rules, and conditions of an MDF object as OData
metadata. This makes it easier for other modules to consume the data.
MDF entities that are associated with more than one parent will be exposed to OData API if one of the parents is
exposed. For example, the Job Profile Builder entity makes its child objects exposed as API entities, such as job
codes and competencies.
You can use the API Visibility field to control whether an MDF object is to be exposed to OData API or not, and
whether it’s editable or read only through API.
1. Go to the Configure Object Definitions page and choose one of the following options:
○ To create a new object definition, select Object Definition in the Create New dropdown list.
○ To edit an existing object definition, select Object Definition in the Search dropdown list and type the name
of the object definition to search. Click and open the object definition from the result list, and choose
Take Action Make Correction .
2. On the Object Definition screen, you have the following options in the API Visibility field. Choose either Editable
or Read Only to allow OData API access to the object:
○ Editable - This option allows you to create and edit object instances through OData API.
○ Read Only - This option only allows you to query object instances through OData API.
○ Not Visible - The object isn’t exposed to OData API.
3. In the API Sub Version field, choose the subversion for your object. For more information, see API
Subversioning [page 112].
4. Proceed with the rest of the configuration tasks and choose Save.
Results
The MDF object is now exposed to OData. The OData metadata will be automatically refreshed shortly after and
you’re able to access the object through the corresponding OData API.
Note
Depending on the frequency of metadata refreshing job scheduled for your instance, the change may not take
effect immediately. You can manually refresh the metadata using one of the following approaches:
● Go to Admin Center OData Metadata Refresh and Export and choose Refresh to refresh the
metadata.
● Send an API request to refresh the metadata:
GET https://<API-endpoint-URL>/odata/v2/refreshMetadata
You can control whether you want to expose system fields of an MDF object to the OData API with API
subversioning.
An MDF object contains a list of technical system fields. When exposed to OData API, all of them may not be
needed. With the API subversioning feature, you can choose to hide these fields in the MDF OData API entity.
Depending on the MDF system field in API Mode Provisioning settings for your company, the system fields may be:
● Removed from all MDF entities regardless of the API Sub Version field setting in the MDF object definition
If you're not sure which provisioning setting was made for your company, contact your SAP implementation
partner or SAP Cloud Support.
During object definition, you have the following options in the API Sub Version field:
Note
The API Sub Version setting takes effect only when the provisioning setting of MDF system field for API is set to
be dependent on it. Otherwise this field is ignored.
Selecting or changing to an API subversion can affect the metadata of the OData API. As a result, your API
request may fail due to a nonexisting property. We recommend that you check the metadata of the MDF OData
API entity and compare it against your API request as a necessary troubleshooting step.
The following table lists all MDF system fields and whether they can be removed:
mdfSystemRecordId Depends
Note
Users can overwrite the system field
name during object definition. If the
user-defined name doesn’t start with
mdfSystem, this field is exposed.
mdfSystemObjectType Yes
MDF Foundation Object entities are Employee Central Foundation Objects that have been migrated to the Metadata
Framework (MDF) and exposed as OData API entites. MDF Foundation Object entities follow the general rules of
MDF OData operations with a few exceptions.
FOCompany Company
FODepartment Department
FODivision Division
This topic contains information about the mapping of MDF objects to the OData API entities.
Each MDF object can be mapped to one OData entity only. Mapping an MDF object to multiple OData entities is not
allowed. When an MDF object is exposed to OData API, the code of the MDF object is used as the name of the
OData API entity type. In the following sections, you'll find detailed mapping relationships between an MDF object
and its corresponding OData API entity.
Note
Composite objects with multiple parents will not be exposed to OData API.
If a parent object is not exposed to OData API, none of its composite children will be exposed regardless of their
API visibility settings.
Mapping of Attributes
The following table shows the mapping relationship between the MDF field/association attributes and OData entity
set attributes.
Property type Field data type For detailed mapping of data types, see Mapping of Data
Types [page 118].
Required Required and Visibility When the API Visibility of the MDF object is set to Read
Creatable Visibility Only, the values of sap:required,sap:creatable,
sap:updatable, and sap:upsertable attributes
Updatable Visibility
are always false regardless of the field required and visibil
Upsertable Visibility
ity settings.
Sortable There is no equivalent attribute in The sap:sortable attribute is always true except for
MDF. the CLOB-type field and localized property of a translata
ble field.
Filterable There is no equivalent attribute in The sap:filterable attribute is always true except
MDF. for the CLOB-type field and localized property of a trans
latable field.
Note
When a MDF field maps to both property and navigation property in OData, the sap:required attribute of these
properties may take different values. These exception are documented in the notes below.
sap:updata sap:upserta
Required Visibility sap:required sap:creatable ble ble
Note
Exception: for
properties mapped
from Generic Ob
jects, User, and
Picklist, and navi
gation properties
mapped from
Enum type, the
value is false.
Note
Exception: this
value is true for
navigation proper
ties that are map
ped from associa
tions and from
these types: At
tachment, Generic
Object, Picklist,
and User.
Note
Exception: this
value is true for
navigation proper
ties that are map
ped from associa
tions and from
these types: At
tachment, Generic
Object, Picklist,
and User.
The following table shows the mapping relationship between the data types of MDF fields and OData properties.
Note
Inline editing of the
attachment naviga
tion property is not
allowed. To add an
attachment for an
MDF entity, you
first create the at
tachment with the
Attachment
OData API, and
then add the at
tachment to the
MDF entity. For
more information,
see Uploading At
tachments to an
MDF Entity [page
168].
following exceptions:
● MDF field
createdDate
maps to OData
property type
createdDateTi
me
● MDF field
lastModifiedD
ate maps to
OData property
type
lastModifiedD
ateTime
Enum One string property. Property: same name Same max length as de *:1
fined in MDF field. De
as MDF field name.
One navigation property fault value is 255.
to the MDFEnumValue Navigation property:
entity. add Nav suffix to MDF
field name. Example:
cust_MyEnumNav
as MDF field.
One navigation property
to the Foundation Ob Navigation property:
ject entity. add Nav suffix to MDF
field name. Example:
cust_MyObjectNav
Generic Object One string property. Property: same name 128 If the source object is
as MDF field. not effective dated and
One navigation property
destination object is ef
to the Generic Object Navigation property:
fective dated, or if the
entity. add Nav suffix to MDF
destination object is
field name. Example:
PickListValue the
cust_MyObjectNav
multiplicity is *:*.
Picklist One string property. Property: same name N/A If the source object is
as MDF field name. effective dated, the
One navigation property
multiplicity is *:*.
to the destination pick Navigation property:
list entity. add Nav suffix to MDF Otherwise, if the field is
field name. Example: required, the multiplic
cust_MyPicklistN ity is *:1; if it's optional,
av the multiplicity is *:0..1.
Translatable One string property for Default value property: Same max length as de 1:*
fined in MDF field. De
default value, one string add _devaultValue
fault value is 255.
property for localized suffix to MDF field
label, and a set of string name. Example,
properties representing cust_myLabel_def
labels in each locale aultValue.
with locales names
Localized value prop
added as suffixes.
erty: add
One navigation property _localized suffix to
to the MDF field name. Exam
MDFLocalizedValu ple,
e entity. cust_myLabel_loc
alized.
Navigation property:
add
_TranslationText
Nav suffix to the MDF
field name. Example:
cust_myLabel_Tra
nslationTextNav.
User One string property. Property: same name 100 1:1 if the field is re
as MDF field name. quired; 1:0..1 if it's op
One navigation property
tional.
to User entity. Navigation property:
add Nav suffix to MDF
field name. Example:
cust_MyUserNav
Note
The user-type audit
fields
mdfSystemCreated
By and
mdfSystemLastMo
difiedBy are map
ped to the following
read-only proper
ties in OData API by
default:
● Properties:
createdBy,
lastModifiedBy
● Navigation
Properties:
createdByNav,
lastModified-
ByNav
Reverse navigation
is not allowed for
the navigation
properties.
Mapping of Associations
The associations in an MDF object are mapped to navigation properties in OData API with the same names. The
associations are mapped as shown in the following table.
Composite A navigation property to the child MDF If it's a 1:M association, or if the source
entity in the association. object is not effective dated and destina
tion object is effective dated, the multi
plicity is 1:*.
Valid When A navigation property to the referred If it's a 1:M association, or if the source
MDF entity in the association. object is not effective dated and destina
tion object is effective dated, the multi
plicity is *:*.
Join By Column A navigation property to the referred If it's a 1:M association, or if the source
destination entity. object is not effective dated and destina
tion object is effective dated, the multi
plicity is *:*.
The key properties of an MDF OData API entity depend on the effective dating setting for the MDF object. The
following table explains which MDF fields are used as keys in OData.
Note
The field names referred to in this table reflect the default names which correspond to the Database Field
Name column. If you have changed the default names, the key property names should reflect these changes.
None 1 externalCode
Basic 2 externalCode
effectiveStartDate
effectiveStartDate
transactionSequence
Note
In a multiple-level parent-child relation, the child inherits the
<ParentEntityName>_externalCode key properties
from all its ancestors. That means if there's a 3-level relation
ship, the "youngest" child will have two
<ParentEntityName>_externalCode key properties in
addition to its own externalCode, one from its parent, and
the other from the parent's parent. However, there will be only
one <ParentEntityName>_effectiveStartDate as
the effective start date is the same for all entities in a composite
association.
MDF exposes translatable text fields as OData properties. For each translatable field, these standard properties and
navigation properties are available in the entity metadata:
● Property <fieldname_defaultValue> represents the default property value. You can assign a value to the
property when you create an entry. Otherwise, it takes the value of the locale-specific property.
● Property <fieldname_localized>: represents the localized property value in the current locale. The
localized value calculated following the rules described in How Is the Localized Value Determined? [page 126].
● Property <fieldname_locale code>: represents the localized value in the locale indicated by the locale
code.
● Navigation property <fieldnameTranslationTextNav>: represents the association with entity
MDFLocalizedValue, where the localized values of the properties are stored.
For example, a description field in an MDF object has a default property <description_defaultValue>, a
localized property <description_localized>, several locale-specific properties such as
<description_en_US>, <description_de_DE>, <description_zh_CN>, etc., and a navigation property
<descriptionTranslationTextNav>.
All these properties and navigation properties can be queried and edited.
The system determines the value of the <fieldname_localized> field based on the following information:
● User locale. The user local corresponds to the language setting the global header bar under Settings
Languages .
● Company locale. The company locale depends on the language setting of your company in provisioning.
● Value of the <fieldname_defaultValue> property.
1. If the user's locale is available, it takes the property value that corresponds to the user's locale.
2. If user's locale isn’t available, it takes the property value that corresponds to company's locale.
3. If neither locale is available, it takes the property value of <fieldname_defaultValue>.
Note
Both queries and edits follow the same rules. If you edit the value of <fieldname_localized>, the
corresponding field of the locale is also changed. For example, the <name_localized> field of the
FOBusinessObject in your company comes from en_US field. If you modify this field with an API request, the
new value is also reflected in the <name_en_US> field.
This type of request can be useful especially for UI consumption where you need to retrieve a record with the labels
of the default locale. Here's a sample request using the FOBusinessUnit entity as an example:
Request
Operation Query
URI https://<API-Server>/odata/v2/
FOBusinessUnit(externalCode='MC_AVIA',star
tDate=datetime'1900-01-01T00:00:00')?
$format=JSON&
$select=externalCode,name_localized
Response
{
"d": {
"__metadata": {
"uri": "https://<API-Server>/odata/v2/
FOBusinessUnit(externalCode='MC_AVIA',startDate=datetime'1900-01-01T00:00:00')",
"type": "SFOData.FOBusinessUnit"
},
"externalCode": "MC_AVIA",
"name_localized": "MC Aviation"
}
}
You often find this type of usage in integration scenarios where you want to export the labels in all languages.
Request
Operation Query
URI https://<API-Server>/odata/v2/
FOBusinessUnit(externalCode='SVCS',startDa
te=datetime'1900-01-01T00:00:00')/
nameTranslationTextNav?$format=JSON
Response
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://<API-Server>/odata/v2/
MDFLocalizedValue('en_US')",
"type": "SFOData.MDFLocalizedValue"
},
"locale": "en_US",
"value": "Services"
},
{
"__metadata": {
"uri": "https://<API-Server>/odata/v2/
MDFLocalizedValue('defaultValue')",
"type": "SFOData.MDFLocalizedValue"
},
"locale": "defaultValue",
"value": "Services"
},
{
"__metadata": {
"uri": "https://<API-Server>/odata/v2/
MDFLocalizedValue('en_GB')",
"type": "SFOData.MDFLocalizedValue"
},
"locale": "en_GB",
"value": "Services"
},
{
"__metadata": {
"uri": "https://<API-Server>/odata/v2/
MDFLocalizedValue('zh_CN')",
"type": "SFOData.MDFLocalizedValue"
},
"locale": "zh_CN",
"value": "服务"
},
{
"__metadata": {
"uri": "https://<API-Server>/odata/v2/
MDFLocalizedValue('pt_BR')",
"type": "SFOData.MDFLocalizedValue"
},
"locale": "pt_BR",
"value": "Serviços"
When you create or edit an entry, you can include the localized texts of translatable fields either through the local-
specific properties or through inline-editing the <fieldname>TranslationTextNav> navigation property. Here
are some examples:
Request Info
Operation Upsert
URI https://<API-Server>/odata/v2/upsert
{
"__metadata": {
"uri": "FOBusinessUnit"
},
Sample Code
{
"__metadata": {
"uri": "FOBusinessUnit"
},
"externalCode": "SERVICE",
"status": "A",
"startDate": "/Date(1508375121000)/",
"nameTranslationTextNav": [
{
"locale": "ko_KR",
"value": "서비스"
},
{
"locale": "pt_BR",
"value": "Serviços"
},
{
"locale": "de_DE",
"value": "Dienstleistungen"
},
{
"locale": "zh_TW",
"value": "服務"
},
{
"locale": "es_ES",
"value": "Servicios"
},
{
"locale": "ru_RU",
"value": "Сервисы"
},
{
"locale": "fr_FR",
"value": "Services"
},
{
"locale": "ja_JP",
"value": "サービス統括本部"
},
{
"locale": "en_US",
"value": "Services"
},
{
"locale": "zh_CN",
"value": "服务"
}
Response
{
"d": [
{
"key": "FOBusinessUnit/externalCode=SERVICE,FOBusinessUnit/
startDate=2017-10-19T00:00:00.000-04:00",
"status": "OK",
"editStatus": "UPSERTED",
"message": null,
"index": 0,
"httpCode": 200,
"inlineResults": null
}
]
}
MDF OData API is based on SAP SuccessFactors HXM Suite OData API framework, currently on OData Version 2.0.
While the majority of MDF OData API operations follow the same rules defined by the framework, there are patterns
specific to MDF when you use MDF OData API entities. In this section, you'll find out details about the specific
patterns and how they affect your OData API usage.
There's a service limit for large MDF OData requests. When you query or edit an MDF OData entity and the
response returns a large number of records that exceed the system-defined limit, the request fails and an error
occurs. You need to adjust the request parameters or payload so that the data size returned is within the limit.
Depending on your request, the limit may vary.
Remember
The service limit is designed to optimize MDF OData performance while ensuring all MDF OData APIs continue
to work in all instances. It has very little impact on your current integrations and UIs.
If you meet such an error, here are a few steps that can help solve the problem:
The following table lists the required permissions for users to query and edit MDF OData API entities:
Permission Description
Administrator Permissions Manage Integration Tools This permission allows users to access OData API entities
Allow Admin to Access OData API through Basic through Basic Authentication. If you're using other authentica
tion methods such as OAuth 2.0, this permission isn’t required.
Authentication
Administrator Permissions Metadata Framework Admin This is the MDF OData admin permission that allows users to
access to MDF OData API query and edit all MDF OData entities. It overrides all entity-
level permissions.
Administrator Permissions Metadata Framework This is the nonadmin MDF OData permission. This permission
Access to non-secured objects allows users to query and edit all nonsecured MDF OData enti
ties.
Note
This permission isn’t required for querying and editing se
cured MDF OData entities.
Entity-level permissions for secured objects If an MDF object is secured, an entity-level permission item is
available under the corresponding permission category speci
fied in object definition. You can enable view, edit, and import/
export permission for this entity. You can also maintain field-
level permission overrides.
This section contains information about query operations on MDF OData entities, including query parameters and
examples. Note that only MDF-specific information is documented here. For more information on general OData
API operations, refer to the OData Operations section.
6.5.2.1.1 asOfDate
Use the asOfDate parameter to query a single record that is effective on a specific date.
Parameter Values
Value Description
Note
Returns only one record that is effective on the specified
date.
Note
Depending on the parameters you use, a query may return zero, one, or multiple records.
If the effective dating type of the entity is Multiple Changes Per Day (MCPD), the record with the largest
sequence number is returned as the effective record of that date.
If you don't specify any date or date period, the query uses today's date as the asOfDate value and fetch the
current effective record.
Use Case
Tip
When you query an effective-dated entity without any date parameters, the result returns a single record
effective on the present date. It defaults to &asOfDate=<today's date>.
The asOfDate parameter retrieves the single records of an entity that is effective on the specified date.
Example:
https://<API-Server>/odata/v2/MDFEntity?$format=JSON&asOfDate=2018-11-07
6.5.2.1.2 fromDate
Use the fromDate parameter to query records that are effective after a specific date.
Parameter Values
Value Description
Note
Multiple records can be returned.
Note
Depending on the parameters you use, a query may return zero, one, or multiple records.
If the effective dating type of the entity is Multiple Changes Per Day (MCPD), the record with the largest
sequence number is returned as the effective record of that date.
If you don't specify any date or date period, the query uses today's date as the asOfDate value and fetch the
current effective record.
Use Case
The fromDate parameter retrieves all effective records of the entity in the specified time period. If the
mdfSystemEffectiveEndDate of a record is later than the specified date, the record is returned.
Example:
https://<API-Server>/odata/v2/MDFEntity?$format=JSON&fromDate=2018-11-07
Related Information
Use the toDate parameter to query records that are effective before a specific date.
Parameter Values
Value Description
Note
Multiple records can be returned in the result.
Note
Depending on the parameters you use, a query may return zero, one, or multiple records.
If the effective dating type of the entity is Multiple Changes Per Day (MCPD), the record with the largest
sequence number is returned as the effective record of that date.
If you don't specify any date or date period, the query uses today's date as the asOfDate value and fetch the
current effective record.
Use Case
The toDate parameter retrieves all effective records of the entity before the specified date. If the
mdfSystemEffectiveStartDate of a record is earlier than the specified date, the record is returned.
Example:
https://<API-Server>/odata/v2/MDFEntity?$format=JSON&toDate=2018-11-07
Related Information
Use the filterParentDate parameter to query a child of an effective-dated entity with date parameters.
In a composite MDF association, a child entity inherits its effective dating attribute from its parent. When you query
a composite child entity, date parameters such as fromDate, toDate, and asOfDate are ignored. In order for
them to work, you must include parameter filterParentDate with value true in the query. This parameter
allows you to filter the child entity based on the effective dates of its parent.
Parameter Values
Value Description
Use Case
See the topic in Related Links section for detailed use cases.
Related Information
6.5.2.1.5 recordStatus
Use the recordStatus parameter to query normal, pending, or pending history data of an MDF entity.
Parameter Values
Value Description
Caution
To query records with pending and pendinghistory status, you must have the Admin access to MDF OData API
permission.
Use Case
In MDF OData API entities, if workflow routing is configured for an object and the Pending Data option is enabled,
data changes pending approval are stored as pending data. Once the data changes are approved, the records are
saved as normal data. If a workflow is declined, canceled, or modified, a pending history is stored in the database.
The following table explains how the parameter values map to the mdfSystemRecordStatus field of the MDF object:
normal N
pending P
pendinghistory PH
You can use parameter recordStatus in an OData query to specify which type of data to retrieve.
https://<API-Server>/odata/v2/cust_TestObject?$format=JSON&recordStatus=normal
https://<API-Server>/odata/v2/cust_TestObject?$format=JSON&recordStatus=pending
https://<API-Server>/odata/v2/cust_TestObject?
$format=JSON&recordStatus=pendinghistory
Note
This parameter is required if you want to query the workflow information of an MDF entity by expanding
wfRequestNav. For more information, see Retrieving Workflow Information for Pending Data [page 152].
COE_UNSUPPORTED_FEATURE
[COE0025]Unsupported feature: Expand workflow navigation
from normal data is NOT supported.
COE_GENERAL_FORBIDDEN
[COE0020]Non admin user is NOT allowed to query pending
data.
Only users with the Admin access to MDF OData API permis
sion can query pending data.
COE_GENERAL_FORBIDDEN
[COE0025]Unsupported feature: Non admin user query pend
ing history data is NOT supported.
Only users with the Admin access to MDF OData API permis
sion can query pending data.
6.5.2.1.6 versionId
Parameter Values
Value Description
<largest version ID> (default) If versionId is not specified, the query returns the record with
the largest version ID.
<user specified version ID> The record with specified version ID is returned.
Use Case
Note
https://<API-Server>/odata/v2/TestObject('Key1')?$format=JSON&versionId=2
Getting to know how &asofDate impacts the query results when it’s used in combination with $expand to query
effective-dated entities and its associations.
Example Entities
Composite Association
When querying an effective-dated entity and expanding to a composite association with &asofDate, the restrictive
criteria of the parameter only works on the base object. However, as a composite association, EntityD has an
effectiveDate type "from parent", the expanded data is also effective on this date.
Request
Operation Query
URI https://<API-server>/odata/v2/EntityP?$format=JSON&
$expand=CompositeAssociation&asOfDate=2020-08-15
Sample Code
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityP(effectiveStartDate=datetime'2020-06-01T00:00:00',externalCode='P')",
"type": "SFOData.cust_EntityP"
},
"externalCode": "P",
"Record": "P2",
"cust_ParentChildComposite": {
"results": [
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
cust_EntityD(cust_EntityP_effectiveStartDate=datetime'2020-06-01T00:00:00',cust_E
ntityP_externalCode='P',externalCode='D')",
"type": "SFOData.cust_EntityD"
},
"externalCode": "D",
"Record": "D2"
}
]
}
}
]
}
}
Valid-When Association
When querying an effective-dated entity and expanding to a valid-when association with the &asofDate, the
restrictive criteria of the parameter works on both the base object and the expanded data.
Request
Operation Query
URI https://<API-server>/odata/v2/EntityP?$format=JSON&
$expand=ValidWhenAssociation&asOfDate=2020-08-15
Response
Sample Code
{
"d": {
"results": [
{
"__metadata": {
In a more complex navigation scenario with $expand where you need to navigate to a composite association and
its referred generic object, the date parameters filter both the base object and the referred generic object. In the
following example, parameter &fromDate=2018-10-01 works on both PickListV2 and values/
parentPickListValueNav:
https://<hostname>/odata/v2/PickListV2?$format=JSON&$select=values/
parentPickListValueNav&$expand=values/parentPicklistValueNav&fromDate=2018-10-01
Example Entities
When querying an effective-dated entity and expanding to a composite association with &fromDate, the restrictive
criteria of the parameter only works on the base object. However, as a composite association, EntityD has an
effectiveDate type "from parent", the expanded data is also effective on this date.
Request
Operation Query
URI https://<API-server>/odata/v2/EntityP?$format=JSON&
$expand=CompositeAssociation&fromDate=2020-05-15
Response
Sample Code
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityP(effectiveStartDate=datetime'2020-01-01T00:00:00',externalCode='P')",
"type": "SFOData.cust_EntityP"
},
"externalCode": "P",
"Record": "P1",
"cust_ParentChildComposite": {
"results": [
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityD(cust_EntityP_effectiveStartDate=datetime'2020-01-01T00:00:00',cust_Entity
P_externalCode='P',externalCode='D')",
"type": "SFOData.cust_EntityD"
},
"externalCode": "D",
"Record": "D1"
}
]
}
},
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityP(effectiveStartDate=datetime'2020-06-01T00:00:00',externalCode='P')",
"type": "SFOData.cust_EntityP"
},
"externalCode": "P",
"Record": "P2",
"cust_ParentChildComposite": {
"results": [
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityD(cust_EntityP_effectiveStartDate=datetime'2020-06-01T00:00:00',cust_Entity
P_externalCode='P',externalCode='D')",
"type": "SFOData.cust_EntityD"
},
Valid-When Association
When querying an effective-dated entity and expanding to a valid-when association with the &fromDate, the
restrictive criteria of the parameter works on both the base object and the expanded data.
Request
Operation Query
URI https://<API-server>/odata/v2/EntityP?$format=JSON&
$expand=ValidWhenAssociation&fromDate=2020-05-15
Response
Sample Code
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityP(effectiveStartDate=datetime'2020-01-01T00:00:00',externalCode='P')",
"type": "SFOData.cust_EntityP"
},
"externalCode": "P",
"Record": "P1",
"cust_ParentChildValidWhen": {
"results": [
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityC(effectiveStartDate=datetime'2020-01-01T00:00:00',externalCode='C')",
"type": "SFOData.cust_EntityC"
},
"externalCode": "C",
"Record": "C1"
}
]
}
},
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityP(effectiveStartDate=datetime'2020-06-01T00:00:00',externalCode='P')",
"type": "SFOData.cust_EntityP"
Getting to know how &toDate impacts the query results when it’s used in combination with $expand to query
effective-dated entities and its associations.
Example Entities
Composite Association
When querying an effective-dated entity and expanding to a composite association with &toDate, the restrictive
criteria of the parameter only works on the base object. However, as a composite association, EntityD has an
effectiveDate type "from parent", the expanded data is also effective on this date.
Operation Query
URI https://<API-server>/odata/v2/EntityP?$format=JSON&
$expand=CompositeAssociation&toDate=2020-07-01
Response
Sample Code
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityP(effectiveStartDate=datetime'2020-01-01T00:00:00',externalCode='P')",
"type": "SFOData.cust_EntityP"
},
"externalCode": "P",
"Record": "P1",
"cust_ParentChildComposite": {
"results": [
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityD(EntityP_effectiveStartDate=datetime'2020-01-01T00:00:00',EntityP_external
Code='P',externalCode='D')",
"type": "SFOData.cust_EntityD"
},
"externalCode": "D",
"Record": "D1"
}
]
}
},
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityP(effectiveStartDate=datetime'2020-06-01T00:00:00',externalCode='P')",
"type": "SFOData.cust_EntityP"
},
"externalCode": "P",
"Record": "P2",
"cust_ParentChildComposite": {
"results": [
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityD(EntityP_effectiveStartDate=datetime'2020-06-01T00:00:00',EntityP_external
Code='P',externalCode='D')",
"type": "SFOData.cust_EntityD"
},
"externalCode": "D",
"Record": "D2"
}
]
}
}
]
}
Valid-When Association
When querying an effective-dated entity and expanding to a valid-when association with the &toDate, the
restrictive criteria of the parameter works on both the base object and the expanded data.
Request
Operation Query
URI https://<API-server>/odata/v2/EntityP?$format=JSON&
$expand=ValidWhenAssociation&toDate=2020-07-01
Response
Sample Code
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityP(effectiveStartDate=datetime'2020-01-01T00:00:00',externalCode='P')",
"type": "SFOData.cust_EntityP"
},
"externalCode": "P",
"Record": "P1",
"cust_ParentChildValidWhen": {
"results": [
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityC(effectiveStartDate=datetime'2020-01-01T00:00:00',externalCode='C')",
"type": "SFOData.cust_EntityC"
},
"externalCode": "C",
"Record": "C1"
}
]
}
},
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityP(effectiveStartDate=datetime'2020-06-01T00:00:00',externalCode='P')",
"type": "SFOData.cust_EntityP"
},
"externalCode": "P",
"Record": "P2",
"cust_ParentChildValidWhen": {
"results": [
{
"__metadata": {
"uri": "https://<API-server>/odata/v2/
EntityC(effectiveStartDate=datetime'2020-05-01T00:00:00',externalCode='C')",
You can use parameter filterParentDate to query a child entity of an effective dated entity with date
parameters.
In a composite MDF association, a child entity inherits its effective dating attribute from its parent. When you query
a composite child entity, date parameters such as fromDate, toDate, and asOfDate will be ignored. In order for
them to work, you must include parameter filterParentDate with value true in the query. This parameter
allows you to filter the child entity based on the effective dates of its parent.
Note
In the example below, EntityP has a composite child EntityC which has three entries. The different results of the
three queries explain how the filterParentDate parameter work:
https://<hostname>/odata/v2/EntityC?
$format=JSON&toDate=2017-10-01&filterParentDate=true
The next example query contains only a date parameter. Without thefilterParentDate parameter, the date
parameter fails to work and is ignored. The result returns all available entries of the child entity:
https://<hostname>/odata/v2/EntityC?$format=JSON&toDate=2017-10-01
The last example query contains only the filterParentDate parameter. This will be treated as if the parent entity
is filtered with the default date parameter, which equals to &filterParentDate=true&asOfDate=<today's
date>. Therefore, the current effective entry Record C3 is returned.
https://<hostname>/odata/v2/EntityC?$format=JSON&filterParentDate=true
You can use the $links option to query links of composite associations of an effective dated entity.
Request
Operation Query
URI https://<API-server>/odata/v2/
PickListV2(effectiveStartDate=datetime'2020-07-17T00:00:00'
,id='ExamplePicklist')/$lins/values
Response
Sample Code
{
"d": {
"results": [
{
"uri": "https://<API-server>/odata/v2/
PickListValueV2(PickListV2_effectiveStartDate=datetime'2020-07-17T00:00:00',PickL
istV2_id='ExamplePicklist',externalCode='ExampleValue1')"
},
{
"uri": "https://<API-server>/odata/v2/
PickListValueV2(PickListV2_effectiveStartDate=datetime'2020-07-17T00:00:00',PickL
istV2_id='ExamplePicklist',externalCode='ExampleValue2')"
},
{
This topic shows examples of filtering parent and child entities whose association type is Composite and
multiplicity is One To Many.
You can filter records of a parent entity based on properties of its child entities. When the parent and child entities
have a one-to-many (1:M) relationship with its children, the query returns all parent records with full list of child
entity records even though only one record of child entity meets the condition in the filter.
We use PickListV2 and PickListValueV2 as example entities. You can search for a picklist that contains a certain
picklist value or you can search for a picklist whose parent picklist contains a certain picklist value.
Request
Operation Query
URI https://<API-server>/odata/v2/PickList?$format=JSON&
$filter=values/externalCode eq 'ABRAND'&$expand=values
Response
The query returns the picklist that has the said picklist value and a full list of child entity records if you choose to
expand the association property.
Sample Code
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://apisalesdemo4.successfactors.com:443/
odata/v2/
PickListV2(effectiveStartDate=datetime'2020-07-17T00:00:00',id='CARBRAND')",
"type": "SFOData.PickListV2"
Request
Operation Query
URI https://<API-server>/odata/v2/PickList?$format=JSON&
$filter=parentPickListNav/values/externalCode eq 'ABRAND'&
$expand=values,parentPickListNav,parentPickListNav/values
Response
The query returns the picklist whose parent picklist has the said picklist value and a full list of child entity records if
you choose to expand association properties.
Sample Code
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://apisalesdemo4.successfactors.com:443/
odata/v2/
PickListV2(effectiveStartDate=datetime'2020-07-01T00:00:00',id='CARCOLOR')",
"type": "SFOData.PickListV2"
},
"id": "CARCOLOR",
"parentPickList": "CARBRAND",
"status": "A",
"values": {
"results": [
{
"__metadata": {
"uri": "https://apisalesdemo4.successfactors.com:
443/odata/v2/
PickListValueV2(PickListV2_effectiveStartDate=datetime'2020-07-01T00:00:00',PickL
istV2_id='CARCOLOR',externalCode='Black')",
"type": "SFOData.PickListValueV2"
},
"externalCode": "Black",
"status": "A"
},
{
"__metadata": {
Related Information
$filter
You can use the wfRequestNav navigation property and the recordStatus parameter to retrieve workflow
information for pending data of MDF entities.
When an MDF Generic Object (GO) with workflow routing configured is exposed to OData, a navigation property
named wfRequestNav is automatically added to the OData API entity. This navigation property allows you to
retrieve the corresponding workflow information for pending data. Although wfRequestNav is available by default
for all MDF OData API entities with a workflow routing, you can only expand it if the Pending Data option is enabled
in the object definition.
Note
To enable workflow and pending data, associate the MDF generic object with a workflow and set the Pending
Data field to Yes in Configure Object Definitions.
You can use &recordStatus=pending to filter all pending records of the entity, and then expand the workflow
information:
https://<API-endpoint-URL>/odata/v2/cust_MDFEntity?
$format=JSON&recordStatus=pending&$expand=wfRequestNav
If you use a key predicate to query a single record, even if it's a pending record, you still need
&recordStatus=pending to be able to expand the workflow information:
https://<API-endpoint-URL>/odata/v2/cust_MDFEntity('CC01')?
$format=JSON&recordStatus=pending&$expand=wfRequestNav
Error Codes
COE_UNSUPPORTED_FEATURE
[COE0025]Unsupported feature: Expand workflow navigation
from normal data is NOT supported.
Related Information
You can use the lastModifiedDateTime field in a $filter option to query the records of an effective-dated entity.
The value of lastModifiedDateTime in a request URI is in date/time format without timezone information:
datetime'yyyy-mm-ddThh:mm:ss'
You can use lastModifiedDateTime together with other time parameters such as fromDate, toDate, and
asOfDate. You can also reverse the $filter condition by placing the date/time value before lastModifiedDateTime
to get different query results.
When you use both lastModifiedDateTime and a date parameter in the same request, the following rules apply:
● The lastModifiedDateTime filter finds all external keys that have at least one record satisfies the filter
condition.
● The time parameter then filters all records under the external keys and returns the ones that satisfy the
condition in the result.
This means that in the returned result, the lastModifiedDateTime value may or may not satisfy the filter condition
in the request. The following example explains how it works:
Example
https://<API-Server>/odata/v2/EmpJob?$format=JSON&$filter=lastModifiedDateTime
gt datetime'2017-01-01T00:00:00'&toDate=2019-01-01
In this example, you try to find all employees who have their job information changed after 2017-01-01 and pull
their entire job history, including the ones that were last modified before 2017-01-01.
The position of date/time value in the lastModifiedDateTime filter determines how the server interprets the query.
When the date/time value is placed after lastModifiedDateTime, the filter finds all external keys that satisfy the
condition. When the date/time value is placed before lastModifiedDateTime, the filter works as you normally
understand it: it returns all records with last modified date/time satisfying the filter.
In the previous example, if you want to display only the job history that were last modified after 2017-01-01, you can
change the query by moving the date/time value before lastModifiedDateTime in the filter:
https://<API-Server>/odata/v2/EmpJob?$format=JSON&
$filter=datetime'2017-01-01T00:00:00' gt lastModifiedDateTime&toDate=2019-01-01
Related Information
Parameters and examples of edit operations for MDF OData API entities, inlcuding insert, upsert, replace, merge,
and delete operations.
As of Q4 2019 Release, upsert operations on all MDF OData entities and MDF Foundation Objects can return
business keys in the response if the keys are provided in the request. That includes both parent objects and child
objects. This information helps you identify which record has been successfully upserted and which has failed.
6.5.3.1.1 workflowConfirmed
Use parameter workflowConfirmed to control whether or not the insert or upsert operation triggers a workflow.
Prerequisites
Either you’ve defined the workflow routing for the MDF object or you’ve associated the object with a business rule
that triggers a workflow.
The API user doesn't have the Admin access to MDF OData API permission.
Caution
The parameter has no impact on admin users with the Admin access to MDF OData API permission. If an admin
user creates or changes a record for a workflow-enabled entity, no workflow is triggered and the record is saved
as normal data.
If the MDF object is associated with one workflow in the Workflow Routing field and another workflow through a
business rule, the business rule-based workflow supersedes when the rule is triggered.
Object cust_ABC is associated with two workflows: workflow Manager from object definition and workflow
EmployeeHR from a business rule. When you change data that doesn't trigger the business rule, the Manager
workflow applies. When you change data that triggers the business rule, the EmployeeHR workflow applies.
Parameter Values
Value Description
A typical scenario to use the workflowConfirmed parameter is on the UI where you create or edit a record. When
you save the record, a dialog pops up for you to submit the changes for approval. If you submit, an API request is
sent with workflowConfirmed=true:
Request
Operation Upsert
URI https://<API-Server>/odata/v2/upsert?
workflowConfirmed=true
Payload
{
"__metadata":{
"uri":"cust_TestObject('TO-001')"
},
"externalCode":"TO-001",
"externalName":"Test Object 001"
}
Response
If the upsert operation is successful, a workflow request is created.
{
"d": [
{
"key": null,
"status": "OK",
"editStatus": "UPSERTED",
"message": null,
"index": 0,
"httpCode": 200,
As a user without the Admin access to MDF OData API permission, if you upsert a workflow-enabled entity without
the workflowConfirmed parameter or set the parameter value to false, you get an error:
Request
Operation Upsert
URI https://<API-Server>/odata/v2/upsert?
workflowConfirmed=false
Payload
{
"__metadata":{
"uri":"cust_TestObject('TO-002')"
},
"externalCode":"TO-002",
"externalName":"Test Object 002"
}
Response
{
"d": [
{
"key": null,
"status": "ERROR",
"editStatus": null,
"message": "To trigger workflow, please set the 'workflowConfirmed'
flag to true. with the index 0",
"index": 0,
"httpCode": 500,
"inlineResults": null
}
]
}
Use parameter warningAcknowledged to skip warning messages during upsert of an MDF record.
Prerequisites
A business rule has been defined for the MDF object to raise a warning message.
The API user doesn't have the Admin access to MDF OData API permission.
Note
The parameter has no impact on admin users with the Admin access to MDF OData API permission. If an admin
user creates or changes a record for an entity that triggers a warning message, the warning is skipped.
Parameter Values
Value Description
A typical scenario to use the warningAcknowledged parameter is on the UI where you create or edit a record.
When you save the record, a warning message pops up. If you choose OK, the warning is acknowledged and an API
request is sent with warningAcknowledged=true.
Request
Operation Upsert
URI https://<API-endpoint-URL>/odata/v2/
upsert&warningAcknowledged=true
"uri":"cust_TestObject('TO-001')"
},
"externalCode":"TO-001",
"externalName":"Test Object 001"
}
Response
If the upsert is successful, a record is created.
{
"d": [
{
"key": null,
"status": "OK",
"editStatus": "UPSERTED",
"message": null,
"index": 0,
"httpCode": 200,
"inlineResults": null
}
]
}
As a user without the Admin access to MDF OData API permission, if you upsert a warning-enabled entity without
the warningAcknowledged parameter or set the parameter value to false, you get an error:
Request
Operation Upsert
URI https://<API-endpoint-URL>/odata/v2/
upsert&warningAcknowledged=false
Payload
{
"__metadata":{
"uri":"cust_TestObject('TO-002')"
},
"externalCode":"TO-002",
"externalName":"Test Object 002"
}
Response
{
"d": [
{
We use PickListValueV2 as the example of a composite child entity and show you how to insert an entry.
Request
Operation Insert
URI https://<API-server>/odata/v2/PickListValueV2
Payload {
"__metadata": {
"uri":
"PickListValueV2(PickListV2_effectiveStartDate=datetime'
1900-01-01T00:00:00',PickListV2_id='OfficeLocation',exte
rnalCode='717011')",
"type": "SFOData.PickListValueV2"
},
"externalCode": "717011",
"PickListV2_effectiveStartDate": "/
Date(-2208988800000)/",
"PickListV2_id": "OfficeLocation",
"status": "A",
"label_en_US": "Guangzhou"
}
Note
You must include at least one label property in the request payload.
Response
You can use the upsert operation with the purgeType parameters to perform a full purge or an incremental
update on an entity.
We use PickListValueV2 as the example of a composite child entity and show you how to fully purge picklist
values or update a picklist value of a picklist.
Request
Operation Upsert
URI https://<API-server>/odata/v2/upsert?purgeType=full
Payload {
"__metadata":{
"uri":"PickListV2(effectiveStartDate=datetime'2020-07-17
T00:00:00',id='ExistingPickList')"
},
"status":"A"
}
Response
Sample Code
{
"d": [
{
"key": "PickListV2/
effectiveStartDate=2020-07-17T00:00:00.000-04:00,PickListV2/id=ExistingPickList",
Request
Operation Upsert
URI https://<API-server>/odata/v2/upsert?purgeType=full
Payload {
"__metadata":{
"uri":"PickListV2(effectiveStartDate=datetime'2020-07-17
T00:00:00',id='PickListExample')"
},
"status":"A",
"values":[{
"__metadata":{
"uri":"PickListValueV2(PickListV2_effectiveStartDate=dat
etime'2020-07-17T00:00:00',PickListV2_id='PickListExampl
e',externalCode='NEWVALUE1')"
},
"status":"A",
"label_en_US":"New Value 1"
},
{
"__metadata":{
"uri":"PickListValueV2(PickListV2_effectiveStartDate=dat
etime'2020-07-17T00:00:00',PickListV2_id='PickListExampl
e',externalCode='NEWVALUE2')"
},
"status":"A",
"label_en_US":"New Value 2"
}]
}
Response
Sample Code
{
"d": [
{
All previous picklist values of the picklist are replaced with the two new values.
Request
Operation Upsert
URI https://<API-server>/odata/v2/upsert?purgeType=incremental
or https://<API-server>/odata/v2/upsert
Payload {
"__metadata":{
"uri":"PickListV2(effectiveStartDate=datetime'2020-07-17
T00:00:00',id='PickListExample')"
},
"status":"A",
"values":[{
"__metadata":{
"uri":"PickListValueV2(PickListV2_effectiveStartDate=dat
etime'2020-07-17T00:00:00',PickListV2_id='PickListExampl
e',externalCode='NEWVALUE1')"
},
"status":"A",
"label_en_US":"New Label"
}
}
Response
Sample Code
{
"d": [
{
"key": "PickListV2/
effectiveStartDate=2020-07-17T00:00:00.000-04:00,PickListV2/id=PickListExample",
"status": "OK",
"editStatus": "UPSERTED",
"message": null,
"index": 0,
"httpCode": 200,
Related Information
Upsert
Edit Parameter: purgeType
You can use the merge operation to incrementally update a property without providing all required properties in the
payload.
We use PickListValueV2 as the example of a composite child entity and show you how to update picklist values
using merge.
Request
Operation Merge
URI https://<API-server>/odata/v2/
PickListValueV2(PickListV2_effectiveStartDate=datetime'2020
-07-17T00:00:00',PickListV2_id='ExamplePicklist',externalCo
de='OldValue')
Payload {
"externalCode":"NewValue"
}
Response
A successful operation returns status 200 OK with no payload.
Related Information
Merge
You can use the replace operation to update an existing record with the fields specified in the request payload and
resets all other fields to their default value. Unlike the merge operation, you must provide all required fields in the
request payload.
We use PickListV2 as an example to show you how to replace an old picklist with a new one. The example picklist
had an old name and its display order was numeric.
Request
Operation Replace
URI https://<API-server>/odata/v2/
PickListV2(effectiveStartDate=datetime'2020-07-17T00:00:00'
,id='OldID')
Payload {
"id":"NewID",
"status":"A"
}
Response
A successful operation returns status 200 OK with no payload. In the updated version, the example picklist has no
name and its display order is reset to the default value that is alphabetic.
Related Information
Replace
You can use the delete operation to delete an entry by passing the key predicate in the request URI.
We use PickListV2 and PickListValueV2 as examples to show you how to delete an entry.
Request
Deleting a Picklist
Operation Delete
Operation Delete
URI https://<API-server>/odata/v2/
PickListValueV2(PickListV2_effectiveStartDate=datetime'2020
-07-17T00:00:00',PickListV2_id='ExamplePicklist',externalCo
de='ExampleValue')
Response
A successful delete operation returns status 200 OK with no response payload. The picklist and picklist value
specified in the URI are deleted from the system.
You can use the $links option to create links for MDF entities.
The following rules apply when you create links between MDF entities (including MDF Foundation Objects):
● You can't create links for MDF Generic Objects with composite type associations.
● A new link is always inserted for a 1:n association. If the same link already exists, the POST operation returns
204 No Content without data change.
● For 1:1 relationship, if a link already exists, the POST operation fails.
We use PickListV2 as the example to show you how to create links for MDF entities. You can't create a new
picklist value by using the $links option because the association between PickListV2 and PickListValueV2
is composite.
Request
Payload
Sample Code
"uri":"PickListV2(effectiveStartDate=datetime'2020-07
-10T00:00:00',id='ParentPickList')"
}
Response
A successful operation returns status 204 (No Content) with no payload. In the updated version, the child picklist is
assigned with a parent picklist.
Related Information
You can use the $links option to update links of noncomposite associations.
We use PickListValueV2 as the example and updates the parent picklist value.
Note
You can't update picklist values of a picklist using the $links option because the association type between
PickListV2 and PickListValueV2 is composite.
Request
URI https://<API-server>/odata/v2/
PickListValueV2(PickListV2_effectiveStartDate=datetime'2020
-07-17T00:00:00',PickListV2_id='ChildPicklist',externalCode
='ExampleValue')/$links/
parentPickListValueNav(PickListV2_effectiveStartDate=dateti
me'2020-07-10T00:00:00',PickListV2_id='ParentPicklist',exte
rnalCode='OldValue')
"uri":"PickListV2(PickListV2_effectiveStartDate=datet
ime'2020-07-10T00:00:00',PickListV2_id='ParentPicklis
t',externalCode='NewValue')"
}
Response
A successful operation returns status 204 (No Content) with no payload. In the updated version, the value of the
child picklist is assigned with a new value from the parent picklist.
Related Information
Context
The attachment navigation property in an MDF entity doesn't allow you to inline edit attachments. To upload an
attachment to an MDF entity, you first create the attachment object with the Attachment entity, and then add this
object to the MDF entity.
Note
You're not allowed to add the same attachment ID to multiple objects. If you want to add the same attachment
to another object, upload it again to create a new attachment ID.
Procedure
Operation Upsert
URI https://<API-Server>/odata/v2/upsert
Payload {
"__metadata": {
"uri": "Attachment"
},
"fileName": "Resume.pdf",
"module": "RECRUITING",
"userId": "cgrant",
"viewable": true,
"fileContent": "PHA
+VGhp...KSHlsHcq9JS"
}
Sample Response:
{
"d": [
{
"key": "Attachment/attachmentId=499",
"status": "OK",
"editStatus": "UPSERTED",
"message": "Upserted successfully",
"index": 0,
"httpCode": 200,
"inlineResults": null
}
]
}
2. Create a new entry or update an existing entry for the MDF entity with the new attachment created in the
previous step.
Sample Request:
Operation Upsert
URI https://<API-Server>/odata/v2/upsert
You can use the upsert operation to create a self-referenced record for custom entities whose effective dating
type isn't MCPD.
Example Entity
Request
Operation Upsert
URI https://<API-server>/odata/v2/upsert
Response
Sample Code
{
"d": [
{
"key": "cust_EntityP/
effectiveStartDate=2020-07-17T00:00:00Z,cust_EntityP/externalCode=ValueA",
"status": "OK",
"editStatus": "UPSERTED",
"message": null,
"index": 0,
"httpCode": 200,
"inlineResults": null
}
]
}
Request
Operation Insert
URI https://<API-server>/odata/v2/cust_EntityP
Response
Sample Code
{
"error": {
"code": "COE_GENERAL_BAD_REQUEST",
"message": {
"lang": "en-US",
"value": "[COE0018]Record '(effectiveStartDate=2020-07-17,
externalCode=ValueA)' of type 'cust_EntityP' already exists."
}
Refer to the sample operation for how the system handles different values of the self-referenced field in payload.
Example Entity
Operation Upsert
URI https://<API-server>/odata/v2/upsert
Payload {
"__metadata": {
"uri":
"cust_EntityP(effectiveStartDate=datetime'2020-07-17T00:
00:00',externalCode='ValueA')"
},
"name": "A",
"cust_goNav": {
"__metadata": {
"uri":
"cust_EntityP(effectiveStartDate=datetime'2020-07-17T00:
00:00',externalCode='ValueA')"
},
"name": "B"
}
}
Response
Sample Code
{
"d": [
{
"key": "cust_EntityP/
effectiveStartDate=2020-07-17T00:00:00Z,cust_EntityP/externalCode=ValueA",
"status": "OK",
"editStatus": "UPSERTED",
"message": null,
"index": 0,
"httpCode": 200,
"inlineResults": null
}
]
}
Refer to the sample operation for how the system handles different values of the same Generic Object field in
payload.
Example Entity
Request
Operation Upsert
URI https://<API-server>/odata/v2/upsert
Response
Sample Code
{
"d": [
{
"key": "cust_EntityP/
effectiveStartDate=2020-07-17T00:00:00Z,cust_EntityP/externalCode=ValueA",
"status": "OK",
"editStatus": "UPSERTED",
"message": null,
"index": 0,
"httpCode": 200,
"inlineResults": null
},
{
"key": "cust_EntityP/
effectiveStartDate=2020-07-17T00:00:00.000Z,cust_EntityP/externalCode=ValueA",
"status": "OK",
"editStatus": "UPSERTED",
"message": null,
"index": 1,
"httpCode": 200,
"inlineResults": null
The API center is a one-stop shop for accessing OData API tools. You'll find the API Center in the Admin Center. If
you can't see it there, check that you have the permission for at least one of the tools hosted on the API Center. For
more information, see Permissions for API Center [page 177].
Note
Users can only access tools for which they have permission. Otherwise the tools are displayed but they are
greyed out.
Related Information
Here is a list of role-based permissions available under Administrator Permissions Manage Integration Tools :
Permission Description
Caution
This permission allows user to view data sent in every SFAPI
call. The data could be sensitive.
Access to SFAPI Metering Details Access the SFAPI Metering Details to see SFAPI usage statistics,
such as number of calls, etc.
Access to Event Notification Subscription Manage subscriptions that send event notification on internal Suc
cessFactors events to external application systems.
Access to Event Notification Audit Log View event notification event logs.
Allow Admin to Access OData API through Basic Authenti Admin access to OData API to do CRUD operations for exposed
cation
entities.
Access to OData API Audit Log Access to the OData API audit log to monitor the API calls.
Manage OData API Basic Authentication Manage OData API Basic Authentication settings.
Access to OData API Version Control Check the current OData API version and switch versions.
Access to API Option Profile Access the API option profile tool to manage processing parame
ters for the User entity.
OData API Competency Rating Import Permissions to import and export OData API competency ratings
Access to OData API Metadata Refresh and Export Access the OData API Metadata Refresh and Export tool.
Access to OData API Data Dictionary Access the OData API Data Dictionary tool.
Manage LinkedIn Account Setup Manage the setup of LinkedIn account in Security Center.
Restrict Access to Data Replication Monitor to Specific Tar Define which permission group has access to which target popula
get Population tions when working with the Data Replication Monitor.
Note
This permission only works when the Access to Data
Replication Monitor permission is granted.
Delete Records from Data Replication Monitor Allow administrators to delete records from the Data Replication
Monitor.
Note
This permission only works when the Access to Data
Replication Monitor permission is granted.
Describes the features, required permissions, and performance tips for the OData API Data Dictionary
In the OData API data dictionary, you can see information about attributes and supported operations.
The look and feel of the OData API data dictionary has been enhanced. It's available in the Admin Center. If you
can't see it there, check that you have the permission, Access to OData API Dictionary (available in Manage
Integration Tools and applicable to both RBP and non-RBP users).
Note
To optimize performance, you can request that Customer Services activate the provisioning switch, Enable
OData API Dictionary Cache (in Web Services).
In the OData API data dictionary, you can use Search All, or narrow your search to Entity, Complex Type, or Function
Import.
Using the search filters Entity, Complex Type, or Function Import also lets you narrow your search by Tag, for
example EC - Payment Information . This then ensures that under Name, you only see the entities available for this
tag.
Information is displayed in a tabular form. A horizontal and vertical scroll bar are available to make viewing easier.
The following columns are available and can be sorted alphabetically in ascending or descending order:
● Property Name
In addition to sorting alphabetically, you can also filter by a defined term.
These columns are fixed. However, the following columns can be arranged to meet your requirements:
● sap.picklist
● Business Key
● Nullable
● sap: required
● sap: creatable
● sap: updatable
● sap: upsertable
● sap:visible
● sap: sortable
● sap: filterable
● sap:semantics
● sap:display-format
● sap:aggregation role
● DefaultValue
● MaxLength
● Precision
● Scale
● sap:inlineRequired
● Navigation Target
Lists all available SFAPI entities and the fields in each entity.
You can use the Legacy SFAPI Data Dictionary tool to look up SFAPI entities, supported operataions, and field
properties.
Note
In Admin Center, the tool is called SFAPI Data Dictionary, but in API Center, it is called Legacy SFAPI Data
Dictionary.
To access the dictionary, you must have the Admin Access to SFAPI Data Dictionary permission under Manage
Integration Tools available in both user-based and role-based permission systems.
The SFAPI Data Dictionary lists all available SFAPI entities in your company. You can expand any entity to see the
supported operations, field names, and detailed attributes for each field in a table. The following attributes are
available for an SFAPI entity:
● Name
● Data Type
● Label
● Max Length
When HTTP Basic Authentication (Basic Auth) is used to access OData API, you can control which IP addresses are
allowed the access using the OData IP whitelisting tool. Please note that this feature is called OData API Basic
Authentication Configuration in the Admin Center and OData IP Whitelisting in the API Center.
You have the following options to limit the access of OData API using Basic Auth:
● Always: Users are allowed to log in using Basic Auth from any IP address.
● Never: No one is allowed to log in using Basic Auth.
● Restrict access to below IPs: Only users from specified IP addresses are allowed to log in using Basic Auth. With
this option enabled, you can enter a list of IP addresses separated by comma. For example: 10.20.30.40,
10.20.30.41, 10.20.*.*.
Note
When you use wildcards, you must enter all four bits of the IP address. For example, 10.10.*.* is a valid
entry and 10.10.* isn't.
Note
The OData IP Whitelisting tool can only control OData API accesses using HTTP Basic Auth. To control all API
accesses, use the Password & Login Policy Settings tool in Admin Center. For more information, see Setting
Password Policy and Exceptions for API Login.
Related Information
The IP whitelisting tool for SFAPI has been moved to the Password and Login Policy Settings page. You can specify
the IP addresses from which the SFAPI can be accessed.
Related Information
Use this tool to register and manage OAuth2 client applications for inbound OData API.
You can find more information about OAuth configuration in Registering Your OAuth2 Client Application [page 13].
Enable API audit logs in API Center so you can view and download API transaction history for troubleshooting API
issues.
Prerequisites
Note
The audit log can contain sensitive data. Make sure that the access to API audit logs is given only to trusted
users. As a safety strategy, grant permission to developers only when necessary for development and
debugging purposes, and then remove access when the tasks are finished. Note that removing a user's access
to the audit log doesn't remove the data from the server.
API Audit logs include API request and response information, including payloads. This information help developers
diagnose issues happened during an API call.
Note
A maximum log count is set for your company. This number varies from 10,000 to 500,000 depending on the
Provisioning setting. When the limit is reached, older entries are purged.
Additionally, audit data in preview instances is subject to the retention time defined in Impact of Audit Data
Purge in Preview Instances. Any audit data that meets either criteria is purged in preview instances.
Depending on the operation type and status, the information kept in the audit log varies. The following tables show
what information is logged in SFAPI and OData API audit logs:
Operation Type Operation Status Request Header Request Payload Response Header Response Payload
Query Success Y N Y N
Failure Y N Y N
Edit Success Y N Y N
Failure Y Y Y Y
Including partial
failure in $batch
and upsert
Operation Type Operation Status Request Header Request Payload Response Header Response Payload
Query Success Y N Y N
Failure Y N Y N
Edit Success Y Y Y Y
Failure Y Y Y Y
Procedure
1. Go to Admin Center API Center Audit Log Settings and turn on audit logs for SFAPI or OData API using
the following options:
OData API Audit Log Switch it on if you want to enable audit log for
OData API.
Enable Payloads in OData API Audit Log for Edit Errors Enable this option if you want to log request
and response payloads for failed edit opera
tions of OData API.
Enable All Payloads If you choose this option, all payloads of query
and edit operations for OData API are logged
for the next 4 hours, regardless of whether it’s
successful or not.
SFAPI Audit Log Switch it on if you want to enable audit log for
SFAPI.
Enable Payloads in SFAPI Audit Log for Edit Operations Enable this option if you want to log request
and response payloads for all edit operations
for SFAPI.
Enable Payloads in SFAPI Audit Log for Edit Operations If you choose this option, all payloads of query
and edit operations of SFAPI are logged for the
next 4 hours, regardless of whether it’s suc
cessful or not.
2. Go to Admin Center API Center and choose one of the following tools:
○ OData API Audit Log: View OData API transaction history and payloads.
○ Legacy SFAPI Audit Log: View SFAPI transaction history and payloads.
3. On the audit log screen, use the filter section to filter or search for the transaction history. For each log, you can
see the following information:
○ Log ID (filterable, text only)
○ Login ID (filterable, text only)
○ Session ID
○ Request ID
○ Status
○ OData API Call
○ Entity
○ Request Time
○ Responses (ms) (filterable)
The response filter supports the following operations:
○ Equal to (=)
○ Greater than (>)
○ Less than (<)
○ Greater than or equal to (>=)
○ Less than or equal to (<=)
The detail screen contains request and response header information, and payload information if it's enabled.
Switch tabs to view detailed information.
Request header:
GET https://<API-Server>/odata/v2/checkUserPermission?
%24format=json&accessUserId=
%27extuser133%27&company=<CompanyID>&permLongValue=-1L&permStringValue=
%27cubetree_access%27&permType=%27cubetree%27
ip address: <Server IP>
host: <API-Server>
x-forwarded-for: <Server IP>
x-forwarded-port: 9003
x-forwarded-proto: http
authorization: Bearer **********
accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
accept: */*
user-agent: Ruby
x-forwarded-host: <API-Server>
Response header:
X-Event-ID: EVENT-UNKNOWN-UNKNOWN-or3areboh40s-20200330013237-191176
CorrelationId: 2febeba5-963d-4f5e-b0b4-1ef2b3566878
Content-Type: application/json;charset=utf-8
DataServiceVersion: 1.0
Vary: Accept-Encoding
Content-Encoding: gzip
SFODataServerTimeZone: America/New_York
Status: 200
You can use the OData API Metadata Management tool to refresh metadata cache and export OData API metadata.
To view OData API Metadata Refresh and Export, select the OData API Metadata Management link in API Center.
You will see two buttons:
● Refresh
● Export
Operation Description
Export Exports and downloads the metadata. The default export file
name is CompanyName-Metadata.xml
In most cases, refresh is automatic, for example when an admin changes or creates an object definition in the
Extension Center or in the Configure Object Definition page or when an Admin enables/disables a feature in the
Upgrade Center leading to a change that requires a refresh of OData metadata.
Use the Refresh option whenever entities or fields are not reflecting the latest changes made to them.
Note
A manual refresh is always required when a user with provisioning access chooses to enable/disable a product
feature or change the language packs via provisioning leading to a corresponding OData API change. In this
case, the change will only be effective after a manual refresh.
You can use the Manage API Option Profile tool to maintain API option settings and manage API option profiles for
the User entity.
Prerequisites
You have the Manage Integration Tools Access to API Option Profile permission assigned to your role.
Context
The User entity allows you to specify a few processing parameters to control extra business logic that can be
optionally applied when you edit a user. For example, if you enable the Process inactive employees option in a
profile. When you use this profile in a User API request, you're able to modify the information of an inactive user.
The options are stored in API option profiles. You can choose different options to enable in different profiles.
Procedure
Currently the OData API Option Profile tool supports only the User OData entity.
4. The corresponding options to the selected entity are displayed on the right. Choose the ones you want to
enable for this profile.
Option Description
Use the user name Choose the default password format for cre
ating users. If no option is specified, the sys
Use the UserID tem uses user name as the default password
option.
Use the e-mail address
Starting from Jun 19, 2020, you can only
Use a system-generated random password choose the system-generated password op
tion when you create new profiles. Existing
profiles that use user name, user ID, and e-
mail as default password values continue to
work.
Caution
We encourage you to review your existing
Default password format API calls and stop using unsafe password
options. You can either edit your API op
tion profiles and change the password
option to Use a system-generated
random password or enable the Use
System Generated Password by Default
Note
Welcome e-mails can't be sent to rehired
Send welcome email to new users employees. For security reasons, e-mails
can only be sent to corporate e-mail ac
counts.
Feature Settings .
Automatic Manager Transfer Automatic insertion of new manager as This option adds the new manager as the
next document recipient if not already next person on the approval chain to receive
For each option, you can spec the form. Choose this if you want the new
ify if you want to transfer the manager to be a part of the review process
document to manager, matrix and remove the old manager from the review
manager, or both. process.
Automatic Inbox Document Transfer To Choose this option if you want to move all
New Manager forms from the old manager's inbox to the
new manager's inbox. Forms in all other fold
ers, such as en route, aren’t transferred.
Automatic En Route Document Transfer To Choose this option if you want to move en-
New Manager route forms from the old manager to the new
manager. Use this option for transferring 360
forms.
Automatic Completed Document Copy to Choose this option if you want to move all
New Manager completed forms of the employee to the new
manager's Completed folder. All other forms
aren’t transferred.
Automatic Process Owner Change To New This option is currently not supported in
Manager For In-Progress Documents When OData API.
Old Manager is Process Owner (Only for
360)
Automatic Process Owner Change To New This option is currently not supported in
Manager For Completed Documents When OData API.
Old Manager is Process Owner (Only for
360)
Remove Inactive Employees' In-Progress Choose this option if you want to delete
Documents forms from the inbox of inactive users.
Remove Inactive Employees' Completed Choose this option if you want to delete com
Automatic Document Removal Documents pleted forms for inactive users.
Remove Inactive Employees' 360 Evalua Choose this option if you want to delete 360
tion Documents participant evaluation forms for inactive
users.
5. Add parameter apiOptionProfileID=<profile ID> to your URI when you insert, update, or upsert a user.
Operation Insert
URI https://<API-Server>/odata/v2/User?
apiOptionProfileID=ID01
Caution
Multiple parameter values aren’t supported. If you include multiple values of the same parameter, for
example, apiOptionProfile=ID01&apiOptionProfile=ID02, only the first parameter value is
processed.
Next Steps
We recommend all users log in to the system and reset their passwords to ensure account security.
The SFAPI Metering Details tool gives you analytics on your API usage up to the last 30 days. You can use this page
to see API call history analytics like how many times the API was called, or what was the total record counts
accessed in your system.
Select one of the following dimensions (views) from the drop-down list to display the API call history:
● Call Count
● Processing Time (Avg)
● Processing Time (Total)
● Bandwidth (Avg)
● Bandwidth (Total)
● Bandwidth (Request Avg)
● Bandwidth (Request Total)
● Bandwidth (Response Avg)
● Bandwidth (Response Total)
● Record Count (Avg)
● Record Count (Total)
● Record Count (Request Avg)
● Record Count (Request Total)
You can choose to display the API call history in the following time periods:
● 6 hours
● 12 hours
● 24 hours
● 2 days
● 4 days
● 7 days
● 15 days
● 30 days
Service Catalogs provide a way to group APIs together in contextually-relevant groups, greatly reducing the size of
the $metadata for each catalog.
Response Description
This XML output follows regular OData metadata format. It contains 2 parts:
1. An OData entity named 'Service'. Currently this entity is not accessible excepts it's definition in metadata.
The metadata document is a static resource that describes the data model and type system understood by that
particular OData service. You can use the metadata document to learn how to query and navigate between the
entities in the system. Metadata extensions provide additional metadata information, which gives you access to
advanced operations such as data retrieval filtration.
Entity Tag (ETag) is used to check if the metadata saved in client cache is the same as the one on the server. The
value of max-age in the Cache-Control header is set to the lifecycle of the client metadata cache. If the cache is
valid, no new request is sent to the server. When a client raises a request for metadata the first time, the server
sends back a response with the latest metadata, along with a response header named ETag. The value of ETag is
unique and matches the metadata version. This value is used for “If-None-Match” in the request header the next
time the same request is raised. The server checks the “If-None-Match” value when a new request arrives. If its
value is the same as the latest ETag generated by the server, the server simply sends back a status code of 304
(Not-Modified) instead of resending the entire metadata.
The metadata document describes the capabilities of the API in your SAP SuccessFactors HXM Suite instance. It
contains the details of each entity that is accessible through the API, including fields, their names and labels, their
data types, and the relationships (associations) between the entities.
The metadata document also describes the operations available in the API. The OData protocol specifies four basic
database style operations: query, insert, update, and delete. SAP SuccessFactors has added a custom operation
called "upsert" which performs an insert or update operations. Typically custom operations perform specific
business transactions, especially if a custom API is easier to manage versus a database style approach against
multiple entities.
Regardless of which operations are used, SAP SuccessFactors HXM Suite applies the appropriate business logic for
each entity type. In other words, even though the operations appear to be database-centric, the API goes through
the business logic in the application layer. The API doesn’t go directly against the database, nor does it bypass the
business logic layer. The entities in the API represent logical application objects familiar to an application user. Note
that the entities don’t represent the actual physical data storage implementation, which can be in a different
structure.
You can use API queries to retrieve the entire OData metadata or the metadata of a selection of entity types from
the API server.
You can use the $metadata system option to query the entire metadata document or the metadata document for
a subset of entity types.
A metadata request using $metadata returns an XML serialization of the service, including the entity data model
(EDM) and the service operation descriptions. The metadata response supports only application/atom+xml
type.
https://<API-Server>/odata/v2/$metadata
● You can also request part of the metadata document by specifying a subset of entity types:
https://<API-Server>/odata/v2/<Entity1>,<Entity2>...<EntityN>/$metadata
For example, the following URI identifies the metadata of the User entity type:
https://<API-Server>/odata/v2/User/$metadata
The following query returns the metadata of the User and Photo entity types:
https://<API-Server>/odata/v2/User,Photo/$metadata
The default language for all property labels (sap:label) in the metadata document is English (en-US). You can
request the metadata in different languages by specifying the language with the sap-language parameter. For
example, the following URI returns a metadata document with all labels in German:
https://<API-Server>/odata/v2/User/$metadata?sap-language=de-DE
You can also use Entity('EntityName') to query the metadata of a single entity type. This type of query allows
you to specify the format of content using the $format query option.
The following example query retrieves the metadata document of the User entity type in JSON format:
https://<API-Server>/odata/v2/Entity('User')?$format=JSON
The returned metadata contains entity set information of navigation properties as shown in the snippet below:
{
"aggregationRole": null,
"businessKey": false,
"defaultValue": null,
"displayFormat": null,
"elmStrength": "Unclassified",
"fieldControl": null,
"filterable": true,
"fromRole": {
"multiplicity": {
"name": "ZERO_TO_ONE",
"symbolString": "0..1"
},
"path": "approverNav_of_Advance/approverNav",
"role": "approverNav",
"EntitySet": "User"
},
"id": false,
"insertable": false,
"insertablePath": null,
"label": null,
"maxLength": null,
"name": "approverOfAdvanceNav",
"navigateToPojo": false,
"path": "User/approverOfAdvanceNav",
"precision": null,
"relationship": {
"deletable": true,
"end1": {
"multiplicity": {
"name": "MANY",
"symbolString": "*"
},
"path": "approverNav_of_Advance/Advance",
"role": "Advance",
"EntitySet": "Advance"
},
"end2": {
"multiplicity": {
"name": "ZERO_TO_ONE",
"symbolString": "0..1"
},
"path": "approverNav_of_Advance/approverNav",
"role": "approverNav",
"EntitySet": "User"
},
"insertable": true,
"name": "approverNav_of_Advance",
"path": "approverNav_of_Advance",
"updatable": true,
"upsertable": true
},
"required": false,
"scale": null,
The OData Metadata document for SAP SuccessFactors HXM Suite contains the following information about the
EntitySet extension.
Example
The OData metadata document for SAP SuccessFactors HXM Suite contains the following information about
properties.
● 0 = hidden
● 1 = read-only
● 3 = optional
● 7 = mandatory
Example
The OData metadata document for SAP SuccessFactors HXM Suite contains the following information about
navigation properties.
sap:filterable true The navigation property can be used in the $filter [page
45] system query option.
● 0 = hidden
● 1 = read-only
● 3 = optional
● 7 = mandatory
Example
Error HTTP
Code Refine Error Message Code Solutions
COE0001 Error messages related to missing N/A Assign the corresponding permissions.
- permissions.
COE0002
COE0005 The given value <a> for property <b> 400 ● Check entity's property attribute in OData API Data
is invalid. <c>. Please check the prop
Dictionary.
erty in Admin Center OData API ● Check the entity metadata and for the property attribute.
Data Dictionary .
COE0008 Invalid function import name: <a>. 400 Enter the correct function name.
Please check the name in Admin
Center OData API Data
Dictionary .
COE0009 Entity {0} is not insertable. 400 Do not insert records to this entity.
COE0010 Entity {0} is not updatable. 400 Do not update records of this entity.
COE0011 Entity {0} is not upsertable. 400 Do not upsert records of this entity.
COE0012 Entity {0} is not deletable. 400 Do not delete records of this entity.
COE0013 Entity {0} is not replaceable. 400 Do not replace records of this entity.
COE0018 Bad request. 400 ● If it is a GET reques, make sure the field names are correct
and the navigation is properly formed.
● If it is a POST request, make sure the mandatory fields
present in the request payload and verify the format in which
the request is being sent.
COE0021 Invalid property names: {0}. Please 400 Enter a correct property name.
check the property name in Admin
Center OData API Data
Dictionary or entity metadata.
COE0022 Properties {0} are not accessible. 400 Do not query inaccessesible properties.
Please check the property name in
Admin Center OData API Data
Dictionary or the entity metadata.
COE0023 Property {0} is not visible. Please 400 Do not query invisible properties.
check the visibility setting of the prop
erty in Admin Center OData API
Data Dictionary or the entity meta
data.
COE0024 Unsupported entity set category: <a>. 400 Query only supported categories.
Only public, restricted, internal, and
beta categories are supported.
COE0025 Unsupported feature: <detail error 400 ● Check if the operation which is being carried on the entity is
message>
supported by the entity in the data dictionary.
● Check if navigation is expanded without the base entity.
● Check if you're expanding the wfRequestNav property of an
MDF entity that does not have workflow enabled. If workflow
is not enabled, you're not allowed to expand wfRequestNav.
COE0026 Unsupported locale: <a>. For a list of 400 Use a supported locale.
supported languages and locales,
please visit 2269945
COE0027 Please add required properties in pay 400 Check the dictionary and add required properties in the payload.
load. <a>. You can check which prop
erties are required for an entity in
Admin Center OData API Data
Dictionary or the entity metadata.
COE0028 Record for entity <a> with key <b> 400| Change key value.
does not exist. Please check the key 404
value.
COE0029 A record for entity <a> with key <b> 400 Change key value.
already exists. Please use another key.
COE0030 <Message text from another mod 400 Check the OData API Data Dictionary or entity metadata.
- ule>. Please check the property set
COE0032 ting in Admin Center OData API
Data Dictionary or the entity meta
data.
COE0033 Maximum length limit exceeded for 400 Reduce the length of the field value.
field <a>: actual length: <b>; maxi
mum length: <c>.
COE0034 Property <a> is not inline-required. Check the OData API Data Dictionary or entity metadata.
Please check the sap:inlineRequired
setting of the property in Admin
Center OData API Data
Dictionary or the entity metadata.
COE0035 Unable to parse option roleNames Verify the format of customOption roleNames.
{0}. Please verify the format.
COE0037 Entity <a> is not editable. Please 400 Do not edit records for the entity.
check the entity setting in Admin
Center OData API Data
Dictionary or the entity metadata.
COE0038 Invalid service alias name: {0}. Please Use the correct alias name. For now, we only have three service
contract your system administrator to
aliases: _CPMEntityService, _FeatureProvisioningService, and
check the service alias name.
_TodoEntryV2Service.
COE0039 <Message text from another module> Do not use service aliases along with entity/function/complex
Type.
COE0041 The given value <a> for parameter 400 Enter the correct parameter value.
<b> is invalid. Please check the value
type in Admin Center OData API
Data Dictionary and use the correct
value in API request.
COE0042 Parameter {0} is missing. Please 400 Add the required parameter and value.
check required parameters in
Admin Center OData API Data
Dictionary and add the required pa
rameters in API request.
COE0044 Expand size {0} exceeds the limit {1}. 454 Reduce $expand size.
COE0046 Expand depth {0} of {1} exceeds the 454 Reduce $expand depth.
limit {2}.
COE0048 Property <a> is missing in function 400 Add the missing property and its value to the payload.
import payload. Please add required
parameters in payload.
COE0049 Value <a> for property <b> in function 400 Enter the correct value for the property.
import payload is invalid. Please cor
rect the value in payload.
COE0052 Expand limit of <n> records was ex 454 Reduce the expand size.
ceeded. Please decrease the number
of records to expand or use filters to
narrow down the result.
COE0053 Invalid service group name: <a>. Enter a valid service group.
Please contract your system adminis
trator to check the service group
name.
Related Information
Error HTTP
Code Error Message Code Solution
LGN00 Internal: Invalid endpoint odata/v2/internal. You 404| You're trying to access an OData API category that's not
01 400 available in your instance.
are not allowed to access the internal category
with an external request. ● If it's an internal category, you have no access to it.
● If it's a test category, you can only access it in a test
Test: Invalid endpoint odata/v2/test. The test cat
instance.
egory is not enabled in the production environ
● If it's a beta category, contact your system adminis
ment.
trator and find out whether you have the access.
Beta: Invalid endpoint odata/v2/beta. The beta
category is not enabled. Please contact your sys
tem administrator to enable the OData API Public
Beta Category.
LGN00 Authentication failed. You do not have the admin 403 Assign the corresponding permission in RBP.
02 permission to access OData API. Please check the
permission setting for your role at Manage
Integration Tools Allow Admin to Access OData
API through Basic Authentication .
LGN00 Authentication credentials are required. Please 401 Provide your credentials
03 provide a valid username, password and company (<username>@<companyID>:<password>) and
ID. encode them using Base64 format.
LGN00 You are not allowed to access OData APIs using 401 Choose the right API server.
04 Basic Auth or OAuth on a non-API server. To do
this, you need to switch to the corresponding API
server. For a list of available servers, visit About
HXM Suite OData APIs [page 8].
LGN00 Wrong token format. Please check your token for 400 Follow the steps in Authentication APIs for OAuth2 [page
05 OAuth authentication. 19] to generate a new OAuth token.
LGN00 Token {0} has expired. Please log in again or per 401 Follow the steps in Authentication APIs for OAuth2 [page
06 form a getToken operation to refresh your access 19] to generate a new OAuth token.
token.
LGN00 Invalid OAuth token {0}. Please check the token 401| Follow the steps in Authentication APIs for OAuth2 [page
07 value. 403 19] to validate your token or generate a new OAuth token
if needed.
LGN00 Company {0} not found. Make sure you have en 401 Enter the correct company ID.
08 tered the correct company ID. Please note that the
company ID is case sensitive.
LGN00 OData API is not enabled for this company. Please 401 Contact SAP Cloud Support to enable OData API for your
09 contact SAP support to enable the feature.
company.
LGN00 Basic Authentication is disabled for company 401 Assign the corresponding permission and add your IP
10 <companyID> or your IP <IP address> is not au address to the allowlist.
thorized to access OData API. To enable Basic Au
thentication, grant the permission to your role at
Manage Integration Tools Manage OData API
Basic Authentication . To authorize your IP ad
dress for OData API access, go to API Center
OData API IP Whitelisting .
LGN00 Authentication failed. You have provided an ex 401 Contact SAP Cloud Support.
11 pired company (status code = <code>). Please
contact your system administrator to reset the ex
piry date.
LGN00 Authentication failed. You have provided an inac Contact your system administrator to re-activate the
12 tive company (status code = <code>). Please con company.
tact your system administrator to re-activate the
company.
LGN00 Authentication failed. We have prevented an at 401 Add an exception as instructed.
13 tempted login from unauthorized IP: <IP address>
to company ID: <companyID> with username:
<username> (status code = <code>). To authorize
the login, add an exception in Admin Center
Company Settings Password & Login Policy
Settings Set API Login Exceptions . For more
information, visit Setting API Login Exceptions
[page 29].
LGN00 Authentication failed. The user account is locked 401 Unlock the user.
14 (status code = <code>). Please contact your sys
tem administrator to unlock your account.
LGN00 Authentication failed. You have entered an incor 401 Enter the correct username and password.
15 rect username or password (status code =
<code>).
LGN00 Authentication failed. The user has no login per 401 Check permission and contact your system administra
16 mission (status code = <code>). Please check if tor to enable permission. If the user is locked, go to
the following permissions are granted: Admin Center Reset User Account .
Basic Authentication
LGN00 Authentication failed. The password has expired 401 Reset your password or contact your system administra
18 (status code = <code>). Please login to SAP Suc tor..
cessFactors and reset the password, or contact
your system administrator.
LGN00 Incorrect format for HTTP Basic Authentication. 401 Enter the credentials in correct format.
20 Please encode your credentials
(<username>@<companyID>:<password>
) using Base64 format. For more information, visit
HTTP Basic Authentication (Deprecated) [page
11].
LGN00 Person-only is not supported when Global Assign 400 Contact your system administrator to turn on Global As
23 ment and Concurrent Employment features are signment and Concurrent Employment features.
not enabled. Please contact your system adminis
trator to enable the features.
LGN00 Employee Central V2 feature is not enabled. Contact your system administrator to turn on Employee
24 Please contact your system administrator to ena Central V2 feature.
ble this feature.
LGN00 Authentication failed. The company ID is invalid 401 Enter a valid company ID.
25 (status code = <code>).
Related Information
HTTP RESPONSE
ERROR STRING CODE RESPONSE ERROR COMMENTS
OAUTH2_ERROR_INVALID_TOKEN_URL 500 The token URL is invalid. The token URL is in an in
correct format.
OAUTH2_ERROR_INVALID_X509_PRIVATE_K 500 The X.509 private key The X.509 private key is
EY contains an error. invalid.
OAUTH2_ERROR_UNABLE_TO_SIGN_SAML_AS 500 Unable to sign SAML as Failed to sign SAML as
SERTION sertion. sertion using the pro
vided X.509 private key.
OAUTH2_ERROR_SAML_ASSERTION_EXPIRED 400 The provided SAML as The SAML assertion usu
sertion is expired. ally expires in 5-10 mi
nutes.
OAUTH2_ERROR_UNABLE_TO_SIGN_TOKEN 403 Unable to sign access to The OAuth server uses a
ken. SuccessFactors server
certificate to sign the ac
cess token. An error oc
curs if the access token
has already been signed,
or if the SuccessFactors
server certificate is miss
ing.
OAUTH2_ERROR_UNABLE_TO_VERIFY_TOKEN 401 Unable to verify the signa The access token has
ture of the access token. modified the HTTP
MITMA. The SuccessFac
tors server public key
couldn't be found.
OAUTH2_ERROR_TOKEN_REJECTED_OR_EXPI 403 The access token is either Token has expired (de
RED rejected or expired. fault is 24 hours).
Related Information
Read and follow the best practices in this topic for optimal OData API performance and better user experience.
Deprecation of Partner API, SFAPI Ad Hoc, and SFAPI for Simple Entities
As of August 1, 2018, All Partner API, SFAPI ad hoc, and SFAPI simple entities (excluding CompoundEmployee API)
have been deprecated.
Instead of querying all records, query only the records that have been modified since your last execution for
integration use cases, and use server pagination to ensure stable results. For more information on server
pagination, see Pagination [page 63].
Compared with client pagination, server pagination is faster and doesn’t suffer data loss and duplication issues
when simultaneous edits occur in the same instance. You can choose either cursor-based or snapshot-based
pagination for your integration use case. See Pagination [page 63] for more information.
Repeating queries in much less than one hour will consume too much API resource, which may be throttled in the
future. In extreme cases, you may even be denied of service because of frequent queries, especially ones that
require heavy backend processing.
Querying records one at a time doesn’t take advantage of database optimization capabilities. Singleton queries are
often used to perform in-memory joins and must be avoided whenever possible. Here are two bad examples:
Instead of pulling many records one at a time using key predicate, use $batch or the $filter IN clause.
In OData API, a user login session is created on the server for each request. This is a resource demanding process,
especially when login audit is enabled. A high volume of requests may lead to excessive usage of system resource
and drastically slow down performance. Therefore, we encourage you to use $batch operation or the IN clause in
$filter query option whenever possible. For example, instead of using the ToDo API to query multiple users with
many requests, use the TodoEntryV2, which allows you to do it in one request.
Use $batch if you want to operate on different entities or if you want to perform different operations on the same
entity.
Use $filter IN if you want to query multile keys using the same entity.
See $filter [page 45] and Batch Operations [page 104] for more information.
Without the $select query option, a query returns all available properties of an entity. For better performance, we
recommend that you add the $select system query option to specify only the properties you need.
Creating login session is a resource demanding task. Instead of creating a session for each HTTP transaction or
each page of paginated data, reuse login sessions.
Do Not Use API for Integration That Is Only Designed for Single User
APIs designed for single users aren't ideal in integration scenarios. Iterating requests for all uses creates one login
session for each user and significantly slows down performance. One example is the ToDo API, which only allows
querying data of a single user. For integration purpose, use the new TodoEntryV2 API. TodoEntryV2 allows you to
query items of multiple users with the OData API Todo Export permission.
The OData API can return a maximum number of 1000 records in a single page. You should tune your batch sizes to
be as large as possible. For more complex transactions, you need to decrease the size to avoid HTTP timeouts.
The $expand statement can be used to request master-detail data. However, using $expand to join a large number
of tables can lead to poor performance. We recommend that you first fetch master by batch and detail on demand
when user requests.
For example, A query that retrieves all JobRequesitions and expands all attachments for each application is
considered too complex and large. This type of queries are error-prone and will be throttled in the future.
1. Bind the master list to simple top level Job Requisition so UI5 loads in on demand via batch operations as the
user scrolls.
2. When the user clicks on a job requisition, only then do you bind the detail list to candidate, filtering on the
selected job requisition id.
3. Only load resume and cover letter when you click on a job candidate.
Note
Caching Picklist and Foundation Object lookups might be efficient for a large data set where "cache efficiency"
can be high.
Poor performance is often a sign of misuse of APIs. As a rule of thumb, always keep your transactions simple.
Compress the data by adding header Accept-Encoding=gzip in your requests for faster data transmission.
Full data sync can lead to high resource consumption and poor performance. Instead of doing a full data sync
intermittently, schedule more frequent data sync but limit the scope to only delta changes.
It’s easy to build a query that does more $select and $expand than you need. It's especially easy with the Boomi
connector, which has UI limitations due to limitations in the Dell Boomi connector toolkit.
As a developers, you might get tired of tweaking your queries to match the content as requirements change so
often. You might be tempted to pull more than you need and release the queries to production without tuning.
The Integration Center can help avoid this situation. Instead of a building a query manually based on the mapping
of fields, the Integration Center generates the query based on the mapping.
Set your client to wait a reasonable amount of time before timing out. Complex operations can take as long as 10
minutes and the network and servers continue to process a transaction during that time. It’s best to wait for the
transaction to complete rather than wasting it.
Retry logic can help to recover transactions that failed due to internet connectivity or backend server issues, but
retry must be done with care based on the type of HTTP error.
In all cases, you must "sleep" a reasonable amount of time from 1 to 5 minutes before attempting a retry.
"Obsessing" by immediately retrying can cause a denial-of-service condition and not give a server time to recover.
Only return a maximum number 5 of times before abandoning your task. For example, the Boomi connector retries
only a maximum of 5 times.
● HTTP response code 5XX other than 500. A 500 error can't recover because there's a problem with your query
or payload.
Note 503 Service Not Available can occur when a server is overloaded. It can also occur during maintenance
periods. Avoid running queries during these published maintenance periods.
● HTTP response code 412 can occur to edit operations during optimistic locking on back-end transactions.
○ For batch operations, only retry failed records. Do not include successful edit operations in the retry
payload. This includes specific transaction in a $batch and specific instances in an multirecord upsert
payload.
○ For 412 errors, only retry once.
● HTTP timeout errors can be retried only if your client timeout matches the SuccessFactors infrastructure
timeout of 5 minutes. If you retry a timeout before this period, you're stacking transactions on top of each
other and performing a possible denial-of-service attack.
● HTTP connection reset error and no response is given from server side.
● 401 Authentication Failed error. You have provided incorrect credentials and retrying doesn't help.
● 404 Not Found error. You can't recover something that doesn’t exist.
● 400 Bad Request error. This error can occur for edit operations that are missing required data or have
incorrect formats.
● Do not retry Insert or Upsert operations where the object has an auto-increment key. Both can result in
duplicate data.
Although it’s technically possible to inline upsert a source object that is referenced in an MDF object as a Generic
Object (GO) or as a Valid When relationship, we don’t recommend it because of performance issues. Use inline
upsert only for objects with a parent-child relationship, in other words, a composite association.
If a parent entity has multiple composite type associations and more than one of them point to the same
destination object, do not upsert the destination object directly. OData API does not know which association to edit
and it always edits the first association. To be able to disginguish one association from another, inline upsert the
parent with the corresponding association.
The Admin access to MDF OData API permission is an admin permission that allows users to query and edit all MDF
OData entities. This permission overrides all entity-level permissions and is intended for integration scenarios only.
If you want to use an MDF OData entity on UI, entity-level permissions might be a better choice.
To prevent server overloads and ensure high availability, we recommend a maximum of 10 concurrent requests /
threads per client. Multithreading is only allowed for editing single records. Do not use it for queries/read
operations, massive upserts, or $batch operations.
● Use OAuth 2.0 as your authentication method for the technical user.
● Create one user for each integration process. Do not share users among different processes.
● Do not put sensitive information in URIs. If you can't avoid it, use HTTP POST tunnel to hide it or use $batch
operations.
Always include the following tracing headers in your requests so that SAP Cloud Support Ops team can easily trace
the API calls and troubleshoot your problems faster:
● X-SF-Correlation-Id - A unique GUID included in the header and in the log file for each HTTP request/page
request.
● X-SF-Execution-Id: Execution ID of the integration process.
● X-SF-Process-Name: Process name of the integration.
● X-SF-Process-Id: Process GUID of the integration.
● X-SF-Client-Tenant-Id: Integration account ID.
Before you implement any integration, always try to get the latest metadata of the entities. Data model changes
can happen without you knowing it and the metadata you have can be out of date.
If an integration breaks, the first thing to do is to check whether the entity data model has changed by looking at
the latest metadata.
Always use immutable IDs such as external ID, person GUID, and external code to retrieve data. Mutable IDs such
as assignment ID are prone to change. Using mutable IDs can break your integrations when the IDs change.
Hyperlinks
Some links are classified by an icon and/or a mouseover text. These links provide additional information.
About the icons:
● Links with the icon : You are entering a Web site that is not hosted by SAP. By using such links, you agree (unless expressly stated otherwise in your agreements
with SAP) to this:
● The content of the linked-to site is not SAP documentation. You may not infer any product claims against SAP based on this information.
● SAP does not agree or disagree with the content on the linked-to site, nor does SAP warrant the availability and correctness. SAP shall not be liable for any
damages caused by the use of such content unless damages have been caused by SAP's gross negligence or willful misconduct.
● Links with the icon : You are leaving the documentation for that particular SAP product or service and are entering a SAP-hosted Web site. By using such links, you
agree that (unless expressly stated otherwise in your agreements with SAP) you may not infer any product claims against SAP based on this information.
Example Code
Any software coding and/or code snippets are examples. They are not for productive use. The example code is only intended to better explain and visualize the syntax and
phrasing rules. SAP does not warrant the correctness and completeness of the example code. SAP shall not be liable for errors or damages caused by the use of example
code unless damages have been caused by SAP's gross negligence or willful misconduct.
Gender-Related Language
We try not to use gender-specific word forms and formulations. As appropriate for context and readability, SAP may use masculine word forms to refer to all genders.
SAP and other SAP products and services mentioned herein as well as
their respective logos are trademarks or registered trademarks of SAP
SE (or an SAP affiliate company) in Germany and other countries. All
other product and service names mentioned are the trademarks of their
respective companies.