# 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

# Superwall

The shared instance of Superwall that provides access to all SDK features.

> **Note:** You must call [`configure()`](/docs/android/sdk-reference/configure) before accessing `Superwall.instance`, otherwise your app will crash.

## Purpose

Provides access to the configured Superwall instance after calling [`configure()`](/docs/android/sdk-reference/configure).

## Signature

```kotlin
companion object {
    val instance: Superwall
}
```

```java
// Java
public static Superwall getInstance()
```

## Parameters

This is a companion object property with no parameters.

## Returns / State

Returns the shared `Superwall` instance that was configured via [`configure()`](/docs/android/sdk-reference/configure).

## Usage

Configure first (typically in Application class):

```kotlin
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        Superwall.configure(
            application = this,
            apiKey = "pk_your_api_key"
        )
    }
}
```

Then access throughout your app:

```kotlin
Superwall.instance.register("feature_access") {
    // Feature code here
}
```

Set user identity and attributes:

```kotlin
Superwall.instance.identify("user123")

Superwall.instance.setUserAttributes(mapOf(
    "plan" to "premium",
    "signUpDate" to System.currentTimeMillis()
))
```

Reset the user:

```kotlin
Superwall.instance.reset()
```

> **Note:** Avoid calling `Superwall.instance.reset()` repeatedly. Resetting rotates the anonymous user ID, clears local paywall assignments, and requires the SDK to re-download configuration state. Only trigger a reset when a user explicitly logs out or you intentionally need to forget their identity. See [User Management](/docs/android/quickstart/user-management) for more guidance.

Set delegate:

```kotlin
Superwall.instance.delegate = this
```

Consume a purchase (2.6.2+):

```kotlin
// Using coroutines
lifecycleScope.launch {
    val result = Superwall.instance.consume(purchaseToken)
    result.fold(
        onSuccess = { token ->
            println("Purchase consumed: $token")
        },
        onFailure = { error ->
            println("Failed to consume: ${error.message}")
        }
    )
}

// Using callback
Superwall.instance.consume(purchaseToken) { result ->
    result.fold(
        onSuccess = { token ->
            println("Purchase consumed: $token")
        },
        onFailure = { error ->
            println("Failed to consume: ${error.message}")
        }
    )
}
```

Show an alert over the current paywall (2.5.3+):

```kotlin
Superwall.instance.showAlert(
    title = "Important Notice",
    message = "Your subscription will renew soon",
    actionTitle = "View Details",
    closeActionTitle = "Dismiss",
    action = {
        // Handle action button tap
        navigateToSubscriptionSettings()
    },
    onClose = {
        // Handle close/dismiss
        println("Alert dismissed")
    }
)
```

Set integration attributes for analytics (2.5.3+):

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

Superwall.instance.setIntegrationAttributes(
    mapOf(
        AttributionProvider.ADJUST to "adjust_user_id_123",
        AttributionProvider.MIXPANEL to "mixpanel_distinct_id_456",
        AttributionProvider.META to "meta_user_id_789",
        AttributionProvider.GOOGLE_ADS to "google_ads_id_101",
        AttributionProvider.GOOGLE_APP_SET to "google_app_set_id_202",
        AttributionProvider.APPSTACK to "appstack_user_id_303"
    )
)
```

## Observe customer info (2.6.6+)

Superwall now exposes purchase history and entitlement snapshots via a `StateFlow<CustomerInfo>`. Each emission contains merged device, web, and external purchase controller data so you can react to subscription changes without wiring up your own polling layer.

```kotlin
lifecycleScope.launch {
  Superwall.instance.customerInfo.collect { info ->
    val activeProductIds = info.activeSubscriptionProductIds
    val activeEntitlementIds = info.entitlements
      .filter { it.isActive }
      .map { it.id }

    updateUi(
      subscriptions = activeProductIds,
      entitlements = activeEntitlementIds
    )
  }
}
```

Need an immediate snapshot (for example during cold start)? Call `Superwall.instance.getCustomerInfo()` to synchronously read the latest cached value, or wire both together:

```kotlin
val cachedInfo = Superwall.instance.getCustomerInfo()
render(cachedInfo)

lifecycleScope.launch {
  Superwall.instance.customerInfo.collect { render(it) }
}
```

Pair the flow with [`SuperwallDelegate.customerInfoDidChange(from:to:)`](/docs/android/sdk-reference/SuperwallDelegate#customerinfodidchangefrom-customerinfo-to-customerinfo) when you need to mirror changes into analytics.

Java usage:

```java
// Access the instance
Superwall.getInstance().register("feature_access", () -> {
    // Feature code here
});

// Set user identity
Superwall.getInstance().identify("user123");
```