Hi @chuck-2b8203, great questions! let me answer each one with what RevenueCat actually does on the backend after the SDK sends us the AdServices token.
1. Where ASA attribution is stored after RevenueCat collects the AdServices token
When Purchases.enableAdServicesAttributionTokenCollection() is called and we receive the token, we exchange it with Apple's AdServices endpoint and store the raw fields (orgId, campaignId, adGroupId, keywordId, adId, claimType, conversionType, countryOrRegion, clickDate, impressionDate) in an internal attribution table.
What lands on the customer object as reserved subscriber attributes depends on whether you have an Advanced Apple Ads integration set up (the one where you Sign in with Apple / authorize the Apple Search Ads Campaign Management API). The Advanced integration is what lets us resolve campaignId → campaignName, adGroupId → adGroupName, keywordId → keyword text. That resolution job then writes the following reserved attributes:
| Attribute | Value |
|---|
| $mediaSource | "Apple Search Ads" |
| $campaign | Campaign name |
| $adGroup | Ad group name |
| $keyword | Keyword text (only when search match is disabled, since Apple doesn't return a keywordId for search-match-on traffic) |
These match the same $mediaSource / $campaign / $adGroup / $keyword keys which can be used for AppsFlyer, Adjust, Branch, Tenjin, etc. They're documented in Customer Attributes → Attribution Data.
A few important caveats:
- No Apple Ads integration, or only Basic: the AdServices token is collected and stored, and your Charts will still get the install/attribution signal, but no
$mediaSource / $campaign / $adGroup / $keyword reserved attributes are written to the customer. Resolving names requires the Advanced integration's auth token. - Organic installs (Apple returns
attribution: false) don't get any of these attributes set. - The
$apple_search_ads_* style key shape you guessed at doesn't exist
2. Does this show up in webhook event.subscriber_attributes?
Yes. Once those reserved attributes are written, they're in the same subscriber_attributes map you already see on webhook events (alongside $idfa, $idfv, $ip, $attConsentStatus, etc.). So on a webhook for a customer attributed to ASA via the Advanced integration, you should see entries like:
"subscriber_attributes": {
"$mediaSource": { "value": "Apple Search Ads", "updated_at_ms": ... },
"$campaign": { "value": "<Campaign Name>", "updated_at_ms": ... },
"$adGroup": { "value": "<Ad Group Name>", "updated_at_ms": ... },
"$keyword": { "value": "<Keyword>", "updated_at_ms": ... }
}
A couple of practical timing notes:
- The AdServices → Apple Ads resolution runs asynchronously after we receive the token. Apple itself recommends allowing up to ~24h for attribution to be available, and we recommend allowing up to 7 days for full coverage. So a
INITIAL_PURCHASE webhook fired immediately after install can land before these attributes are written. They'll be present on later events for the same subscriber. - The reason you don't see ASA attributes on your current customers is likely what you suspected: token collection isn't shipped to a public build yet, and TestFlight/sandbox usually returns placeholder values that we filter out.
3. Reading per-customer ASA attribution programmatically
You have three options that all read the same underlying reserved attributes:
- Webhooks —
event.subscriber_attributes as above. This is what we generally recommend for forwarding to your analytics. - Customer API v2 —
GET /v2/projects/{project_id}/customers/{customer_id}/attributes returns the full attribute list, including the $mediaSource / $campaign / $adGroup / $keyword entries when set (API reference). The v1 /subscribers/{app_user_id} response also exposes them under subscriber.subscriber_attributes. - Scheduled Data Exports — the customer-level attribute fields (
media_source, campaign, ad_group, keyword) are exported per-row, so this is the easiest path if you're doing offline analysis or backfilling.
So the short version: set up the Advanced Apple Ads integration, ship token collection to a public build, and you'll see ASA attribution land in subscriber_attributes (and therefore in webhooks and the Customer API attributes list) under the standard reserved keys. Without the Advanced integration, ASA only powers Charts and Apple-Ads-specific destinations, not the per-customer attribute map.
Hope that helps!