> For the complete documentation index, see [llms.txt](https://docs.nullify.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nullify.ai/configuration/account-and-teams/saviynt-scim.md).

# LIAM / Saviynt provisioning

Use your existing identity lifecycle system to manage Nullify users through the tenant-specific SCIM Users API. This is an integration contract for your LIAM/Saviynt administrator, not an installed or certified Nullify Saviynt connector. The connection and its production lifecycle tests must be completed before relying on automated access changes.

Keep the official production **Nullify** application (`37d9fe25-ca83-49ac-a099-054de06b1aad`) for Microsoft sign-in and any independent Azure DevOps connection. No second Entra application or Azure DevOps permission is needed for this SCIM API client. See [Microsoft Entra setup](/configuration/account-and-teams/entra-sso-and-scim.md#scim-provisioning) to connect the directory and generate a credential.

## Connection and account mapping

Copy **SCIM base URL** from **Settings → Identity → SCIM provisioning** exactly. Configure HTTPS requests relative to that URL with `Authorization: Bearer <SCIM credential>` and JSON SCIM payloads. Store the credential in your approved secret store; it can manage all SCIM users and their roles in this workspace. Do not use an Azure application secret or reuse another workspace's credential.

[Saviynt's Integration Exchange](https://exchange.saviynt.com/products/postman) describes SCIM account/access visibility and provisioning through its connector frameworks. Your administrator must select and configure the supported connector in your licensed Saviynt version. These instructions describe the HTTP contract; they do not prescribe unverified Saviynt configuration field names or provide a preconfigured connector export.

| Identity-system field                                | Nullify SCIM field                                    | Contract                                                                                                |
| ---------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| Connected directory's immutable Entra user object ID | `externalId`                                          | Required on create; GUID. Not an email, group ID, Saviynt identity ID or account ID.                    |
| User principal name                                  | `userName`                                            | Required on create; use the agreed login name.                                                          |
| Returned Nullify resource ID                         | `id`                                                  | Persist as the target account identifier. Use it in `/Users/{id}`; do not substitute `externalId`.      |
| Approved Nullify role                                | `roles`                                               | Complete array containing one lowercase `admin`, `editor` or `viewer` value.                            |
| Approved lifecycle state                             | `active`                                              | Boolean; false removes access. True also requires a valid assigned role.                                |
| Display name                                         | `displayName` or `name.givenName` / `name.familyName` | Nullify stores one combined name. If both representations are sent, structured `name` takes precedence. |
| Primary email                                        | `emails`                                              | Send the agreed primary email with `primary: true`.                                                     |

Map only supported attributes. Groups, team memberships, enterprise profile extensions, password changes, bulk operations, sorting and ETags are not managed through this API. Teams remain a separate Nullify access setting.

## Import and reconcile

1. Authenticate with `GET /Users?count=0`. Then inspect `/ServiceProviderConfig`, `/ResourceTypes` and `/Schemas` for the Users resource and expected capabilities; static discovery alone does not prove the credential works.
2. Import `GET /Users?startIndex=1&count=200`. Read `Resources`, `totalResults`, `startIndex` and `itemsPerPage`; advance by the returned page size until all results are read. An omitted count defaults to 100; 200 is the maximum. Stop and alert on an unexpectedly empty page before the reported total is reached.
3. Match an account with `GET /Users?filter=externalId eq "<Entra object ID>"`. Use the canonical lowercase object ID. `userName eq "<UPN>"` is also supported; other filter expressions are rejected. URL-encode query parameters.
4. Persist the returned `id`. If no account exists, create it with the exact approved Entra object ID. A `409` create response is a conflict, not success: reconcile by the same immutable identity and verify the result before updating it. Never adopt a different person's account solely because its email or login name matches.

The collection contains SCIM-managed users, including deactivated users; deleted records are excluded. It is not an inventory of every workspace member. An existing account must first have a verified Microsoft identity link to be claimed by SCIM provisioning. A directory connection or matching email alone does not authorize account linking. Inventory and link intended members before [Entra-only activation](/configuration/account-and-teams/entra-sso-and-scim.md#require-microsoft-sign-in).

## Create and change access

For `POST /Users`, send the core User schema and your approved mappings:

```json
{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "externalId": "<member's Entra object GUID>",
  "userName": "<member's UPN>",
  "displayName": "<member's display name>",
  "active": true,
  "roles": [{"value": "viewer"}]
}
```

Creation returns `201` and the target resource `id`. Use `PATCH /Users/{id}` for partial changes. Always replace the full desired role array on a role change; do not send additive role assignments. Two roles or an unknown role return `400` and leave the previous access unchanged. Empty or omitted roles on create or PUT leave the user unactivated; they do not grant viewer access.

Use PUT only when intentionally replacing the complete resource. It defaults an omitted `active` to true and treats omitted roles as empty, so partial-update clients should use PATCH. An omitted name on PUT clears it; an unrelated PATCH preserves it. Nullify returns the combined stored name rather than preserving separate given/family components. To change a full name, send one `displayName` or the complete structured name, then verify the response.

Deactivation uses PATCH with the PatchOp schema and `Operations: [{"op":"replace","path":"active","value":false}]`. To revoke role assignment too, include a second operation replacing `roles` with `[]`. Removing a member from the source roster without sending an update does not revoke their Nullify access. A role-only update does not reactivate a deactivated user; restore access explicitly with one approved role and `active: true`.

`DELETE /Users/{id}` removes access and returns `204`; the record is retained and subsequent reads return `404`. A later approved create using the same externalId restores that retained account, so do not retry stale create tasks after an offboarding decision.

Keep two usable Microsoft workspace administrators. A change that removes the last qualified administrator can return `403`; alert, verify a replacement, and retry rather than reporting successful offboarding. Revoke a SCIM credential in **Settings → Identity** when retiring the connector; requests using it are rejected immediately.

## Production acceptance

Use an explicitly approved pilot roster. Verify create and reconciliation, pagination, each role, admin-to-viewer replacement, deactivation, deletion, approved restoration, credential revocation and cross-workspace rejection. Confirm the actual Microsoft sign-in and existing-session behavior after access changes, not just the HTTP response.

Record the directory/object mappings, target remote IDs, connector owner, import schedule, retry policy and observed revocation delay. A `400`, `403`, `409` or transport failure is not a completed access change. Alert and read back the authoritative account after recovery; do not mark a lifecycle task complete merely because it was queued. Keep automatic provisioning disabled until the customer's actual connector configuration and these results are verified.
