Overview
When RevenueCat is your purchase SDK, RevenueCat — not AppDNA — owns every store transaction. AppDNA learns about purchases, renewals, cancellations and refunds from RevenueCat’s webhook. Each RevenueCat event is converted into AppDNA’s own canonical events (purchase_completed, subscription_renewed, …) with source: "revenuecat", so they
appear in revenue, lifetime value, attribution, audiences and dashboards exactly like purchases made through AppDNA’s own
billing.
RevenueCat event mapping is enabled at release. Authenticated RevenueCat deliveries are counted as soon as the
integration is connected.
Provider events count for revenue, not for activity. RevenueCat events count for revenue, lifetime revenue and
attribution. They never count as sessions, active users, retention or billable MAU — those come only from the AppDNA SDK
running in your app. RevenueCat purchases and renewals do count toward MTPU (monthly tracked paying users), once per
user after de-duplication with SDK purchases.
Configure the SDK for RevenueCat
SetbillingProvider to revenueCat in your AppDNA configuration. With that setting:
- AppDNA never finishes (iOS) or acknowledges (Android) a store transaction — RevenueCat does.
- RevenueCat is not linked into AppDNA itself (on Android adding it to your app does not change that; for iOS see when a provider counts as linked), so in every published build tapping a plan on an AppDNA paywall does not open a store sheet. The SDK emits
purchase_failedwitherror_type: "providerNotAvailable"and calls your paywall delegate’sonPaywallPurchaseFailedwitherrorType: "providerNotAvailable"and the tappedproductId. - For the same reason,
AppDNA.billing.restorePurchases()throwsproviderNotAvailablein every published build: restore through RevenueCat (Purchases.restorePurchases). - The SDK does not emit device-side
subscription_renewed,subscription_canceledorsubscription_renewal_failedevents: the webhook is the single source for them.
onPaywallPurchaseFailed, when errorType is
providerNotAvailable, start the purchase for productId with RevenueCat (Purchases.purchase(...)), then dismiss the
paywall. The purchase then reaches AppDNA through this webhook.
Use RevenueCat’s CustomerInfo as the source of truth for entitlements; the SDK’s own entitlement reads come from the
store and can differ (for example, RevenueCat promotional grants).
Identity
Call RevenueCat’sPurchases.logIn with the same user id you pass to AppDNA.identify. AppDNA matches each
RevenueCat event to the AppDNA user whose identify id equals RevenueCat’s app user id (or one of its aliases).
Events from an anonymous RevenueCat user are still stored and counted, and are linked to the user once a later event
carries an identified app user id.
Do not track purchases yourself
Once the webhook is connected, do not track purchase or subscription events yourself withAppDNA.track
(purchase_completed, subscription_started, …). The webhook is the purchase source; a host-tracked copy that does not
carry RevenueCat’s transaction id would be counted a second time. For UI analytics around your own purchase screens, use custom event names (for example
checkout_sheet_opened).
Connect
- Open Integrations in the dashboard, choose RevenueCat → Connect, enter your RevenueCat Secret API Key and press Test & Connect (admin role).
- The dialog shows the Webhook URL. If you are the organisation owner, it also shows the Authorization header value once, with Copy. An admin who is not the owner sees “Ask your organisation owner to reveal the webhook credential on this integration’s page”.
- In RevenueCat → Project settings → Integrations → Webhooks, add a webhook:
- Webhook URL: the URL from AppDNA.
- Authorization header value: the value from AppDNA (it has the form
Bearer <secret>; AppDNA also accepts the secret without theBearerprefix). - Environment: send both sandbox and production events (AppDNA separates them, below).
- Press Send test event in RevenueCat. AppDNA answers 200 — the test event is stored but never counted.
Reveal and rotate (owner only)
On the integration’s page the Authorization value is masked. Only the organisation owner can Reveal and Copy it, or Rotate it. Rotating invalidates the old value immediately: RevenueCat deliveries fail with 401 until you paste the new value into RevenueCat. RevenueCat retries a failed delivery up to five times, 5, 10, 20, 40 and 80 minutes apart (about 2 hours 35 minutes in all), and then stops, so deliveries made during the paste window are not lost. Disconnecting and reconnecting the integration keeps the existing value.Optional: webhook signing secret
RevenueCat can also sign each delivery (headerX-RevenueCat-Webhook-Signature). To require signatures:
- In RevenueCat → Webhooks, enable signing and copy the signing secret (RevenueCat shows it once).
- On the AppDNA integration page, paste it into Webhook signing secret (optional) and save (owner only).
Sandbox and production
RevenueCat marks every eventSANDBOX or PRODUCTION. Sandbox events are written to AppDNA’s sandbox dataset and
never reach production revenue, dashboards or subscription analytics.
Event mapping
Every published event carries
environment, source: "revenuecat" and AppDNA’s bookkeeping keys (provider,
rc_event_type, provider_placeholder_ids and _appdna_origin, and rc_event_id / provider_stable_id when known). Every event except
subscription_transferred also carries is_trial, plus, when RevenueCat sends them,
product_id (for Google Play, the subscription id before the : of RevenueCat’s <subscription>:<base plan>),
store_product_id (the full RevenueCat product id), transaction_id, original_transaction_id, store, price
(RevenueCat’s price_in_purchased_currency), currency, period_type, country_code and entitlement_ids;
subscription_transferred adds only store. Only the three events that move money — purchase_completed, subscription_renewed and
purchase_refunded — carry revenue_usd and count as revenue; on subscription_started, subscription_canceled,
subscription_expired and subscription_renewal_failed the price is informational and is never counted. Retries of the
same RevenueCat event are deduplicated, and a purchase that both RevenueCat and another source report is counted once.
Renewals of a subscription RevenueCat reports are counted from RevenueCat only: the AppDNA SDK’s own
subscription_renewed for that subscription is kept as an event but adds no revenue, even when its transaction id differs.

