> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appeeky.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Attribution

> Connect Appeeky Analytics so Appeeky Spec knows where each install came from and what it paid, and judges listing changes and experiments on installs and revenue.

Ranks show whether a listing change moved you in search. Attribution shows whether it brought installs that pay. With [Appeeky Analytics](https://docs.appeeky.com/docs/analytics-overview.md) in your app, Appeeky Spec knows where each install came from (organic store search, an Apple Search Ads keyword, a smart link, the Google Play referrer) and the trials and revenue it led to.

You can use Appeeky's hosted service, or run the Appeeky Analytics server yourself and keep the data in your own account.

| | Hosted | Self-hosted |
| - | - | - |
| Set up with | `attribution init` | `attribution init --self <url>` |
| Key | `APPEEKY_API_KEY` | `ATTRIBUTION_TOKEN` (your server's `ADMIN_TOKEN`) |
| Purchases from | RevenueCat connected in Appeeky | A RevenueCat webhook to your server |
| Smart links | `https://ingest.appeeky.com/l/<slug>` | `https://<your server>/l/<slug>` |

## Set it up: hosted

The spec needs the App Store app id or the Google Play package under `stores:` first.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
export APPEEKY_API_KEY=…
npx @appeeky/spec attribution init
# Created App Store app 1234567890 · key apk_pub_…
# Created Google Play app com.example.sleep · key apk_pub_…
# Wrote attribution: to appeeky.spec.yaml.
#
# In the app (React Native and Expo; Swift, Kotlin and Flutter work the same way):
#
#   Appeeky.init({ key: Platform.OS === "ios" ? "apk_pub_…" : "apk_pub_…" });
#   Appeeky.identify(await Purchases.getAppUserID());
#
# Connect RevenueCat in Appeeky if it is not connected: purchases reach attribution from there.
```

It turns attribution on for each store's app in Appeeky. Running it again is safe. The publishable key (`apk_pub_…`) goes into the app; it can only write events for that one app. Never put your Appeeky API key in an app.

## Set it up: self-hosted

First run your own server ([Self-hosting Appeeky Analytics](https://docs.appeeky.com/docs/analytics-self-hosting.md)). Then:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
export ATTRIBUTION_TOKEN=…        # the server's ADMIN_TOKEN
npx @appeeky/spec attribution init --self https://analytics.sleepsounds.workers.dev
# Created App Store app app_ios123 · key apk_pub_…
# Created Google Play app app_and456 · key apk_pub_…
# Wrote attribution: to appeeky.spec.yaml.
#
# In the app (React Native and Expo; Swift, Kotlin and Flutter work the same way):
#
#   Appeeky.init({ key: Platform.OS === "ios" ? "apk_pub_…" : "apk_pub_…", endpoint: "https://analytics.sleepsounds.workers.dev/v1/ingest" });
#   Appeeky.identify(await Purchases.getAppUserID());
#
# In RevenueCat, Integrations → Webhooks:
#   URL: https://analytics.sleepsounds.workers.dev/v1/webhooks/revenuecat
#   Authorization header: your REVENUECAT_WEBHOOK_SECRET
#   Environment: production
```

It finds each store's app on your server by its store id, or creates it (`Found` instead of `Created` when it was there). Keep `ATTRIBUTION_TOKEN` in your shell, your CI secrets or the [Docker `.env`](https://docs.appeeky.com/docs/spec-self-hosting), never in the repo.

## In the app

Add the SDK, then the two lines `init` printed: `Appeeky.init` with the key, and `Appeeky.identify` with the RevenueCat user id, so each purchase is matched to the install it came from. See the [Analytics quickstart](https://docs.appeeky.com/docs/analytics-quickstart.md) for React Native and Expo, and the [Swift](https://docs.appeeky.com/docs/analytics-sdk-swift.md), [Kotlin](https://docs.appeeky.com/docs/analytics-sdk-android.md) and [Flutter](https://docs.appeeky.com/docs/analytics-sdk-flutter.md) SDKs.

## In the spec

`init` writes the `attribution:` block. An app in Appeeky Analytics is one platform, so an app on both stores gets one id per store:

```yaml theme={"theme":{"light":"github-light","dark":"github-dark"}}
attribution:
  via: self                                     # or hosted
  url: https://analytics.sleepsounds.workers.dev   # self-hosted only
  app:
    app-store: app_ios123
    google-play: app_and456
```

With one store, `app` is a single id. Each release is measured with its own store's app. Run `doctor` to check that attribution is set up.

## What it feeds

**`verify`.** A listing change is meant to bring more people from store search. With attribution, `verify` compares organic installs per day (`organic_installs`), the revenue they brought (`organic_revenue`) and the share that paid (`organic_paid`) before and after each push. When no target moved, these decide the release before store conversion or downloads do. Revenue counts what those installs have paid so far. Older installs have had longer to pay, so a rise after a push is a conservative reading. See [Releases](https://docs.appeeky.com/docs/spec-releases.md#when-no-target-moved).

**`data pull`.** It adds these files, and works with self-hosted attribution alone, without an Appeeky key:

| File | What it holds |
| - | - |
| `attribution-sources.csv` | Installs, trials, payers and revenue by source and campaign |
| `attribution-keywords.csv` | Apple Search Ads keyword ids and the revenue they led to (the words are in your Search Ads account) |
| `attribution-campaigns.csv`, `attribution-countries.csv` | Installs, payers and revenue by campaign and by country, last 30 days |
| `smart-links.csv` | Clicks, installs and revenue per smart link, last 30 days |

With one app per store, each file gets the store in its name.

**Experiments.** Organic installs can be an experiment's metric too, for any change that is not a listing push.

## Smart links

A smart link is one URL that sends each visitor to the right store, with its clicks, installs and revenue counted to it. Create one for an experiment with `--link`, and optionally name the channel:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
npx @appeeky/spec experiment add --type ugc --link tiktok \
  --hypothesis "Before/after hooks get installs" --metric link_installs --target 50
# Started 2026-11-04-before-after-hooks-get-installs · check on 2026-11-18
#   baseline link_installs 0 (last 14 days)
#   link https://analytics.sleepsounds.workers.dev/l/ab12xy · post this link; its installs and revenue are counted to it
```

Put the link in the post, the bio or the ad. Use `link_clicks`, `link_installs` or `link_revenue` as the metric, and `verify` closes the experiment from the link once it is due (`experiment close` does it on demand). Channels include `tiktok`, `instagram`, `creator`, `tiktok_ads` and `meta_ads`.

On iOS the App Store passes nothing from a click to a new install, and Appeeky Analytics does not fingerprint devices. Link installs are, in practice, Google Play installs. For an App Store only app, Spec warns you: measure `link_clicks`, or `organic_installs` around the post.

Related: [Experiments and screenshots](https://docs.appeeky.com/docs/spec-experiments.md#smart-links) · [Releases](https://docs.appeeky.com/docs/spec-releases.md) · [Smart links in Appeeky Analytics](https://docs.appeeky.com/docs/analytics-smart-links.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.