# SAP SuccessFactors — HRIS API: gotchas

> Canonical URL: https://developers.apideck.com/apis/hris/sap-successfactors/gotchas

## GET /hris/employees

Custom fields are extracted from both `personalInfoNav` and
`employmentNav.jobInfoNav` on each employee record. Custom fields stored on
other navs are not surfaced in `custom_fields` — use `pass_through` to access them.

`custom_fields[].value` is human-readable for picklist-backed properties and for a
verified set of Foundation-Object-backed properties (`position`, `costCenter`,
`payGrade`, `businessUnit`, `employeeClass`). For picklists, the tenant's picklist label
is returned (`"Monday to Sunday"` rather than the option ID `"663865"`), resolved by
preferring `en_GB`, then `en_US`, then any other English locale, then the sole authored
locale when a tenant-created picklist has only one; if no label is usable the option's
external code is returned. Foundation-Object-backed properties are resolved by matching
each object's own active-status and label convention — a best-effort convention match,
not a guarantee, so any other Foundation-Object-backed property still returns the raw
downstream ID. Expect a mix of readable labels and raw IDs across `custom_fields`.

`custom_fields[].name` carries the property's SAP field label (`"FLSA Work Week"`) and is
populated for every custom field the tenant's metadata describes.

Address fields `line1`–`line5` are passed through verbatim from SAP's positional
fields `address1`–`address5` with no per-country recomposition. SAP SuccessFactors
interprets these positions differently per country, and the meaning of each position
also varies per tenant — two SAP customers in the same country can configure
`address1`–`addressN` semantics differently via Manage Business Configuration.
Belgium and the Netherlands are confirmed examples of per-tenant variation.
The country code is exposed in `addresses[].country` as ISO-2 (e.g. `"BE"`, `"NL"`) —
consumers must use it together with knowledge of the specific tenant's configuration
to interpret the semantic meaning of each line.

Fields `address6` through `address20` are not surfaced in the unified response — the
JSON mapping declares `line1`–`line5` only. Consumers that need higher-numbered
positional fields must use `pass_through`.

The `street_number` field is not populated for SAP SuccessFactors employees.

This limitation is by SAP design: their OData v2 API exposes a single merged
`PerAddressDEFLT` entity for all countries and does not surface per-country
field semantics. See SAP KBA 2902948.

On the home address (`addresses[].type` `home`), `city`, `state` and `postal_code` can
contain SAP's numeric option IDs (for example `"358"`) when the tenant's home-address
layout for that country makes the field a dropdown. SAP does not report which countries
use dropdowns, so list them in the Employees configuration settings (Countries where home
address City, State or Postal Code is a dropdown, as 3-letter country codes such as
`BEL`). For a listed country, `city` and `state` return the option's label
(`"Antwerpen"`) and `postal_code` returns the postal code itself (`"2060"`). A value stays
the raw option ID when its country is not listed, when the tenant's address setup has no
dropdown for that field, or when the lookup cannot be completed. Other address types, such as `primary`,
are not resolved and may still contain option IDs.

Email type is resolved from the `externalCode` of the email-category picklist
option (ecEmailType), which is locale-independent, so the resolved type does not
depend on the tenant's configured language. The externalCode to unified type
mapping is: B maps to work, P to personal, PI to primary, S to secondary, BI to
billing, and O to other. `B`, `P` and `O` are SAP-seeded defaults present in every
tenant; `PI`, `S` and `BI` must be configured as picklist options by the tenant to
take effect. Any email whose ecEmailType option uses an externalCode outside this
set, or has no externalCode, resolves to other.

SAP SuccessFactors' Person entities are each keyed by `personIdExternal` plus a
type/category field — `PerEmail` by `emailType`, `PerPhone` by `phoneType`, and so on
(SAP SuccessFactors OData V2 API Reference Guide, §9.4.1.9.2). If a tenant deactivates
that type field, every row for the person resolves to the same key, so SAP collapses
them down to one before the request reaches Apideck, and there is no request-side
workaround — the tenant's SAP administrator must re-activate the type field to
retrieve every row. For `emails[]` this means a person with several email addresses
on file returns only one, and — since the type-decode data collapses too — that entry
resolves to `other` rather than its real type. SAP has confirmed this generalizes to
any entity sharing the same composite-key shape, which would include `PerPhone`
(keyed by `personIdExternal + phoneType`); `phone_numbers[]` derives `type` from
`isPrimary` rather than a type decode, so if the same collapse occurs there it would
surface as a shorter array rather than a mislabeled one.

`preferred_language` is derived from the `externalCode` of the employee's
`nativePreferredLang` picklist option, which is locale-independent. The value is the
BCP-47 language subtag, uppercased (e.g. `ru_RU` resolves to `RU`, `en` to `EN`). It
is `null` when the externalCode is absent, system-generated, or not a parseable
ISO-639-1 two-letter code.

## GET /hris/employees/{id}

Custom fields are extracted from both `personalInfoNav` and
`employmentNav.jobInfoNav` on each employee record. Custom fields stored on
other navs are not surfaced in `custom_fields` — use `pass_through` to access them.

`custom_fields[].value` is human-readable for picklist-backed properties and for a
verified set of Foundation-Object-backed properties (`position`, `costCenter`,
`payGrade`, `businessUnit`, `employeeClass`). For picklists, the tenant's picklist label
is returned (`"Monday to Sunday"` rather than the option ID `"663865"`), resolved by
preferring `en_GB`, then `en_US`, then any other English locale, then the sole authored
locale when a tenant-created picklist has only one; if no label is usable the option's
external code is returned. Foundation-Object-backed properties are resolved by matching
each object's own active-status and label convention — a best-effort convention match,
not a guarantee, so any other Foundation-Object-backed property still returns the raw
downstream ID. Expect a mix of readable labels and raw IDs across `custom_fields`.

`custom_fields[].name` carries the property's SAP field label (`"FLSA Work Week"`) and is
populated for every custom field the tenant's metadata describes.

Address fields `line1`–`line5` are passed through verbatim from SAP's positional
fields `address1`–`address5` with no per-country recomposition. SAP SuccessFactors
interprets these positions differently per country, and the meaning of each position
also varies per tenant — two SAP customers in the same country can configure
`address1`–`addressN` semantics differently via Manage Business Configuration.
Belgium and the Netherlands are confirmed examples of per-tenant variation.
The country code is exposed in `addresses[].country` as ISO-2 (e.g. `"BE"`, `"NL"`) —
consumers must use it together with knowledge of the specific tenant's configuration
to interpret the semantic meaning of each line.

Fields `address6` through `address20` are not surfaced in the unified response — the
JSON mapping declares `line1`–`line5` only. Consumers that need higher-numbered
positional fields must use `pass_through`.

The `street_number` field is not populated for SAP SuccessFactors employees.

This limitation is by SAP design: their OData v2 API exposes a single merged
`PerAddressDEFLT` entity for all countries and does not surface per-country
field semantics. See SAP KBA 2902948.

On the home address (`addresses[].type` `home`), `city`, `state` and `postal_code` can
contain SAP's numeric option IDs (for example `"358"`) when the tenant's home-address
layout for that country makes the field a dropdown. SAP does not report which countries
use dropdowns, so list them in the Employees configuration settings (Countries where home
address City, State or Postal Code is a dropdown, as 3-letter country codes such as
`BEL`). For a listed country, `city` and `state` return the option's label
(`"Antwerpen"`) and `postal_code` returns the postal code itself (`"2060"`). A value stays
the raw option ID when its country is not listed, when the tenant's address setup has no
dropdown for that field, or when the lookup cannot be completed. Other address types, such as `primary`,
are not resolved and may still contain option IDs.

Email type is resolved from the `externalCode` of the email-category picklist
option (ecEmailType), which is locale-independent, so the resolved type does not
depend on the tenant's configured language. The externalCode to unified type
mapping is: B maps to work, P to personal, PI to primary, S to secondary, BI to
billing, and O to other. `B`, `P` and `O` are SAP-seeded defaults present in every
tenant; `PI`, `S` and `BI` must be configured as picklist options by the tenant to
take effect. Any email whose ecEmailType option uses an externalCode outside this
set, or has no externalCode, resolves to other.

SAP SuccessFactors' Person entities are each keyed by `personIdExternal` plus a
type/category field — `PerEmail` by `emailType`, `PerPhone` by `phoneType`, and so on
(SAP SuccessFactors OData V2 API Reference Guide, §9.4.1.9.2). If a tenant deactivates
that type field, every row for the person resolves to the same key, so SAP collapses
them down to one before the request reaches Apideck, and there is no request-side
workaround — the tenant's SAP administrator must re-activate the type field to
retrieve every row. For `emails[]` this means a person with several email addresses
on file returns only one, and — since the type-decode data collapses too — that entry
resolves to `other` rather than its real type. SAP has confirmed this generalizes to
any entity sharing the same composite-key shape, which would include `PerPhone`
(keyed by `personIdExternal + phoneType`); `phone_numbers[]` derives `type` from
`isPrimary` rather than a type decode, so if the same collapse occurs there it would
surface as a shorter array rather than a mislabeled one.

`preferred_language` is derived from the `externalCode` of the employee's
`nativePreferredLang` picklist option, which is locale-independent. The value is the
BCP-47 language subtag, uppercased (e.g. `ru_RU` resolves to `RU`, `en` to `EN`). It
is `null` when the externalCode is absent, system-generated, or not a parseable
ISO-639-1 two-letter code.


[API reference](https://developers.apideck.com/apis/hris/reference.md) · [Coverage matrix](https://developers.apideck.com/apis/hris/coverage)
