> ## Documentation Index
> Fetch the complete documentation index at: https://launchdarkly-preview.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Using the SCIM API

> This topic explains how to use the SCIM API to work with users, roles groups in your SSO-enabled LaunchDarkly account.

> For the complete documentation index, see [llms.txt](/llms.txt). For the full documentation snapshot, see [llms-full.txt](/llms-full.txt).

## Overview

This topic explains how to use the System for Cross-domain Identity Management (SCIM) API to work with users, roles, and teams in your SSO-enabled LaunchDarkly account.

The SCIM API enables user provisioning of account members from identity providers (IdPs). SCIM enables IdPs such as Okta, OneLogin, or Entra ID to tell LaunchDarkly which users should or should not have an account, and optionally determine what level of access users have in LaunchDarkly.

SCIM uses web (REST) APIs to exchange data formatted in JSON, with standard schema that works with two resource types: `User` and `Group`. The LaunchDarkly SCIM API maps the `Group` resource to Launchdarkly teams. We provide an extension schema to work with LaunchDarkly roles. To learn more, read [Teams](/home/account/teams) and [Roles](/home/account/roles).

## Authentication

The SCIM API is only supported for the OAuth2 authorization type. Not all supported third-party providers support user provisioning through SCIM. To learn more about how LaunchDarkly treats users imported from the IdP, read [Default initial role](/home/account/sso#default-initial-role).

All SCIM endpoints require OAuth2 authentication with the `scim` scope. Regular personal access tokens will not work. You must use a SCIM-enabled OAuth client.

The following information is generally needed to configure SCIM in your IdP. The actual names for these fields may vary by IdP:

<Feature flag="docs-enable-non-commercial-content" match={"commercial"} fallbackValue={"commercial"}>\* SCIM Base URI: `https://app.launchdarkly.com/trust/scim/v2` \* Authorization method: `oauth2` \* Authorization URI: `https://app.launchdarkly.com/trust/oauth/authorize` \* Access token URI: `https://app.launchdarkly.com/trust/oauth/token` \* Unique Identifier field for account members: `userName`</Feature>

<Feature flag="docs-enable-non-commercial-content" match={"federal"} fallbackValue={"commercial"}>\* SCIM Base URI: `https://app.launchdarkly.us/trust/scim/v2` \* Authorization method: `oauth2` \* Authorization URI: `https://app.launchdarkly.us/trust/oauth/authorize` \* Access token URI: `https://app.launchdarkly.us/trust/oauth/token` \* Unique Identifier field for account members: `userName`</Feature>

<Feature flag="docs-enable-non-commercial-content" match={"eu"} fallbackValue={"commercial"}>\* SCIM Base URI: `https://app.eu.launchdarkly.com/trust/scim/v2` \* Authorization method: `oauth2` \* Authorization URI: `https://app.eu.launchdarkly.com/trust/oauth/authorize` \* Access token URI: `https://app.eu.launchdarkly.com/trust/oauth/token` \* Unique Identifier field for account members: `userName`</Feature>

If you are using the LaunchDarkly app from the [Okta](/home/account/okta) or [OneLogin](/home/account/onelogin) catalog, authentication is handled automatically during the setup wizard.

For Entra ID or any other provider you configure manually, you will also need a **Client ID** and **Client Secret**. [Start a Support ticket](https://support.launchdarkly.com/hc/en-us/requests/new) and we will generate these for you.

<Card icon="link">
  You can also use the REST API: [OAuth2 Clients](/api-reference/oauth2-clients/get-clients)
</Card>

## API endpoints

This section describes all endpoints provided with the SCIM API.

All SCIM endpoints are prefixed with the base URL:

<Feature flag="docs-enable-non-commercial-content" match={"commercial"} fallbackValue={"commercial"}>`https://app.launchdarkly.com/trust/scim/v2`</Feature>

<Feature flag="docs-enable-non-commercial-content" match={"federal"} fallbackValue={"commercial"}>`https://app.launchdarkly.us/trust/scim/v2`</Feature>

<Feature flag="docs-enable-non-commercial-content" match={"eu"} fallbackValue={"commercial"}>`https://app.eu.launchdarkly.com/trust/scim/v2`</Feature>

### User endpoints

\| Method

\| Endpoint

| Description |
| - |

\| ----------------

\| --------------------------------------------------------------------------------------------------------------- |
\| `POST`

\| `/Users`

\| Create a new user (member)                                                                                      |
\| `GET`

\| `/Users/{id}`

\| Get a user by ID                                                                                                |
\| `GET`

\| `/Users`

\| Query/list users (supports filtering)                                                                           |
\| `POST`

\| `/Users/.search`

\| Search users (POST query) - accepts filter, `startIndex`, and count in request body instead of query parameters |
\| `PUT`

\| `/Users/{id}`

\| Full update of a user (requires a complete user object)                                                         |
\| `PATCH`

\| `/Users/{id}`

\| Partial update of a user                                                                                        |
\| `DELETE`

\| `/Users/{id}`

\| Delete (deactivate) a user                                                                                      |

### Group endpoints

Groups in LaunchDarkly map to teams.

\| Method

\| Endpoint

| Description |
| - |

\| --------------

\| -------------------------------------------------- |
\| `POST`

\| `/Groups`

\| Create a new team                                  |
\| `GET`

\| `/Groups/{id}`

\| Get a team by ID                                   |
\| `GET`

\| `/Groups`

\| List all teams (supports filtering and pagination) |
\| `PUT`

\| `/Groups/{id}`

\| Full update of a team                              |
\| `PATCH`

\| `/Groups/{id}`

\| Add or remove members from a team                  |
\| `DELETE`

\| `/Groups/{id}`

\| Delete a team                                      |

### Schema endpoints

\| Method

\| Endpoint

| Description |
| - |

\| ----------

\| -------------------------------------------------------- |
\| `GET`

\| `/Schemas`

\| Get SCIM schema definitions (core and extension schemas) |

## User schema and attributes

LaunchDarkly uses the standard SCIM 2.0 user schema (`urn:ietf:params:scim:schemas:core:2.0:User`) and supports a custom extension schema (`urn:ietf:params:scim:schemas:extension:launchdarkly:2.0:User`) for LaunchDarkly-specific permissions.

### Required fields

\| Field

\| Type

| Description |
| :- |

\| :----

\| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
\| `emails`

\| Array

\| At least one email is required. The `value` must be a valid email address and must be unique within the account. Each email object can include:<br />

* `value` (string, required): Email address<br />

* `primary` (boolean): Whether this is the primary email<br />

* `type` (string): Email type (for example, "work") |

<Note title="Email (not userName) is required">
  While the SCIM 2.0 standard typically requires `userName`, LaunchDarkly's implementation requires `emails` instead. The `userName` field is optional and defaults to the email address if not provided. Most IdPs should map their unique identifier to the `userName` field for proper user matching.
</Note>

### Optional and behavioral fields

\| Field

\| Type

| Description |
| :- |

\| :------

\| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
\| `userName`

\| String

\| **Recommended.** While SCIM standards typically require this, LaunchDarkly relies on `email` as the primary identifier. If `userName` is omitted, the API defaults it to the email address. The value is in lowercase and trimmed. |
\| `active`

\| Boolean

\| Default: `true`. Set to `false` to deactivate the user (revoke access).                                                                                                                                                            |
\| `externalId`

\| String

\| Identifier from the IdP. Used to map users between systems.                                                                                                                                                                        |
\| `name`

\| Object

\| A complex object containing:<br />

* `givenName` (string, max 256 chars): First name<br />

* `familyName` (string, max 256 chars): Last name                                                                                         |

### Launch

Darkly extension attributes (roles)

LaunchDarkly allows you to define roles using either the standard extension namespace or root-level attributes for convenience.

\| Field

\| Type

| Description |
| :- |

\| :-----

\| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
\| `role`

\| String

\| The built-in role: `"reader"`, `"writer"`, `"admin"`, or `"noAccess"`. Default: `"reader"` (or the SAML default role if configured). Cannot be `"owner"` on create/update. |
\| `customRole`

\| String

\| A comma-separated list of custom role keys (for example, `"qa-team,release-mgr"`).                                                                                         |
\| `customRolesArray`

\| Array

\| An array of custom role keys (for example, `["qa-team", "release-mgr"]`).                                                                                                  |

Root-level fields, such as `role`, take precedence over fields that are nested in the extension namespace. If both `customRolesArray` and `customRole` are provided, `customRolesArray` takes precedence.

You can also use the extension namespace format, which is useful for IdPs like Azure Entra ID:

```json theme={null}
{
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User",
    "urn:ietf:params:scim:schemas:extension:launchdarkly:2.0:User"
  ],
  "emails": [{"value": "user@example.com"}],
  "urn:ietf:params:scim:schemas:extension:launchdarkly:2.0:User": {
    "role": "admin",
    "customRole": "custom-role-1"
  }
}
```

You cannot create new custom attributes using the SCIM API, but you can set the `role` and `customRole` attributes for SCIM users. For SAML-based SSO, LaunchDarkly supports mapping only the `role`, `customRole`, and `teamKey` attributes.

To learn more about these attributes, read [Roles](/home/account/roles).

For an in-depth guide on how to use custom roles with IdPs, read [Creating roles](/guides/teams-roles/custom-roles).

### Read-only fields in responses

The following fields are automatically generated by LaunchDarkly and cannot be set or modified:

\| Field

\| Type

| Description |
| :- |

\| :-----

\| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
\| `id`

\| String

\| The LaunchDarkly member ID (used for updates and deletes)                                                                                                                                                                                                                                               |
\| `schemas`

\| Array

\| An Array of SCIM schema identifiers                                                                                                                                                                                                                                                                     |
\| `meta`

\| Object

\| A Metadata object containing:<br />

* `resourceType`: Resource type (for example, "User")<br />

* `created`: Creation timestamp (Unix milliseconds)<br />

* `lastModified`: Last modification timestamp (Unix milliseconds)<br />

* `location`: URL to fetch this resource<br />

* `version`: Version number |

## Example user requests

### Create user (POST)

**Endpoint:** `POST /scim/v2/Users`

**Minimal request:**

```json theme={null}
{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "emails": [
    {
      "value": "alice@example.com",
      "primary": true
    }
  ],
  "name": {
    "givenName": "Alice",
    "familyName": "Smith"
  }
}
```

**Request with custom roles (root level):**

```json theme={null}
{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "emails": [{"value": "bob@example.com"}],
  "role": "reader",
  "customRolesArray": ["approver", "beta-tester"]
}
```

**Response:** `201 Created` with the created user object including the LaunchDarkly `id` field.

### Update user (PUT)

**Endpoint:** `PUT /scim/v2/Users/{id}`

PUT requires the **complete** user object. All fields should be included.

```json theme={null}
{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "id": "507f1f77bcf86cd799439011",
  "externalId": "external-123",
  "userName": "jane",
  "name": {
    "givenName": "Jane",
    "familyName": "Smith"
  },
  "emails": [
    {
      "value": "jane.smith@example.com",
      "primary": true,
      "type": "work"
    }
  ],
  "active": true,
  "role": "admin",
  "customRole": "custom-role-1"
}
```

**Response:** `200 OK`

### Update user (PATCH)

**Endpoint:** `PATCH /scim/v2/Users/{id}`

LaunchDarkly supports both SCIM PATCH format and JSON Patch (RFC

6902. for partial updates.

**SCIM patch format:**

```json theme={null}
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [
    {
      "op": "replace",
      "path": "/active",
      "value": false
    }
  ]
}
```

**Update custom roles:**

```json theme={null}
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [
    {
      "op": "replace",
      "path": "/customRolesArray",
      "value": ["new-role-key"]
    }
  ]
}
```

**JSON patch format (alternative):**

```json theme={null}
[
  {
    "op": "replace",
    "path": "/role",
    "value": "writer"
  },
  {
    "op": "replace",
    "path": "/emails/0/value",
    "value": "newemail@example.com"
  }
]
```

**Supported operations:** `add`, `remove`, `replace`

### Get user (GET)

**Endpoint:** `GET /scim/v2/Users/{id}`

**Response:** `200 OK` with complete user object.

**Complete response example:**

```json theme={null}
{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "id": "507f1f77bcf86cd799439011",
  "externalId": "external-123",
  "userName": "user@example.com",
  "name": {
    "givenName": "John",
    "familyName": "Doe"
  },
  "emails": [
    {
      "value": "user@example.com",
      "primary": true,
      "type": "work"
    }
  ],
  "active": true,
  "role": "reader",
  "customRole": "custom-role-1,custom-role-2",
  "customRolesArray": ["custom-role-1", "custom-role-2"],
  "urn:ietf:params:scim:schemas:extension:launchdarkly:2.0:User": {
    "role": "reader",
    "customRole": "custom-role-1,custom-role-2"
  },
  "meta": {
    "resourceType": "User",
    "created": 1234567890000,
    "lastModified": 1234567890000,
    "location": "/scim/v2/Users/507f1f77bcf86cd799439011",
    "version": "1"
  }
}
```

### Query users (GET)

**Endpoint:** `GET /scim/v2/Users`

**Query parameters:**

* `filter`: SCIM filter expression (for example, `userName eq "user@example.com"`)
* `startIndex`: 1-based starting index for pagination
* `count`: Number of results per page

**Examples:**

```
GET /scim/v2/Users?filter=userName eq "user@example.com"
GET /scim/v2/Users?startIndex=1&count=100
```

**Response:**

```json theme={null}
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
  "totalResults": 1,
  "itemsPerPage": 100,
  "startIndex": 1,
  "Resources": [
    {
      "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
      "id": "507f1f77bcf86cd799439011",
      "userName": "user@example.com",
      "emails": [{"value": "user@example.com", "primary": true}],
      "active": true,
      "role": "reader"
    }
  ]
}
```

### Search users (POST)

**Endpoint:** `POST /scim/v2/Users/.search`

This is an alternative to `GET /scim/v2/Users` that accepts filter parameters in the request body.

**Request:**

```json theme={null}
{
  "filter": "userName eq \"user@example.com\"",
  "startIndex": 1,
  "count": 10
}
```

**Response:** Same format as `GET /scim/v2/Users` (ListResponse)

### Delete user (DELETE)

**Endpoint:** `DELETE /scim/v2/Users/{id}`

**Response:** `204 No Content` (or `200 OK` with empty body)

**Note**: Deleting a user deactivates them and removes them from the account and all teams. Cannot delete an owner.

## Group schema and attributes

Groups in LaunchDarkly map to teams.

### Required fields

\| Field

\| Type

| Description |
| :- |

\| :-----

\| :------------------- |
\| `displayName`

\| String

\| The name of the team |

### Optional fields

\| Field

\| Type

| Description |
| :- |

\| :-----

\| :---------------------------------------------------------------------------------------------------------------------------------------------------- |
\| `externalId`

\| String

\| External identifier from the IdP                                                                                                                      |
\| `members`

\| Array

\| Array of member objects, each containing:<br />

* `value` (string): Member ID (LaunchDarkly member ID)<br />

* `display` (string): Member email address |

## Example group requests

### Create group (POST)

**Endpoint:** `POST /scim/v2/Groups`

**Request:**

```json theme={null}
{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
  "displayName": "Engineering Team",
  "externalId": "eng-team-123",
  "members": [
    {
      "value": "507f1f77bcf86cd799439011",
      "display": "alice@example.com"
    }
  ]
}
```

**Response:** `201 Created` with the created group object including the LaunchDarkly team ID.

### Update group members (PATCH)

**Add members:**

```json theme={null}
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [
    {
      "op": "add",
      "path": "members",
      "value": [
        {
          "value": "507f1f77bcf86cd799439011"
        }
      ]
    }
  ]
}
```

**Remove members:**

```json theme={null}
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [
    {
      "op": "remove",
      "path": "members",
      "value": [
        {
          "value": "507f1f77bcf86cd799439011"
        }
      ]
    }
  ]
}
```

### Query parameters for GET /scim/v2/Groups

* `filter`: SCIM filter expression (for example, `displayName eq "Engineering"`)
* `excludedAttributes`: Set to `"members"` to exclude member details
* `count`: Number of results per page
* `startIndex`: 1-based starting index for pagination

**Example:**

```
GET /scim/v2/Groups?filter=displayName eq "Engineering"&excludedAttributes=members
GET /scim/v2/Groups?count=10&startIndex=1
```

## Error responses

All errors follow the SCIM error format:

```json theme={null}
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
  "detail": "Error message here",
  "status": 400
}
```

### Common error messages

\| Error

\| Status code

| Description |
| - |

\| ---------------

\| ----------------------------------- |
\| `member already exists`

\| 409 Conflict

\| Email already exists in account     |
\| `member not found`

\| 404 Not Found

\| User ID does not exist              |
\| `Invalid email address`

\| 400 Bad Request

\| Email format is invalid             |
\| `'role-name' is not a valid primary role`

\| 400 Bad Request

\| Invalid role value                  |
\| `Cannot create an owner`

\| 400 Bad Request

\| Cannot create users with owner role |
\| `Cannot deactivate an owner`

\| 400 Bad Request

\| Cannot deactivate account owner     |
\| `Unknown custom role 'role-key'`

\| 400 Bad Request

\| Custom role doesn't exist           |
\| `Cannot change properties on deactivated members other than 'active'`

\| 400 Bad Request

\| Cannot update deactivated users     |
\| `Name length must not exceed 256 characters`

\| 400 Bad Request

\| Name field too long                 |

To learn more about how to remediate SCIM assertion consumer errors, read [SSO SCIM Error Messages](https://support.launchdarkly.com/hc/en-us/articles/15530970517275-SSO-SCIM-Error-Messages).

## API constraints and troubleshooting

### Owner restrictions

* You cannot create users with the `"owner"` role (returns 400 error: "Cannot create an owner")
* Any attempt to update an existing owner's role using SCIM is silently ignored (no error is returned)
* You cannot deactivate an owner (returns 400 error: "Cannot deactivate an owner")
* Only one owner is allowed per account

### Deactivated users

You cannot update attributes (name, role, and so forth) on a user who is currently deactivated (`active: false`). You must reactivate them first or simultaneously with the update.

### Role keys

When assigning custom roles, you must use the role **key**, and not the human-readable name or ID. Custom roles must exist in the account before they can be assigned.

### Email uniqueness

Email addresses must be unique within an account. If you attempt to provision a user with an email that already exists, you will receive a 409 Conflict error.

### Field validation

* `givenName` and `familyName` must be ≤ 256 characters each
* Email addresses must use valid format
* Role values must be one of: `"reader"`, `"writer"`, `"admin"`, or `"noAccess"`

### Okta compatibility

For Okta requests (identified by User-Agent header starting with "okta"), the `customRole` string field is not included in responses. Only `customRolesArray` is returned.

### Extension schema declaration

LaunchDarkly's implementation is lenient and will accept root-level extension attributes (`role`, `customRole`, `customRolesArray`) even if the extension schema is not declared in the `schemas` array. However, for strict SCIM 2.0 compliance, include `urn:ietf:params:scim:schemas:extension:launchdarkly:2.0:User` in the `schemas` array when using these attributes.
