Actions

Close the paywall, restore purchases, open links, request OS permissions, and call back into your app — everything a paywall asks its host to do.

A paywall runs inside your app, and some things only the host can do: dismiss the paywall, open a link, prompt for a permission, run your app's code. All of it goes through useActions():

import { useActions } from "superwall/hooks";

const { close, restore, openUrl, requestPermission, requestCallback } = useActions();

The actions

ActionUse it for
close()Closing the paywall — the X button. Closing is not navigation.
restore()Restore purchases. Fire-and-forget: success arrives as a transaction_complete event or a dismissed paywall — there is no return value to await. See Purchases.
openUrl(url)Terms, privacy, any link. Always this, never <a href>.
openExternalUrl(url)Open in the system browser instead of in-app.
openDeepLink(link)Deep link into the app.
customPlacement(name, params?)Fire a Superwall placement — which can present another paywall.
requestPermission(type)OS permission prompt. Resolves "granted" | "denied" | "unsupported".
requestCallback(name, options?)Run your app's code and await its answer. Resolves { status: "success" | "failure", data? }.
requestStoreReview("in-app" | "external")Store review prompt.

Links go through openUrl, never an <a href>. Inside a webview, an anchor either does nothing or navigates the paywall away from itself — openUrl hands the URL to the host so it opens the way the platform expects.

Closing works the same way: the paywall lives on a navigation stack of its own pages, but leaving the paywall isn't a navigation — it's close(). See Pages & navigation.

Permissions

const status = await requestPermission("notification");
// "granted" | "denied" | "unsupported"

Permission types: notification, camera, microphone, location, background_location, contacts, read_images, read_video (Android only), tracking.

Callbacks — ask your app a question

A callback runs code in your app and hands the answer back to the paywall — anything the paywall cannot know on its own: does this account exist, is this referral code valid, what did the user pick during signup.

const result = await requestCallback<{ exists: boolean }>("checkAccount");

if (result.status === "success" && result.data?.exists) {
  router.push("welcome-back");
}

Type the answer with a claim, as above — the generic is your statement of what the app returns.

Permission vs callback

A permission asks the OS; a callback asks your app. Both resolve from code the paywall does not control, which shapes how you use them:

  • Show something while they run. The OS prompt or your app's code takes as long as it takes.
  • Treat a denial as an ordinary outcome, not an error. A user who declines notifications is still a user — design the path that continues without.

In development

In superwall dev, actions don't reach a real host — they're logged in the studio's event log, and permission, callback, and purchase requests prompt you to pick the outcome. That makes both branches of every flow testable before a device ever sees it. See The studio.

The permissions example shows requestPermission and requestCallback side by side, with a denial treated as an outcome rather than an error. See Examples.

How is this guide?

On this page