---
title: "Outbound provisioning"
description: "Push the users and groups assigned to an application to its SCIM 2.0 API, keep them in step, and deactivate them when they lose access."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.ezghcloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Outbound provisioning

Outbound provisioning keeps an application's own user list in step with your directory, over the
application's SCIM 2.0 API. EZGH SSO creates the people [assigned](/sso/assignments) to the
application there, updates them when they change, and deactivates them when they're unassigned or
suspended. It can push the application's assigned groups too.

Provisioning works for `oidc` and `saml` applications. `ezgh` applications are EZGH products and
aren't provisioned (`409 provisioning_not_supported`). You need `sso.provisioning.update` to
change the settings (in `SsoFullAccess`), and `sso.provisioning.get` to see them and their status.

## Turn on provisioning

You need the application's SCIM base URL and a bearer token from the application. The
[catalog](/sso/saml-applications#the-catalog) has the base URL and mappings for the applications
that support SCIM, with the vendor's steps for getting a token.

### Console

1. Open **EZGH SSO** > **Applications**, the application, and its **Provisioning** tab.
2. Enter the **SCIM base URL** and **Bearer token**, or select **Start from a catalog app** to
fill in the base URL and mappings.
3. Check the **Attribute mappings**, and choose whether to **Push groups** and **Delete users at
the application**.
4. Select **Save and turn on**.
### CLI

```sh
ezgh sso applications provisioning set Slack --catalog slack --bearer-token-stdin < slack-token.txt

ezgh sso applications provisioning set Wiki --base-url https://wiki.acme.com/scim/v2 --bearer-token-file token.txt \
  --attribute-mapping 'userName=${user.email}' --attribute-mapping 'displayName=${user.name}' --push-groups
```

The token is read from standard input or a file, never from an argument. `provisioning set`
changes only the settings you give.
### API

Call [UpdateProvisioning](/sso-api/UpdateProvisioning/) (`PUT`):

```sh
curl -X PUT https://sso.ezghcloud.com/v1/organizations/$ORG_ID/sso/applications/$APP_ID/provisioning \
  -H "Authorization: Bearer $EZGH_API_KEY" -H "Content-Type: application/json" \
  -d '{ "baseUrl": "https://wiki.acme.com/scim/v2", "bearerToken": "…", "pushGroups": true }'
```

`PUT` replaces the settings whole: a field you leave out takes its default, except the bearer
token, which is kept. Send `If-Match` with the `ETag` from
[GetProvisioning](/sso-api/GetProvisioning/); if the settings changed since you read them,
the request is `412 precondition_failed`.

## Settings

| Field | Description | Default |
| --- | --- | --- |
| `baseUrl` | The application's SCIM 2.0 base URL: https, a public host on port 443, no query. A trailing `/` is dropped. | Required |
| `bearerToken` | Write-only: stored encrypted, never shown or recorded. `bearerTokenSet` says whether one is stored. Required the first time, **and whenever `baseUrl` changes**, so the stored token is never sent to a new URL. | |
| `attributeMappings` | 1 to 50 mappings. See [Attribute mappings](#attribute-mappings). | The defaults |
| `enabled` | Push changes. Turning it off stops pushes and drops the queued ones; nobody is deactivated at the application. | `true` |
| `pushGroups` | Push the groups assigned to the application, with their members. See [Groups](#groups). | `false` |
| `hardDelete` | Delete users at the application when they're deleted from the directory. See [Deprovisioning](#deprovisioning). | `false` |

The answer also shows `configured` (`false` until the first `PUT`), `status`, and when the settings
were created and last changed.

- **Changing `baseUrl` starts over:** what was pushed to the old URL is forgotten, everyone is
  pushed to the new one, and nothing is deprovisioned at the old one.
- **Turning it on, or changing anything while it's on,** pushes everyone assigned and everyone
  pushed before again. Users whose pushed values haven't changed aren't sent anything.

## Attribute mappings

A mapping is `{ "target", "source" }`. The mappings must include `userName`, and each target
appears once.

`target` is a SCIM attribute path, up to 320 characters:

- a core attribute or sub-attribute: `userName`, `displayName`, `title`, `name.givenName`;
- one typed value of a multi-valued attribute: `emails[type eq "work"].value`;
- an extension's attribute:
  `urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department`.

`id`, `externalId`, `active`, `meta`, `schemas`, `groups`, `members` and `password` can't be
mapped. EZGH SSO sets `externalId` to the directory user's ID on users it creates, and `active`
from their status and assignment.

`source` is up to 512 characters of text with these variables:

| Variable | Value |
| --- | --- |
| `${user.email}` | The email address |
| `${user.name}` | The display name |
| `${user.givenName}`, `${user.familyName}` | The given and family names |
| `${user.department}` | The department: set on the user, or sent by [inbound SCIM](/sso/scim) |
| `${user.id}`, `${user.slug}` | The user's ID and slug (`usr_…`) |
| `${user.tags.<key>}` | The value of one of the user's tags |

A value that comes out empty isn't sent.

The default mappings:

```json
[
  { "target": "userName", "source": "${user.email}" },
  { "target": "emails[type eq \"work\"].value", "source": "${user.email}" },
  { "target": "name.givenName", "source": "${user.givenName}" },
  { "target": "name.familyName", "source": "${user.familyName}" },
  { "target": "displayName", "source": "${user.name}" }
]
```

`ezgh sso applications provisioning set --default-mappings` goes back to them. The catalog's
`scim.attributeMappings` use the same format.

## What's pushed

| A user who is | Is pushed as |
| --- | --- |
| Assigned (directly or through a group) and not suspended, including a user pending activation | `active: true` |
| Unassigned, suspended, or deleted without `hardDelete` | `active: false` |
| Deleted, with `hardDelete` | `DELETE` |

A push is queued, in the same transaction as the change, whenever something may change what an
application should have:

- assignments added, removed or replaced, directly or for a group;
- a user's profile changed, or the user suspended, reactivated or deleted, by you or by
  [inbound SCIM](/sso/scim);
- group membership changed: by you, by inbound SCIM, by an identity provider's
  [group mappings](/sso/identity-providers#group-mappings) at sign-in, or by deleting an identity
  provider;
- a group renamed or deleted.

Each push reads the directory as it is when it runs, so the order of changes doesn't matter.

**How a user is pushed:** a user EZGH SSO already knows at the application is updated with
`PATCH`. Otherwise EZGH SSO looks for them by `externalId` (a user it created), then by `userName`,
and only then creates them with `POST`. A user found by `userName` is **adopted**: EZGH SSO
updates them, but didn't create them, so reconcile doesn't repair them. Updates replace the mapped
attributes (a mapped multi-valued attribute is replaced whole) and `active`; attributes you don't
map are never touched. A change that doesn't alter anything pushed sends nothing.

## Groups

With `pushGroups`, the groups assigned to the application are pushed with `displayName` (the
group's name), `externalId` (the group's ID) and their members who are active at the application.
A group is looked up by `externalId`, then `displayName`, before it's created. Members are added
and removed with `PATCH`, and only users EZGH SSO pushed are ever removed.

A group that's no longer pushed (unassigned, deleted, or `pushGroups` turned off) is deleted at the
application if EZGH SSO created it, and left alone otherwise.

## Deprovisioning

- **Unassigning or suspending** a user always sends `active: false`, which most applications
  treat as deactivation. The user stays at the application.
- **Deleting** a directory user sends `active: false`, or with `hardDelete`, `DELETE`.
- **Deleting the application**, or the directory, stops provisioning without deprovisioning anyone
  at the application. To deactivate everyone first, unassign them and wait for the pushes.

## Retries and status

| The application answers | EZGH SSO |
| --- | --- |
| `429` | Pauses every push to the application until `Retry-After` (a minute without one, an hour at most). Not counted as a failed attempt. |
| A timeout, a connection error, `408` or `5xx` | Retries from 10 seconds, doubling up to an hour, and gives up after 15 attempts. |
| Any other `4xx` | Stops that user's or group's push at once. |

A failure is kept as the user's (or group's) `lastError`: the status, and the application's SCIM
`detail` and `scimType`, up to 300 characters. A user whose push stopped stays flagged until a
later change, a reconcile or a settings change pushes them successfully. Requests time out after
30 seconds, and answers over 1 MiB are refused.

[GetProvisioning](/sso-api/GetProvisioning/) (`ezgh sso applications provisioning get`)
shows `status`:

| `state` | Meaning |
| --- | --- |
| `notConfigured` | No settings yet. |
| `disabled` | Turned off. |
| `paused` | The application answered `429`; pushes resume at `pausedUntil`. |
| `syncing` | Pushes are queued. |
| `error` | Some user's or group's last push failed. |
| `idle` | Everything is pushed. |

`status` also counts the queued pushes, users (`active`, `inactive`, `errors`) and pushed groups,
and shows the last success, the last error, and the last and next reconcile. To see each user or
group:

```sh
ezgh sso applications provisioning users Wiki --status error
ezgh sso applications provisioning groups Wiki
```

[ListProvisionedUsers](/sso-api/ListProvisionedUsers/) (`status`: `active`, `inactive`,
`none` or `error`) and [ListProvisionedGroups](/sso-api/ListProvisionedGroups/) (`status`:
`error`) list each record newest first: the application's ID for it, whether EZGH SSO created it
there (`createdByEzgh`), whether a push is pending, and the last push and error.

## Reconcile

A reconcile compares the application's users with the directory and repairs drift. It runs every
week, counted from when provisioning was turned on, and within seconds when you ask:

### Console

On the application's **Provisioning** tab, select **Reconcile now**.
### CLI

```sh
ezgh sso applications provisioning reconcile Wiki
```
### API

Call [ReconcileProvisioning](/sso-api/ReconcileProvisioning/). It answers `202`, or `409
provisioning_not_enabled` when provisioning is off.

A reconcile lists the application's users (`GET /Users`, at most 10,000), then pushes again:

- each user EZGH SSO created there that's missing, has a mapped value or `active` that differs, or
  should be gone;
- each flagged user;
- each assigned user who isn't there yet;
- each pushed group.

Users EZGH SSO adopted, or never touched, aren't repaired. If listing fails with a `429`, the
reconcile pauses and retries; another failure retries in 15 minutes, except a `4xx`, after which
every user EZGH SSO created is pushed again instead (shown in `lastReconcileError`).

## Activity in Trails

Each push that changes something at the application is recorded in [Trails](/sso/trails) as a
service event by `sso.ezghcloud.com`, naming the application and the user or group:
`ProvisionUser` (`create`, `update` or `adopt`), `DeprovisionUser` (`deactivate` or `delete`) and
`ProvisionGroup` (`create`, `update` with the members added and removed, or `delete`). A push that
stops is recorded once, with `errorCode` `provisioning_failed`. Retries and pauses aren't recorded,
and neither the bearer token nor attribute values ever are.

Source: https://docs.ezghcloud.com/sso/provisioning/index.mdx
