VRPlatformVRPlatform
Run in Production

Data Synchronization

Reconcile versioned API resources from at-least-once webhooks

Webhook bodies tell you what changed and in which source version. The authenticated API remains the source of truth. For team issues, use GET /teams/issues for current state; team.issues.changed only tells you that state changed.

Resource Algorithm

For connection, sync, reservation, listing, statement, and transaction events:

  1. Load the current resources and versions at startup.
  2. Subscribe to the event groups you need and complete explicit verification.
  3. Verify the raw-body signature and timestamp before parsing.
  4. Deduplicate by Webhook-Id inside your durable processing transaction.
  5. Compare data.resourceVersion with the stored version for resourceId.
  6. Ignore an event whose version is not newer.
  7. Fetch data.href with your API key and selected team when it is newer.
  8. Replace local state and version from the current response.
  9. If a deleted tombstone returns 404, remove local state at that version.
  10. Reconcile periodically and after receiver downtime.

Events are globally unordered. Compare versions independently for each resource. Do not apply event bodies as resource patches.

Team Issue Snapshot Algorithm

  1. Page through GET /teams/issues at startup.
  2. Store the complete current open issue set and snapshotVersion per team.
  3. Create a webhook subscription for team.issues.changed through POST /webhooks, then complete explicit verification.
  4. Verify every delivery against the exact raw body and timestamp.
  5. Check Webhook-Id against processed deliveries before handling; record it as processed only after your transaction commits.
  6. Compare the event's snapshotVersion with the stored team version.
  7. Ignore an event whose version is not newer.
  8. Fetch that team from GET /teams/issues when it is newer.
  9. Replace the team's complete issue set with the response.
  10. Remove locally stored codes absent from the new snapshot.
  11. Inspect durable failed deliveries through GET /webhook-deliveries, then replay one through POST /webhook-deliveries/{id}/replay when your receiver recovers.
  12. Periodically reconcile snapshots even when no webhook arrived, e.g. every 6–24 hours, and always after receiver downtime.

Webhooks are at least once and can be duplicated or reordered. Comparing versions and refetching the full snapshot makes those delivery properties safe.

Pseudocode

async function handleIssueEvent(event: IssueChangedEvent) {
  const current = versions.get(event.teamId) ?? -1;
  if (event.data.snapshotVersion <= current) return;

  const snapshot = await fetchTeamIssues(event.teamId);
  issuesByTeam.set(event.teamId, snapshot.issues);
  versions.set(event.teamId, snapshot.snapshotVersion);
}

The example omits signature verification, durable deduplication, and transaction boundaries for clarity. Implement those before calling the handler.

Resolution Is Absence

The webhook's changedCodes can contain opened, changed, or resolved codes. It is not a patch. When a code resolves, it is absent from the next open-state snapshot. Replace local state instead of merging only codes in the event.

Startup and Recovery

If the receiver was offline, fetch snapshots first, then inspect failed deliveries. Replaying an old delivery is safe because it preserves the same webhook delivery ID and version checks ignore stale state.

See Portfolio health, Webhooks, and the generated Issue Catalog.

On this page