# 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

# Variables & Personalization

React to user attributes, device state, and placement parameters — and write paywalls the dashboard can experiment on without a rebuild.

Everything your app and the SDK tell a paywall about the presentation arrives through `useVariables()`: who the user is, what device they're on, and what the placement was called with. Read these defensively and a single paywall can greet a returning user by name, adapt to platform, or react to any parameter your app passes — all without a rebuild.

## `useVariables()`

```tsx
import { useVariables } from "superwall/hooks";

const { device, user, params } = useVariables();
```

Three records, three sources:

* **`device`** — filled in by the SDK: `platform`, `deviceModel`, `osVersion`, `appVersion`, `deviceLocale`, `regionCode`, `deviceCurrencyCode`, `subscriptionStatus`, `activeEntitlements`, `daysSinceInstall`, `totalPaywallViews`, and more.
* **`user`** — whatever your app set via `setUserAttributes` (`user.firstName`, `user.plan`, …).
* **`params`** — whatever the placement was called with (`params.placementName`, plus anything the app passed alongside it).

```tsx
const name = typeof user.firstName === "string" ? user.firstName : undefined;

<h1>{name ? `Welcome back, ${name}` : "Go Pro"}</h1>
<span>{device.platform ?? "—"}</span>
```

## Guard every read

All three records are filled in by the host — your paywall controls none of them, so every read needs a fallback:

* For &#x2A;*`device`** fields, `?? "—"` (or any sensible default) suffices — the SDK guarantees the shape, just not that a value has arrived yet.
* For &#x2A;*`user`*&#x2A; and &#x2A;*`params`**, the host controls the *type* too, so check it before using it: `typeof params.placementName === "string"`. An attribute your app sets as a number today might be a string tomorrow, and the paywall must not crash either way.

> **Warning:** `device.isSandbox` is a string, not a boolean. Compare it as one.

While previewing, every one of these values is editable live in the studio's **Variables** panel — user attributes, device properties, placement params, and per-product variables — seeded from your app's real sample data. Change a value and watch the paywall react. See [The studio](/docs/framework/studio).

## `useUser()`

Shorthand for when you only need the user record:

```tsx
import { useUser } from "superwall/hooks";

const user = useUser();
```

Identical to `useVariables().user` — reach for it when the device and params records aren't needed.

## `useDevice()`

The same device record as `useVariables().device&#x60;, plus &#x2A;*`orientation`**:

```tsx
import { useDevice } from "superwall/hooks";

const { orientation, platform, deviceModel } = useDevice();
```

`orientation` is `"portrait" | "landscape"`, measured in the page itself — it updates the moment the device turns, so you can build layouts that answer to rotation. The orientation example reflows to a two-column grid in landscape rather than shrinking the portrait layout; see [Examples](/docs/framework/examples).

## Built to be experimented on

Notice what's missing: variables are never *declared* in code. What the paywall reads — user attributes, device state, placement params, product variables, trial eligibility — is supplied by the app and the store at runtime, and the studio overrides all of it live while previewing.

Write every read defensively — guarded, typed, with a designed fallback — and every one of those values becomes a knob the dashboard can turn without a rebuild. A paywall that renders sensibly for any combination of inputs can be A/B tested freely.

> **Tip:** The personalization example shows the full doctrine in one project: `?? "—"` for SDK-guaranteed device fields, `typeof` checks for host-controlled user and params reads, and designed fallbacks for every string. See [Examples](/docs/framework/examples).