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

# Groups

> Shared wallets and state for teams, families, and organizational pools

Groups let multiple participants share a wallet. A group is its own ledger entity with independent balances, separate from any individual participant. Useful for families, teams, or organizational pools.

Groups exist at the organization level and are not scoped to a single program. Balance operations are program-scoped (you specify `program_id` when adjusting), but the group itself can participate across programs.

## Creating a Group

```bash theme={null}
curl -X POST https://api.scrip.dev/v1/groups \
  -H "Authorization: Bearer $SCRIP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Smith Family",
    "members": [
      {"participant_id": "{alice_id}", "role": "ADMIN"},
      {"participant_id": "{bob_id}", "role": "MEMBER"}
    ]
  }'
```

| Field     | Required | Description                                               |
| --------- | -------- | --------------------------------------------------------- |
| `name`    | Yes      | Display name (1-255 characters)                           |
| `members` | Yes      | Initial members. At least one must have the `ADMIN` role. |

## Membership

### Adding members

```bash theme={null}
POST /v1/groups/{id}/members
{
  "members": [
    {"participant_id": "{participant_id_1}", "role": "MEMBER"},
    {"participant_id": "{participant_id_2}", "role": "MEMBER"}
  ]
}
```

### Removing members

```bash theme={null}
DELETE /v1/groups/{id}/members/{participant_id}
```

Members are soft-deleted with a `LEFT` status. Use `include_former=true` when listing to see former members. Removing a member does not affect the group's balance.

### Roles

| Role     | Description                          |
| -------- | ------------------------------------ |
| `ADMIN`  | Can manage group membership          |
| `MEMBER` | Standard member (default if omitted) |

The last `ADMIN` in a group cannot be demoted or removed.

## Group Balances

Adjust a group's balance directly via the API:

```bash theme={null}
POST /v1/groups/{id}/balances/adjust
{
  "program_id": "{program_id}",
  "asset_id": "{asset_id}",
  "type": "CREDIT",
  "amount": "500",
  "description": "Monthly team allocation"
}
```

`GET /v1/groups/{id}/balances` returns a list of `{asset_id, balance}` objects, one per asset, representing aggregate totals. Unlike participant balances, which are bucketed into `available` and `held`, group balances do not distinguish between buckets.

## Crediting Groups from Rules

Rules can credit a group's wallet instead of the participant's by adding a `target` to the action. This rule pools purchase points into a family group:

```json theme={null}
{
  "name": "Family Pool Points",
  "condition": "event.type == 'purchase'",
  "actions": [
    {
      "type": "CREDIT",
      "asset_id": "{asset_id}",
      "amount": "${{ event.amount * 2 }}",
      "target": {"type": "GROUP", "id": "{group_id}"}
    }
  ]
}
```

The participant who triggers the event must be a member of the target group, or the action fails.

The same targeting works for state actions. For example, tracking how many purchases the group has made:

```json theme={null}
{
  "name": "Track Team Purchases",
  "condition": "event.type == 'purchase'",
  "actions": [
    {"type": "COUNTER", "key": "team_purchases", "value": "1", "target": {"type": "GROUP", "id": "{group_id}"}},
    {"type": "CREDIT", "asset_id": "{asset_id}", "amount": "10", "target": {"type": "GROUP", "id": "{group_id}"}}
  ]
}
```

## Groups in CEL Conditions

When a rule evaluates, the `groups` variable contains a list of groups the participant belongs to. Each entry has:

| Field        | Type     | Description            |
| ------------ | -------- | ---------------------- |
| `id`         | `string` | Group UUID             |
| `name`       | `string` | Group display name     |
| `tags`       | `list`   | Group tags             |
| `counters`   | `map`    | Group counters         |
| `attributes` | `map`    | Group attributes       |
| `tiers`      | `map`    | Group tier memberships |

```javascript theme={null}
// Only fire if participant is in at least one group
groups.size() > 0

// Check a group-level counter
groups.exists(g, g.counter.team_purchases < 100.0)

// Check a group-level tag
groups.exists(g, "premium_team" in g.tags)
```

<Note>
  `groups` is a list because a participant can belong to multiple groups. Prefer `groups.exists(g, ...)` over assuming a specific list position.
</Note>

## Group State

Groups support the same state types as participants: tags, counters, and attributes.

```bash theme={null}
# Tags
PUT /v1/groups/{id}/state/tags/{tag}
DELETE /v1/groups/{id}/state/tags/{tag}

# Attributes
PUT /v1/groups/{id}/state/attributes/{key}
PATCH /v1/groups/{id}/state/attributes
DELETE /v1/groups/{id}/state/attributes/{key}

# Counters
PUT /v1/groups/{id}/state/counters/{key}
POST /v1/groups/{id}/state/counters/{key}/increment
DELETE /v1/groups/{id}/state/counters/{key}
```

`PUT` on a counter sets an absolute value and is last-write-wins; `increment` adds `delta` atomically, so concurrent writers never lose updates, and negative deltas decrement. See [Increment a group counter](/api-reference/groups/increment-a-group-counter).

Group state is available in CEL via the `groups` variable and can be updated from rule actions using `"target": {"type": "GROUP", "id": "{group_id}"}`.

## Archiving Groups

```bash theme={null}
DELETE /v1/groups/{id}
```

Archived groups are excluded from listings unless `include_archived=true` is set. Archived groups cannot be modified.
