# User & group provisioning for custom providers

Last validated Jan 5, 2026

> **Note:** Custom SCIM is currently in alpha.

Tailscale supports System for Cross-domain Identity Management (SCIM) to provision users and groups from your identity provider to Tailscale.

Some identity providers, like [Okta][docs-okta-scim] and [Microsoft Entra][docs-entra-scim], support SCIM natively. For others, Tailscale's SCIM API endpoints can be called directly to provision users and groups.

## Features

The following provisioning features are supported:

* **Create users** in Tailscale from your identity provider
* **Update user attributes** in Tailscale from your identity provider
* **Deactivate users** in your identity provider to suspend them in Tailscale
* **Group push** from your identity provider to Tailscale

## Enable Provisioning

**Generate a SCIM API key**

1. In the [User management](https://console.tailscale.com/admin/settings/user-management) page of the admin console, under **User & Group Provisioning**, select **Enable Provisioning**.

   ![Enable User & Group Provisioning](integrations/identity/custom-scim/generate-api-key.png)

2. Copy the generated key to the clipboard. This key will be provided to the SCIM API as a bearer token in the `Authorization` header.

   ![Generate SCIM API key](integrations/identity/custom-scim/copy-generated-api-key.png)

## SCIM API

SCIM is a relatively simple RESTful API that operates on `User` and `Group` resources. The data schema and protocol are defined in [RFC7643][xt-rfc-7643] and [RFC7644][xt-rfc-7644].

Tailscale's SCIM API endpoint is located at `https://api.tailscale.com/scim/v2`.

### Example API calls

Here are examples of some of the most common SCIM API calls. Refer to the SCIM specifications for more information.

#### List users

To list all users, send a `GET` request to the `/Users` endpoint:

```shell
curl -H "Authorization: Bearer ${SCIM_KEY}" \
  https://api.tailscale.com/scim/v2/Users
```

You can use the [`filter` query parameter][xt-rfc-7644-filter] to search for users that match a query, and the [`attributes` parameter][xt-rfc-7644-attributes] to only return certain attributes for each user. For example, to get the `username` field for a user that matches a specific `externalId`:

```shell
curl -s \
  -H "Authorization: Bearer ${SCIM_KEY}" \
  'https://api.tailscale.com/scim/v2/Users?filter=externalId+eq+"123"&attributes=username' \
  | jq .

{
  "Resources": [
    {
      "id": "LDeihfrJiC21DEVEL",
      "meta": {
        "resourceType": "User",
        "location": "Users/LDeihfrJiC21DEVEL"
      },
      "schemas": [
        "urn:ietf:params:scim:schemas:core:2.0:User"
      ],
      "userName": "test@example.com"
    }
  ],
  "itemsPerPage": 100,
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:ListResponse"
  ],
  "startIndex": 1,
  "totalResults": 1
}
```

#### Create a user

To create a user, send a `POST` request to the `/Users` endpoint with a JSON body containing the user attributes:

```shell
curl -X POST \
  -H "Authorization: Bearer ${SCIM_KEY}" \
  https://api.tailscale.com/scim/v2/Users \
  --data-raw '{
  "schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],
  "displayName":"Test user",
  "externalId":"123",
  "username":"test@example.com",
  "emails":[{"value":"test@example.com"}]
}'
```

At a minimum, `username` and `emails` are required. If `externalId` is provided, it must match the same `sub` value provided for the user [in OpenID Connect][docs-custom-oidc].

The response will contain an `id` field, which is the unique identifier for the user in Tailscale. This ID is used to reference the user in subsequent API calls.

```json
{
  "id": "LDeihfrJiC21DEVEL",
  "userName": "test@example.com",
  "active": true,
  "displayName": "test@example.com",
  "name": { "familyName": "-", "givenName": "-" },
  "emails": [{ "primary": true, "value": "test@example.com" }],
  "meta": {
    "resourceType": "User",
    "created": "2025-05-21T22:50:13Z",
    "lastModified": "2025-05-21T22:50:13Z",
    "location": "Users/LDeihfrJiC21DEVEL",
    "version": "W/\"d-ukZmkHbatJDyDZqNS3OXGGwBEd5b61thjSo78ucJA\""
  },
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"]
}
```

#### Update user attributes

Individual attributes can be updated using a `PATCH` request to the `/Users/{id}` endpoint with a JSON Patch body. For example, to suspend a user, set the `active` attribute to `false`:

```shell
% curl -X PATCH \
  -H "Authorization: Bearer ${SCIM_KEY}" \
  https://api.tailscale.com/scim/v2/Users/LDeihfrJiC21DEVEL
  --data-raw '{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [{
    "op": "replace",
    "value": {"active":false}
  }]
}'
```

#### Delete a user

To delete a user, send a `DELETE` request to the `/Users/{id}` endpoint:

```shell
% curl -X DELETE \
  -H "Authorization: Bearer ${SCIM_KEY}" \
  https://api.tailscale.com/scim/v2/Users/LDeihfrJiC21DEVEL
```

#### List groups

To list all groups, send a `GET` request to the `/Groups` endpoint:

```shell
curl -H "Authorization: Bearer ${SCIM_KEY}" \
  https://api.tailscale.com/scim/v2/Groups
```

This endpoint supports the same `filter` and `attributes` query parameters as the [`/Users` endpoint][ar-list-users].

#### Create a group

To create a group, send a `POST` request to the `/Groups` endpoint with a JSON body containing the group attributes:

```shell
curl -X POST \
  -H "Authorization: Bearer ${SCIM_KEY}" \
  https://api.tailscale.com/scim/v2/Groups
  --data-raw '{
  "schemas":["urn:ietf:params:scim:schemas:core:2.0:Group"],
  "displayName":"test group",
  "members":[
    {"value":"LDeihfrJiC21DEVEL","display":""}
  ]
}'
```

The `members` field identifies the IDs of users to add to the group. Each user must have a "display" field, but it can be empty.

#### Update group attributes

Update group attributes using a `PATCH` request to the `/Groups/{id}` endpoint with a JSON Patch body. For example, to add a group member, send an `add` operation on the `members` field:

```shell
% curl -X PATCH \
  -H "Authorization: Bearer ${SCIM_KEY}" \
  https://api.tailscale.com/scim/v2/Groups/sZkaCVkrC221DEVEL
  --data-raw '{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [
    {
      "op": "add",
      "path": "members",
      "value": [
        {
          "value": "LDeihfrJiC21DEVEL",
          "display": "%s"
        }
      ]
    }
  ]
}'
```

Or to remove a member:

```shell
% curl -X PATCH \
  -H "Authorization: Bearer ${SCIM_KEY}" \
  https://api.tailscale.com/scim/v2/Groups/sZkaCVkrC221DEVEL
  --data-raw '{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [
    {
      "op": "remove",
      "path": "members[value eq \"%s\"]"
    }
  ]
}'
```

#### Delete a group

To delete a group, send a `DELETE` request to the `/Groups/{id}` endpoint:

```shell
% curl -X DELETE \
  -H "Authorization: Bearer ${SCIM_KEY}" \
  https://api.tailscale.com/scim/v2/Groups/sZkaCVkrC221DEVEL
```

[ar-list-users]: #list-users

[docs-okta-scim]: /docs/integrations/identity/okta/okta-scim

[docs-entra-scim]: /docs/integrations/identity/entra/entra-id-scim

[docs-custom-oidc]: /docs/integrations/identity/custom-oidc

[xt-rfc-7643]: https://www.rfc-editor.org/rfc/rfc7643.html

[xt-rfc-7644]: https://www.rfc-editor.org/rfc/rfc7644.html

[xt-rfc-7644-filter]: https://www.rfc-editor.org/rfc/rfc7644.html#section-3.4.2.2

[xt-rfc-7644-attributes]: https://www.rfc-editor.org/rfc/rfc7644.html#section-3.4.2.5
