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:
- Load the current resources and versions at startup.
- Subscribe to the event groups you need and complete explicit verification.
- Verify the raw-body signature and timestamp before parsing.
- Deduplicate by
Webhook-Idinside your durable processing transaction. - Compare
data.resourceVersionwith the stored version forresourceId. - Ignore an event whose version is not newer.
- Fetch
data.hrefwith your API key and selected team when it is newer. - Replace local state and version from the current response.
- If a
deletedtombstone returns404, remove local state at that version. - 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
- Page through
GET /teams/issuesat startup. - Store the complete current open issue set and
snapshotVersionper team. - Create a webhook subscription for
team.issues.changedthroughPOST /webhooks, then complete explicit verification. - Verify every delivery against the exact raw body and timestamp.
- Check
Webhook-Idagainst processed deliveries before handling; record it as processed only after your transaction commits. - Compare the event's
snapshotVersionwith the stored team version. - Ignore an event whose version is not newer.
- Fetch that team from
GET /teams/issueswhen it is newer. - Replace the team's complete issue set with the response.
- Remove locally stored codes absent from the new snapshot.
- Inspect durable failed deliveries through
GET /webhook-deliveries, then replay one throughPOST /webhook-deliveries/{id}/replaywhen your receiver recovers. - 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.
