Privacy & data handling

What leaves the device, and what we never see

This page describes behaviour you can verify: the SDK is open source, the wire schema is published, and the list of things it will not collect is normative rather than aspirational.

COLLECTED
  • Event name, millisecond timestamp, sequence number
  • Flat event properties you choose to send
  • Session id, scoped to one install
  • Install id: a random UUID, salted and SHA-256 hashed on device
  • App id (bundle id), app version and build number, SDK version
  • OS name and version, device model, CPU architecture
  • Locale and the device's region setting (not derived from IP)
  • Whether the build is a debug build
  • Screen size and scale, light or dark mode, and whether it is a TestFlight install — real values only if your app supplies them; otherwise placeholder values (no screen, no colour scheme, not TestFlight) are sent
  • A hashed user id — only if your app calls identify(), hashed on the device with your salt
NEVER COLLECTED
  • IP addresses — not stored with any event
  • IDFA or any advertising identifier
  • IDFV, Keychain values, device serial
  • Location, of any precision
  • Free text, screenshots, session replay
  • Email, name or account identifier, unless you deliberately call identify(), which hashes it first
  • Anything that links one app to another, or one device to another — unless your app calls identify() with the same account id on several devices, which links them by design

Why the install id is not a person

The id starts as a random UUID generated on the device, is hashed with a salt you choose and commit with your app, and never leaves in raw form. It is scoped to one app: the same device running two apps produces two unrelated ids, and no backend can join them. Deleting the app deletes it. It counts installs, which is why the dashboard says "installs, not people" on the tile rather than letting you round it up to users.

The identity consent group is what keeps it. It is in the SDK's default since 0.3.0; withhold it (an app passes [.usage, .diagnostics], as the 0.2.0 default did) and the id becomes per-session instead of persistent: every session then counts as a new install, and retention numbers are gone.

No cookies, no banner code

The SDK sets no cookies, uses no web view, and sends no IP address to be retained. There is nothing for a consent banner to describe, and no ATT prompt: NSPrivacyTracking is false and there are no tracking domains.

What your own app must disclose is still yours to determine — declare Product Interaction, Other Diagnostic Data and, with identity consent on (the default since 0.3.0), Device ID, all not linked. The SDK's own privacy manifest declares those three; your nutrition label must match. Call identify() and you add User ID, and every category becomes linked to the user. This is a description of the SDK's behaviour, not legal advice.

Consent, and what it costs

usage — events and sessions. Denied: nothing is captured.
diagnostics — OS version, device model, locale, region, screen, light or dark mode. Denied: documented fallbacks are sent instead.
identity — a persistent install id, and the hashed user id from identify(). Granted by default since 0.3.0 (not on 0.2.0). Denied: per-session id, so installs count sessions, retention becomes unavailable and the dashboard says so on the tile.

Retention and access

Raw events
At least 90 days — the default and the shortest window, set per project up to 400 — then deleted by a nightly job. One exception: each hashed install id and the first day it was seen are kept until the app is deleted, so new-versus-returning stays countable after the raw rows are gone.
Daily rollups
Kept indefinitely — counts per day, per event, per property. No individual events remain behind them.
Your account
Email address, for magic-link sign-in. No password to leak. Sign-in throttling keys are HMACs, so no IP or address is stored in the clear, and a sign-in session records neither your IP address nor your browser's user agent.