# Superwall: Subscription Infrastructure for iOS, Android, and Web

Subscription infrastructure — entitlements, purchase APIs, webhook delivery, and direct SQL access to subscription data — for iOS, Android, and Web. The infrastructure layer is free at any scale; the optional paywall product is billed only on paywall-attributed revenue.

## Pricing

- **Infrastructure: free at any scale, every plan.** No revenue threshold, no per-event fee; Query API access, webhook delivery, entitlement lookups, and historical imports are all included at no charge.
- **Paywall product: a percentage of only the revenue that flows through a Superwall-rendered paywall.** Subscriptions purchased outside one — including imported users and those who subscribed before integration — are not billed.

Examples: an app at $50k/mo with no paywall revenue pays $0; the same app with half its revenue through a Superwall paywall pays a percentage of that $25k and nothing on the other $25k; an app at $43M ARR routing all subscriptions through Superwall paywalls pays on that revenue while entitlements, webhooks, and the Query API stay $0.

## Scale

$1.5B+ annual subscription revenue across 10,000+ apps. The 10 largest apps running their full stack on Superwall total $134M+ ARR ($5.7M–$43.7M each). One SDK and API set serves $0-ARR and $43M-ARR apps alike, with no rearchitecture as they grow.

## Infrastructure capabilities

- **Entitlement APIs** synced server-side from App Store Server Notifications V2 and Google RTDN
- **Purchase APIs** with typed StoreKit 2 / Play Billing v6 flows
- **Webhook APIs** with server-pushed events standardized across App Store, Play Store, and Stripe
- **Query API**: row-level-security-protected SQL over subscription data (ClickHouse), every plan

Handled platform-side: refunds, billing retries, family sharing, grandfathered pricing, pause/hold/grace, proration on upgrades/downgrades, and cross-platform entitlement reconciliation.

## Migration

Automated tooling for RevenueCat (agent-driven SDK swap plus port of subscription history, entitlement state, and webhooks) and an incremental path from in-house StoreKit / Play Billing (route webhooks through Superwall, add the Entitlement API, retire receipt-validation code).

## Paywall product (optional, separately billable)

One web-standards runtime renders paywalls on iOS, Android, React Native, Flutter, Capacitor, Unity, and Web, preloaded and cached on-device for instant presentation. Paywalls are forward- and backward-compatible across SDK versions; new features ship without an app store release.

## Architecture

Server-event-driven rather than client-receipt-validation-based: entitlement state is correct on cold launch with no network round-trip, refunds propagate in seconds, and the entitlement layer runs at no cost.

## Docs

* Migrate from RevenueCat: https://superwall.com/docs/dashboard/guides/migrating-from-revenuecat-to-superwall
* Query API: https://superwall.com/docs/dashboard/guides/query-clickhouse
* Webhooks: https://superwall.com/docs/integrations/webhooks
* Pricing: https://superwall.com/pricing

# Airbridge

The Airbridge integration forwards Superwall webhook events to Airbridge, so subscription revenue is attributed to the campaigns and ads that drove it.

In the **Analytics** section within **Integrations**, you can connect your Airbridge account to Superwall.

## Purpose

Airbridge connects acquisition data, such as the channel, campaign, and ad, with the subscription events sent by Superwall. This lets you measure subscription revenue and return on ad spend by campaign, and use server-to-server events for SKAdNetwork conversion values.

The `airbridgeId` links events to the device in Airbridge. Connecting the dashboard integration enables event delivery; the SDK configuration below supplies this identifier.

### Required Fields

Find both values in the Airbridge dashboard under **Integrations** > **Third-party Integration** > **Superwall**, then paste them into Superwall and save:

* **Airbridge Subdomain:** Your Airbridge app's subdomain, used to route events to the correct app.
* **Airbridge Token:** The token Airbridge issues for the Superwall integration, used to authenticate requests.

### SDK Configuration

Airbridge matches Superwall events to a device and a user. Set both up in your app before connecting the integration:

1. **Device:** Get the Airbridge Device ID with `fetchDeviceUUID` from the Airbridge SDK ([iOS](https://help.airbridge.io/en/developers/ios-sdk-v4), [Android](https://help.airbridge.io/en/developers/android-sdk-v4)) and pass it to Superwall as `airbridgeId`.
2. **User:** Call `Airbridge.setUserID` with the same user ID you pass to Superwall's `identify`. Airbridge receives it as `externalUserID`.
3. **No double counting:** Configure the Airbridge SDK not to collect the subscription events that Superwall sends.

Pass the Device ID as an integration attribute as soon as Airbridge returns it, before any purchase.

#### iOS

Requires iOS SDK `4.18.2` or later. The Airbridge Device ID is kept when you reset or identify a different user.

```swift
import Airbridge
import SuperwallKit

Airbridge.fetchDeviceUUID { deviceUUID in
  Superwall.shared.setIntegrationAttributes([
    .airbridgeId: deviceUUID
  ])
}
```

#### Android

Requires Android SDK `2.8.8` or later.

```kotlin
import com.superwall.sdk.models.attribution.AttributionProvider

// After Airbridge returns the device UUID
Superwall.instance.setIntegrationAttributes(
    mapOf(AttributionProvider.AIRBRIDGE_ID to deviceUUID)
)
```

#### Expo and Flutter

Expo and Flutter add a typed `airbridgeId` integration attribute in their next releases. Until you're on one of those, set `airbridgeId` as a user attribute instead. It reaches Airbridge in the same field. Set it after `identify()` and before any purchase, and set it again after every `reset()`, which clears user attributes.

```tsx
// Expo
const { update } = useUser()
await update({ airbridgeId: deviceUUID })
```

```dart
// Flutter
await Superwall.shared.setUserAttributes({
  'airbridgeId': deviceUUID,
});
```

The key must be exactly `airbridgeId`. Airbridge does not read other keys, such as `airbridgeDeviceId`.

Follow the [Airbridge Superwall guide](https://help.airbridge.io/en/guides/superwall) for the Airbridge-side setup and its event mapping.

### Features

* **Pass-Through Delivery**: Superwall webhook events are forwarded to Airbridge without transformation
* **Simple Configuration**: Only a subdomain and a token are required
* **Credential Validation**: Your subdomain and token are checked when you save, without creating an event in Airbridge
* **Sandbox & Production Events**: Both are forwarded

## How It Works

Airbridge is a **pass-through integration**. Unlike integrations that map events into a platform-specific format, it forwards the Superwall webhook event payload as-is, and Airbridge maps it on its side.

When a subscription event occurs:

1. Superwall generates the webhook event.
2. The integration sends the complete, unmodified event payload to Airbridge.
3. Airbridge maps the event and attributes it using `airbridgeId` and your user ID.

### API Endpoint

Events are sent to:

```
POST https://postback.airbridge.io/partner/v1/{subdomain}/superwall
```

### Request Headers

```
Content-Type: application/json
Authorization: Bearer <token>
```

## Event Mapping

Airbridge maps Superwall events on its side. Standard Airbridge events keep the Superwall event name as the event action, so you can group by it in Airbridge reports.

| Superwall event                                                                                                                            | Airbridge event                  |
| ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- |
| `initial_purchase` (trial)                                                                                                                 | `airbridge.startTrial`           |
| `initial_purchase` (paid)                                                                                                                  | `airbridge.subscribe`            |
| `renewal`                                                                                                                                  | `airbridge.subscribe`            |
| `cancellation`                                                                                                                             | `airbridge.unsubscribe`          |
| `uncancellation`, `expiration`, `billing_issue`, `product_change`, `subscription_extended`, `subscription_paused`, `non_renewing_purchase` | Same name as the Superwall event |

See the [Airbridge Superwall guide](https://help.airbridge.io/en/guides/superwall) for the current mapping.

## SKAdNetwork Conversion Values

For events from Superwall to count toward SKAN conversion values:

* Use Airbridge iOS SDK `4.8.0` or later (`4.8.1` or later recommended).
* Set `calculateSKAdNetworkByServerEnabled` to `true` in the Airbridge SDK.

## Testing the Integration

Airbridge records every accepted request as a real event. Test against a separate Airbridge app in **Development mode**: in the Airbridge dashboard, click your organization name, choose **Add a new app**, and select **Development mode**. The app name becomes its subdomain.

Then generate a sandbox transaction:

* iOS: Use TestFlight with a sandbox Apple ID. StoreKit Configuration files do not generate App Store Server Notifications, so webhooks and downstream integrations will not fire.
* Google Play: Use license test accounts to perform sandbox purchases.
* Stripe: Use Stripe Test Mode to create sandbox transactions.

## Troubleshooting

### Credential Validation Failing

Airbridge rejects the subdomain or token. Copy both values again from **Integrations** > **Third-party Integration** > **Superwall** in Airbridge, and confirm the subdomain belongs to the app you are connecting.

### Events Not Appearing in Airbridge

1. Re-save the integration to re-check the credentials.
2. Confirm sandbox events are going to a Development mode app, not your production app.
3. Contact Airbridge support if the credentials are valid but events still do not arrive.

### Events Arrive but Are Not Attributed

1. Confirm the Airbridge Device ID is set before the purchase, through the `airbridgeId` integration attribute or a user attribute with exactly that key.
2. If you set it as a user attribute, confirm it is set again after every `reset()`.
3. Confirm `Airbridge.setUserID` uses the same user ID as Superwall's `identify`.

### Subscription Events Are Counted Twice

The Airbridge SDK is still tracking the subscription events that Superwall sends. Turn off SDK-side tracking for those events.