Integrations

Adjust

The Adjust integration automatically sends Superwall subscription and payment events to Adjust via the server-to-server (S2S) event API. Configure per-platform app tokens, map individual event tokens, and track subscription lifecycle events with device-level attribution.

In the Analytics section within Integrations, you can connect your Adjust account to Superwall.

Features

  • Per-Platform Configuration: Separate app tokens and S2S security tokens for iOS and Android
  • Per-Event Token Mapping: Configure an individual Adjust event token for each of the 14 subscription event types. Leave a token blank to skip that event.
  • Revenue Tracking: Automatic revenue attribution for purchase and renewal events
  • S2S Event API: Events sent server-to-server to https://s2s.adjust.com/event
  • Device Identification: Supports Adjust device ID, IDFA, IDFV, GPS advertising ID, and Google App Set ID
  • Callback Parameters: Product ID, transaction ID, and offer code sent as JSON with each event
  • Meta AEM Parameters: Optional IP address, device name, OS version, and ATT status for Meta attribution
  • Sandbox Support: Sandbox events are forwarded with environment: "sandbox"

Configuration

Adjust requires separate credentials for iOS and Android. You can configure one or both platforms.

Platform settings

FieldPlatformDescription
app_token_iosiOSAdjust app token for your iOS app. If iOS and Android share the same Adjust app, enter the same value in both fields.
app_token_androidAndroidAdjust app token for your Android app.
s2s_token_iosiOSOptional S2S security token from Adjust Dashboard > Settings > S2S Security.
s2s_token_androidAndroidOptional S2S security token for Android.
sales_reportingAllRevenue reporting mode: "Revenue" (gross) or "Proceeds" (net after store fees).

Event tokens

Each event type has its own token field. Create the corresponding event in Adjust's AppView, then paste the 6-character token here. Leave blank to skip that event type.

FieldEventTriggered when
event_token_trial_startTrial Startinitial_purchase with TRIAL period
event_token_direct_sub_startDirect Subscription Startinitial_purchase with NORMAL or INTRO period
event_token_trial_convertTrial Convertrenewal with isTrialConversion = true
event_token_renewalRenewalrenewal (not a trial conversion)
event_token_trial_cancelTrial Cancelcancellation with TRIAL period
event_token_cancelCancelcancellation with NORMAL or INTRO period
event_token_uncancelUncanceluncancellation
event_token_billing_issueBilling Issuebilling_issue
event_token_product_changeProduct Changeproduct_change
event_token_subscription_pausedSubscription Pausedsubscription_paused
event_token_one_time_purchaseOne-Time Purchasenon_renewing_purchase
event_token_trial_expireTrial Expireexpiration with TRIAL period
event_token_expireExpireexpiration with NORMAL or INTRO period
event_token_refundRefundAny event with price < 0 (takes precedence over all other mappings)

Example configuration

{
  "app_token_ios": "your_ios_app_token",
  "app_token_android": "your_android_app_token",
  "s2s_token_ios": "your_ios_s2s_token",
  "s2s_token_android": "your_android_s2s_token",
  "sales_reporting": "Revenue",
  "event_token_trial_start": "abc123",
  "event_token_direct_sub_start": "def456",
  "event_token_trial_convert": "ghi789",
  "event_token_renewal": "jkl012",
  "event_token_cancel": "mno345",
  "event_token_refund": "pqr678"
}

SDK Setup

The Adjust integration requires the Adjust device ID to be set in Superwall so that events can be attributed to the correct device. Set this as early as possible after the Adjust SDK initializes.

Setting the Adjust device ID

iOS (Swift):

import Adjust

// After Adjust SDK initializes
if let adid = Adjust.adid() {
    Superwall.shared.setIntegrationAttributes([
        .adjustId: adid
    ])
}

Android (Kotlin):

import com.adjust.sdk.Adjust

// After Adjust SDK initializes
Adjust.getAdid()?.let { adid ->
    Superwall.instance.setIntegrationAttributes(mapOf(
        IntegrationAttribute.ADJUST_ID to adid
    ))
}

Flutter (Dart):

import 'package:adjust_sdk/adjust.dart';

// After Adjust SDK initializes
final adid = await Adjust.getAdid();
if (adid != null) {
  await Superwall.shared.setIntegrationAttributes({
    IntegrationAttribute.adjustId: adid,
  });
}

Setting additional device identifiers

For richer attribution data, pass platform-specific advertising identifiers via setUserAttributes:

iOS (Swift):

import AdSupport
import AppTrackingTransparency

// After ATT consent is granted
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
let idfv = UIDevice.current.identifierForVendor?.uuidString

var attrs: [String: String] = [:]
if idfa != "00000000-0000-0000-0000-000000000000" {
    attrs["idfa"] = idfa
}
if let idfv {
    attrs["idfv"] = idfv
}
Superwall.shared.setUserAttributes(attrs)

Android (Kotlin):

import com.google.android.gms.ads.identifier.AdvertisingIdClient

// On a background thread
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context)
if (!adInfo.isLimitAdTrackingEnabled) {
    Superwall.instance.setUserAttributes(mapOf(
        "advertisingId" to adInfo.id
    ))
}

What happens without it

If no device identifiers are found in userAttributes, the event is skipped and not sent to Adjust. At least one of the following must be present: adjustAdid (or adjustId), idfa, idfv, advertisingId, gps_adid, or google_app_set_id.

Event Mapping

Superwall events are mapped to 14 discrete Adjust event types. INTRO and NORMAL periods are treated identically. Refund detection (price < 0) takes precedence over all other mappings.

Superwall EventConditionAdjust EventRevenue?
initial_purchaseTRIAL periodtrial_startNo
initial_purchaseNORMAL or INTRO perioddirect_sub_startYes
renewalisTrialConversion = truetrial_convertYes
renewalisTrialConversion = falserenewalYes
cancellationTRIAL periodtrial_cancelNo
cancellationNORMAL or INTRO periodcancelNo
uncancellationanyuncancelNo
billing_issueanybilling_issueNo
product_changeanyproduct_changeNo
subscription_pausedanysubscription_pausedNo
non_renewing_purchaseanyone_time_purchaseYes
expirationTRIAL periodtrial_expireNo
expirationNORMAL or INTRO periodexpireNo
Any eventprice < 0refundNo

Each Adjust event type corresponds to the event_token_* field in your configuration. If no token is configured for an event type, that event is skipped.

Callback Parameters

Every Adjust event includes a callback_params field containing a JSON-encoded string with:

ParameterDescriptionAlways present?
product_idThe product identifierYes
transaction_idThe transaction identifierYes
offer_codeThe promotional offer code usedOnly if present

Revenue Tracking

Revenue is only included for these event types:

  • direct_sub_start
  • trial_convert
  • renewal
  • one_time_purchase

All other events (cancellations, expirations, billing issues, refunds) do not carry revenue data.

Revenue calculation

SettingFormulaDescription
"Revenue"priceInPurchasedCurrencyGross revenue in the purchased currency
"Proceeds"priceInPurchasedCurrency * takehomePercentageNet revenue after store fees

Revenue is omitted when the calculated amount is below 0.001. The currency code from the transaction is sent alongside the revenue value.

Device Identification

Adjust uses device-level identifiers for attribution. The integration reads these from userAttributes on the Superwall event:

User attributeAdjust parameterPlatform
adjustAdid or adjustIdadidAll
idfaidfaiOS
idfvidfviOS
advertisingId or gps_adidgps_adidAndroid
google_app_set_idgoogle_app_set_idAndroid

adjustAdid is the preferred attribute name. The legacy adjustId name is also accepted for backward compatibility. Any one device identifier is sufficient for the event to be sent.

Meta AEM parameters

When present in userAttributes, these additional parameters are forwarded to support Meta's Aggregated Event Measurement:

User attributeAdjust parameterPlatform
ip_addressip_addressAll (IPv4 only)
device_namedevice_nameAll
os_versionos_versionAll
attStatusatt_statusiOS only

Platform Support

The integration determines the platform from the store field on each event:

StorePlatformSupported?
APP_STOREiOSYes
PLAY_STOREAndroidYes
STRIPESkipped
PADDLESkipped

Stripe and Paddle events do not identify the originating app platform, so they cannot be attributed in Adjust and are skipped.

Sandbox Handling

Sandbox events are sent to Adjust. The environment field on the S2S request is set to "sandbox" for sandbox events and "production" for production events. Adjust uses this field to distinguish test data from real data.

Testing the Integration

1. Validate credentials

When you save the integration, Superwall sends a test event to verify that your app tokens and S2S tokens are valid. If no event tokens are configured yet, credentials are still accepted.

2. Trigger test events

  • iOS: Use TestFlight with a sandbox Apple ID. Note that StoreKit Configuration files do not generate App Store Server Notifications, so webhooks and downstream integrations will not fire.
  • Android: Use license test accounts to perform purchases.

3. Verify in Adjust

Check the Adjust dashboard:

  1. Event Log: Confirm events are arriving with the correct event token
  2. Callback Data: Verify callback parameters contain the expected product_id and transaction_id
  3. Revenue: Confirm revenue amounts and currency codes for purchase events
  4. Device Attribution: Ensure events are attributed to the correct device via adid

Best Practices

  1. Set the Adjust device ID early: Call setIntegrationAttributes with the Adjust ID as soon as the Adjust SDK initializes. Events without any device identifier are skipped.
  2. Pass advertising identifiers: Include idfa/idfv (iOS) or advertisingId (Android) via setUserAttributes for richer attribution.
  3. Configure S2S security: Set the per-platform S2S tokens to authenticate requests and prevent spoofed events.
  4. Only configure tokens you need: Leave event tokens blank for event types you don't want to track. This keeps your Adjust event log focused.
  5. Be consistent with revenue reporting: Choose Revenue or Proceeds and keep the same setting across all your analytics integrations.
  6. Monitor for opted-out devices: Adjust returns HTTP 451 when a device has opted out of tracking. These events are skipped automatically.

Troubleshooting

Events not appearing

  1. Check device identifiers: At least one of adjustAdid/adjustId, idfa, idfv, advertisingId, gps_adid, or google_app_set_id must be set in user attributes.
  2. Check event token: The specific event type must have a token configured (e.g., event_token_trial_start). Events without tokens are skipped.
  3. Check app token: The per-platform app token (app_token_ios or app_token_android) must be set for the event's platform.
  4. Check store: Only APP_STORE (iOS) and PLAY_STORE (Android) events are supported. Stripe and Paddle events are skipped.
  5. Check for 451 responses: The device may have opted out of tracking. Adjust returns HTTP 451 in this case and the event is skipped.

Revenue not tracking

  1. Check event type: Only direct_sub_start, trial_convert, renewal, and one_time_purchase carry revenue.
  2. Check amount: Revenue must be >= 0.001 to be included.
  3. Check sales reporting: Verify your Revenue vs Proceeds setting.
  4. Refunds: Refund events (price < 0) do not include revenue data in the Adjust request.

Device attribution issues

  1. Check attribute name: Use adjustAdid (preferred) or adjustId in user attributes.
  2. Check platform identifiers: Verify idfa/idfv (iOS) or advertisingId/gps_adid (Android) are being passed.
  3. Check platform detection: APP_STORE maps to iOS, PLAY_STORE maps to Android.

S2S API errors

  • App token not found (404): The app token is not recognized by Adjust. Verify it matches your app in the Adjust dashboard.
  • Authentication failure (401/403): If using S2S tokens, verify they match the tokens in Adjust Dashboard > Settings > S2S Security.
  • Device opted out (451): The device has opted out of tracking. This is not an error — the event is skipped.

How is this guide?

On this page