Skip to main content
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

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"}
    ]
  }'
FieldRequiredDescription
nameYesDisplay name (1-255 characters)
membersYesInitial members. At least one must have the ADMIN role.

Membership

Adding members

POST /v1/groups/{id}/members
{
  "members": [
    {"participant_id": "{participant_id_1}", "role": "MEMBER"},
    {"participant_id": "{participant_id_2}", "role": "MEMBER"}
  ]
}

Removing members

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

RoleDescription
ADMINCan manage group membership
MEMBERStandard 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:
POST /v1/groups/{id}/balances/adjust
{
  "program_id": "{program_id}",
  "asset_id": "{asset_id}",
  "type": "CREDIT",
  "amount": "500",
  "description": "Monthly team allocation"
}
Group balances are returned as a flat map of {asset_id: amount} representing aggregate totals per asset. 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:
{
  "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:
{
  "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:
FieldTypeDescription
idstringGroup UUID
namestringGroup display name
tagslistGroup tags
countersmapGroup counters
attributesmapGroup attributes
tiersmapGroup tier memberships
// Only fire if participant is in at least one group
groups.size() > 0

// Check a group-level counter
groups.size() > 0 && get(groups[0].counters, "team_purchases", 0.0) < 100.0

// Check a group-level tag
groups.size() > 0 && "premium_team" in groups[0].tags
groups is a list because a participant can belong to multiple groups. If your program uses a single group per participant, groups[0] is a convenient shorthand. For programs where participants may be in multiple groups, use groups.size() checks and index carefully.

Group State

Groups support the same state types as participants: tags, counters, and attributes.
# 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

# Counters
PUT /v1/groups/{id}/state/counters/{key}
DELETE /v1/groups/{id}/state/counters/{key}
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

DELETE /v1/groups/{id}
Archived groups are excluded from listings unless include_archived=true is set. Archived groups cannot be modified.