Skip to content

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:

  1. Import the business identities with the importBusinessIdentities mutation.
  2. Link the business identities to Microsoft Entra ID users with the associateBusinessIdentities mutation.

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 ​

  1. Go to the Microsoft Entra admin center.
  2. Go to App registrations and click New registration.
  3. Enter a name, for example Identity Stack HR integration.
  4. Select Accounts in this organizational directory only.
  5. Click Register.
  6. 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 ​

  1. Go to API permissions on the application.
  2. Click Add a permission.
  3. Select APIs my organization uses and search for Identity Stack.
  4. Select Application permissions, then select Admin.
  5. Click Add permissions.
  6. 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.default

The 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 codeCause
AUTH_NOT_AUTHENTICATEDIdentity 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_AUTHORIZEDThe 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 ​

FieldTypeDescription
sourceString!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:

FieldTypeRequiredDescription
externalIdStringYesThe identifier of the record in the source system. Each external ID can occur only one time in the set.
affiliationTypeAffiliationTypeYesEMPLOYMENT, ENROLLMENT, ENGAGEMENT, or OTHER
firstNameStringYesCannot be empty
lastNameStringYesCannot be empty
birthDateLocalDateNoYYYY-MM-DD
nationalityStringNoA two-letter country code, for example DK
personalEmailStringNoA private email address, not the work address
personalPhoneStringNoA private phone number, not the work number
personalNumbers[PersonalNumberInput!]Notype (DK_CPR) and value. One number for each type.
identityDocumentReferences[IdentityDocumentReferenceInput!]Notype (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:

FieldDescription
createdThe number of new business identities
updatedThe number of business identities with changed values
deletedThe number of business identities that were not in the set
unchangedThe 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.

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 ​

FieldTypeDescription
sourceString!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:

FieldDescription
associatedThe number of business identities that got a new link
unchangedThe number of business identities that already had the link
unknownExternalIdsThe external IDs that match no business identity of the source
unknownUserPrincipalNamesThe UPNs that match no Microsoft Entra ID user in your organization
conflictingUserPrincipalNamesThe 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 0101901234 or 010190-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.