Skip to Content
ConceptsOffline & Persistence

Offline & Persistence

Tracked events and survey responses are persisted locally before being sent, and marked synced once the network request succeeds — so a page reload or a dropped connection doesn’t lose data mid-flight. flush() (web only — see Platform Parity) forces a send of whatever’s currently pending, ahead of the periodic timer.

Web’s storage fallback

Web persists to IndexedDB. If IndexedDB is unavailable (private browsing in some browsers, storage quota exceeded, etc.), it falls back to localStorage transparently — isReady() returns true either way, and isFallbackMode() tells you which one you’re in if you need to know. Callers don’t need to branch on this; both modes support the same add/get/mark-synced operations.

Locally-stored session and cached data are obfuscated with a simple XOR cipher keyed on the API key before being written to localStorage. This is not encryption — the API key itself is public (it ships in your client-side bundle), so this only deters casual inspection via browser DevTools, not a real confidentiality guarantee. Don’t rely on it to protect sensitive data.

Event history retention

user.eventHistory (used by EVENT_FREQUENCY/EVENT_RECENCY/EVENT_ABSENCE trigger rules — see How Triggering Works) is capped: 90-day retention, 100 timestamps per event name, 50 distinct event names tracked. Once a given event’s timestamp list exceeds its cap, older entries are dropped and the entry is marked truncated — a windowed count against a truncated history becomes a lower bound, which is sound for “greater than N” checks and unsound for “less than N” or “exactly N” ones. This is a known, accepted limit of bounded local storage, not a bug to work around.