<!-- AI agents: this is the Markdown version of a Reception documentation page. Index: /llms.txt -->

# Subscription providers

Connect RevenueCat, Superwall or Adapty, and subscription changes become automation triggers: a message when a trial is cancelled, help when a payment fails, a thank-you when someone subscribes. Your provider tells Reception; your app needs no extra code. Apps that use StoreKit without a provider connect the App Store instead.

## How it works {#how-it-works}

Your provider sends every subscription change to a webhook URL that belongs to your app in Reception. Reception finds the person the event is about, updates their subscription in the dashboard, and runs the automations that listen for that event on every device of theirs that has registered with Reception. People whose devices never registered are unknown to Reception and get nothing.

Each event reaches a person once per automation, with the same rules as any automation: the Who step, the Wait step, an ongoing conversation and the offer and rating checks all apply. See [Automations](/docs/automations).

## Integrate in your app {#identify}

Reception matches the provider’s user ID to one of two things:

- The user ID you pass to `identify`, when your provider uses the same ID. With RevenueCat this is the App User ID you log in with; with Adapty the `customer_user_id`; with Superwall the user ID you identify with.
- A reserved [metadata](/docs/accounts#metadata) key, for anonymous users or when the IDs differ: `revenuecat_id`, `superwall_id` or `adapty_id`, set to the provider’s own ID for this person.

Set the metadata key where you set the rest of your metadata, after the provider has an ID:

```swift
Reception.shared.setMetadata([
    "plan": "free",
    "revenuecat_id": Purchases.shared.appUserID,
])
```

In React Native:

```typescript
Reception.setMetadata({ plan: 'free', revenuecat_id: await Purchases.getAppUserID() });
```

For Superwall use `Superwall.shared.userId`, for Adapty the `profileId` of the current profile. Reception keeps the last value it received, so a later metadata update that leaves the key out doesn’t lose it. An anonymous person who later signs in keeps the IDs their anonymous session reported.

## Connect a provider {#connect}

Open **Settings → your app → Integrations** in the dashboard and choose your provider. Each card opens a guided setup: a link that opens the provider’s dashboard where the webhook is created, the webhook URL and secret to paste there, and a prompt for your coding agent. It takes about two minutes.

![The Integrations settings page with cards for RevenueCat, Superwall, Adapty and the App Store](/docs/images/dashboard-subscriptions-overview.webp "Settings → your app → Integrations. One card per provider; connect the one that bills your subscribers.")

### RevenueCat {#revenuecat}

![The RevenueCat setup dialog: Open RevenueCat, the webhook URL to copy, Connect and create the secret, and the agent prompt](/docs/images/dashboard-subscriptions-revenuecat.webp "The RevenueCat guide before connecting. Every value has a copy button.")

1. Press **Open RevenueCat** ([app.revenuecat.com/overview](https://app.revenuecat.com/overview)), choose your project and go to **Integrations → Webhooks → Add new configuration**.
2. Copy the **Webhook URL** from Reception into RevenueCat’s URL field.
3. Press **Connect and create the secret** in Reception and copy the **Authorization header value** into RevenueCat’s Authorization header field. Leave the event filter at all events, for production and sandbox, and save.
4. Copy the prompt for your coding agent and run it in your app’s repository. It passes RevenueCat’s App User ID to Reception, or confirms nothing is needed when both already use the same user ID.
5. Send a test event from RevenueCat’s webhook page. It appears under **Recent events** in the dialog, marked as a test that proves the connection. Real events show the person they matched and how many devices the automation reached.

![The RevenueCat dialog after connecting: the last event received with a green check, and the secret shown with a copy button](/docs/images/dashboard-subscriptions-revenuecat-connected.webp "After Connect, the secret appears with a copy button, and the latest event RevenueCat sent shows whether Reception found the person. Reveal brings the secret back later.")

### Superwall {#superwall}

![The Superwall setup dialog with the webhook URL and a field for the signing secret](/docs/images/dashboard-subscriptions-superwall.webp "Superwall shows its signing secret once; paste it here and press Connect.")

1. Press **Open Superwall** ([superwall.com/dashboard](https://superwall.com/dashboard)), choose your app and go to **Integrations → Webhooks → New webhook**.
2. Copy the **Webhook URL** from Reception into Superwall’s endpoint field and create the webhook.
3. Copy the signing secret Superwall shows once (it starts with `whsec_`) into Reception’s **Signing secret** field and press **Connect**.
4. Run the agent prompt in your app’s repository so Superwall’s user ID reaches Reception.

Superwall’s delivery log shows each event Reception accepted, and the dialog shows the last one received.

### Adapty {#adapty}

1. Press **Open Adapty** ([app.adapty.io/integrations/customwebhook](https://app.adapty.io/integrations/customwebhook)).
2. Copy the **Webhook URL** from Reception into Adapty’s production and sandbox endpoint fields.
3. Press **Connect and create the secret** in Reception and copy the **Authorization header value** into Adapty’s Authorization header field, then save. Adapty verifies the URL at once; Reception answers it.
4. Choose the events to send. Reception reads the trial, subscription, billing and refund events; others are accepted and ignored.
5. Run the agent prompt in your app’s repository so Adapty’s profile ID reaches Reception.

### App Store {#app-store}

For apps that bill through StoreKit directly, without a provider. App Store Connect allows one Server Notifications URL per app, so this is not for apps on RevenueCat, Superwall or Adapty: their provider already uses it.

![The App Store setup dialog: Open App Store Connect, the URL to paste as Production and Sandbox Server URL, Connect, and the SDK update prompt](/docs/images/dashboard-subscriptions-appstore.webp "Apple signs its notifications, so there is no secret to copy. Connect needs the app’s bundle identifier.")

1. Set your app’s bundle identifier under **Push Notifications** if it isn’t set yet. Apple names the app by it.
2. Press **Open App Store** ([appstoreconnect.apple.com/apps](https://appstoreconnect.apple.com/apps); with your App Store ID set, it opens your app’s App Information page) and find **App Store Server Notifications**.
3. Copy the **Webhook URL** into both the **Production Server URL** and the **Sandbox Server URL**, with **Version 2 Notifications**, and save.
4. Press **Connect** in Reception. Apple signs every notification, so there is no secret.
5. Ship Reception iOS SDK 1.7 or react-native-reception 1.5; the agent prompt covers the update. The SDK reports the person’s StoreKit transaction ids with the device, which is how Apple’s notifications find them. Older SDKs report none, so Apple’s events reach nobody until the update ships; everything else keeps working.

Reception checks Apple’s certificate chain and signature on every notification and that it names your bundle identifier. Sandbox notifications are accepted like production ones, so you can test with a sandbox account. If your app passes an `appAccountToken` at purchase that equals the user ID you pass to `identify`, that matches too, even before the SDK update.

Disconnecting a provider refuses its requests until you connect again. Reception’s secrets can be revealed and replaced in the dialog; replace one in the provider’s dashboard too.

## Prompts for your coding agent {#prompts}

Each dialog’s **Copy prompt for your coding agent** button copies the prompt for that provider. They never contain the webhook secret, and they leave the dashboard steps to you. For reference:

```text RevenueCat prompt {collapsed}
Reception, the in-app support chat already integrated in this app, will receive RevenueCat webhook events so subscription changes can trigger its automations. Make sure Reception knows which person each event is about:

- If the RevenueCat App User ID (Purchases.logIn) is the same value as the user ID passed to Reception.shared.identify, nothing needs to change.
- Otherwise add the RevenueCat user ID to Reception's metadata under the reserved key `revenuecat_id`. Swift: include `"revenuecat_id": Purchases.shared.appUserID` in the dictionary passed to Reception.shared.setMetadata(_:), merged with the metadata the app already sets (setMetadata replaces the whole dictionary). React Native: `Reception.setMetadata({ ...existing, revenuecat_id: await Purchases.getAppUserID() })`.
- Set it once the RevenueCat SDK is configured and again whenever the ID changes (sign-in, sign-out), in the same place the app already updates Reception's identity or metadata.

Docs: https://reception.sh/docs/subscriptions.md#identify. Agent index: https://reception.sh/llms.txt. Reception keeps the last value it received, so a later metadata update without the key loses nothing.
Keep the app's code style and flows; change nothing else. The webhook itself is set up by a person in the dashboards, not in code.
```

```text Superwall prompt {collapsed}
Reception, the in-app support chat already integrated in this app, will receive Superwall webhook events so subscription changes can trigger its automations. Make sure Reception knows which person each event is about:

- If the user ID passed to Superwall.shared.identify is the same value as the user ID passed to Reception.shared.identify, nothing needs to change.
- Otherwise add the Superwall user ID to Reception's metadata under the reserved key `superwall_id`. Swift: include `"superwall_id": Superwall.shared.userId` in the dictionary passed to Reception.shared.setMetadata(_:), merged with the metadata the app already sets (setMetadata replaces the whole dictionary). React Native: `Reception.setMetadata({ ...existing, superwall_id: await Superwall.shared.getUserId() })`.
- Set it once the Superwall SDK is configured and again whenever the ID changes (sign-in, sign-out), in the same place the app already updates Reception's identity or metadata.

Docs: https://reception.sh/docs/subscriptions.md#identify. Agent index: https://reception.sh/llms.txt. Reception keeps the last value it received, so a later metadata update without the key loses nothing.
Keep the app's code style and flows; change nothing else. The webhook itself is set up by a person in the dashboards, not in code.
```

```text Adapty prompt {collapsed}
Reception, the in-app support chat already integrated in this app, will receive Adapty webhook events so subscription changes can trigger its automations. Make sure Reception knows which person each event is about:

- If the customer user ID passed to Adapty.identify is the same value as the user ID passed to Reception.shared.identify, nothing needs to change.
- Otherwise add the Adapty user ID to Reception's metadata under the reserved key `adapty_id`. Swift: include `"adapty_id": profile.profileId (from Adapty.getProfile())` in the dictionary passed to Reception.shared.setMetadata(_:), merged with the metadata the app already sets (setMetadata replaces the whole dictionary). React Native: `Reception.setMetadata({ ...existing, adapty_id: (await adapty.getProfile()).profileId })`.
- Set it once the Adapty SDK is configured and again whenever the ID changes (sign-in, sign-out), in the same place the app already updates Reception's identity or metadata.

Docs: https://reception.sh/docs/subscriptions.md#identify. Agent index: https://reception.sh/llms.txt. Reception keeps the last value it received, so a later metadata update without the key loses nothing.
Keep the app's code style and flows; change nothing else. The webhook itself is set up by a person in the dashboards, not in code.
```

```text App Store prompt {collapsed}
Reception, the in-app support chat already integrated in this app, will receive App Store Server Notifications so subscription changes can trigger its automations. Make the app report the person's purchases to Reception:

- Update the Reception Swift package to 1.7.0 or later (React Native: react-native-reception 1.5.0 or later). Nothing else is required: the SDK reports the original transaction ids of the person's App Store purchases with the device.
- Optional, if the app already sets `appAccountToken` on StoreKit purchases: keep it equal to the user ID passed to Reception.shared.identify (it must be a UUID). Reception matches it too.

Docs: https://reception.sh/docs/subscriptions.md#app-store. Agent index: https://reception.sh/llms.txt.
Keep the app's code style and flows; change nothing else. The App Store Connect setup is done by a person in the dashboard, not in code.
```

## Events {#events}

Pick these in the Trigger step under **Subscription states**. They become available once a provider is connected to the app; until then the Trigger step and the templates point you to the Integrations settings.

| Event | When |
| --- | --- |
| `trial_started` | A free trial started. |
| `trial_converted` | A free trial became a paid subscription. |
| `trial_cancelled` | A free trial was cancelled; it still runs to its end. |
| `trial_expired` | A cancelled free trial ran out. |
| `subscription_started` | A paid subscription or a one-time purchase started. |
| `subscription_renewed` | A paid subscription renewed. |
| `subscription_cancelled` | Auto-renew was turned off; access stays until the period ends. |
| `billing_issue` | A renewal payment failed. |
| `subscription_expired` | A paid subscription ran out. |
| `refunded` | A purchase was refunded. |

Reception maps each provider’s own event names to these: RevenueCat’s `CANCELLATION` during a trial is `trial_cancelled`, its `RENEWAL` with a trial conversion is `trial_converted`, and a cancellation by customer support is `refunded`; Adapty’s `subscription_renewal_cancelled` is `subscription_cancelled`; Superwall’s lowercase event types follow the same rules. From the App Store, `SUBSCRIBED` with a free-trial offer is `trial_started`, `DID_CHANGE_RENEWAL_STATUS` with auto-renew turned off is `trial_cancelled` or `subscription_cancelled`, `DID_FAIL_TO_RENEW` is `billing_issue`, `EXPIRED` and `GRACE_PERIOD_EXPIRED` are `subscription_expired` (or `trial_expired`), `REFUND` is `refunded`, and `ONE_TIME_CHARGE` is `subscription_started`; Apple reports a trial that converts as a plain `DID_RENEW`, so it is `subscription_renewed`. Uncancellations, product changes and other events are accepted and change nothing.

The same events also update the person’s subscription in the dashboard: a purchase, renewal or conversion marks them as paying, an expiration or refund as not paying. The person’s Who step choices in automations keep reading the App Store state your app reports and your `subscription_status` metadata.

## Verify the connection {#verify}

1. Register a test device with your app and give it the provider ID, through `identify` or the metadata key.
2. Create an automation for `subscription_cancelled` with a message and turn it on.
3. Send a cancellation for that user from your provider’s test tools, or cancel a sandbox subscription.
4. The message appears in the device’s chat, as unread or with a push if the automation sends one.

The provider’s dialog lists its **Recent events**, newest first; press the refresh arrow after sending a test. Each row shows the provider’s event type, the Reception event it became, who it matched and how many devices the automation reached. Test events (RevenueCat’s test, Adapty’s URL verification, App Store Connect’s test notification) are marked as proving the connection.

A request Reception refused shows up there too, with the reason: the secret or signature didn’t match (reveal or replace the secret in Reception and update it in the provider), the provider wasn’t connected yet, or an App Store notification named another bundle identifier. The next accepted request clears it. An event marked as reaching nobody means Reception doesn’t know the person yet: check the ID your app reports, or run the agent prompt.
