SOFTWARE / SYSTEMS / AIEngineering news. Technical depth.
Architecture / 4 MIN READ

What should a sync API do when a client returns with an expired change token?

When a client presents an expired change token, return an explicit reset response and require a fresh snapshot instead of guessing at missed changes.

A sync API should not guess its way forward when a client presents an expired change token. Return an explicit reset response, tell the client its token is no longer usable, and require a fresh snapshot before more deltas. Keep the contract simple: initial snapshot to establish baseline state, incremental sync for changes since the last token, tombstones for deletes, and a reset flow that lets the client preserve unsent local edits while rebuilding its cache.

What should the server return when the token is expired?

Return a distinct, documented failure that means “your local sync cursor is too old; start over.” The Google Calendar sync guide documents this directly: when a sync token is invalid or expired, the server responds with HTTP 410 Gone, and the client should clear local stored sync state and perform a new full sync.

For stateful offline sync APIs, that is a good default. Do not silently fall back to approximate diffs, and do not reinterpret the old token as pagination state. A delta token represents a server snapshot boundary, not a page number. The Microsoft Graph delta query overview is explicit that state tokens are opaque, represent snapshot state, encode prior query parameters, and should be replayed rather than modified by the client.

A practical response shape is:

  • 410 Gone or an equivalent application error
  • machine-readable code such as SYNC_TOKEN_EXPIRED
  • optional hint like reset_required: true
  • the canonical endpoint for a fresh snapshot

How is delta sync different from ordinary pagination?

Treat them as separate concerns.

Pagination is about splitting one result set into pages. Delta sync is about asking for changes since a previous server state. The Google Calendar sync guide and Microsoft Graph delta query overview both distinguish between paging within a sync session and the state token used to continue change tracking later.

In the usual full-sync flow, pagination continues within the current session until the server returns the final sync or delta token for that run. That final token is then used for the next incremental request. Microsoft Graph also documents some resource-specific “sync from now” variants, so this sequence should be described as the normal full-sync path rather than an absolute rule for every implementation.

That means your API should support this flow:

  1. Initial snapshot: list current items, possibly across several pages.
  2. Checkpoint issuance: only after the snapshot is complete, return the next change token.
  3. Incremental sync: return creates, updates, and deletions since that token.
  4. Incremental pagination: if too many changes exist, page through them, then emit a replacement token on the last page.

Require a stable query shape for a sync lineage. The Google Calendar sync guide says each list request in a sync sequence should use the same set of query parameters, including the initial request, while also noting that incremental sync supports only a restricted set of parameters. The Microsoft Graph delta query overview says state tokens encode the initial query parameters, so clients should use the returned continuation URLs instead of trying to restate or vary those parameters themselves.

How should deletes and resets work in practice?

Always include deletion markers in incremental responses. The Google Calendar sync guide states that incremental results always include deleted entries so clients can remove them locally. Without tombstones, an offline client can never converge after missing a delete.

A workable reset protocol looks like this:

  • Client keeps server mirror and pending local mutations separately.
  • Sync request with expired token returns 410.
  • Client preserves pending local mutations, discards only the stale server mirror and token.
  • Client performs a fresh snapshot.
  • Client rebuilds the mirror from snapshot pages.
  • Client reapplies pending local mutations through normal conflict rules.
  • Client stores the new change token.

Worked example: a field app cached tasks on Monday, went offline for 40 days, and your server retains deltas for 30. On reconnect, /tasks/sync?token=abc returns 410 SYNC_TOKEN_EXPIRED. The app keeps two unsent edits created offline, wipes its mirrored task list, downloads the latest snapshot, then replays those two edits against current server versions. That avoids data loss while still respecting the expired cursor.

When does this advice not apply?

This contract is for stateful offline sync, not for ordinary read APIs. If clients only need recent pages for browsing, use pagination alone. If your system cannot retain tombstones or issue opaque snapshot tokens, offer a simpler export/import or last-modified strategy and document its weaker guarantees.

Follow-up Q: Should the server auto-start a full sync instead of returning 410?
Usually no. Large snapshots have different cost and timeout behavior. Make reset explicit so clients can switch code paths safely.

Follow-up Q: Can the client recover from an expired token without clearing everything?
Only if the server offers a documented recovery cursor or per-record reconciliation API. Otherwise, require a full resync.

Next step: document one canonical reset response, one tombstone format, and one client algorithm that separates mirrored server state from pending local edits.

Reviewed: 2026-09-06.

SOURCES & REVIEW

Sources are linked throughout this guide. Product capabilities can change; consult the linked documentation for your deployment.

Read our editorial approach ↗