Superwall + web2wave integration

Grant and revoke Superwall entitlements automatically when a web2wave subscription changes.

🤖

Integrate with an AI coding agent — Cursor, Claude Code, Codex, Windsurf and others.

Open your mobile app project in the agent, paste the prompt below, and answer its questions. The agent reads the web2wave mobile integration playbook, detects your subscription provider and attribution tool, and does the integration step by step.

You are integrating web2wave into this mobile app.

1. Read the web2wave mobile integration playbook and follow it end to end:
   https://raw.githubusercontent.com/web2wave/integration-skills/main/docs/web2wave-mobile-integration-playbook.md
   (Docs index for agents: https://docs.web2wave.com/llms.txt)

2. First inspect this project: the platform (iOS, Android, Flutter, React Native, Unity),
   the subscription provider (RevenueCat, Adapty, Qonversion, Apphud, Superwall) and the
   attribution tool (AppsFlyer, Adjust, Branch). Show me what you found and ask me to confirm.

3. Ask me every question the playbook lists, in its order. Do not guess settings or secrets.
   Never hardcode API keys: ask me for my web2wave API key and keep it in config / env.

4. Implement only the branch we agreed on. Use the web2wave SDK for this platform and take
   code samples from the documentation pages the playbook links to.

5. At the end, list the web2wave project settings I still have to fill in, and run or
   describe the acceptance tests from the playbook.

Where to paste it: the chat or agent panel in Cursor and Windsurf, the terminal session in Claude Code and Codex. You can also save the playbook as a project skill or instructions file and keep the prompt short.

web2wave can sync web subscriptions to Superwall. When a user buys, renews, cancels or is refunded on web, web2wave grants or revokes a Superwall entitlement for the matching Superwall user via the Superwall API. Your app then unlocks premium through its regular Superwall entitlement checks.

How it works

  1. The user pays on web (quiz / paywall).
  2. Your app tells web2wave which Superwall user this is — by sending the superwall_profile_id user property (the value of Superwall.shared.userId after identify()).
  3. web2wave calls Superwall:
    • subscription active, trialing or past due → grant the entitlement until the next charge date;
    • subscription canceled, expired, refunded, etc. → revoke the entitlement.

Every sync is written to the user's event log (superwall_entitlement_granted, superwall_entitlement_revoked, superwall_entitlement_grant_failed, …), so you can see what happened per user.

Step 1. Add Superwall settings to your web2wave project

Open Project settings → Deeplinks (the Superwall block), enable Synchronize subscriptions with Superwall and fill in:

  • Superwall Project ID — your project id from the Superwall dashboard.
  • Superwall API Key — an organization-scoped key from Superwall Settings → API Keys with the entitlements:read and entitlements:write scopes. Keep it private: it is used by web2wave on the server only and must never be shipped in the app.
  • Superwall Entitlement — the entitlement identifier from your Superwall dashboard (for example pro). You can enter several entitlements separated by commas.

Choose which ID to use

Create Superwall user ID on subscription — enable it only if your app identifies Superwall with the same ID as web2wave (email or user_id). In that case web2wave does not wait for superwall_profile_id and uses the ID chosen in ID for Superwall:

OptionID sent to Superwall
Email or user_idemail if known, otherwise user_id
user_idalways the web2wave user_id
app_profile_id or user_idapp_profile_id user property, otherwise user_id

If your app uses its own Superwall IDs, leave it off and send superwall_profile_id instead (Step 2).

Per-price overrides

Different plans can unlock different entitlements. Open a price and fill Custom Superwall Entitlements:

FieldUsed when
Superwall Entitlement Androidthe user's platform is Android
Superwall Entitlement IOSthe user's platform is iOS
Superwall Entitlementany other case

Priority: platform field of the price → price field → project setting. Leave a field empty to fall back to the next level.

Step 2. Send the Superwall user ID to web2wave

Option A. Send it after install

If the user buys on web first and installs the app later, resolve the web2wave user_id from the deeplink (see Pass subscription from web to app or deferred deeplinks) and send the Superwall user ID with the API:

curl -X POST "https://api.web2wave.com/api/user/properties?user=WEB2WAVE_USER_ID" \
  -H "api_key: YOUR_WEB2WAVE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"property": "superwall_profile_id", "value": "SUPERWALL_USER_ID"}'

As soon as the property is saved, web2wave syncs the user's active subscriptions to Superwall — no extra call is needed.

Or use the web2wave SDK — read the Superwall user ID after the web2wave user_id is resolved from the deeplink, and send it with setSuperwallProfileID. Call it after identify() so that you send the same ID Superwall uses:

import SuperwallKit
import Web2Wave

func sendSuperwallUserIDToWeb2Wave(web2waveUserId: String) async {
    Web2Wave.shared.apiKey = "your-api-key"

    let superwallUserID = Superwall.shared.userId // Superwall user ID after identify()

    switch await Web2Wave.shared.setSuperwallProfileID(
        web2waveUserId: web2waveUserId,
        superwallProfileID: superwallUserID
    ) {
    case .success: print("Successfully sent Superwall ID to Web2Wave API")
    case .failure(let error): print("Error sending data to Web2Wave API: \(error)")
    }
}
import com.superwall.sdk.Superwall
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import web2wave.Web2Wave

private val scope = CoroutineScope(Dispatchers.IO)

fun sendSuperwallUserIDToWeb2Wave(web2waveUserId: String) {
    Web2Wave.initWith("YOUR_API_KEY_TO_WEB2WAVE")

    val superwallUserID = Superwall.instance.userId // Superwall user ID after identify()

    scope.launch {
        Web2Wave.setSuperwallProfileID(web2waveUserId, superwallUserID)
    }
}
import 'package:superwallkit_flutter/superwallkit_flutter.dart';
import 'package:web2wave/web2wave.dart';

Future<void> sendSuperwallUserIDToWeb2Wave(String web2waveUserId) async {
  Web2Wave.shared.initialize(apiKey: 'your-api-key');

  final superwallUserID = await Superwall.shared.getUserId(); // after identify()

  final result = await Web2Wave.shared.setSuperwallProfileID(
    web2waveUserId: web2waveUserId,
    superwallProfileId: superwallUserID,
  );

  if (result.isSuccess) {
    print('Successfully sent Superwall ID to Web2Wave API');
  } else {
    print('Error sending data to Web2Wave API: ${result.errorMessage}');
  }
}

SDK methods setSuperwallProfileID (Unity: SetSuperwallProfileID) are available in: Swift 1.2.0+, Kotlin 1.2.0+, Java 1.2.0+, Flutter 1.1.11+, React Native 1.4.0+, Unity 1.3.0+. See the mobile SDK overview.

Option B. Pass it in the WebView URL

If you open the quiz or paywall inside your app, add the ID as a query parameter:

https://yourdomain.com/quiz-slug?superwall_profile_id=SUPERWALL_USER_ID

See Embed quizzes and paywalls into mobile apps. URL-encode the value if it contains special characters.

Step 3. The entitlement appears in Superwall

After the sync, the user has an active manually granted entitlement in the Superwall dashboard, and the Superwall SDK reports it as active. All later changes — renewals, cancellations, refunds — are reflected automatically.

Good to know

  • Expiration date. The entitlement is granted until the subscription's next charge date; the next renewal replaces it. If there is no next charge date, the grant has no expiration and lasts until web2wave revokes it.
  • Renewals. Before each grant, web2wave revokes the user's previous active grants for the same entitlement, so there is always one active grant.
  • Device ID. Superwall requires a device for a grant. web2wave uses the superwall_device_id user property if you set one, otherwise a synthetic ID (web2wave:<user_id>).
  • Retries. Temporary API failures are retried automatically; failed attempts are visible in the user's event log.
  • No Superwall ID, no sync. Users without superwall_profile_id (and without Create Superwall user ID on subscription) are skipped.