Delta synchronization
Why delta exists
Most integrations with a workforce management system are ongoing: an external payroll engine, an HR portal, or a planning dashboard needs to stay in sync with Protime data day after day. The naive approach – re-fetching every record in a collection on each run – wastes bandwidth, increases latency, and puts unnecessary load on both systems. The problem grows proportionally with the size of the workforce and the frequency of synchronization.
Delta synchronization solves this by letting the API remember where a consumer left off in the stream of changes, so subsequent requests return only the records that were created, updated, or deleted since the last call.
How change tracking works
The delta mechanism is built around a cursor that the API maintains on behalf of each consumer for each collection. The lifecycle has two phases:
Phase 1 – Initial load
When an integrator first adds the delta query parameter to a collection GET request, the API returns all records that match the current filters. Because this dataset can be large, the response is paginated: each page contains a nextLink until the final page, which instead contains a deltaLink. The deltaLink encodes an opaque cursor that marks “everything up to this point has been delivered.”
Phase 2 – Incremental updates
Subsequent requests use the deltaLink (routed through the /delta/ path). The API returns only the changes that occurred since the cursor was created. Unlike the initial load, delta responses are returned in a single, non-paginated page. Each response includes a fresh deltaLink for the next call.
flowchart TD
A["GET /collection?delta"] -->|"paginated response"| B["Follow nextLink pages"]
B -->|"last page contains deltaLink"| C["Store deltaLink"]
C --> D["GET /delta/collection?deltaToken=..."]
D -->|"single-page response<br/>with changes"| E["Process InsertOrUpdate<br/>/ Delete"]
E -->|"store new deltaLink"| C
Change types
Every item in a delta response carries a changeType field:
| changeType | Meaning |
|---|---|
| InsertOrUpdate | The record was created or modified. The full current state of the record is included in data. |
| Delete | The record was removed. Only the identifier and changeVersion are present in data. |
The API deliberately collapses “insert” and “update” into a single type because the consumer’s correct action is the same in both cases: store or overwrite the record.
Ordering and deduplication with changeVersion
Each record in a delta response includes a changeVersion string. This value is string-comparable – a lexicographic comparison determines which version is newer. The changeVersion serves two purposes:
- Ordering – if a consumer receives the same record more than once (for example, because of a retry or overlapping requests), it can compare
changeVersionvalues and discard the older copy. - Conflict resolution – when the same record appears in both a delta response and a webhook event, the higher
changeVersionwins.
If a newly received changeVersion is lexicographically less than or equal to the one already stored for the same record, the incoming change can safely be ignored.
Expiration
A delta cursor expires 72 hours after it was last used. The consumer must call the deltaLink at least once within every 72-hour window to keep the cursor alive – even if no changes are expected and the response is empty.
If the cursor expires, the API responds with 410 Gone. At that point the consumer must restart from Phase 1 (full initial load) to re-establish a cursor.
One delta per collection per consumer
The API maintains exactly one active delta cursor per collection per set of OAuth2 client credentials. Starting a new delta on the same collection implicitly replaces the previous cursor. This design prevents orphaned cursors from accumulating and ensures that the consumer always has a single, unambiguous synchronization position.
Comparison with webhooks
Delta and webhooks address the same fundamental need – staying in sync – but with different trade-offs:
| Aspect | Delta | Webhooks |
|---|---|---|
| Initiation | Consumer polls on its own schedule | API pushes events in near-real-time |
| Ordering guarantee | No guarantee; deduplicate with changeVersion |
Events may arrive out of order |
| Infrastructure | No inbound endpoint required | Consumer must expose an HTTPS endpoint |
| Recovery | Re-fetch from last cursor | Must reinitialize after expiry/disable |
Many integrations use both: webhooks for low-latency awareness and delta as a periodic consistency check or fallback.
Further reading
- Track changes with delta – step-by-step instructions for setting up and maintaining a delta flow
- Delta reference – properties, token format, and expiration rules
- Webhook lifecycle – the push-based alternative to delta polling