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: push saves, promote ships. Every push mints a sealed, immutable version; nothing your users see changes until promote points production at it.

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).

Superwall for Agents is in private beta: pushing needs it enabled on each Superwall app you push to. If a push says it isn't enabled for an app yet, contact support@superwall.com for access.

superwall push

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

FlagWhat it does
--id <id>Limit the push to one paywall (repeatable)
--platform <p>Limit the push to one platform (repeatable), see below
--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 and the CLI reference.
  • 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:

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.

Every pushed version is previewable in the dashboard: opening the paywall there opens the hosted studio on the live version, with the others one switch away.

Several platforms

One project can push to several platforms (an iOS app, an Android app, a web app) as long as the apps belong to the same Superwall project. Each paywall's config.ts lists the platforms it's for:

export default definePaywall({ platforms: ["ios", "android"], products: { … } });

superwall.lock binds one Superwall app per platform under apps. The first push that meets a new platform binds it: when your account has exactly one app of that platform, it's picked for you; when it has several, push asks; when it has none, push says so. In CI there's no one to ask, so bind it once interactively, or add the line yourself, "web": "<app id>", with ids from superwall apps list.

A paywall pushed to two platforms becomes two dashboard paywalls, each with its own versions, and the output names them:

plus-upgrade · ios
  Linked to paywall 208551
  Pushed version 4 (3f2a9c1)
plus-upgrade · android
  Linked to paywall 208560
  Pushed version 2 (8b71e04)

--platform <p> narrows push, promote, and publish to one platform (the bound app's id works too), and promote --version needs it when the paywall is on several:

superwall push --platform android
superwall promote --id plus-upgrade --platform ios --version 3

Until a project is bound on a second platform, platforms is optional and everything pushes to the one app. Once it is, every paywall has to say which platforms it's for; a push stops on a silent one rather than create a paywall on the wrong app.

Source snapshots

Every push also snapshots your superwall/ source to Superwall, so the exact code each version was built from is recoverable. The -m "why" note is recorded on that source commit, it only lands when the source actually changed.

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

If any import reaches outside the project directory, the push warns and records the version as non-portable. The pushed source can't be rebuilt elsewhere. The warning names the first five offenders, then counts the rest. Either copy the shared code into superwall/components/, or install it from a package registry. See 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:

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
superwall promote --id plus-upgrade --platform ios --version 5   # a paywall on several platforms

--version/-v (with a single --id, plus --platform when that paywall is on several platforms) picks a specific version. Pinning forward and 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.

push and publish both require git. The source snapshot is part of each.

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:

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.

How is this guide?

On this page