Device-ID tracking is no longer a matter of reading one value at app launch and attaching it to every event. A production measurement system must select identifiers by purpose, respect platform permission and reset states, separate account identity from device signals, and continue operating when no advertising ID exists.
This implementation guide focuses on architecture: consent state, event contracts, data storage, deterministic and aggregate attribution, explicit unmatched states, deletion, and testing. For platform retrieval examples, see How to Find a Device ID on iOS and Android.
Start with a purpose matrix
Inventory every reason your app wants an identifier before integrating an SDK.
| Purpose | Preferred signal | Avoid |
|---|---|---|
| Signed-in product experience | First-party account ID | Treating a device as a person |
| Anonymous app analytics | Installation ID, IDFV, FID, or App Set ID as appropriate | Advertising ID by default |
| Advertising measurement | IDFA after ATT authorization; Google Advertising ID when available | Hardware IDs or reset bridging |
| Deep-link routing | Signed link parameters and server-side state | Putting personal data in a URL |
| Fraud or app integrity | App Attest, DeviceCheck, or Play Integrity as appropriate | A permanent fingerprint presented as identity |
| Customer support | App-generated diagnostic ID | Advertising IDs in tickets or logs |
For each row, record owner, platform, lawful basis or consent requirement, disclosure, retention, processors, and deletion behavior. This is an engineering specification, not a blanket claim of legal compliance. Requirements differ by product, data flow, and jurisdiction; involve qualified privacy counsel where needed.
Model state explicitly
A boolean tracking_enabled cannot represent modern platform behavior. Use a state machine.
iOS ATT state
Apple exposes four states through AppTrackingTransparency:
notDeterminedrestricteddeniedauthorized
Only the authorized path should enable IDFA access and tracking that falls under Apple’s definition. Apple states that without permission the IDFA is all zeros and developers may not use a different identifier, including a hashed email or phone number, to continue tracking across other companies’ apps or websites.
Android advertising state
Represent at least:
- Advertising ID available
- User limitation signal enabled where exposed
- Advertising ID deleted or zeroed
AD_IDpermission absent for a target that requires it- Google Play services unavailable
- API error or timeout
These are expected product states, not exceptional crashes. Google Play policy says the advertising ID should replace other device identifiers for advertising when it is available, and reset or deletion choices must be respected.
Product privacy state
Platform permission is only one input. Your application may also need a regional consent or opt-out state, age treatment, account deletion state, or customer contract restriction. Compute an effective policy from all applicable inputs and default to the less permissive behavior when signals conflict.
Separate identity layers
Use different keys for different jobs:
account_id signed-in first-party account
installation_id one installed app instance
vendor_or_set_id permitted analytics across your own apps
advertising_id platform advertising identifier, when permitted
session_id one app session
event_id idempotency key for one eventNever use an advertising ID as the account primary key. Never assume an installation ID survives reinstall. Never silently merge a new advertising ID with an old profile after reset.
An identity graph should store relationships with purpose, source, first-seen time, last-seen time, and authorization state. When permission changes, revoke the relationship for future processing and follow the deletion or suppression rules your policy requires.
Implement collection in phases
Phase 1: initialize essential app services
At cold start, generate or retrieve the installation ID, create a session ID, and initialize only services allowed before consent. Queue essential operational events locally if needed, but do not initialize an advertising SDK early merely because it is convenient.
Phase 2: resolve policy
Load regional/product privacy state and read the platform authorization status. Decide which SDK modules and fields are allowed. Keep this decision in one policy service rather than duplicating checks across screens.
type MeasurementMode =
| "essential_only"
| "first_party_analytics"
| "ads_measurement";
type MeasurementPolicy = {
mode: MeasurementMode;
allowAdvertisingId: boolean;
retentionDays: number;
};The exact policy must come from your real data practices. A type definition does not establish permission by itself.
Phase 3: request permission in context
On iOS, add a clear NSUserTrackingUsageDescription and request ATT only when the person can understand the purpose. Do not block unrelated functionality or manipulate the prompt. On Android, provide disclosures and controls required for your data use and treat a deleted advertising ID as unavailable.
Phase 4: start permitted modules
Initialize analytics, attribution, ads, and personalization modules according to the resolved policy. Re-resolve on app foreground because settings can change outside the app.
Phase 5: handle revocation
When a user opts out or deletes an account:
- Stop prohibited future collection.
- Clear local identifiers and queued payloads covered by the request.
- Rotate the installation or diagnostic ID where appropriate.
- Send deletion or suppression requests to processors.
- Record a non-identifying audit outcome.
Do not keep a hidden mapping whose purpose is to reverse the reset.
Design an auditable event contract
Every event should state what happened without smuggling identity into generic properties.
{
"event_id": "evt_01J...",
"event_name": "checkout_completed",
"event_version": 3,
"occurred_at": "2026-05-07T09:30:00Z",
"platform": "ios",
"app_version": "8.4.1",
"installation_id": "ins_...",
"account_id": "acct_...",
"measurement_mode": "first_party_analytics",
"attribution": {
"source": "owned_email",
"method": "signed_link",
"confidence": 1.0
},
"properties": {
"currency": "USD",
"net_revenue": 24.0
}
}Omit fields the current policy does not allow. Do not send null, zeros, or a placeholder value that downstream systems might interpret as a real ID. Version event schemas and validate at ingestion.
Recommended attribution fields include:
method: deterministic link, store referrer, platform aggregate, or unattributedmatched: an explicit boolean so missing attribution is not silently guessedtouch_timestampattribution_windowcampaign_idandcreative_idprovideris_reengagement
This makes uncertainty queryable instead of hiding it inside one campaign column.
Implement deep-link attribution without overclaiming
Installed-app path
Use verified Universal Links and Android App Links. Sign sensitive campaign state or store it server-side behind an opaque token. Validate the destination and route only to an allowlisted screen.
This can deterministically prove that a particular link opened the app when the link and event carry the same signed interaction ID. It does not prove the link caused every later purchase.
Android install path
For eligible Google Play installs, the Play Install Referrer API returns referrer content and click/install timestamps. Read it once through the supported client, send the raw value over a protected connection, parse it server-side, and enforce idempotency. Treat missing, duplicate, delayed, and malformed responses as normal test cases.
iOS privacy-preserving attribution
Use Apple-supported privacy-preserving frameworks and App Store Connect analytics for aggregate campaign measurement where applicable. Do not build a substitute stable identifier from device characteristics. Apple states that developers may not derive device data for the purpose of uniquely identifying it.
Leave unsupported installs unattributed
Some attribution systems infer a click-to-install match from request and timing signals when no deterministic link or platform signal survives. That inference is not a device ID and not proof of identity. Deeplinkly does not use this method: an unsupported install remains unattributed.
- Return the attribution method with every supported match.
- Keep an explicit unmatched state.
- Keep aggregate platform results separate from user-level attribution.
- Never build a durable cross-app profile from device characteristics.
- Validate aggregate lift with holdouts or incrementality tests.
On Apple platforms, do not use a different identifier or device fingerprint to bypass ATT. Review the exact SDK behavior and current platform terms before adding any attribution provider.
Store less and protect what remains
Identifiers are linkable data. Apply the same controls as other sensitive analytics data:
- TLS in transit and managed encryption at rest
- Restricted service accounts and role-based access
- Redaction in logs, crash reports, screenshots, and support tools
- Separate raw identity tables from general analytics access
- Retention by purpose, not “forever”
- Documented deletion and suppression propagation
- Vendor contracts and SDK inventory
- Detection for unexpected new fields or destinations
Tokenization can reduce casual exposure. Hashing alone does not make a stable identifier anonymous because the hash remains linkable and may be reproducible.
Reconcile without forcing equality
Ad networks, stores, product analytics, billing, and an MMP observe different events. Build a reconciliation table rather than rewriting one system to equal another.
| System | What it can establish | Typical gap |
|---|---|---|
| Ad network | Eligible impressions, clicks, and network-attributed outcomes | Self-attribution rules and reporting delays |
| App store | Product-page views, downloads, source categories, aggregate campaign data | Privacy thresholds and store-specific definitions |
| App telemetry | Opens and in-app events received by your service | Offline use, consent, SDK loss, and reinstall |
| Billing | Purchases, renewals, refunds, and proceeds | Account/device mapping and settlement delay |
| Attribution provider | Matches under its configured methods and windows | Unmatched and aggregate cases |
Track deltas by day, platform, version, country, method, and authorization state. Investigate sudden changes; do not promise 100% attribution accuracy.
Test the lifecycle, not just the happy path
Create automated and manual test cases for:
iOS
- ATT not determined, restricted, denied, and authorized
- First launch before and after device unlock
- IDFV available and temporarily
nil - All vendor apps removed and one reinstalled
- Universal Link installed and not-installed paths
- SDK initialization before consent is blocked
- Permission changed in Settings between sessions
Android
- Advertising ID available, reset, and deleted
- App targets Android 13+ with and without merged
AD_IDpermission - Google Play services missing, outdated, or returning an error
- App Set ID app scope and developer scope
- App Set ID reset conditions
ANDROID_IDchange after factory reset or signing-key change- Play Install Referrer success, timeout, duplicate read, and malformed value
Backend
- Duplicate event IDs
- Out-of-order events
- Deleted or revoked identifiers
- Late attribution updates
- Confidence threshold changes
- Data export and deletion completion
- One processor failing during deletion propagation
Monitor null rate, zero-ID rate, consent-state distribution, events by SDK version, match method, confidence distribution, referrer errors, duplicate rate, and deletion latency. Alert on changes by app version so a release regression is visible quickly.
Rollout plan
- Inventory: map SDKs, identifiers, destinations, retention, and owners.
- Minimize: remove fields and SDK modules without an approved purpose.
- Centralize policy: build one state resolver used by every collector.
- Version schemas: separate identity and attribution method explicitly.
- Shadow test: compare old and new pipelines without using new data for decisions.
- Canary release: start with a small app-version cohort and watch missing/duplicate rates.
- Reconcile: document expected differences between store, network, MMP, and billing.
- Delete-test: prove a request reaches internal stores, backups according to policy, and processors.
- Review: repeat after every SDK, platform, or data-use change.
Implementation checklist
- Every identifier has one documented purpose and owner.
- Advertising modules remain off until the effective policy permits them.
- Unavailable identifiers are omitted, not replaced with hidden fallbacks.
- Account, installation, vendor/set, advertising, session, and event IDs are separate.
- Advertising-ID resets are not bridged.
- Unsupported installs remain explicitly unattributed and are never guessed from device characteristics.
- URLs contain opaque or signed state, not personal data.
- Raw identifiers are redacted from logs and support systems.
- Retention and deletion behavior are tested end to end.
- Dashboards expose unmatched traffic and measurement uncertainty.
A durable mobile measurement system is not the one that captures the most identifiers. It is the one that produces useful evidence while accurately representing scope, consent, reset behavior, and uncertainty.
Primary sources
- Apple: User privacy and data use
- Apple: AppTrackingTransparency
- Apple: App privacy details
- Android: Best practices for unique identifiers
- Android: App Set ID
- Google Play: Advertising ID policy
- Google Play: User Data policy
- Google Play: Install Referrer API