Verified revenue
trackPurchase reports what your app saw at the moment of sale: one event, counted once, never corrected. It can’t know about the renewal next month, the refund next week, or the trial that quietly lapsed. Connecting RevenueCat replaces that with the store’s own record of what happened.
What changes
A Verified revenue card appears on the dashboard showing net revenue for the window, with gross and refunded broken out — a month that grossed $900 and refunded $450 nets the same as one that grossed $450 cleanly, and those are not the same month. Alongside it: new subscriptions, renewals, trial conversions and cancellations.
It also credits revenue back to the source that acquired the customer. A renewal in month nine still belongs to the TikTok video that brought them in, which is something a purchase event can never tell you — there’s no click to trace a renewal back to.
1. Generate a webhook token
Open your app’s Settings and expand Revenue — RevenueCat. Press Generate token. The token is shown once: only a hash of it is stored, so there is no way to look it up later. Copy it before you close the panel — if you lose it, generate a new one, which invalidates the old.
2. Add the webhook in RevenueCat
In RevenueCat, go to Integrations → Webhooks → Add and fill in the URL shown in your settings panel, with the token as the Authorization header — pasted exactly, with no Bearer prefix.
Send both production and sandbox. Sandbox events are stored with their own environment and hidden by the dashboard’s environment filter, so your test purchases never inflate real revenue.
Then press Send test event. A green response means the token is right. Test events are acknowledged but deliberately not stored, so nothing appears on the dashboard until a real purchase or renewal arrives — the card reads Waiting for first event until then.
3. Tag purchases with the device
This is the step that decides whether verified revenue is useful. Skip it and revenue still arrives, and the totals are still right — but RevenueCat has no idea which of your users made the purchase, so none of it can be credited to a source. It fails quietly, which is why it’s worth doing up front.
Tag the purchase itself. Apple carries the token through the entire subscription, so you set it once and never again:
let result = try await product.purchase(options: [
.appAccountToken(Astronaut.shared.deviceId)
])It must be a UUID — Apple silently drops anything else. Astronaut.shared.deviceId already is one.
If your app uses the RevenueCat SDK rather than store notifications, configuring it with Astronaut.shared.deviceId as the app user ID does the same job, and you can skip the StoreKit option.
What Apple carries, and when
The token rides the whole subscription lifecycle from a single call at purchase:
- Free trial — the trial is itself a transaction, so it carries the token
- Trial converting to paid — arrives as a renewal on the same original transaction
- Every renewal after that — Apple includes the token in renewal info specifically so it survives
- Refunds and cancellations — same subscription, same identity
Existing subscribers stay unattributed
appAccountToken is set at the moment of purchase, so subscriptions bought before you shipped it never had one — and can’t gain one later. Their renewals keep arriving, keep counting toward net revenue, and stay grouped under unattributed on the per-source table.
That bucket is deliberate. Revenue you can’t attribute is still revenue, and hiding it would make the source table quietly disagree with the revenue figure above it.
Checking it worked
Make one sandbox purchase after shipping the change. If the Verified revenue card shows it and the per-source table credits it to a real source rather than unattributed, the tagging is working end to end.