# RevenueCat webhook

Wire RevenueCat to ActivationPal so revenue, trials, and churn land as `rc_*` events next to your client events.

Other guides: [quickstart.md](quickstart.md) · [events.md](events.md) · [agent-api.md](agent-api.md) · [apps.md](apps.md) · [apple-ads.md](apple-ads.md)

SDK events carry a `device_id`; RevenueCat events carry the RevenueCat app user id. Pass `userId: Purchases.shared.appUserID` to `configure` ([quickstart.md](quickstart.md)) to join them: purchases land on the buying device's timeline, revenue splits by country and pricing tier, and the revenue chart, revenue mix, and RevenueCat-health cards populate.

## Setup

RevenueCat dashboard → **Project → Integrations → Webhooks → New webhook**:

| Field | Value |
|---|---|
| Webhook URL | `https://activationpal.com/api/rc` (same URL for every app) |
| Authorization header value | the app's ActivationPal API key verbatim — `ap_pk_...`, no `Bearer` prefix |
| Environment | Production |
| Event types | all |

One webhook per app. The key is the same one the SDK uses ([apps.md](apps.md)); it routes events to the right app.

## What arrives

Each event is stored as `rc_` + the lowercased RevenueCat type, full payload in `props`:

`rc_initial_purchase`, `rc_renewal`, `rc_cancellation`, `rc_uncancellation`, `rc_expiration`, `rc_billing_issue`, `rc_non_renewing_purchase`, `rc_product_change`, `rc_transfer`

Dashboard semantics:

- Trial start = `rc_initial_purchase` with `period_type: "TRIAL"` — price 0, counted as a trial, not revenue.
- Trial conversion = `rc_renewal` with `is_trial_conversion: true` — counted as new-subscriber revenue, not a renewal.
- Refund = `rc_cancellation` with `cancel_reason: "CUSTOMER_SUPPORT"`.
- One-time purchase = `rc_non_renewing_purchase`.
- Transfer = `rc_transfer` — `transferred_from`/`transferred_to` arrays, attributed to the receiving user.
- Revenue on every surface = sum of positive `price` props on `rc_*` events (gross).

Each event is stamped with the last device that reported the same user id (or an alias) via the SDK — that places purchases on user timelines and city-level geo. RevenueCat's event id is stored uniquely: webhook retries never double-count.

## Gotchas

- SANDBOX events are stored `env=debug`, outside release numbers — set the environment to Production. Test purchases with `paywall_purchased` from a DEBUG build instead.
- No `userId`, no join: `rc_*` events still arrive and total correctly, but user timelines and funnel-to-revenue joins stay empty.
- Rotating the API key ([apps.md](apps.md)) kills the old key immediately — update the webhook's Authorization header in the same sitting, or RevenueCat gets 401s and flags the webhook as failing.
- An event that fails auth is not redelivered by ActivationPal; RevenueCat retries non-200s on its own schedule.

## Verify

After a production purchase (or the next real renewal): dashboard live feed, or with an agent API key ([agent-api.md](agent-api.md)):

```bash
curl -s "https://activationpal.com/api/v1/get_events?app=myapp&source=revenuecat" \
  -H "Authorization: Bearer $AP_KEY"
```
