Groups
Create a named group of Payees, add and remove members one at a time or in bulk, and handle the ETags that group changes require.
A Group is a named set of Payees, such as "California nurses" or "Q4 inspection crew". This page shows you how to create a group, add and remove Payees, and delete a group. Groups replace V1 collaborator groups.
What changed from V1
In V1, a collaborator group carried eligibility requirements, and adding a collaborator to a group sent them those requirements. In V3 those jobs are split:
- Groups organize Payees into cohorts.
- Engagements carry requirements. To make a set of Payees sign a document or upload a license, attach the requirement to an Engagement and assign those Payees to it. See Requirements and eligibility.
| V1 | V3 |
|---|---|
POST /payments/collaborator-group | POST /v3/payments/groups |
eligibilityRequirements on the group | requirementDefinitionIds on an Engagement |
PATCH /payments/collaborator/{id}/add-group/{groupId} | POST /v3/payments/groups/{groupId}/members |
collaboratorGroupId on the invite | A separate add-member call after creating the Payee |
The group resource
| Field | What it means |
|---|---|
id | The group ID. |
name | Required on create. |
type | Always Payee. Required on create. |
description, externalId, metadata | Your labels and IDs. A duplicate externalId returns 409 ResourceConflict. |
status | Active or Deleted. |
memberCount | How many Payees are in the group. |
membershipVersion | A counter that goes up every time membership changes. It's what the group's ETag is built from. |
accountId | Your Account. Every member must be a Payee owned by this Account. |
Create a group
curl -X POST https://api.wingspan.app/v3/payments/groups \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"name": "California nurses",
"type": "Payee",
"description": "RNs licensed in CA",
"externalId": "GRP-CA-RN"
}'// 201 Created (trimmed)
{
"id": "wug8qBmy8cBW3YJNjBDrHD",
"name": "California nurses",
"type": "Payee",
"status": "Active",
"memberCount": 0,
"membershipVersion": 0,
"events": { "createdAt": "2026-09-24T15:40:00Z" }
}Changes require If-Match
Updating a group, deleting it, and adding or removing members all require an If-Match header with the group's current ETag. This stops two people from changing membership at the same time and silently overwriting each other.
Get the ETag from GET /v3/payments/groups/{groupId} or from the response to your last change:
curl -i https://api.wingspan.app/v3/payments/groups/wug8qBmy8cBW3YJNjBDrHD \
-H "Authorization: Bearer $WINGSPAN_TOKEN"
# ETag: "..."A request without If-Match returns 412. If-Match: * is accepted and applies the change to the group's current version. If the group changed since you read it, you get 412 with code: PreconditionFailed. Read the group again and retry with the new ETag. See Concurrency and ETags.
Add a payee
curl -X POST https://api.wingspan.app/v3/payments/groups/wug8qBmy8cBW3YJNjBDrHD/members \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-H "If-Match: $GROUP_ETAG" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "payeeId": "M9ISYbzJElXs4zIHv76rjT" }'// 201 Created
{
"id": "W6uJfLU9dBxmBI6Eu9uoS8",
"accountId": "z9uv0jPAxTqSLs5UKwv1DE",
"groupId": "wug8qBmy8cBW3YJNjBDrHD",
"payeeId": "M9ISYbzJElXs4zIHv76rjT",
"status": "Active",
"membershipVersion": 1,
"events": { "createdAt": "2026-09-24T15:41:12Z" }
}Save the member id. You need it to remove the Payee later. A Payee that is already an active member returns 409, and a Payee that isn't in your Account returns 404.
This call can also return 202 with an AsyncOperation when adding the member takes longer to process. Poll GET /v3/platform/operations/{operationId} from the Location header, pacing on Retry-After. See Async operations.
Changing group membership is a sensitive action. If you call with a person's login session, it needs a recent MFA step-up (HighRiskWriteAction). Without it you get 403 with code: StepUpMfaRequired. The same applies to removing members, bulk adds, and deleting a group. A backend calling with an API key has no session to step up.
Add up to 100 payees at once
POST /v3/payments/groups/{groupId}/members/bulk adds 1 to 100 Payees. The whole request is validated first and applied in one transaction: either every Payee is added (or re-added, if previously removed) or none are.
curl -X POST https://api.wingspan.app/v3/payments/groups/wug8qBmy8cBW3YJNjBDrHD/members/bulk \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-H "If-Match: $GROUP_ETAG" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"members": [
{ "payeeId": "M9ISYbzJElXs4zIHv76rjT" },
{ "payeeId": "JDft9g7rxT3I8yuWXpMBg6" }
]
}'This returns 202 with an AsyncOperation. Poll it until status is terminal. The GroupMember.Created webhook event is available for subscription and fires for each member added.
Remove a payee
curl -X DELETE https://api.wingspan.app/v3/payments/groups/wug8qBmy8cBW3YJNjBDrHD/members/W6uJfLU9dBxmBI6Eu9uoS8 \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "If-Match: $GROUP_ETAG"The path uses the member id from the add response, not the payeeId. It returns 204.
Update, list, and delete groups
PATCH /v3/payments/groups/{groupId}changesname,description,externalId(empty string clears it), andmetadata. RequiresIf-Match.GET /v3/payments/groupslists your groups. Filter withfilter[externalId][eq].DELETE /v3/payments/groups/{groupId}deletes a group. RequiresIf-Match. Returns409if the group still has members, so remove them first.
Keep the member id values from your add responses if you need to remove Payees later.
What can go wrong
| Status | code | What it means |
|---|---|---|
403 | StepUpMfaRequired | The login session needs recent multi-factor authentication for this change. |
404 | ResourceNotFound | The group, Payee, or member doesn't exist in your Account. |
409 | ResourceConflict or InvalidStateTransition | Duplicate externalId, deleting a group that still has members, or adding a Payee that is already an active member. |
412 | PreconditionFailed | Your If-Match ETag is out of date. Read the group and retry. |
422 | ValidationError | A required field is missing, or members is empty or over 100. |
Related
Updated 24 minutes ago