Appy

Attribution

How attribution works

Appy matches each new install to the link that brought it, or counts it as organic. This page explains where Appy looks for the link, which installs are verified, and what is stored and for how long.

The SDK reports every cold start and every Appy link that opens the app. When a link opens the app, Appy returns that link. On the first launch after install, Appy looks for the link in this order and stops at the first answer:

  1. 1

    The link that opened the app

    A Universal Link or App Link on your link domain opened the installed app, and the SDK sent that URL.

  2. 2

    A click id from the redirect page

    In-app browsers such as Instagram often ignore Universal Links. Appy’s redirect page then opens your custom scheme with an appy_click_id, which points to the exact tap.

  3. 3

    The store’s install referrer

    On Android, Google Play and Huawei AppGallery hand the link to the app after install. The SDK reads it on the first launch.

  4. 4

    A recent tap that fits the install

    When none of the above exists, as on most iOS installs, Appy looks for a recent tap on one of your links that fits the new install. A tap can be claimed by one install only.

foundfoundfoundfoundLink thatopened the app1Click idappy_click_id2Store installreferrer3Recent tapthat fits the install4Organicno Appy linkAttributedverified: trueAttributedverified: false
Appy stops at the first source that has the link. Only the first three make an install verified.

The result is one of two states. An attributed install carries the link, its parameters, the UTM source, medium and campaign of the tap, and the tap time. An organic install did not come from an Appy link, or Appy could not connect it to one.

Attributed and organic installs

WhereWhat you see
The app, on the first launchonDeepLink receives found (with isDeferred true), notFound or failed, and onAttribution receives the attribution: organic, or the link’s slug and title with source, medium, campaign and tap time.
Your backendGET /v1/apps/{appId}/installs/{installId}, GET /v1/apps/{appId}/installs?userId= and the filtered install list GET /v1/apps/{appId}/installs return each install with attribution.status (attributed or organic) and attribution.verified.
App statisticsGET /v1/apps/{appId}/stats, the dashboard and the MCP tool get_app_attribution: installs first seen in a window, attributed, verified and organic, installs by store, a row per day, results by UTM source, medium and campaign, events and revenue, and the links that brought them. Add a link slug to see one link’s results. See App statistics.

The first launch is answered once. If the app retries because a response was lost, Appy returns the same answer instead of looking again, and later launches without a link never pick up someone else’s tap.

Verified installs

The install lookup returns attribution.verified. It is true only when Appy can prove the link: the link opened the app, a click id came back, or a store install referrer carried the link. An install matched to a recent tap is attributed but not verified.

json
{
  "installId": "7f0c2b9e-3a61-4c2e-9d0b-5c1e8f2a9b10",
  "platform": "ios",
  "userId": "u_123",
  "store": "app_store",
  "attribution": {
    "status": "attributed",
    "verified": true,
    "link": {
      "id": "8d3f5a0b-2c4e-4f7a-9b1d-6e8f0a2c4d6e",
      "slug": "spring",
      "url": "https://acme.appy.to/spring"
    },
    "source": "referral",
    "medium": null,
    "campaign": null,
    "clickedAt": "2026-09-25T10:04:05Z"
  }
}

Use verified installs for money

Routing a new user to the right screen is a good use of any attributed install. Granting a referral reward, a discount or account access is not: require verified from your backend, or ask the user to confirm the others.

Strict attribution

Strict attribution is a per-app setting: only count installs Appy can verify; everything else is organic. Turn it on in the dashboard under Apps & SDK > your app > Strict attribution, or set strictAttribution with POST /v1/apps or PATCH /v1/apps/{appId}. It is off by default and needs no change in the app.

  • On Android, installs from Google Play or AppGallery that started from the link stay attributed, because the install referrer carries it.
  • On iOS, the App Store passes nothing from the tap to the install, so a first launch from the home screen reports notFound and the install counts as organic.
  • Links that open an installed app are always delivered, with or without strict attribution.
WindowLengthWhat it means
Tap record24 hoursA tap on a link can be delivered as a deferred deep link, with its parameters and tap time, for 24 hours. A click id older than that no longer resolves.
Matching a tap to a new install2 hoursWithout a click id or install referrer, Appy only considers taps from the last two hours. Installs opened later count as organic.
Store install referrerKept by the storeGoogle Play keeps the referrer for 90 days after the install. After the tap record has expired, Appy still resolves the link from the referrer, without the tap time.
First-launch answer5 seconds by defaultThe SDK waits deferredDeepLinkTimeout (iOS) or deferredDeepLinkTimeoutMillis (Android) for the answer before reporting a timeout. Attribution still arrives later.

What is stored, and for how long

DataWhat it containsKept for
Tap recordsThe link, the clicked URL and its public parameters, platform, OS version, the device model on Android when the browser reports it, and the tap time. No IP address.24 hours
Network index for matchingTap references filed under a keyed hash of the network address. The hash changes every day and cannot be turned back into an address without Appy’s secret.2 hours
InstallsInstall id, platform, OS version, device model, app and SDK version, locale, the installing store, the user id you set, the matched link and its parameters.Until you delete them through the API, or the account is deleted
EventsEvent name, properties, revenue, currency, user id, whether it came from the SDK or your server, timestamps.Until you delete them through the API, or the account is deleted
  • Tap records are written only for accounts with at least one registered app, and only for iOS and Android taps. The regular click statistics of your links are separate and unchanged.
  • Parameters whose names start with appy_ are never stored or returned.
  • With tracking turned off in the app, Appy stores nothing for that device and only resolves links the user opened.

Deleting an app keeps its data

Deleting an app revokes its publishable key and keeps its installs and events, which can then no longer be reached or deleted through the API. Erase a user’s data before you delete the app.

Deleting a user’s data

When someone closes their account or asks to be forgotten, erase their data from your backend with a secret key. The SDKs have no delete call, because your backend owns the relationship with the user.

bash
curl -X DELETE "https://api.appy.to/v1/apps/$APP_ID/installs?userId=u_123" \
  -H "Authorization: Bearer $APPY_SECRET_KEY"

This deletes every install of the app that reported the user id, every event of those installs, and every other event sent with that user id, and answers with the counts: {"deletedInstalls": 1, "deletedEvents": 3}. To delete one install and its events, call DELETE /v1/apps/{appId}/installs/{installId}. Deleting is permanent, and app statistics computed afterwards no longer include the deleted data. Details are in the REST API guide.

Deleting stored data does not stop an installed app from sending more. Before you delete, clear the user id in the app (setUserId(nil) on iOS, setUserId(null) on Android) or turn tracking off (isTrackingEnabled = false); otherwise the next launch is stored as a new, organic install.

Privacy

  • The SDKs do not read advertising identifiers (IDFA, GAID), do not show the App Tracking Transparency prompt and send nothing to ad networks.
  • The matching layer never stores an IP address.
  • The install id is random, lives in the app’s own storage and disappears with the app.
  • One switch, isTrackingEnabled, stops events and the first-launch lookup. Set it to false before the SDK starts to wait for consent.

Apple does not allow deriving data from a device to identify it uniquely, whatever the user answered to the tracking prompt, so review deferred deep linking with your privacy team. Apps that want only verified installs attributed turn on strict attribution.

Apple campaign analytics

App Store installs cannot be matched one by one without a tap on a link, but Apple counts them per campaign. When an app has ios.appStoreId and ios.providerToken (the App Store Connect provider token), every Appy link that sends someone to that app’s App Store page gets pt, ct (the link slug, cut to Apple’s 30-character limit) and mt=8.

  • App Store Connect then reports impressions, product page views and first-time downloads per Appy link under App Analytics > Sources > Campaigns.
  • Apple shows a campaign after 24 hours and once it has at least five first-time downloads. Use slugs of up to 30 characters to keep campaigns apart.
  • These numbers are Apple’s own aggregate count, next to Appy’s per-install attribution, not part of it. Campaign parameters you set on a target yourself are kept.

Check a setup

bash
curl -s https://acme.appy.to/.well-known/apple-app-site-association | jq .
curl -s https://acme.appy.to/.well-known/assetlinks.json | jq .
curl -s "https://app-site-association.cdn-apple.com/a/v1/acme.appy.to" | jq .
adb shell pm get-app-links com.acme.shop
  • Both association files must answer 200 with JSON and no redirect, and list your Team ID and bundle id, or your package name and signing certificate fingerprints.
  • The third command shows what Apple’s CDN has cached, which can lag behind the live file by up to a day. During development, add ?mode=developer to the entitlement and turn on Associated Domains Development on the device to bypass the CDN.
  • The free Universal Link Validator checks a domain’s association files from the browser.