Skip to main content
Groups are the most write-heavy resource in the Volunteer API. They’re the canonical path for programmatically organizing volunteers into stable collections — teams, affinity groups, departmental cohorts — and they have full CRUD plus a dedicated member-management surface. If your integration needs to manage who’s part of what, Groups are where most of the work happens. This workflow page walks through the operational patterns: creating Groups (with or without initial members), updating metadata, syncing membership rosters, managing parent-child hierarchies, and the dual-write patterns for keeping Group state and member state in sync. If you haven’t yet, skim the Groups concept page for the field reference and endpoint inventory.

When to use this workflow


The seven endpoints

The split between Group endpoints (/groups/{id}) and member endpoints (/groups/{id}/members) lets you manage metadata and membership independently — useful when these change at different cadences or come from different sources.

Lifecycle: create → update → manage members → delete

The four operational phases for a typical Group’s lifetime: In practice, Groups spend most of their lifetime in the “manage members” phase — adding new volunteers, removing departed ones, periodically syncing rosters. Create and delete happen rarely; member sync is the most common operation.

Phase 1: Create a Group

cURL
⚠️ Spec gap (audit #21): POST /groups returns 200 OK on success — not 201 Created per HTTP convention for resource creation. Check for 200 (or any 2xx) as success.

Creating with initial members

The members array in the request body lets you populate the Group at creation time:
JavaScript
This is the most efficient way to set up a Group from scratch — one request handles both the Group creation and the initial roster, rather than create-then-PUT-members.

Creating a child Group

To create a Group as a child of an existing one, provide the parent’s ID:
JavaScript
The new Group becomes a child of Group 100. See Phase 5: Managing hierarchies for working with parent-child relationships.

Phase 2: Update Group metadata

PUT /groups/{id} is full-record replacement. Use the GET-then-PUT pattern to avoid clearing fields by omission:
JavaScript
⚠️ Spec gap (audit #23): The PUT /groups/{id} request body description in the spec reads "Group to create" — a copy-paste error from the POST endpoint. The actual behavior is full-record update.

Updating metadata only (without re-PUT-ing members)

If you want to update metadata but the Group’s member list is large or unstable, two options: Option A: separate the metadata update from the members update. The members field is required on PUT /groups/{id} — you can’t omit it. But you can re-PUT the current members (effectively a no-op for membership) while changing metadata:
JavaScript
Option B: cache the members for the duration of the update window. For workflows doing multiple metadata updates in a row, cache the members list to avoid re-fetching:
JavaScript
Invalidate the cache after any operation that changes membership (a member-list PUT, a single add/remove, etc.).

Phase 3: Manage members

For dedicated membership changes — separate from metadata — use PUT /groups/{id}/members. This endpoint takes only a members array (no metadata fields).
cURL
⚠️ Spec gap (audit #24): The PUT /groups/{id}/members request body description in the spec also reads "Group to create" — same copy-paste issue. The actual behavior is member list replacement.

Replacement semantics — what this PUT really does

The PUT replaces the entire member list with what you submit. Implications: This is not an “add these members” PUT — it’s a “the Group’s members are now these IDs” PUT.

Add a single member (GET-then-PUT union)

JavaScript

Remove a single member (GET-then-PUT subset)

JavaScript

Batch multiple changes

For workflows that need to add and remove multiple members in one operation:
JavaScript
This is more efficient than separate add/remove calls — one GET, one PUT, all changes applied atomically.

Phase 4: Sync rosters from an external system

The most common operational pattern: a partner integration mirrors a team roster from an external HR/CRM system into a VOMO Group.
JavaScript
This single sync handles three cases atomically: The PUT is atomic from the API’s perspective — partial failure is unlikely, and the resulting state matches the external roster exactly.

Scheduled roster sync

For ongoing maintenance (run nightly or hourly):
JavaScript
Per-Group failures don’t stop the whole sync — log them and continue.

Phase 5: Managing hierarchies

For organizations with structured team hierarchies, build parent-child Groups that mirror the external structure:
JavaScript
The topological sort matters because creating a child Group requires its parent to already exist in VOMO. Without sorting, you’d get “parent not found” errors.

Moving a Group to a different parent

JavaScript
Changing parent_id moves the Group in the hierarchy. This affects:
  • The Group’s location in tree displays
  • Any reporting that aggregates by Group hierarchy
  • Potentially the Group’s accessibility (if access is hierarchical)
The exact downstream behavior isn’t documented in the spec — confirm against live behavior for production workflows that depend on hierarchical access.

Phase 6: Deletion

DELETE /groups/{id} removes the Group:
JavaScript
⚠️ Spec gap (audit #22): DELETE /groups/{id} returns 200 OK, not the HTTP-conventional 204 No Content for empty-body deletes. Code should accept either as success.

Deletion considerations

For production workflows, prefer archival to deletion when possible:
JavaScript
This keeps the Group visible in reports (so historical data isn’t lost) but functionally inactive.

A reference Groups workflow client

JavaScript

Common bugs to avoid

A few patterns that cause subtle issues:

Don’t omit members from PUT /groups/{id} thinking it preserves membership

The members field is part of the request body for PUT /groups/{id}. Omitting it means submitting an undefined value — which may be interpreted as “clear all members” or rejected as a validation error. Always include the current member IDs (or use the dedicated /members endpoint).

Don’t use member emails instead of IDs

The members array in PUT bodies expects integer User IDs, not emails. If your external data is keyed by email, convert to IDs before submitting:
JavaScript

Don’t add members one at a time in a loop without throttling

Sequential single-add operations against a Group create N requests, each requiring a GET + PUT. For batches of changes, use applyMemberChanges or replaceMembers to consolidate into one PUT.
JavaScript

Where to go next

Build a Group from a Query

The end-to-end recipe that combines user-querying with Group population.

Read a Project's Schedule

The companion read workflow for Projects.

Groups

The reference page for Group resource fields and relationships.

Understand Write Limitations

What can and can’t be done with Groups (and other resources).
Last modified on May 22, 2026