# 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

# Push, Promote & Publish

Ship paywalls with git semantics: push seals an immutable version, promote points production at it, publish does both.

Shipping has git semantics on purpose: &#x2A;*push saves, promote ships.** Every push mints a sealed, immutable version; nothing your users see changes until promote points production at it.

```bash
superwall push                    # build + version. Production untouched.
superwall promote                 # point production at the latest push
superwall publish                 # push + promote in one step
superwall publish -m "Q3 test"    # record why
```

The scaffolded project mirrors these as package scripts (`dev`, `push`, `promote`, `ship`).

> **Note:** Pushing requires the **headless paywalls** feature to be enabled on your Superwall application — it's a server-side flag, so if a push says it isn't enabled, the account owner needs to have it turned on.

## `superwall push`

Builds every paywall, versions the changed ones, and leaves production alone. Re-running with nothing changed is a no-op.

| Flag                   | What it does                               |
| ---------------------- | ------------------------------------------ |
| `--id <id>`            | Limit the push to one paywall (repeatable) |
| `--rename <old>=<new>` | Declare a directory rename (see below)     |
| `-m <note>`            | Record why this version exists             |

The **first push binds** each paywall — creating it on Superwall if needed — and records the binding in `superwall.lock`. Commit that file: it's what makes every machine and CI push to the same paywalls. After that, push always updates the same paywall; no IDs ever appear in your code.

A push refuses — before anything is written — when:

* **A selected paywall has diagnostics.** Publishing is immutable; fix the named problems first. They're the same warnings `superwall dev` prints.
* **A product in `config.ts` doesn't exist on the dashboard.** Every variable on it would be undefined on device. Create the products first — see [Products](/docs/framework/products) and the [CLI reference](/docs/framework/cli).
* **A directory rename is unresolved** (below).

## Renames

Renaming a paywall directory is detected, never guessed. Interactively, push asks:

```
? `pro-upgrade` is not in superwall.lock. Is it a new paywall, or renamed?
  › Renamed from plus-upgrade   paywall 208540
    Create a new paywall
```

Choosing the rename keeps the live paywall attached to the new directory. In CI there's no one to ask, so declare it — anything unresolved stops the push rather than silently creating a duplicate:

```bash
superwall push --rename plus-upgrade=pro-upgrade
```

Deleting a paywall directory never blocks a push: the dashboard paywall keeps serving, and restoring the directory re-binds it.

## Source snapshots

Every push also snapshots your `superwall/` source to Superwall, so the dashboard can show — and diff — the exact code each version was built from. The `-m "why"` note is recorded there too.

What never leaves your machine: `.env` files, `node_modules/`, `.superwall/`, and anything your `.gitignore` lists.

> **Warning:** If any import reaches outside the project directory, the push warns naming each offender, and the dashboard disables remote editing for that paywall — the pushed source can't be rebuilt elsewhere. Copy shared code into `superwall/components/` instead. See [Project structure](/docs/framework/project-structure).

## `superwall promote`

Points production at a pushed version. Promote never rebuilds — it only moves the live pointer, so it's instant, and rollback is the same move in reverse:

```bash
superwall promote                              # latest push, every paywall
superwall promote --id plus-upgrade            # just one
superwall promote --id plus-upgrade --version 5
# → Rolled back version 7 → 5
```

`--version`/`-v` (with a single `--id`) picks a specific version — pinning forward or rolling back are the same operation.

## `superwall publish`

Push + promote in one step. It also warns about other paywalls that are pushed-but-not-live, so nothing ships half-forgotten.

`publish` requires git — the source snapshot is part of every publish.

## CI

Interactive machines authenticate once with `superwall login`. In CI, set `SUPERWALL_API_KEY` (an `sk_…` key) in the environment — `superwall/.env` works locally and is gitignored. `dev` needs no login at all.

A typical CI ship step:

```bash
superwall push --rename old=new -m "$COMMIT_MESSAGE"   # renames declared, reason recorded
superwall promote
```

Because `superwall.lock` is committed, CI pushes to exactly the same paywalls as every developer machine.