# 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

# OneSignal

Sync Superwall subscription state to OneSignal user tags for messaging, segmentation, and Journeys.

In the **Communication** section within **Integrations**, you can connect your OneSignal app to Superwall.

Use this integration when OneSignal is your messaging or lifecycle marketing destination and you want subscription state available as OneSignal user tags.

## Features

* **Subscription state tags**: Updates OneSignal users with the latest Superwall subscription status.
* **Messaging segmentation**: Use tags such as `subscription_status`, `active_subscription`, and `product_id` in OneSignal segments and Journeys.
* **Lifecycle context**: Includes store, environment, purchase, expiration, transaction, project, and application details.
* **Sandbox controls**: Sends sandbox events to a sandbox OneSignal app when configured, or skips them when sandbox credentials are blank.
* **Typed SDK attributes**: iOS and Android SDKs expose typed OneSignal integration attributes for the OneSignal User ID.

## Configuration

OneSignal requires an App ID and REST API key to update users through the OneSignal User API. Configure production credentials, and optionally configure sandbox credentials if you want sandbox subscription events to update a separate OneSignal app.

| Field                    | Description                                                                         |
| ------------------------ | ----------------------------------------------------------------------------------- |
| OneSignal App ID         | Your production OneSignal app ID.                                                   |
| REST API key             | Your production OneSignal REST API key.                                             |
| Sandbox OneSignal App ID | Optional. Used for sandbox events. If blank, sandbox events are skipped.            |
| Sandbox REST API key     | Optional. Used for sandbox events. If blank, sandbox events are skipped.            |
| Test OneSignal User ID   | Optional. Used only by the dashboard validation action to update a known test user. |

## SDK Setup

Superwall updates OneSignal users after subscription events happen. To match those events to the correct OneSignal user, pass the OneSignal User ID to Superwall as soon as OneSignal makes it available.

If your app uses OneSignal external IDs, call `OneSignal.login(...)` before reading the OneSignal User ID.

### iOS

```swift
// After OneSignal initializes and identity is set
if let oneSignalId = OneSignal.User.onesignalId {
    Superwall.shared.setIntegrationAttributes([
        .onesignalId: oneSignalId
    ])
}
```

### Android

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

// After OneSignal initializes and identity is set
OneSignal.User.onesignalId?.let { oneSignalId ->
    Superwall.instance.setIntegrationAttributes(
        mapOf(AttributionProvider.ONESIGNAL_ID to oneSignalId)
    )
}
```

### Other SDKs

If your Superwall SDK does not yet expose a typed OneSignal integration attribute, set `onesignalId` as a user attribute instead.

```swift
Superwall.shared.setUserAttributes([
    "onesignalId": oneSignalId
])
```

```kotlin
Superwall.instance.setUserAttributes(
    mapOf("onesignalId" to oneSignalId)
)
```

```dart
await Superwall.shared.setUserAttributes({
  'onesignalId': oneSignalId,
});
```

Superwall also accepts `$onesignalUserId`, `$onesignalId`, `oneSignalUserId`, and `onesignalUserId` as raw user-attribute aliases, but `onesignalId` is the recommended key.

## Tags

Superwall updates OneSignal user tags through the OneSignal User API.

| Tag                        | Description                                                                    |
| -------------------------- | ------------------------------------------------------------------------------ |
| `app_user_id`              | Your app's user identifier from the Superwall event.                           |
| `period_type`              | Subscription period type, such as `trial`, `intro`, or `normal`.               |
| `purchased_at`             | Purchase time as an ISO timestamp.                                             |
| `expiration_at`            | Expiration time as an ISO timestamp, when available.                           |
| `store`                    | Store that produced the event, such as `app_store`, `play_store`, or `stripe`. |
| `environment`              | `production` or `sandbox`.                                                     |
| `last_event_type`          | Original Superwall event name.                                                 |
| `superwall_event_type`     | Normalized Superwall event type, such as `sw_subscription_start`.              |
| `product_id`               | Product identifier from the purchase.                                          |
| `transaction_id`           | Transaction identifier for the event.                                          |
| `original_transaction_id`  | Store original transaction identifier, when available.                         |
| `subscription_status`      | Current status inferred from the latest event.                                 |
| `active_subscription`      | `true` when the latest event represents active access.                         |
| `superwall_project_id`     | Superwall project ID.                                                          |
| `superwall_application_id` | Superwall application ID, when available.                                      |

## Subscription Status

The `subscription_status` tag is designed for segmentation and Journey branching.

| Status          | When it is set                                                                                                                   |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `trial`         | Trial started or trial uncancelled.                                                                                              |
| `intro_offer`   | Intro offer started or intro offer uncancelled.                                                                                  |
| `active`        | Subscription start, renewal, uncancellation, product change, trial conversion, intro offer conversion, or non-renewing purchase. |
| `cancelled`     | Trial, intro offer, or regular subscription cancellation.                                                                        |
| `billing_issue` | Billing issue event.                                                                                                             |
| `paused`        | Subscription paused event.                                                                                                       |
| `expired`       | Trial, intro offer, or regular subscription expiration.                                                                          |
| `refunded`      | Refund event.                                                                                                                    |
| `test`          | Test event.                                                                                                                      |
| `unknown`       | Events that cannot be mapped to a subscription state.                                                                            |

`active_subscription` is `true` for `active`, `trial`, `intro_offer`, `cancelled`, and `billing_issue` statuses. A cancelled subscription can remain active until its expiration date, so it is still treated as active access.

## Sandbox Handling

Sandbox events use the sandbox OneSignal App ID and REST API key. If either sandbox value is missing, Superwall skips sandbox events instead of sending them to your production OneSignal app.

Production events always use the production OneSignal App ID and REST API key.

## Validation

The dashboard validation action requires a test OneSignal User ID. Validation sends a Superwall test event to that user and updates the same tags the integration uses for real events.

If you do not configure a test OneSignal User ID, validation fails without sending a request. Real subscription events can still be sent when they include `onesignalId`.

## Troubleshooting

### Tags are not updating in OneSignal

1. Confirm the production OneSignal App ID and REST API key are configured.
2. Confirm your app sends `onesignalId` to Superwall before subscription events occur.
3. Confirm the value is the OneSignal User ID, not an external ID or push subscription ID.
4. Confirm the user exists in the OneSignal app configured in Superwall.

### Sandbox events are skipped

1. Confirm both sandbox fields are configured.
2. Confirm the sandbox REST API key belongs to the sandbox OneSignal app.
3. Confirm the event environment is `sandbox`.

### Validation fails

1. Add a test OneSignal User ID in the integration settings.
2. Confirm the test user exists in the selected OneSignal app.
3. Confirm the REST API key has permission to update users.

## References

* [OneSignal Users and aliases](https://documentation.onesignal.com/docs/en/users)
* [OneSignal Update user API](https://documentation.onesignal.com/reference/update-user)
* [RevenueCat OneSignal integration](https://www.revenuecat.com/docs/integrations/third-party-integrations/onesignal)