RevenueCat
If your app uses RevenueCat, connect its webhook and Unolink receives every trial, purchase, renewal, cancellation and refund straight from RevenueCat, including renewals and refunds that happen when the app isn't even open. Revenue in your funnel becomes net of refunds. Pro plan and up.
1. Connect
In Unolink, open your app → Settings → RevenueCat → Connect. Copy the webhook URL and the authorization header value (shown once).
2. Add the webhook in RevenueCat
In RevenueCat, open your project → Integrations → Webhooks → Add new configuration:
- Webhook URL and Authorization header value: paste them.
- Environment: both. Sandbox purchases are counted as test devices, so they stay out of your real numbers.
- App: this app. Events: all.
Save, then Send test event. The RevenueCat card in Unolink shows when it arrives.
3. Tell Unolink who the user is
Purchases are joined to the install (and the link it came from) through the user ID your app reports: RevenueCat's
own app user ID. With SDK 0.3.2 or later, one line keeps it current. Call it once at launch, before or after
configure:
Unolink.connectRevenueCat { Purchases.isConfigured ? Purchases.shared.appUserID : nil }
Unolink.connectRevenueCat { Purchases.sharedInstance.appUserID }
import Purchases from "react-native-purchases"
Unolink.connectRevenueCat(Purchases)
Unolink.connectRevenueCat(
() => Purchases.appUserID,
addListener: Purchases.addCustomerInfoUpdateListener,
removeListener: Purchases.removeCustomerInfoUpdateListener,
);
Unolink reads the ID at launch, whenever the app comes back to the foreground and whenever it changes, and sends it
only when it's new. It doesn't add RevenueCat to your app: you hand RevenueCat in. On iOS and Android, also call
Unolink.revenueCatUserChanged() after Purchases.logIn and Purchases.logOut to send the new ID at once (otherwise
it goes with the next event). React Native and Flutter notice the change by themselves.
Without connectRevenueCat (an older SDK, or if you'd rather do it yourself), call setUserId with RevenueCat's
ID after Purchases.configure, after every Purchases.logIn and Purchases.logOut, and on every launch. Use one or
the other, not both:
Unolink.setUserId(Purchases.shared.appUserID)
Unolink.setUserId(Purchases.sharedInstance.appUserID)
Unolink.setUserId(await Purchases.getAppUserID())
Unolink.setUserId(await Purchases.appUserID);
For a guest, that's RevenueCat's anonymous ID, so a purchase made before signing up joins the install right away.
After logIn, it's the account's ID. Unolink keeps every ID an install has reported, so the guest's purchase stays
joined.
If your app only reports the account ID, a guest's purchase still joins, but only with RevenueCat's next event for that customer that lists both IDs (for example the first renewal). Until then the person shows as not paid yet. Reporting RevenueCat's ID avoids that wait.
When someone taps "continue as guest", call Unolink.signUpSkipped() too (SDK 0.3.2 or later, React Native 0.3.1;
older SDKs: track("sign_up_skipped")), so the funnel's Continued as guest step counts them. See
Guests and accounts.
Unolink never stores the ID itself, only a keyed hash that's different for each of your apps. A purchase that arrives before the app has reported its user waits, and joins the install as soon as it does. Payments that come days or weeks later still count, and the Report shows how long people take: see Payments and user IDs, which also covers privacy and Apple's rules.
What arrives
| RevenueCat | In Unolink |
|---|---|
INITIAL_PURCHASE (trial) | Started trial |
INITIAL_PURCHASE, NON_RENEWING_PURCHASE, trial conversion | Paid, with revenue |
RENEWAL | Renewed, with revenue |
CANCELLATION (refund) | Refunded, revenue subtracted |
CANCELLATION, EXPIRATION, BILLING_ISSUE | Cancelled subscription, Subscription expired, Billing issue |
Once RevenueCat is connected it is the source of truth for purchases and trials: if your app also calls
Unolink.purchase() or trialStarted(), those calls are ignored, so nothing is counted twice. Renewals, cancellations
and the rest can be added as funnel steps in any funnel (Funnel → Edit).