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

# Wire SCIM provisioning: Okta, Microsoft Entra ID, and OneLogin

> Step-by-step setup for syncing Orbit users and groups from your identity provider over SCIM 2.0 — IdP configuration, attribute mapping, group-to-role mapping, deprovisioning semantics, and the sync errors you're most likely to see.

# Wire SCIM provisioning

Connect your identity provider to Orbit so team seats follow your directory:
assign someone to the Orbit application in your IdP and they get an Orbit
account with a role you choose; unassign them and access is revoked — no one
on the team manually maintains the member list. This guide walks the full
setup for Okta, Microsoft Entra ID (formerly Azure AD), and OneLogin, plus the
attribute and group mapping you'll configure once. For the endpoint spec
itself, see the [SCIM 2.0 provisioning reference](/compliance/scim-provisioning).

## What SCIM does in Orbit

SCIM manages the **lifecycle** of dashboard users inside one organization:

* **Provision** — a person added to the Orbit app in your IdP appears in your
  Orbit organization, ready to sign in.
* **Update** — name and email changes in the directory flow through on the
  next sync.
* **Deactivate** — unassigning a person revokes their access and every active
  session.
* **Role** — synced users land in a role you control: a default role plus an
  optional group-to-role map.

SCIM changes membership, not login. It pairs with SAML single sign-on — SAML
controls how users sign in, SCIM controls which users exist — and most
enterprise IdPs expect both. Both are per-organization, tenant-owned
controls: nothing enables SCIM except an owner in your workspace, an
organization without an IdP can manage seats manually forever, and Orbit does
not require SCIM for any compliance certification — it's your provisioning
option, not a mandate.

## Prerequisites

Before you start:

1. An **owner** role in the Orbit organization (token generation and SCIM
   settings are owner-only).
2. Your **organization slug** — find it under **Settings → Organization** in
   the dashboard. Every SCIM call is keyed by it.
3. An IdP with a SCIM 2.0 client:
   * **Okta** — an application with Provisioning enabled.
   * **Microsoft Entra ID** — an enterprise application with Provisioning set
     to Automatic.
   * **OneLogin** — an app connector with provisioning turned on.
   * Any other RFC 7643/7644-compliant client works the same way.

Then, in Orbit:

1. Open **Settings → SCIM**.
2. Click **Generate Bearer Token**. The token is shown **once** — only its
   hash is stored — so copy it into a password manager or directly into the
   IdP. Rotating later invalidates the previous token immediately.
3. Copy the fully qualified **Base URL** shown on the same page:
   `https://api.orbit.devotel.io/scim/v2/{orgSlug}`. These endpoints live at
   the API root, **not** under `/api/v1`.
4. Enable SCIM in the settings panel, then use **Test connection** on the
   page before pointing the IdP at it — the test verifies the token exists,
   the Base URL resolves, and the discovery documents build. An organization
   cannot be flipped into the enabled state without a token.

## Set up the IdP

### Okta

1. In the Okta admin console, open your Orbit application (or add one with
   SCIM 2.0 provisioning).
2. Go to **Provisioning → Configure Connection** and set:
   * **SCIM connector base URL**:
     `https://api.orbit.devotel.io/scim/v2/{orgSlug}`
   * **Authorization**: HTTP Header → Bearer token, with the token from
     Orbit.
3. Click **Test Connector Configuration**. When it passes, enable **Create
   Users**, **Update User Attributes**, and **Deactivate Users** under
   **To App**.
4. Assign people or groups, then check the health response (below) or the
   **Settings → SCIM** activity panel to confirm the first sync landed.

### Microsoft Entra ID

1. In the Entra admin center, open your enterprise application →
   **Provisioning**, and set **Provisioning Mode** to **Automatic**.
2. Under **Admin Credentials**, set **Tenant URL** to
   `https://api.orbit.devotel.io/scim/v2/{orgSlug}` and **Secret Token** to
   your Bearer token.
3. Click **Test Connection**, save, then scope the users and groups you want
   to sync.
4. **Start provisioning** and confirm the cycle on the Orbit side.

### OneLogin and generic clients

In OneLogin, paste the Base URL as the SCIM connecter endpoint and the token
as the Bearer credential on the app's **Configuration** tab, then enable
provisioning. Any SCIM 2.0 client follows the same three inputs: Base URL,
Bearer token, "users and groups" as the synced resources. The health check
and error behavior in this guide apply to all of them.

## Attribute mapping

Orbit accepts the standard SCIM user schema. Anything your IdP sends outside
the mapped fields — locale, timezone, addresses, phone numbers, enterprise
extensions — is accepted and ignored, so stock Okta and Entra attribute
templates provision without trimming.

| Orbit user field | SCIM attribute                                         | Notes                                                                                    |
| ---------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
| Email / username | `userName`, or the `primary` entry of `emails`         | Required — at least one must carry a valid email. `userName` wins when both are present. |
| Name             | `displayName`, or `name.givenName` + `name.familyName` | The dashboard splits the combined value into given/family when no `displayName` arrives. |
| Active           | `active`                                               | `false` marks the account provisioned-inactive (see lifecycle below).                    |
| IdP-side id      | `externalId`                                           | Stored as the directory's stable identifier map. Optional.                               |

Leave the IdP's default attribute mappings in place — they already send these
fields.

## Group → role mapping

Roles in Orbit are `owner`, `admin`, `developer`, `billing`, and `viewer`.
Synced users get a role from two knobs you set under **Settings → SCIM** (or
with `PATCH /api/v1/settings/scim`):

* `defaultRole` — the role for anyone provisioned without a group mapping.
  Start with `viewer`; it's the least-privilege floor and every flow still
  works from there.
* `groupMapping` — a map of IdP group name → Orbit role, for example
  `{ "Orbit Admins": "admin", "Orbit Support": "viewer" }`. On each sync,
  group membership re-applies the mapped role.

Groups in Orbit are **synthesized from this map**: the IdP sees one group per
role referenced in `groupMapping` (id `role:{role}`), and PATCHing members
into an IdP group re-maps those users to the mapped role. There is no
free-standing team entity on the SCIM side — the group-to-role map **is** the
teams model for provisioning. Map IdP groups to the Orbit roles you actually
use, and treat every mapping as a privilege assignment: a mapping like
`"Orbit Admins" → "owner"` makes every member of that IdP group an owner on
the next sync.

A role ceiling bounds what provisioning can assign (default `admin`). A push
that targets a role above the ceiling is refused with
`SCIM_ROLE_CEILING_EXCEEDED` — nothing is assigned. Keep `owner` assignment
to the dashboard, and raise the ceiling in settings only if a group genuinely
needs the higher role.

## Provisioning lifecycle

**Create** — the IdP POSTs a user; Orbit creates (or resurrects a previously
deactivated account matching the email) at the resolved role. If your IdP
retries a push after a partial failure, the create is keyed on email, so a
repeat lands on the same account rather than duplicating it.

**Update** — full-replace (PUT) and partial (PATCH) both work; Orbit applies
only the mapped fields above. Okta tends to PUT the whole resource, Entra
tends to PATCH — both are equivalent here.

**Deactivate** — the IdP sends `active: false`. Orbit marks the account
inactive and the user stops signing in, but the row stays, so reactivating in
the IdP restores the same account with its history.

**Deprovision** — the IdP sends DELETE. This is the real removal: a cascade
revokes active sessions and API access, removes the member from the roster,
and transfers their owned assets to an owner. Re-running a DELETE after the
user is gone answers 404 — safe to retry from the IdP's side.

**Errors** — failures surface in the SCIM error envelope
(`urn:ietf:params:scim:api:messages:2.0:Error`) with the HTTP status and, when
the spec defines one, a `scimType`. The failures you'll actually see:

| Status / scimType        | Meaning                                                                             | Fix                                                                                        |
| ------------------------ | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `400 invalidValue`       | Missing or malformed required field (usually no email/`userName`)                   | Map the IdP's username/email attribute; re-provision the user.                             |
| `401 invalidCredentials` | Bearer token missing or wrong                                                       | Regenerate under **Settings → SCIM**, paste the new token into the IdP.                    |
| `404`                    | Unknown organization slug, or the user id is gone                                   | Verify the slug in **Settings → Organization**; refresh the IdP's user assignment.         |
| `409 uniqueness`         | The email belongs to a different organization, or exists on the platform unattached | Invite that person first, or free the email in the other organization, then re-push.       |
| `429`                    | Rate limit (30 requests/minute per token)                                           | IdP syncs batch well under this; a manual script should page with `count` (max 500).       |
| `5xx`                    | Platform-side failure                                                               | The IdP's normal retry re-attempts; creates are keyed on email so retries don't duplicate. |

## Rate limits and pagination

SCIM traffic is capped at **30 requests per minute per Bearer token** — the
token is the only credential on these endpoints, so the cap is keyed to it
(not your IP; an IdP behind shared egress can't starve another tenant). Okta
and Entra sync cycles fall far below this. If you script provisioning
yourself, page list calls with `startIndex`/`count` (`count` max **500**,
responses always include `totalResults`) instead of polling single records.

## Troubleshooting IdP sync errors

When a push fails, work in this order:

1. **Health first.** `GET /api/v1/settings/scim/health` reports an
   `authenticationStatus` (`not_configured`, `no_requests_yet`,
   `authenticating`, `auth_failing`), the timestamps of the last request and
   last failure, per-user provisioning counts, and a best-effort
   `detectedIdp` from the caller's User-Agent. The **Settings → SCIM → Test
   connection** result lands here too.
2. **Read the code, not the status.** Several failures share HTTP status
   codes; the `scimType` and the provisioning error code tell you which gate
   tripped it — [Troubleshooting: authentication gates](/troubleshooting/auth-and-api-keys)
   maps each SCIM code (including `SCIM_ROLE_CEILING_EXCEEDED`) to its fix.
3. **Match the IdP's template to the mapping table above.** Most first-sync
   failures are a missing `userName`/email attribute, not a connectivity
   problem.
4. **Rotate the token when in doubt.** If you can't tell which token the IdP
   holds, rotate it in Orbit and paste the fresh value — the old one dies
   immediately, so no half-configured window.

## See also

* [SCIM 2.0 provisioning reference](/compliance/scim-provisioning) — endpoint
  table, token endpoint, and the health-check response fields.
* [Authenticate Orbit API requests](/authentication) — API keys vs. session,
  and where SAML SSO signs users in.
* [Troubleshooting: authentication gates](/troubleshooting/auth-and-api-keys) —
  every SCIM rejection code and its fix.
* [Settings API](/api-reference/settings) — read and patch SCIM settings,
  and manage team members directly from the API.
