> For the complete AppRefer docs index, see https://apprefer.com/llms.txt (every page as markdown) or https://apprefer.com/llms-full.txt (all docs in one file).

# Privacy and data handling

AppRefer tracks only with the user's permission, hashes personal data on the device, keeps test data separate and lets your server erase a user when they delete their account.

## Tracking consent

The SDKs never show the App Tracking Transparency prompt. From SDK 0.6.0 they read the status your app already has (on Android, the consent you pass with `setTrackingConsent()`) and send it with the install. Matching an install to an ad click, sending conversions to ad networks and keeping advanced-matching data happen only when the user allowed tracking. Without permission an install gets Apple AdServices or organic attribution, and analytics, AppRefer Billing and Support keep working. The server checks the stored consent again on every forward and retry.

> **Installs that sent no consent**
>
> Installs from SDKs older than 0.6.0 send no consent status, and AppRefer treats those as allowed so existing apps keep their attribution. To treat a missing status as “not allowed”, set `trackingConsentRequired: true` on the app with `PATCH /api/v1/apps/{appId}` once every live version of your app is on 0.6.0 or later (or if older installs must never be tracked). See [Tracking consent](https://apprefer.com/docs/attribution.md#tracking-consent).

Requests from installs that allowed tracking go to `trk.apprefer.com`; everything else goes to `apprefer.com`, and the two never fall back to each other. In your iOS privacy manifest, declare only `trk.apprefer.com` in `NSPrivacyTrackingDomains`. How undecided installs are held, and the `trackingConsentRequired` and `probabilisticMatching` app settings, are described under [Attribution](https://apprefer.com/docs/attribution.md#tracking-consent).

## Turning collection off

If your app has its own privacy switch, call `setCollectionEnabled(false)` (SDK 0.9.1+). The choice is saved on the device. It stops attribution, consent reports, conversion-value updates and product events, and clears events waiting to be sent. AppRefer Billing and Support keep working, and Billing stops sending the install's device ID. To resume, call `setCollectionEnabled(true)` and then `configure()`.

**Swift**

```swift
await AppRefer.setCollectionEnabled(false)
```

**Kotlin**

```kotlin
AppRefer.setCollectionEnabled(false)
```

**Dart**

```dart
await AppReferSDK.setCollectionEnabled(false);
```

**TypeScript**

```typescript
await AppRefer.setCollectionEnabled(false);
```

When a user signs out, call `reset()` (SDK 0.9.0+). It clears the user's ID, queued events and Support session on the device and keeps the install's own attribution. Nothing more is sent until you call `configure()` for the next user.

## On-device hashing

Personal data passed to `setAdvancedMatching()` is SHA-256 hashed on the device, so AppRefer never receives it in plain text. The SDK only sends it for installs that allowed tracking.

| Field | Example input | What is hashed |
| --- | --- | --- |
| Email | User@Example.com | Trimmed and lower-cased: user@example.com |
| Phone | +1 (415) 555-1234 | Digits only: 14155551234 |
| First name | John | Trimmed and lower-cased: john |
| Last name | Doe | Trimmed and lower-cased: doe |
| Date of birth | 19900115 | Trimmed; pass it as YYYYMMDD |

These hashes are attached to conversions for Meta and TikTok, which expect hashed values. Email, phone and name details that arrive with a RevenueCat webhook are hashed on the server in the same way, and are kept only for installs that allowed tracking.

## Data retention

| Data | Kept for | Purpose |
| --- | --- | --- |
| Clicks (including IP address) | 30 days | Matching clicks to installs |
| Attributions | 400 days | Install-to-campaign mapping, long enough to cover yearly renewals |
| Forwarding retry queue | 30 days | Retrying failed sends to ad networks |
| Webhook de-duplication | 30 days | Ignoring repeated RevenueCat events |
| Held trials | 30 days | Qualified trials |
| Qualified purchase results and store notices | 400 days | Qualified purchases (observe only); ids stored as one-way hashes |
| Qualified purchase activity record | Up to 45 days | Counted SDK event ids and times per install, only for event names a rule counts; deleted when the user is erased |
| SKAdNetwork postbacks | 90 days | Postback validation |
| Audit log | 365 days | Who changed what |
| Billing events and store links | 400 days | Purchase idempotency and linking old app versions |
| Support sessions | 400 days after last use | In-app chat identity |
| Support screenshots | 400 days (unsent uploads: 1 day) | Attachments in conversations |

Records expire automatically. Aggregated analytics (installs, events, revenue) are kept in the reporting warehouse for long-term dashboards.

## Test data stays separate

Data sent with a test key (`pk_test_`) and store sandbox purchases are kept apart from live data:

-   They are never sent to ad networks.
-   They don't appear in live dashboard views or revenue; use the **Sandbox** switch to see them.
-   Installs are matched separately from live installs.
-   Support conversations from test keys sit in a separate test inbox.

App Review buys with sandbox accounts in your release build, so a sandbox purchase sent with your live key still unlocks the purchase (AppRefer Billing) but is recorded as sandbox and never counted or forwarded.

## SDK dependencies

No AppRefer SDK bundles another analytics or advertising SDK. The iOS SDK has no third-party dependencies. The others use only standard platform libraries:

-   **Android:** Kotlin coroutines, Google Play Install Referrer and the Google Play services advertising ID library.
-   **Flutter:** `shared_preferences`, `http`, `crypto`, `device_info_plus`, `package_info_plus`, `intl` and the official `in_app_purchase` plugins.
-   **React Native:** AsyncStorage and `react-native-iap` as peer dependencies.

## Erasing a user (account deletion)

When someone deletes their account in your app, your server calls `POST /api/v1/apps/{appId}/users/erase` with an organisation API key that has the `users:erase` scope.

```bash
curl -X POST https://apprefer.com/api/v1/apps/APP_ID/users/erase \
  -H "Authorization: Bearer ak_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "requestId": "del_8f3a2c91", "appUserId": "user_123" }'
```

| Field | Required | Description |
| --- | --- | --- |
| `requestId` | Yes | Your deletion job ID, 8 to 64 letters, digits, \_ or -. Retrying with it is safe. |
| `appUserId` | Yes | The user ID you set with setUserId and use for purchases. |
| `deviceIds` | No | Up to 20 device IDs. Each counts only if AppRefer already links it to this user. |
| `environment` | No | `all` (default), `live` or `sandbox` |

AppRefer finds the user's devices itself, from `setUserId` and their events. It removes their attribution records, queued conversions, held trials, support conversations, the support context your backend sent and AppRefer Billing identity straight away and answers 202. Rows in the reporting warehouse follow within a few hours, once recent rows can be changed. Check progress with `GET /api/v1/apps/{appId}/users/erase/{requestId}`.

-   **Analytics events** become anonymous counts: no user, device or session ID, no properties, and the time cut to the day, so they no longer link to each other or to anyone.
-   **Accounting records** (payments, refunds, renewals, trial starts and the billing ledger) keep their amounts, dates and products, with the user replaced by a keyed one-way stand-in. They are never listed in the dashboard or API, and are deleted seven years after their date, which covers the six years after the end of the financial year that tax record-keeping rules require.
-   App settings, links and integrations are never touched. Conversions already sent to ad networks can't be recalled.
-   Reusing a `requestId` for a different user returns 409. 30 days after the job finished AppRefer forgets the request and returns 409 `REQUEST_ID_EXPIRED`.

### Live and sandbox data

By default one call erases the user's live and sandbox data together. Send `"environment": "sandbox"` to erase only test data (installs and support chats made with a test key, App Store and Google Play sandbox purchases), for example to clear your testers before launch, or `"live"` for the reverse. The value is returned on the job; anything else is a 400, and retrying a `requestId` with a different environment is a 409.

A sandbox-only erase never touches live data, so it keeps the records AppRefer holds once per user for both sides (the Billing customer record, account tokens and store links). Use the default if you want those gone too.

> **Account deletion must use the default**
>
> In-app account deletion must use `all`. `live` leaves the user's sandbox purchases, test installs and test-key chats tied to them. Use a separate `requestId` per environment (for example an `sb_` prefix for tester clean-ups): a `requestId` used once with `sandbox` can't later be reused for the full deletion.

> **Your data, secured**
>
> Attribution data and ad network credentials are encrypted in transit and at rest, and integration secrets are never shown again after you save them. Your data is only sent to the ad networks and services you connect.

---

Source: https://apprefer.com/docs/privacy · Last updated: 2026-10-27
