Restore Purchases Can Break Your Apple Search Ads ROAS (Here’s the Fix)
If you’re running Apple Search Ads and you use a purchase analytics pipeline (often RevenueCat), it’s easy to assume “install → purchase” attribution will be consistent. But the moment you support Restore Purchases (especially for subscriptions), attribution can drift—sometimes silently—leading to ROAS that looks broken even when clicks and installs are doing fine.
This post is about a specific failure mode: restored purchases not being counted against the original Apple Search Ads install (or being double-counted elsewhere). You can diagnose it quickly, then fix it by tightening how you pass the AdServices install token through your receipt/purchase flow.
The symptom: ROAS instability with “healthy” top-funnel metrics
A common pattern indie teams see:
- Impressions/taps are reasonable (or TTR is stable).
- Installs convert at a normal-ish rate.
- But ROAS is noisy: days where it spikes, days where it collapses, or certain campaigns/keywords never settle.
- Support tickets mention “I restored my subscription” (or users commonly re-install / switch devices).
Why that happens: Apple Search Ads installs are attributed via Apple’s AdServices framework. Revenue attribution then depends on your purchase mapping (e.g., RevenueCat) correctly linking the purchase/receipt back to the attribution token that corresponded to the install.
When a user restores, the app is often fetching entitlements without a “new purchase event” that looks like a fresh billing transaction. If your pipeline doesn’t consistently tie that restore flow back to the original attribution context, ROAS can be understated, delayed, or credited incorrectly.
What to verify in your stack (minimum checklist)
Start with these checks in order. They’re targeted to restore-related attribution issues.
1) Confirm you’re resolving the attribution token and storing it persistently
Apple provides an attribution token via AdServices (resolved within ~24h). Your app should:
- Receive the attribution token for the install.
- Convert it into a value you can store (or store the raw token if your mapping layer requires it).
- Persist it long enough for purchase events—restores can happen well after install.
Practical check: find your current logic that stores the ASA attribution identifier (whatever field you use) and ensure it survives:
- App relaunches
- Device reboot
- User sign-out / sign-in
- Time gaps between install and restore
If you clear the stored attribution context too aggressively (for example, on logout or after the first screen), restores will be unmapped.
2) Verify your RevenueCat setup is actually using the same user identity across restore
Restore Purchases frequently happens when a user:
- Reinstalls the app
- Logs into the same account
- Expects entitlement to come back
In RevenueCat (or any purchase layer), identity usually hinges on how you define “user”:
- Are you setting a consistent user ID?
- Do you change user IDs between install and restore?
- Do you create a new anonymous user identity on every fresh launch?
Practical check: if you support account login, test a flow where you install via ASA → later log in (or switch account identity) → restore. Confirm that the restore is attributed to the same logical user that the install was associated with.
If the user identity changes, the purchase may get treated as “belonging” to a different install context (or get recorded without the original attribution mapping).
3) Make sure your restore path triggers the same purchase-entitlement pipeline as initial purchases
Different apps implement restore in different ways:
- Some call StoreKit restore and then update entitlements manually.
- Some rely on subscription SDK callbacks.
- Some call restore and then “refresh receipts” in a separate place.
Practical check: instrument (log) your restore entry point and verify that:
- The SDK is notified.
- Entitlement updates flow through the same event pipeline as initial purchase.
- You’re not bypassing the “log purchase / receipt” mechanism when it’s a restore.
A very common mistake is: initial purchases go through RevenueCat ingestion, restores go through a different code path that updates UI but never fully feeds attribution mapping.
4) Look for duplicate entitlement updates that can cause double-credit or inconsistent mapping
Restores can fire multiple callbacks depending on how you integrate StoreKit / receipt refresh.
Practical check: during a restore, log:
- How many “purchase/restore” callbacks you receive
- Whether entitlement transitions are idempotent in your code
- Whether you trigger “finalize purchase” logic more than once
If restore events are duplicated, ROAS can also look wrong (especially if some events become “mapped” and others don’t).
A focused test: isolate restore behavior from new purchase behavior
You don’t need perfect attribution to do a useful test—you need to see if restores behave differently.
Test design
- Pick one Apple Search Ads campaign/keyword where top-funnel metrics are stable.
- Do a clean install and confirm you receive/store the attribution token.
- Do not purchase initially.
- Wait until a later session and trigger Restore Purchases.
Then compare to:
- A second fresh install (same ASA campaign/keyword).
- Perform a new purchase.
What to observe
- Does ROAS credit show up similarly for restore vs new purchase?
- Does RevenueCat show restore revenue mapped to the same install context?
- Does your internal analytics indicate entitlement updates happen, but revenue attribution does not?
If restore consistently produces under-credit (but new purchases credit fine), you’ve basically confirmed an attribution mapping gap on the restore flow.
Fix strategy: make restore events attribution-aware
The exact fix depends on your SDK choices, but the principle is consistent:
When restore happens, the system that records revenue must still have access to the install attribution context that you captured at (or shortly after) the ASA-driven install.
Here are concrete approaches that work in practice.
Approach A: Persist the attribution context long enough and never wipe it on logout
- Store the ASA attribution identifier immediately after install attribution is resolved.
- Persist it in a durable way (user defaults / encrypted storage) and don’t clear it when the app “recreates” user state.
- Optionally rotate it only when you’re confident a new ASA install occurred.
This prevents restores from becoming “unattributed” just because the restore happened weeks later.
Approach B: Ensure restore triggers the same RevenueCat ingestion path
- Route restore through the same entitlement/purchase handler used for new purchases.
- Avoid separate manual entitlement updates that don’t go through the SDK’s ingestion callbacks.
If your restore code currently only updates UI, refactor it so the underlying receipt/entitlement reconciliation is the shared path.
Approach C: Keep the user identity stable for the restore lifecycle
- If you use account login, ensure the identity that RevenueCat uses remains consistent before and after login.
- Don’t generate a brand-new anonymous user ID on every app cold start if the user can later restore expecting continuity.
Even if you need separate “anonymous” and “logged-in” identities, you must map them carefully (so the restore updates land under the correct identity).
Approach D: Add idempotency guards to prevent duplicate restore credits
- Track restore transaction identifiers or entitlement states.
- Ensure your receipt ingestion / “finalize purchase” logic can safely run multiple times without creating multiple mapped revenue records.
How to sanity-check the fix
After you change persistence + restore routing, validate with the metrics you already trust:
- ROAS should become less erratic on days where restores are common.
- The delta between campaigns should stabilize.
- If you segment by “new purchase vs restore-heavy users,” restore-heavy segments should stop looking like attribution failures.
Also sanity-check that subscription conversions didn’t change dramatically—this is a data mapping problem, not a creative/bid problem.
Where this matters most in ASA
Apple Search Ads isn’t “wrong” here—the attribution token ties to installs. The weak link is that restore/re-entitlement can happen after the original install context is no longer accessible unless you designed for it.
This is one of those issues where top-funnel metrics can keep looking fine while ROAS quietly breaks. Fixing restore attribution often makes decisions easier because you stop fighting phantom performance.
Quick note about tooling (and how AdsBuddy fits in)
If you want a fast way to spot mismatches between ASA delivery and revenue mapping, AdsBuddy compares Apple Search Ads performance with your revenue signals and gives a prioritized list of changes you can approve (you apply them). Restore issues are exactly the kind of “looks like conversion, but isn’t revenue mapping” problem that benefits from that kind of cross-signal review.
Takeaway
Restore Purchases can break your Apple Search Ads ROAS when your app or revenue pipeline doesn’t carry the ASA attribution context through the restore lifecycle. The fix is usually straightforward: persist attribution identifiers reliably, route restore through the same ingestion path as new purchases, and keep user identity consistent. Do a simple controlled test (restore vs new purchase) and confirm ROAS stabilizes.