# 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

# Project Structure

How a superwall/ directory is laid out, the two files the CLI manages, and the rules that keep a project portable.

Everything Superwall-related in your app lives in one `superwall/` directory — or the repo root, if you keep paywalls in a dedicated repo. It's a self-contained npm project: clone it, install, run `superwall dev`, and it works. Your host app needs no npm setup of its own.

## Layout

```ts
superwall/
├── package.json          depends on `superwall`, react, react-dom
├── tsconfig.json
├── superwall.d.ts        generated — commit, never edit
├── superwall.lock        dashboard bindings — commit
├── .gitignore
├── components/           components shared across paywalls
├── messages/             shared string catalogs (en.ts, de.ts, …)
├── assets/               shared images, video, fonts
├── paywalls/<id>/        one directory per paywall
│   ├── config.ts         required — definePaywall({ name, products })
│   ├── app/              pages — index.tsx (required), layout.tsx, more pages
│   ├── components/       this paywall's own components
│   ├── messages/         this paywall's own strings
│   └── assets/           this paywall's own assets
└── funnels/<id>/         same shape, for funnels
```

`components/`, `messages/`, and `assets/` work at both levels: shared at the root, local inside a paywall. `@/…` imports resolve from the `superwall/` root:

```ts
import { Button } from "@/components/Button";
```

## The rules

A few conventions keep every project buildable, portable, and understandable at a glance:

* **`app/` holds pages and nothing else.** Every `.tsx` file in `app/` is a page — lowercase-kebab filename, default-exported component. `layout.tsx` at the top level is the one reserved name; stylesheets may sit beside pages. Anything else belongs in `components/`. A stray file in `app/` is a warning in dev and blocks a push.
* **Every paywall starts at `app/index.tsx`** and must have a `config.ts`.
* **The directory name is the identifier.** It's the URL in dev and the dashboard binding on push — lowercase-kebab. The `name` in `config.ts` is only the human-readable label shown in the dashboard.
* **No build tooling.** No vite config, no `index.html`, no entry point — the framework owns the build end to end.
* **Never name the package `"superwall"`** in `package.json`. That would shadow the framework import. `superwall create` names it after your app.

Commands work from your app root or from inside `superwall/` alike, and a globally installed `superwall` always defers to the project's own installed version — so everyone on the team builds with the version the project pins.

## Two files the CLI manages — commit both

### `superwall.d.ts`

Regenerated on every `dev` and `push`. It's what makes `router.push("plans")` autocomplete and reject typos, gives `getProduct` and `purchase` their typed product references, and makes asset imports typecheck. Never edit it; never delete it.

### `superwall.lock`

Binds each paywall directory to its paywall on the dashboard, and records which Superwall app the project pushes to. Committing it is what makes every machine — and CI — push to the same paywalls. Nothing about the dashboard ever appears in `config.ts`; the lock file is the only place bindings live.

Renaming a paywall directory is safe: the next `push` notices and asks whether it's a rename (keeping the live paywall attached) or a brand-new paywall. In CI, declare it with `--rename old=new`. See [Push, promote & publish](/docs/framework/push-and-promote).

## Keep imports inside the project

Import from within `superwall/` or from packages listed in its `package.json`. An import that reaches outside — say `../../src/theme` — still builds on your machine, but the pushed source can no longer be rebuilt anywhere else, so the dashboard disables remote editing for that paywall and the push warns, naming each offender.

Copy shared code into `superwall/components/` instead. Duplication here is deliberate: it's what keeps the project self-contained.

## `.env`

`superwall/.env` (with your app root's `.env` as a fallback) holds project credentials — `SUPERWALL_API_KEY` for CI pushes. It's gitignored and never leaves your machine: source pushes exclude `.env*`, `node_modules/`, `.superwall/`, and anything your `.gitignore` lists.

## Funnels

Multi-step flows — onboarding quizzes, web funnels — use exactly the same layout as paywalls and live under `superwall/funnels/<id>/`. A funnel is one surface whose steps are pages, not a chain of separate paywalls. See [Pages & navigation](/docs/framework/navigation).