updated_at, so polling Users alone misses a significant class of “user activity” events.
If you haven’t yet, skim the Polling Overview for the core polling pattern.
When to use this workflow
| Scenario | This workflow fits |
|---|---|
| Sync user records to an external CRM | ✓ Primary use case |
| Mirror VOMO users into a data warehouse | ✓ |
| Trigger follow-up workflows on new user signup | ✓ With the new-user detection pattern |
| Detect users who recently updated their profile | ✓ |
| Detect when a user participated in a shift | ✗ Doesn’t work via User polling — see the participation caveat |
| Detect when a user earned a certificate | ✗ Same caveat — track via separate polling or admin UI |
| One-time full user sync | ✗ Use the initial-sync pattern instead |
The baseline pattern
The simplest user-change polling worker:JavaScript
Distinguishing new from modified users
updated_after returns both newly-created and modified users. Sometimes you want to treat them differently — fire a welcome email on creation, fire an update event on modification.
The distinction: for newly-created users, created_at equals updated_at (or is very close). For updates, updated_at > created_at.
JavaScript
Why this works (and why it’s approximate)
The heuristic assumes that creating a user producescreated_at and updated_at simultaneously (with sub-second resolution). For most cases this is correct.
Edge cases:
- A user created and then immediately modified within the same poll window — both
created_atandupdated_atare recent, but they’re not equal. The heuristic might miss the “created” event and treat it as an update. - Bulk imports where many users are created in batch — they may all share the same
created_atbut have slightly differentupdated_at.
JavaScript
The participation caveat
The most important thing to understand about User polling: new participations do not advance the User’supdated_at.
| Scenario | Does User’s updated_at advance? |
|---|---|
| User updates their email, phone, address | ✓ Yes |
| User’s membership status changes | ✓ Yes (likely) |
| User signs up for a Project Date | ✗ No — the Participation is a separate record |
| User checks in to a shift | ✗ No |
| User’s hours are recorded | ✗ No |
| User is awarded a Certificate | ✗ Likely no |
/users?updated_after=X will not detect new volunteer activity.
Why this matters
A partner integration that promises “we’ll detect when your volunteers serve” cannot deliver on that promise via User polling alone. The integration architecture needs to account for this gap.What to do instead
For detecting new participations, three options:Option A: Poll GET /users/{id} for each user
After detecting a user change via the list endpoint, fetch detail to see their full participation list:
JavaScript
Option B: Periodic full participation scan
On a slower cadence (daily, weekly), iterate all users and check their participations:JavaScript
Option C: Poll Project Dates instead
If your integration mainly needs participation data for specific Projects, poll the Project Dates and pull participants from there:JavaScript
Combining updated_after with other filters
The polling pattern can be narrowed with additional filters when not all users need processing:
Poll only verified users
JavaScript
user_status as a server-side filter, so this is client-side filtering — the API returns all updated users, and your code filters in memory. The poll still consumes rate budget for all changed users, but downstream processing is narrowed.
Poll only users with email matches
JavaScript
Detecting deletions
Polling has a fundamental gap: deleted records don’t appear in queries. If a user is deleted in VOMO, polling/users?updated_after=X won’t show them — they’re just gone.
For partner integrations that need to mirror deletions to external systems, the only path is reconciliation:
JavaScript
A subtle gotcha: “deletion” vs. “not accessible”
A user may “disappear” from/users results for reasons other than deletion:
- The user was moved to a different organization within the family
- The token’s permissions changed
- The user was banned or soft-deleted but still exists
Reading user detail during the poll
The list endpoint returns abbreviatedUserResource objects. If your integration needs the full UserDetailResource (with participations and profile_field_values), fetch detail per user:
JavaScript
When to fetch detail vs. when to skip
| Workflow | Fetch detail? |
|---|---|
| Sync basic profile to external CRM | No — list shape is sufficient |
| Mirror participation history | Yes — detail’s participations array is the data |
| Display Form responses | Yes — detail’s profile_field_values is the data |
| Just track “was modified” without specific fields | No |
| Update external system with new email/phone | No — list shape has those |
Throttling and resource limits
Per-poll-cycle request cost on/users:
JavaScript
A reference user-change poller
A complete reference implementation incorporating the patterns above:JavaScript
Monitoring
Track these metrics per customer:| Metric | What it tells you |
|---|---|
| Processed-users per poll cycle | Sync activity rate; sudden spike or drop is worth investigating |
| Latest checkpoint timestamp | Confirms polling is actually running |
Lag time (now - latest_checkpoint) | How fresh is the data? Sustained increase = polling issue |
| Dead-letter queue depth | Failures accumulating — needs investigation |
| Poll cycle duration | Sustained increase may indicate large batches or rate-limit slowdown |
4xx/5xx error rates | Auth issues, rate limits, server problems |
now - latest_checkpoint < 2 * poll_interval. Beyond that, no recent activity means polling has stalled.
Production checklist
For a User-change polling worker:- Checkpoint persisted per-customer in durable storage
- Checkpoint advanced to the latest
updated_atactually seen (not wall-clock time) - Per-user failures isolated; don’t fail whole batch on one bad record
- Failed records go to a dead-letter queue
- Rate-limit-aware throttling in place
- Distinct paths for new-user vs. updated-user processing (where business logic differs)
- Deletion detection runs as a separate (slower) reconciliation
- Participation-related workflows use a different polling strategy (Project Dates, full scans, etc.)
- Per-customer monitoring dashboards exist
- Alerts on stalled checkpoints and growing DLQ
Where to go next
Detecting Project Changes
The Project-specific polling pattern with schedule-change considerations.
Reconciliation Patterns
The slow-scan patterns for deletion detection and gap recovery.
Change Detection Best Practices
The cross-cutting patterns — checkpointing, idempotency, drift.
Users
The reference page for User fields and the upsert behavior.