Appearance
Custom integration
A custom integration sends business identities from your own source system to Identity Stack. The source system is the authority for your employee records. Usually this is an HR system. Run the integration on a schedule, for example every night.
Each run has two steps:
- Import the business identities with the
importBusinessIdentitiesmutation. - Link the business identities to Microsoft Entra ID users with the
associateBusinessIdentitiesmutation.
The steps are separate. The Microsoft Entra ID user of a new employee is often not ready at import. You can do the second step again later, without a new import.
Authentication
The integration authenticates with an access token from your own Microsoft Entra ID tenant. The integration uses the OAuth 2.0 client credentials flow. No user signs in.
Identity Stack accepts the token when:
- The tenant of the token is your tenant, and your tenant is registered with Identity Stack.
- The token has the Admin role of the Identity Stack application.
Before you start, make sure that:
- The Identity Stack enterprise application is installed in your tenant. See Setup Identity Stack.
- You can register applications and grant admin consent in Microsoft Entra ID, for example as a Privileged Role Administrator.
1. Register an application
- Go to the Microsoft Entra admin center.
- Go to App registrations and click New registration.
- Enter a name, for example
Identity Stack HR integration. - Select Accounts in this organizational directory only.
- Click Register.
- Write down the Application (client) ID and the Directory (tenant) ID.
2. Add a credential
Go to Certificates & secrets on the application.
- Certificate (recommended). Upload the public key of a certificate. Keep the private key on the server that runs the integration.
- Client secret. Click New client secret. Copy the value immediately. Microsoft Entra ID shows it only one time. Make a new secret before the old secret expires.
Keep the credential in a secure store, for example Azure Key Vault. Do not put it in source code.
3. Give the application the Admin role
- Go to API permissions on the application.
- Click Add a permission.
- Select APIs my organization uses and search for Identity Stack.
- Select Application permissions, then select Admin.
- Click Add permissions.
- Click Grant admin consent for your tenant.
The Admin role gives the integration the same access as an administrator in Identity Stack Admin. Give the credential only to the integration.
4. Get an access token
Request a token from the Microsoft identity platform with the client credentials flow. Replace {tenant-id}, {client-id}, and {client-secret} with your values. URL-encode each value in the request body. A client secret can contain characters such as + or &.
http
POST /{tenant-id}/oauth2/v2.0/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id={client-id}
&client_secret={client-secret}
&scope=https%3A%2F%2Fapi.identitystack.dk%2F.defaultThe response contains access_token and expires_in. Use the token until it expires, then get a new token. A Microsoft Authentication Library (MSAL) does this for you.
5. Call the API
Send each request with POST to https://api.identitystack.dk/graphql. Put the token in the Authorization header: Bearer {access-token}.
Errors
The response has the HTTP status 200 also when an operation fails. Always examine the errors list in the response. Each error has a message, and most errors have a code in extensions.code.
When Identity Stack does not accept the token, the API does not return HTTP 401. The request continues without authentication, and the response has one of these errors:
| Error code | Cause |
|---|---|
AUTH_NOT_AUTHENTICATED | Identity Stack did not accept the token. The token is missing or expired, the scope of the token request is not correct, or the tenant is not registered with Identity Stack. |
AUTH_NOT_AUTHORIZED | The token has no Admin role. Make sure that admin consent is granted. Get a new token after the consent. |
Source name
Both mutations name the source, for example "Company HR". Use the same name in each call. Identity Stack compares business identities only with business identities of the same source. You cannot use the name Manual. Identity Stack keeps it for business identities that an administrator creates.
Import business identities
The importBusinessIdentities mutation sends all the business identities of one source. Send the complete set each time, not only the changes.
Input
| Field | Type | Description |
|---|---|---|
source | String! | The name of the source. See Source name. |
identities | [ImportBusinessIdentityInput!]! | The complete set of business identities. The set cannot be empty. |
Each item in identities has these fields:
| Field | Type | Required | Description |
|---|---|---|---|
externalId | String | Yes | The identifier of the record in the source system. Each external ID can occur only one time in the set. |
affiliationType | AffiliationType | Yes | EMPLOYMENT, ENROLLMENT, ENGAGEMENT, or OTHER |
firstName | String | Yes | Cannot be empty |
lastName | String | Yes | Cannot be empty |
birthDate | LocalDate | No | YYYY-MM-DD |
nationality | String | No | A two-letter country code, for example DK |
personalEmail | String | No | A private email address, not the work address |
personalPhone | String | No | A private phone number, not the work number |
personalNumbers | [PersonalNumberInput!] | No | type (DK_CPR) and value. One number for each type. |
identityDocumentReferences | [IdentityDocumentReferenceInput!] | No | type (PASSPORT or IDENTITY_CARD), issuingCountry, and documentNumber |
The lists personalNumbers and identityDocumentReferences replace the stored lists. For example, Identity Stack removes a document that is not in the list.
Result
importBusinessIdentitiesResult has these fields:
| Field | Description |
|---|---|
created | The number of new business identities |
updated | The number of business identities with changed values |
deleted | The number of business identities that were not in the set |
unchanged | The number of business identities with no changes |
Example
graphql
mutation {
importBusinessIdentities(
input: {
source: "Company HR"
identities: [
{
externalId: "E-10042"
affiliationType: EMPLOYMENT
firstName: "Anne"
lastName: "Jensen"
birthDate: "1990-01-01"
nationality: "DK"
personalNumbers: [{type: DK_CPR, value: "010190-1234"}]
identityDocumentReferences: [
{type: PASSPORT, issuingCountry: "DK", documentNumber: "123456789"}
]
}
]
}
) {
importBusinessIdentitiesResult {
created
updated
deleted
unchanged
}
}
}Behavior
Identity Stack compares the set with the stored business identities of the source:
- A new external ID gives a new business identity.
- A changed value updates the business identity.
- Identity Stack deletes a business identity that is not in the set. It also deletes the link to the Microsoft Entra ID user.
- An import does not change the links of the other business identities.
- An import does not change business identities from other sources.
A deleted business identity cannot come back. If the person comes back later, the next import makes a new business identity. Link it to the Microsoft Entra ID user again.
The import is complete or it does not occur. If one business identity is not valid, Identity Stack rejects the full import and changes nothing. Identity Stack rejects the import when:
- The set is empty.
- Two business identities have the same external ID.
- A first name or last name is empty.
- A CPR number is not valid.
- A country code is not a valid two-letter code.
- A document reference has no document number.
Log the result of each import. A large number of deleted business identities can show an error in the source data.
Link business identities to Microsoft Entra ID users
The associateBusinessIdentities mutation links business identities to Microsoft Entra ID users. For each business identity, send the external ID and the User Principal Name (UPN) of the Microsoft Entra ID user.
Input
| Field | Type | Description |
|---|---|---|
source | String! | The name of the source. Use the same name as in the import. |
associations | [BusinessIdentityAssociationInput!]! | The pairs to link. Each item has externalId and userPrincipalName. The list cannot be empty. Each external ID can occur only one time. |
Result
associateBusinessIdentitiesResult has these fields:
| Field | Description |
|---|---|
associated | The number of business identities that got a new link |
unchanged | The number of business identities that already had the link |
unknownExternalIds | The external IDs that match no business identity of the source |
unknownUserPrincipalNames | The UPNs that match no Microsoft Entra ID user in your organization |
conflictingUserPrincipalNames | The UPNs of Microsoft Entra ID users that already have a link to a different business identity |
Example
graphql
mutation {
associateBusinessIdentities(
input: {
source: "Company HR"
associations: [
{externalId: "E-10042", userPrincipalName: "anne.jensen@contoso.dk"}
]
}
) {
associateBusinessIdentitiesResult {
associated
unchanged
unknownExternalIds
unknownUserPrincipalNames
conflictingUserPrincipalNames
}
}
}Behavior
- Identity Stack links the business identity to the Microsoft Entra ID user. If the business identity has a link to a different Microsoft Entra ID user, the new link replaces it.
- A business identity that is not in the call keeps its link.
- The mutation never removes a link.
- If the Microsoft Entra ID user already has a link to a different business identity, Identity Stack does not change the link. The result shows the UPN as a conflict.
Identity Stack processes each pair separately. A pair that fails does not stop the other pairs. An unknown UPN usually means that the Microsoft Entra ID user is not yet synchronized to Identity Stack. Send the pair again at the next run.
Value formats
- CPR number. Use 10 digits, with or without a dash, for example
0101901234or010190-1234. The number must be a valid CPR number. - Country codes. Use the two-letter code from ISO 3166-1 alpha-2, for example
DK. This applies to the nationality and to the issuing country of a document. - Birth date. Use the format
YYYY-MM-DD.