- Deletions don’t appear in
updated_afterqueries (the record is just gone) - Silent processing failures advance the checkpoint despite the change not being applied
- Field changes that don’t advance
updated_at(rare but possible) - Worker crashes mid-batch that fail to track which records were processed
- External-system writes that fail asynchronously — the integration thought it succeeded but didn’t
What reconciliation does
A reconciliation pass:Reads the expected state from your integration's external store
What you believe VOMO contains based on prior sync activity.
Computes the diff
Records in VOMO but not in your store (missed creates / updates), records in your store but not in VOMO (missed deletions), records with mismatched fields (drift).
Applies corrective actions
Re-process missed records, mark deletions in downstream systems, alert on unexplained drift.
The minimum reconciliation: daily catch-up
The simplest useful reconciliation: every night, re-read yesterday’s changes and verify they were processed.JavaScript
What this catches
| Issue | How reconciliation catches it |
|---|---|
| Polling worker crashed mid-batch | Records modified during the crash weren’t in any successful poll; reconciliation re-discovers them |
| Per-record processing failure went to DLQ but was forgotten | Reconciliation finds the un-processed records and tries again |
| Polling worker silently advanced checkpoint past records | Reconciliation re-reads the time range, doesn’t trust the checkpoint |
| Network error during a poll’s pagination | Records on later pages were missed; reconciliation re-fetches them |
What this doesn’t catch
| Issue | Why |
|---|---|
| Deletions | Deleted records aren’t in updated_after results — need a separate scan |
| Drift between polling and external state | Need a three-way reconciliation — see below |
Changes that don’t advance updated_at | These are invisible to time-based reconciliation; need full-state scan |
Deletion detection
Deletions are the hardest thing to detect in a polling architecture. The pattern: periodically read the full current state, compare to what your external store thinks exists, and treat the difference as deletions.The full-state scan
JavaScript
Run cadence
| Resource | Suggested deletion-scan cadence |
|---|---|
| Users | Weekly (deletions are rare; freshness less critical than other changes) |
| Projects | Weekly |
| Groups | Daily (Groups can be intentionally deleted as part of admin cleanup) |
| Campaigns | Weekly |
| Forms | Monthly |
| Certificates | Monthly |
Distinguishing deletion from inaccessibility
A user “missing” from the list query could mean:| Cause | Action |
|---|---|
| User was deleted in VOMO | Propagate deletion to external |
| User moved to a different organization | Don’t propagate — handle as scope change |
| Token’s permissions narrowed | Don’t propagate — handle as scope change |
| User was banned/soft-deleted but record still exists | Don’t propagate as full deletion |
GET /users/{id}) resolves the ambiguity:
200response → user exists but didn’t match list query (investigate filters)404response → user is truly gone
Three-way state reconciliation
The most rigorous pattern: compare three sources of truth. Each pair has a possible disagreement:| Comparison | Detects |
|---|---|
| VOMO ↔ Partner state | Polling gaps (Partner missed a VOMO change) |
| Partner state ↔ External | Push failures (Partner thought it pushed but External didn’t receive) |
| VOMO ↔ External (transitive) | End-to-end correctness — what the customer ultimately experiences |
The end-to-end audit
JavaScript
When to run end-to-end audits
| Cadence | Purpose |
|---|---|
| Weekly random sample | Detect drift early |
| After any significant code change | Verify the change doesn’t regress sync correctness |
| When a customer reports an issue | Confirm whether issue is broader than the single reported case |
| Before quarterly reporting cutoffs | Confirm data integrity for downstream reports |
Incremental vs. full reconciliation
Two distinct cadences for different purposes:Incremental (daily) reconciliation
Re-reads a recent time window. Lower cost, catches recent gaps.JavaScript
Full (weekly) reconciliation
Re-reads everything. Higher cost, catches everything including deletions and drift.JavaScript
Combining the two
For most production integrations, both cadences run: Different cadences catch different issue classes; the layered approach catches more than any single pass.Per-resource reconciliation patterns
Different resources have different reconciliation needs:Users
| Concern | Pattern |
|---|---|
| Polling caught most changes | Daily incremental — re-read yesterday |
| Detect deletions | Weekly full scan |
| Detect drift in email/name/phone | Monthly random-sample audit |
| Detect missed participations | Daily Project Date scan (see below) |
Projects
| Concern | Pattern |
|---|---|
| Metadata change gaps | Daily incremental |
| Schedule drift (Project Date count differs from snapshot) | Weekly full scan with all_dates comparison |
| Project deletion (rare but happens) | Weekly full scan |
| Project Date deletions | Re-fetched as part of schedule diff |
Groups
| Concern | Pattern |
|---|---|
| Membership drift | Daily — re-read members for active Groups |
| Group deletion (more common — admin cleanup) | Daily full scan |
| Parent-child relationship changes | Daily — re-check parent_id on changed Groups |
Forms / Form Completions
| Concern | Pattern |
|---|---|
| Form structure changes | Weekly |
| Missed Form Completions | Daily incremental per active Form |
| Completion modifications | Detected by updated_after |
Participation reconciliation (the hardest case)
Because participations aren’t directly polled, reconciliation is the only path. Run daily:JavaScript
Per-customer orchestration
For partner integrations serving many customers, reconciliation across them needs orchestration:JavaScript
Staggering
Avoid running reconciliation for all customers at the same minute — spike load and rate-limit hits will follow. Stagger:JavaScript
Per-customer cadence
Not every customer needs the same cadence. Small customers (a few hundred users) might be fine with weekly full reconciliation; large customers (50,000+ users) need daily.JavaScript
Reconciliation as documentation
A useful side-effect of reconciliation: it produces an audit trail. Each reconciliation run records:- When it ran
- What it checked
- What gaps it found
- What corrective actions it took
- Whether the integration is operating correctly
JavaScript
When reconciliation finds too much
A successful reconciliation finds zero gaps. A few gaps per day is normal. A sudden spike in gaps means something is wrong with polling:| Pattern | Likely cause |
|---|---|
| Daily incremental finds 50%+ of records as gaps | Polling is broken — checkpoint not advancing, workers stalled, or polling cadence is too long |
| Weekly full finds 100s of “deleted” records | Either a real mass-deletion event, or polling missed creates (records exist in VOMO and externally but not in your partner state) |
| Sudden field-level drift on a specific field | Mapping logic is broken — recent code change may have regressed |
| One customer has high gap rate; others normal | Customer-specific issue — token, rate-limiting, or org-specific data problem |
Production checklist
For reconciliation:- Daily incremental reconciliation per resource per customer
- Weekly full reconciliation including deletion detection
- Three-way audits (sample-based) at least weekly
- Reconciliation is throttled — doesn’t compete with regular polling
- Per-customer cadence is configurable, not hardcoded
- Staggered scheduling avoids same-minute spikes
- Audit trail of every reconciliation run is preserved
- Per-customer “sync health” view exposes the data
- Alerts fire when gap rate exceeds threshold (e.g., >1% daily)
- When reconciliation finds too much, pause auto-correction and surface for review
Where to go next
Change Detection Best Practices
The cross-cutting reliability patterns: checkpointing, idempotency, drift detection.
Sync Architecture Patterns
The broader architectural picture for sync designs.
Detecting User Changes
The polling pattern that reconciliation complements.
Detecting Project Changes
Same for Projects, including the schedule-diff pattern.