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

# Groups

> Grant scopes to users in bulk by organizing them into groups.

A **group** is a named collection of scopes defined at the application level. Assign a user to a group and, on their next login, the group's scopes are added to their access token. Groups let you manage authorization by role — `admins`, `editors`, `billing` — instead of granting scopes to each user one by one.

## Prerequisites

* A Prelude account with access to the Auth API
* An **Application ID** (`appID`) — see [Applications](/session/documentation/applications)
* Your **Management API key** for backend calls
* One or more **allowed scopes** declared on the application — see [Scopes](/session/api-reference/management/config/scopes/list-scopes)

## How it works

A group owns a set of scopes, and every scope a group owns must be one of the application's **allowed scopes**. Users are assigned to groups; a user can belong to several groups.

Two things happen at different times:

* **At session creation** (login), the user's current group membership is snapshotted onto the session.
* **At every access-token mint** (login *and* refresh), each group on the session is looked up in the current application configuration and the scopes of the matching groups are merged into the token's `scope` claim — alongside the user's own scopes.

```mermaid theme={null}
flowchart LR
  A[App allowed scopes] --> B[Group: admins<br/>owns read, write]
  B --> C[User assigned to admins]
  C -->|snapshot at login| D[Session groups: admins]
  D -->|resolved at every mint| E[Access token<br/>scope: read write]
```

This split has two consequences worth remembering:

<Note>
  **Membership is pinned at session creation.** Adding or removing a user from a group does not change the scopes of their already-active sessions — it takes effect the next time they log in. Changing the **scopes a group owns**, on the other hand, is reflected on the next access-token refresh of every existing session, because group scopes are resolved from the live configuration at mint time.
</Note>

## Configure groups

<Steps>
  <Step title="Declare the scopes on your application">
    A group can only own scopes the application already allows. Add them first:

    ```bash theme={null}
    curl -X POST https://api.prelude.dev/v2/session/apps/{appID}/config/scopes \
      -H "Authorization: Bearer $MANAGEMENT_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "scope": "read" }'
    ```
  </Step>

  <Step title="Create a group">
    Create a group and give it a subset of the allowed scopes. A group name is unique per application and may contain letters, digits, `-` and `_`.

    ```bash theme={null}
    curl -X POST https://api.prelude.dev/v2/session/apps/{appID}/config/groups \
      -H "Authorization: Bearer $MANAGEMENT_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "name": "admins", "scopes": ["read", "write"] }'
    ```

    If any scope is not in the application's allowed scopes, the request fails with `scope_not_allowed` and lists every offending scope.
  </Step>

  <Step title="Assign users to the group">
    Assign a user to the group. The call is idempotent — assigning a user who is already a member succeeds without changing anything.

    ```bash theme={null}
    curl -X POST https://api.prelude.dev/v2/session/apps/{appID}/users/{userID}/groups \
      -H "Authorization: Bearer $MANAGEMENT_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "group": "admins" }'
    ```

    The next time the user logs in, their access token's `scope` claim will include `read` and `write`.
  </Step>

  <Step title="(Optional) Expose group names as a claim">
    Group **scopes** always land in the `scope` claim. If you also want the group **names** in the token, map the `groups` input in your [claims mapping](/session/api-reference/management/config/claims/get-claims-mapping):

    ```json theme={null}
    { "roles": { "$input": "groups", "$type": "string-array" } }
    ```

    This adds e.g. `"roles": ["admins"]` to the token. Group names are resolved from the user's profile, so this claim stays in sync with membership changes on the next claims recomputation.
  </Step>
</Steps>

## Update or remove a group

Replace a group's scopes with `PUT /config/groups/{groupName}`; the new set takes effect on the next access-token mint of every session that belongs to the group.

```bash theme={null}
curl -X PUT https://api.prelude.dev/v2/session/apps/{appID}/config/groups/admins \
  -H "Authorization: Bearer $MANAGEMENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "scopes": ["read", "write", "admin"] }'
```

Remove a single user from a group with `DELETE /users/{userID}/groups/{groupName}` (idempotent), or delete the group definition entirely with `DELETE /config/groups/{groupName}`. Once a group is deleted, its scopes are no longer granted at mint time.

<Warning>
  Group **membership** and group **scopes** are enforced only through the access token minted by Prelude. Always authorize sensitive operations on your backend against the `scope` claim of a freshly minted (or refreshed) access token, not against a cached one.
</Warning>

## API reference

* [List groups](/session/api-reference/management/config/groups/list-groups)
* [Create group](/session/api-reference/management/config/groups/create-group)
* [Get group](/session/api-reference/management/config/groups/get-group)
* [Set group scopes](/session/api-reference/management/config/groups/set-group-scopes)
* [Delete group](/session/api-reference/management/config/groups/delete-group)
* [Add user to group](/session/api-reference/management/users/add-user-to-group)
* [Remove user from group](/session/api-reference/management/users/remove-user-from-group)
