RevenueCat to Meta CAPI: verified subscription revenue and deduplication
Updated July 10, 2026 · 10 min read
RevenueCat is not just a billing abstraction. In a paid acquisition stack it is the revenue authority that decides when an app store transaction became a real monetization event.
The technical problem is carrying the client identity envelope into RevenueCat early enough that the later webhook can emit a financially correct Meta CAPI event without becoming anonymous server noise.
Setup, end to end
- 1In your Meta Events Manager, note the dataset (pixel) ID and generate a Conversions API access token. Confirm the dataset is the one your ad account actually optimizes on.
- 2Capture Meta click identity at checkout: read the click identifiers available on the device and attach them to the RevenueCat customer as subscriber attributes so the server event can carry them.
- 3Send purchases to Meta from a trusted server context, such as a RevenueCat webhook handler or a server integration, instead of from the app. A crash after purchase should not skip the event.
- 4On each INITIAL_PURCHASE and RENEWAL event, build a CAPI Purchase with the price and ISO currency, the hashed customer identifiers, and the captured click identity.
- 5Generate one event ID per purchase and send it on both the CAPI event and any client Purchase event, so Meta deduplicates to a single conversion.
- 6Verify in Test Events, then check Event Match Quality on the Purchase event and raise it by adding any missing identity parameters.
Subscriber attributes should carry attribution state
RevenueCat webhooks can only forward what RevenueCat or your backend knows. Attach attribution fields before the purchase is completed.
await Purchases.setAttributes({
zoruko_visitor_id: visitorId,
meta_event_id: eventId,
meta_fbp: identity.fbp ?? "",
meta_fbc: identity.fbc ?? "",
landing_session_id: landingSessionId,
});Webhook event classification
function mapRevenueCatEvent(type: string) {
if (type === "INITIAL_PURCHASE") return "Purchase";
if (type === "RENEWAL") return "Purchase";
if (type === "TRIAL_STARTED") return "StartTrial";
return undefined; // cancellation/refund is handled by lifecycle reporting, not acquisition Purchase
}The mistakes that break it
- Firing the event from the app's success callback instead of the verified server event. Crashes after purchase then silently drop conversions.
- Sending renewals with the original trial price (often $0) instead of the renewal amount, so revenue optimization sees nothing.
- Omitting the click identity, which tanks Event Match Quality and attribution even though the event 'arrives'.
- Using a different event ID (or none) on client and server events, causing double counting or discarded events.
- Pointing the token at the wrong Meta dataset, so events land somewhere your ad account never reads.
How this maps onto the concepts
RevenueCat is your server source of truth. The CAPI call is the server path from the client and server model. You still want a client Purchase event for identity, deduplicated with the shared event ID.
If you also run Adapty, Superwall, or your own receipt validation, the same pattern applies: fire CAPI from wherever the purchase is verified, carry identity, deduplicate.
Frequently asked questions
Why isn't my RevenueCat + Facebook integration tracking purchases?
Usually because the purchase is verified in RevenueCat but never forwarded to Meta's Conversions API, or it's forwarded without a value, currency, or click identity. Fire a CAPI Purchase from a RevenueCat webhook on each verified purchase and renewal, with the real amount and hashed identifiers.
Should I send RevenueCat purchases to Meta from the client or the server?
From the server. RevenueCat's webhook fires when a purchase is actually verified, so a crash or backgrounding after checkout can't skip it. Keep a client event only for identity, deduplicated with a shared event ID.
How do I track subscription renewals in Meta with RevenueCat?
Handle the RENEWAL webhook event and send a CAPI Purchase with the renewal's actual price and currency. Don't reuse the trial or intro price, and keep trials and paid conversions as separate events.
How do I keep Meta from double counting RevenueCat purchases?
Assign one event ID per purchase and send it on both the server CAPI event and any client Purchase event. Meta deduplicates on that ID and counts the conversion once.
Related guides
Not sure which signal path is breaking?
Zoruko audits your web and mobile code for broken Meta and TikTok signal chains, then ships each fix as a pull request you review.