RevenueCat
Connect a RevenueCat project to SaaS Pro Max for read-only revenue analytics across the App Store, Play Store, Stripe and web billing. The connector reads customers and subscriptions through the v2 API and keeps them current with webhooks. It never writes to RevenueCat.
See the revenue module for how the numbers are derived.
1. Create a secret API key
RevenueCat dashboard → Project settings → API keys → New API key. It needs
read access to customers and subscriptions; the key looks like sk_….
Note the project id (proj…) from Project settings → General. Every v2
request is scoped to a project, so the connector cannot sync without it.
2. Add the connector
In the console: Revenue → Connectors → Add a connector.
- Provider: RevenueCat
- API key: the secret key from step 1
- Project id: required
- Webhook secret: the value you will configure as the Authorization header in
step 3 — pick a long random string, for example
openssl rand -base64 32 - Reporting currency: the currency the dashboard totals in
Creating a connector requires revenue.write and a passkey verified in the last
ten minutes. Both values are envelope-encrypted into the vault immediately and
are never shown again.
The connector list then shows the webhook URL:
https://<your host>/api/v1/webhooks/revenuecat/<connectorId>?t=<token>
3. Configure the webhook
RevenueCat dashboard → Project settings → Integrations → Webhooks:
- Webhook URL: the URL above
- Authorization header value: exactly the webhook secret you stored in step 2
- Event types: all subscription events. At minimum
INITIAL_PURCHASE,RENEWAL,CANCELLATION,UNCANCELLATION,EXPIRATION,BILLING_ISSUE,PRODUCT_CHANGE,SUBSCRIPTION_PAUSEDandNON_RENEWING_PURCHASE.
RevenueCat does not sign its webhooks: it sends the Authorization header value back verbatim on every request, and SaaS Pro Max compares it against the stored secret in constant time. Treat that value as a password — anyone who has it can post events. Rotate it by editing the connector and the RevenueCat integration together.
Send the built-in Test event from the dashboard to check the wiring; it is accepted, verified and then deliberately ignored, so it never adds a row.
4. Backfill
Adding a connector enqueues a sync immediately. It pages
/v2/projects/<projectId>/customers and /v2/projects/<projectId>/subscriptions
by following next_page. The hourly job re-runs it, and Sync now runs one on
demand.
What the connector reads
| RevenueCat field | Becomes |
|---|---|
app_user_id |
The billing customer; subscriber_attributes.$email supplies the email that links it to a person |
original_transaction_id |
The subscription identity, so a year of renewals is one subscription rather than twelve |
transaction_id |
One payment per purchase or renewal |
price_in_purchased_currency + currency |
The payment amount; price (USD) is the fallback |
period_type = TRIAL |
Stored as trialing, contributing no MRR until it converts |
Inferred billing intervals
RevenueCat does not report a billing interval, so it is inferred from the length
of the purchased period (purchased_at_ms → expiration_at_ms):
| Period | Interval |
|---|---|
| ≤ 2 days | daily |
| ≤ 10 days | weekly |
| ≤ 45 days | monthly |
| ≤ 75 / 135 / 225 days | 2, 3 or 6 monthly |
| ≤ 400 days | yearly |
| ≤ 800 days | 2 yearly |
| longer, or no expiry | lifetime — no MRR, counted in payments |
The windows are wide enough to absorb store drift: a "monthly" App Store period is 30 or 31 days and a yearly one 365 or 366.
NON_RENEWING_PURCHASE is always treated as a one-off: it produces a payment and
no MRR.
A CANCELLATION means auto-renew was switched off — the subscription is marked
canceled at that moment. When its reason is CUSTOMER_SUPPORT it is also
recorded as a refund against the original transaction, which is how RevenueCat
reports support-issued refunds.