From f5ec9ece9c13438d1dad6e275fd42fa340061514 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Wed, 8 Apr 2026 12:48:38 +0200 Subject: [PATCH 1/2] s --- content/docs/integrations/adjust.mdx | 367 +++++++++++++++++---------- 1 file changed, 237 insertions(+), 130 deletions(-) diff --git a/content/docs/integrations/adjust.mdx b/content/docs/integrations/adjust.mdx index 96d1377..0633f90 100644 --- a/content/docs/integrations/adjust.mdx +++ b/content/docs/integrations/adjust.mdx @@ -1,197 +1,304 @@ --- title: "Adjust" -description: "The Adjust integration automatically sends Superwall subscription and payment events to Adjust via the server-to-server (S2S) event API. Track subscription lifecycle events, attribute revenue, and measure campaign performance with automatic event mapping and device-level identification." +description: "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: +In the **Analytics** section within **Integrations**, you can connect your Adjust account to Superwall. -### Required fields +## Features -Fill out the following fields and **click** the **Enable Adjust** button at the bottom right to save your changes: +- **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"` -- **App Token:** Your Adjust app token. -- **Event Token:** The default event token used when sending events to Adjust. -- **Sales Reporting:** Which revenue value to report in Adjust. Choose between **Proceeds** (after store taxes and fees) or **Revenue**. +## Configuration -### Features +Adjust requires separate credentials for iOS and Android. You can configure one or both platforms. -- **Automatic Event Mapping**: Converts Superwall events to Adjust-compatible event names -- **Revenue Tracking**: Automatic revenue attribution with currency support -- **S2S Event API**: Events are sent server-to-server for reliable delivery -- **Device Identification**: Uses Adjust device IDs, IDFA, and GPS advertising ID for attribution -- **Callback Parameters**: Custom data attached to each event for downstream use -- **Custom Event Names**: Optional override of default event name mappings -- **S2S Security**: Optional authentication token for secure server-to-server communication +### Platform settings -### Configuration +| Field | Platform | Description | +|-------|----------|-------------| +| `app_token_ios` | iOS | Adjust app token for your iOS app. If iOS and Android share the same Adjust app, enter the same value in both fields. | +| `app_token_android` | Android | Adjust app token for your Android app. | +| `s2s_token_ios` | iOS | Optional S2S security token from Adjust Dashboard > Settings > S2S Security. | +| `s2s_token_android` | Android | Optional S2S security token for Android. | +| `sales_reporting` | All | Revenue reporting mode: `"Revenue"` (gross) or `"Proceeds"` (net after store fees). | -#### Required settings +### Event tokens -| Field | Description | Example | -|-------|-------------|---------| -| `app_token` | Your Adjust app token | `"abc123def456"` | -| `event_token` | Default event token for Adjust | `"xyz789"` | -| `sales_reporting` | Which value to report | `"Revenue"` or `"Proceeds"` | +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. -#### Optional settings +| Field | Event | Triggered when | +|-------|-------|---------------| +| `event_token_trial_start` | Trial Start | `initial_purchase` with TRIAL period | +| `event_token_direct_sub_start` | Direct Subscription Start | `initial_purchase` with NORMAL or INTRO period | +| `event_token_trial_convert` | Trial Convert | `renewal` with `isTrialConversion = true` | +| `event_token_renewal` | Renewal | `renewal` (not a trial conversion) | +| `event_token_trial_cancel` | Trial Cancel | `cancellation` with TRIAL period | +| `event_token_cancel` | Cancel | `cancellation` with NORMAL or INTRO period | +| `event_token_uncancel` | Uncancel | `uncancellation` | +| `event_token_billing_issue` | Billing Issue | `billing_issue` | +| `event_token_product_change` | Product Change | `product_change` | +| `event_token_subscription_paused` | Subscription Paused | `subscription_paused` | +| `event_token_one_time_purchase` | One-Time Purchase | `non_renewing_purchase` | +| `event_token_trial_expire` | Trial Expire | `expiration` with TRIAL period | +| `event_token_expire` | Expire | `expiration` with NORMAL or INTRO period | +| `event_token_refund` | Refund | Any event with `price < 0` (takes precedence over all other mappings) | -| Field | Description | Example | -|-------|-------------|---------| -| `s2s_token` | S2S security authentication token | `"your_s2s_token"` | -| Custom event name mappings | Override default event names per event type | `"sw_trial_start"` mapped to a custom name | - -#### Example configuration +### Example configuration ```json { - "app_token": "your_adjust_app_token", - "event_token": "your_default_event_token", + "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", - "s2s_token": "your_optional_s2s_token" + "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):** +```swift +import Adjust + +// After Adjust SDK initializes +if let adid = Adjust.adid() { + Superwall.shared.setIntegrationAttributes([ + .adjustId: adid + ]) +} +``` + +**Android (Kotlin):** +```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):** +```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, + }); } ``` -### Event mapping +### Setting additional device identifiers + +For richer attribution data, pass platform-specific advertising identifiers via `setUserAttributes`: + +**iOS (Swift):** +```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):** +```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 transformed into Adjust event names using the standard mapper: +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. -#### Complete event mapping +| Superwall Event | Condition | Adjust Event | Revenue? | +|-----------------|-----------|--------------|----------| +| `initial_purchase` | TRIAL period | `trial_start` | No | +| `initial_purchase` | NORMAL or INTRO period | `direct_sub_start` | Yes | +| `renewal` | `isTrialConversion` = true | `trial_convert` | Yes | +| `renewal` | `isTrialConversion` = false | `renewal` | Yes | +| `cancellation` | TRIAL period | `trial_cancel` | No | +| `cancellation` | NORMAL or INTRO period | `cancel` | No | +| `uncancellation` | any | `uncancel` | No | +| `billing_issue` | any | `billing_issue` | No | +| `product_change` | any | `product_change` | No | +| `subscription_paused` | any | `subscription_paused` | No | +| `non_renewing_purchase` | any | `one_time_purchase` | Yes | +| `expiration` | TRIAL period | `trial_expire` | No | +| `expiration` | NORMAL or INTRO period | `expire` | No | +| Any event | `price < 0` | `refund` | No | -| Superwall Event | Period Type | Adjust Event Name | -|-----------------|-------------|-------------------| -| `initial_purchase` | TRIAL | `sw_trial_start` | -| `initial_purchase` | INTRO | `sw_intro_offer_start` | -| `initial_purchase` | NORMAL | `sw_subscription_start` | -| `renewal` | trial conversion | `sw_trial_converted` | -| `renewal` | INTRO | `sw_intro_offer_converted` | -| `renewal` | NORMAL | `sw_renewal` | -| `cancellation` | TRIAL | `sw_trial_cancelled` | -| `cancellation` | INTRO | `sw_intro_offer_cancelled` | -| `cancellation` | NORMAL | `sw_subscription_cancelled` | -| `uncancellation` | TRIAL | `sw_trial_uncancelled` | -| `uncancellation` | INTRO | `sw_intro_offer_uncancelled` | -| `uncancellation` | NORMAL | `sw_subscription_uncancelled` | -| `expiration` | TRIAL | `sw_trial_expired` | -| `expiration` | INTRO | `sw_intro_offer_expired` | -| `expiration` | NORMAL | `sw_subscription_expired` | -| `billing_issue` | any | `sw_billing_issue` | -| `subscription_paused` | any | `sw_subscription_paused` | -| `product_change` | any | `sw_product_change` | -| `non_renewing_purchase` | any | `sw_non_renewing_purchase` | -| Any with `price < 0` | any | `sw_refund` | +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. -You can optionally override these default event names with custom mappings in your integration settings. +## Callback Parameters -### Callback parameters +Every Adjust event includes a `callback_params` field containing a JSON-encoded string with: -Every Adjust event includes callback parameters encoded as JSON. These are sent alongside the event for downstream processing: +| Parameter | Description | Always present? | +|-----------|-------------|-----------------| +| `product_id` | The product identifier | Yes | +| `transaction_id` | The transaction identifier | Yes | +| `offer_code` | The promotional offer code used | Only if present | -- `event_name`: The mapped Superwall event name (e.g., `sw_trial_start`) -- `product_id`: The product identifier for the transaction -- `transaction_id`: The transaction identifier -- `original_transaction_id`: The original transaction identifier (for renewals and modifications) +## Revenue Tracking -These callback parameters are included in the `callback_params` query parameter as a JSON-encoded string. +Revenue is only included for these event types: +- `direct_sub_start` +- `trial_convert` +- `renewal` +- `one_time_purchase` -### Revenue tracking +All other events (cancellations, expirations, billing issues, refunds) do not carry revenue data. -#### Automatic revenue attribution +### Revenue calculation -Revenue is automatically included for events with non-zero amounts. The `sales_reporting` setting determines which value is used: +| Setting | Formula | Description | +|---------|---------|-------------| +| `"Revenue"` | `priceInPurchasedCurrency` | Gross revenue in the purchased currency | +| `"Proceeds"` | `priceInPurchasedCurrency * takehomePercentage` | Net revenue after store fees | -| Setting | Value Used | Description | -|---------|------------|-------------| -| `"Revenue"` | `price` | Gross revenue before store fees | -| `"Proceeds"` | `proceeds` | Net 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. -When revenue is present: -- **Positive revenue**: Purchases, renewals, conversions -- **Negative revenue**: Refunds (automatically deducted) -- **Zero revenue**: Events like cancellations, expirations, and billing issues do not include revenue data +## Device Identification -Revenue is sent with the corresponding currency code from the transaction. +Adjust uses device-level identifiers for attribution. The integration reads these from `userAttributes` on the Superwall event: -### User and device identification +| User attribute | Adjust parameter | Platform | +|----------------|-----------------|----------| +| `adjustAdid` or `adjustId` | `adid` | All | +| `idfa` | `idfa` | iOS | +| `idfv` | `idfv` | iOS | +| `advertisingId` or `gps_adid` | `gps_adid` | Android | +| `google_app_set_id` | `google_app_set_id` | Android | -Adjust uses device-level identifiers rather than user IDs for attribution. The integration reads these values from `userAttributes` on the Superwall event: +`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. -#### Device identifier hierarchy +### Meta AEM parameters -| Identifier | Platform | Description | -|------------|----------|-------------| -| `adjustId` | All | Primary Adjust device ID (required) | -| `idfa` | iOS | Identifier for Advertisers | -| `gps_adid` | Android | Google Play Services advertising ID | +When present in `userAttributes`, these additional parameters are forwarded to support Meta's Aggregated Event Measurement: -**Important:** If no `adjustId` is present in `userAttributes`, the event is skipped entirely. Make sure your app sets the Adjust device ID on the Superwall user so that events can be attributed correctly. +| User attribute | Adjust parameter | Platform | +|----------------|-----------------|----------| +| `ip_address` | `ip_address` | All | +| `device_name` | `device_name` | All | +| `os_version` | `os_version` | All | +| `attStatus` | `att_status` | iOS only | -#### Platform detection +## Platform Support -The integration determines the platform from the store field on the event: +The integration determines the platform from the `store` field on each event: -- `APP_STORE` maps to iOS (uses `idfa` if available) -- `PLAY_STORE` maps to Android (uses `gps_adid` if available) +| Store | Platform | Supported? | +|-------|----------|------------| +| `APP_STORE` | iOS | Yes | +| `PLAY_STORE` | Android | Yes | +| `STRIPE` | — | Skipped | +| `PADDLE` | — | Skipped | -### Sandbox handling +Stripe and Paddle events do not identify the originating app platform, so they cannot be attributed in Adjust and are skipped. -Adjust does **not** support separate sandbox environments. All sandbox events are skipped and will not be sent to Adjust. +## Sandbox Handling -Only production events are forwarded to the Adjust S2S event API. If you need to test the integration, use production or live test transactions. +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 +## Testing the Integration -#### 1. Validate credentials +### 1. Validate credentials -When you save the integration, Superwall sends a test event to verify that your `app_token` and `event_token` are valid. If the test fails, double-check your tokens in the Adjust dashboard. +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 production events +### 2. Trigger test events -Since sandbox events are skipped, testing requires live transactions: - **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. -- **Google Play**: Use license test accounts to perform purchases. +- **Android**: Use license test accounts to perform purchases. -#### 3. Verify in Adjust +### 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 values -3. **Revenue**: Confirm revenue amounts and currency codes are correct -4. **Device Attribution**: Ensure events are attributed to the correct device +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 +## Best Practices -1. **Set Adjust device IDs early**: Configure the `adjustId` in Superwall user attributes as soon as the Adjust SDK initializes, so that all subscription events can be attributed. -2. **Include advertising IDs**: Pass `idfa` (iOS) or `gps_adid` (Android) in user attributes for richer attribution data. -3. **Use S2S security**: Configure the `s2s_token` to authenticate server-to-server requests and prevent spoofed events. -4. **Revenue model consistency**: Choose gross revenue or proceeds and keep it consistent across all your analytics tools. -5. **Custom event names**: Use custom event name mappings if your Adjust setup requires specific naming conventions. -6. **Monitor attribution**: Regularly verify that events in the Adjust dashboard match your expected subscription activity. +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 +## Troubleshooting -#### Events not appearing +### Events not appearing -1. **Check adjustId**: The `adjustId` must be present in `userAttributes`. Events without it are skipped entirely. -2. **Check app token**: Verify the app token matches your Adjust app. -3. **Check event token**: Ensure the event token is valid in your Adjust dashboard. -4. **Check environment**: Sandbox events are always skipped. Only production events are sent. -5. **Check S2S token**: If configured, verify the S2S authentication token is correct. +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 +### Revenue not tracking -1. **Check amount**: Only non-zero amounts include revenue data. -2. **Check sales reporting**: Verify whether you selected Revenue or Proceeds. -3. **Check currency**: Ensure the currency code is present on the transaction. +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 +### Device attribution issues -1. **Check adjustId**: This is the primary identifier and must be set in user attributes. -2. **Check IDFA/GPS ad ID**: These are supplementary identifiers. Ensure they are passed in user attributes if available. -3. **Check platform detection**: Verify the store field is correct (APP_STORE for iOS, PLAY_STORE for Android). +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 +### S2S API errors -The Adjust S2S event API receives events as URL query parameters. Common issues include: -- **Invalid app token**: Returns an error from the Adjust endpoint. -- **Invalid event token**: The event token must exist in your Adjust dashboard. -- **Authentication failure**: If using `s2s_token`, ensure it matches your Adjust S2S security settings. +- **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. From c9808a913cc9bcf49d74db3ed75599a3b8301ed1 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Wed, 8 Apr 2026 12:52:34 +0200 Subject: [PATCH 2/2] fix --- content/docs/integrations/adjust.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/integrations/adjust.mdx b/content/docs/integrations/adjust.mdx index 0633f90..aa7667f 100644 --- a/content/docs/integrations/adjust.mdx +++ b/content/docs/integrations/adjust.mdx @@ -224,7 +224,7 @@ When present in `userAttributes`, these additional parameters are forwarded to s | User attribute | Adjust parameter | Platform | |----------------|-----------------|----------| -| `ip_address` | `ip_address` | All | +| `ip_address` | `ip_address` | All (IPv4 only) | | `device_name` | `device_name` | All | | `os_version` | `os_version` | All | | `attStatus` | `att_status` | iOS only |