# 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

# CLI Reference

Every superwall command — create, dev, push, promote, publish — with flags, auth, and the checks that run before anything ships.

The CLI has git semantics on purpose: &#x2A;*push saves, promote ships.** Every push mints a sealed version; nothing users see changes until promote points production at it. This page is the command reference — [Push, promote & publish](/docs/framework/push-and-promote) explains the model.

```sh
superwall create                  # scaffold superwall/ inside your app
superwall dev                     # studio on http://localhost:6100
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 package scripts mirror these (`dev`, `push`, `promote`, `ship`).

## Auth

Run `superwall login` once interactively. In CI, set `SUPERWALL_API_KEY` (an `sk_…` key) — the project's `.env` is the usual home for it. `dev` needs no login.

## `superwall create`

Scaffolds a complete project: the directory skeleton, a starter paywall, dependencies installed, your Superwall app connected, and a git init if needed.

| Flag               | What it does                                                                      |
| ------------------ | --------------------------------------------------------------------------------- |
| `--example <name>` | Start from an [example](/docs/framework/examples) instead of the default starter. |

## `superwall dev`

Hosts [the studio](/docs/framework/studio) for the project — or several at once with a glob (`superwall dev examples/*`). Regenerates `superwall.d.ts` first, so route and product types are always current.

| Flag           | What it does                                                 |
| -------------- | ------------------------------------------------------------ |
| `--port`, `-p` | Port, default `6100` — moves to the next free port if taken. |
| `--host`       | Bind address, for previewing from another device.            |

Project problems (stray files in `app/`, duplicate routes) print as warnings here — the same ones that block a push, so fix them as they appear.

## `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 to one paywall; repeatable.                    |
| `--rename <old>=<new>` | Declare a directory rename so CI can resolve it.     |
| `-m <note>`            | Record why, shown with the version in the dashboard. |

A push refuses — before anything is written — when:

* a selected paywall has diagnostics (publishing is immutable; fix first),
* a product in `config.ts` doesn't exist on the dashboard (every variable on it would be undefined on device),
* a directory rename is unresolved (below).

The first push binds each paywall — creating it on Superwall if needed — and records the binding in `superwall.lock`; commit that file. After that, push always updates the same paywall; no IDs ever appear in your code.

Every push also snapshots your `superwall/` source, so the dashboard can show and diff the code each version was built from. `.env`, `node_modules/`, and gitignored files never leave the machine.

### Renames

Renaming a paywall directory is detected, never guessed. Interactively, push asks whether the unfamiliar directory is a rename (keeping the live paywall attached) or a new paywall. In CI, declare it — anything unresolved stops the push rather than creating a duplicate:

```sh
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.

## `superwall promote`

Points production at a pushed version. Promote never rebuilds — it only moves the live pointer.

| Flag                  | What it does                                                                     |
| --------------------- | -------------------------------------------------------------------------------- |
| `--id <id>`           | Limit to one paywall.                                                            |
| `--version`, `-v <n>` | Pick a specific version (with a single `--id`) — which is also the **rollback**. |

```sh
superwall promote --id plus-upgrade --version 5
# → Rolled back version 7 → 5
```

## `superwall publish`

Push + promote in one step. Takes `-m <note>`. Also warns about other paywalls that are pushed-but-not-live, so nothing ships half-forgotten.

## Before pushing: create the products

A push refuses if a `config.ts` names a product the dashboard doesn't have. The fix is a command away — the same CLI writes products directly:

```sh
superwall entitlements list --json                 # grab the NUMERIC entitlement id
superwall products create pro_3999_year \
  --project <id> --app <id> \
  --name "Annual" --price 39.99 --period year \
  --trial-days 7 --entitlement <numeric-id> --json
```

* `--entitlement` takes the **numeric id** (`55688`), not the identifier (`pro`) — the identifier fails with a decode error.
* Pass `--project` explicitly when your account has several, or the command errors with "Multiple projects found".
* `--price` is major units (`39.99`); `--period` is `day|week|month|year`; `--trial-days` sets the intro offer.
* `--dry-run` confirms the target before writing anything.

## Two gates worth checking early

* **Headless paywalls must be enabled on the application** — otherwise every push fails with "Headless paywalls are not enabled for this application". It's a server-side feature flag; check with `superwall apps list --json` and look for `headless_paywalls` in `features_enabled`. Contact us to have it turned on.
* **One broken surface blocks the whole push.** A leftover scaffold aimed at a nonexistent product stops everything — push what you built with repeated `--id` flags instead of touching unrelated directories.

For the full error-message-to-fix table, see [Troubleshooting](/docs/framework/troubleshooting).