AppsFlyer

This guide describes how to add and configure AppsFlyer integration.

AppsFlyer is the world's leading mobile attribution & marketing analytics platform, helping app marketers around the world make better decisions.

How does Integration Work?

This integration works in two ways.

1. Receive Attribution Data from AppsFlyer

Once you successfully configured AppsFlyer integration, Apphud will receive attribution data from it. You can view this data on the user's page:

AppsFlyer - Raw attribution on a User Page

2. Send Subscription Events to AppsFlyer

Apphud can also send all subscription events to AppsFlyer. So you could view these events in the AppsFlyer dashboard and AppsFlyer could pass this data to their partners. This will help to measure the efficiency of your ad campaigns.

How to Add Integration?

Step 1 - Install SDKs

Step 2 - Preparations in AppsFlyer

S2S Security Token

Apphud now supports the latest AppsFlyer V3 API that uses Security Tokens for authentication instead of the legacy Dev Key. This provides a more secure and future-proof integration.

To generate a token: AppsFlyer → Security Center → Tokens → Create token with S2S type.
See the official AppsFlyer Security Tokens guide.

Apphud - AppsFlyer Security Center: creating an S2S token

Test App ID (optional)

If you maintain a separate AppsFlyer app for testing not to mix test and live data, get its Test App Store app ID (iOS) or Test Package Name (Android).

📘

Note

Information about how to create an app for debug purposes can be found in AppsFlyer documentation.

Step 3 - Authentication

At Apphud go to Connections -> Integrations (All or Attribution tab).
Click on "Add connection" for AppsFlyer and choose a platform in the opened Source pop-up.

For new integrations, paste the previously copied S2S Security token into the respective Apphud connection configuration field.

No need to paste AppsFlyer Dev Key into Developer key field, as it's deprecated.
Please do not use it for new integrations.

Appsflyer - Add new Connection (Authentication tab)

You may have one more app created in AppsFlyer used for testing purposes not to mix test and live data. If you have such an app, paste previously copied test App Store app ID or test Package Name into Test App ID field.

Step 4 - Additional Configuration

Environment and Revenue

Configure generic Environment and Revenue settings as described on Integrations overview → Configuration tab.

Set up filters (optional)

Navigate to Filters tab. Set up filters if needed as described on Integrations overview → Filters tab.

Step 5 - Set up Events list

Open the Events tab of the configuration. AppsFlyer uses a strict naming dictionary — Apphud pre-fills default partner names like apphud_trial_started, apphud_subscription_renewed. You can customize names or disable some.

AppsFlyer - Events configuration List

Note that each network has its own limitations on permitted characters in event names. To avoid problems, use only lower-case alpha-numeric characters (a–z and 0–9) for in-app event names.
See AppsFlyer help.

📘

Note

You can use AppsFlyer built-in rich events names. View more information here.

❗️

Important Note

In order to receive AppsFlyer attribution data from Facebook (Meta), you should accept Facebook’s "Advanced Mobile Measurement Agreement" using this link.

📘

Note

You can read more how Apple and Google calculate their commissions here and here.

Step 6 - Test connection with Sandbox purchases

If you want sandbox purchases not to mix with your production purchases in AppsFlyer, you should create a Test App ID.

  • Create a new app in AppsFlyer as described here.
  • Enter the newly created Test App ID to_Test App ID_ field in the AppsFlyer Integration page in Apphud.
  • iOS: Change App ID to a Test App ID when initializing AppsFlyer SDK:
AppsFlyerLib.shared().appleAppID = "0123456789"​
  • Android: Change Package Name to a Test Package Name.

Testing Subscription Events

  1. Please make sure AppsFlyer SDK is properly integrated in your app and AppsFlyer integration is configured in Apphud.
  2. Re-install the app and launch it again.
  3. Make a test purchase in your app.
📘

Note

You can read more about testing purchases here.

  1. You will see attribution data on the sandbox user's page.
  2. Open "Events" tab in AppsFlyer and you will see Apphud events there:
Apphud - AppsFlyer Events tab showing Apphud events

Pass Attribution Data to Apphud (required)

Send attribution data to Apphud (or at least Appsflyer ID). Implement delegate methods or listener.

AppsFlyerLib.shared().delegate = self
...

func onConversionDataSuccess(_ conversionInfo: [AnyHashable : Any]!) {
    Apphud.setAttribution(data: ApphudAttributionData(rawData: conversionInfo), from: .appsFlyer, identifer: AppsFlyerLib.shared().getAppsFlyerUID(), callback: nil) { _ in }
}

func onConversionDataFail(_ error: Error) {
    Apphud.setAttribution(data: ["error" : error.localizedDescription], from: .appsFlyer, identifer: AppsFlyerLib.shared().getAppsFlyerUID()) { _ in }
}
val listener = object : AppsFlyerConversionListener {
    override fun onConversionDataSuccess(map: MutableMap<String, Any>?) {
        val uid = AppsFlyerLib.getInstance().getAppsFlyerUID(app)
        Apphud.addAttribution(ApphudAttributionProvider.appsFlyer, map, uid)
    }
    override fun onConversionDataFail(p0: String?) {
        val uid = AppsFlyerLib.getInstance().getAppsFlyerUID(app)
        Apphud.addAttribution(ApphudAttributionProvider.appsFlyer, null, uid)
    }
}
AppsFlyerLib.getInstance().init("APPSFLYER_DEV_KEY", listener, this)
AppsFlyerLib.getInstance().startTracking(this)
// initialize Apphud
val uid = AppsFlyerLib.getInstance().getAppsFlyerUID(this)
Apphud.addAttribution(ApphudAttributionProvider.appsFlyer, null, uid)
final options = AppsFlyerOptions(
      afDevKey: _appSecrets.appsFlyerKey,
      appId: _appSecrets.appsFlyerAppId,
      showDebug: true,
      timeToWaitForATTUserAuthorization: 15,
      manualStart: true,
    );

    final appsflyerSdk = AppsflyerSdk(options);

    await appsflyerSdk.initSdk(registerConversionDataCallback: true);

    appsflyerSdk.onInstallConversionData((result) async {
      final uid = await appsflyerSdk.getAppsFlyerUID();
      final status = result['status'];
      final payload = Map<String, dynamic>.from(result['payload'] as Map);
      if (status == 'success') {
        final data = Map<String, dynamic>.from(result);

        Apphud.addAttribution(
          provider: ApphudAttributionProvider.appsFlyer,
          data: payload,
          identifier: uid,
        );
      } else {
        Apphud.addAttribution(
          provider: ApphudAttributionProvider.appsFlyer,
          identifier: uid,
          data: {
            'error': payload['data'],
          },
        );
      }
    });

    appsflyerSdk.startSDK();
[AppsFlyerTracker sharedTracker].delegate = self;
...

- (void)onConversionDataSuccess:(NSDictionary *)conversionInfo{
    [Apphud addAttributionWithData:conversionInfo from:ApphudAttributionProviderAppsFlyer identifer:AppsFlyerTracker.sharedTracker.getAppsFlyerUID callback:^(BOOL callback) {}];
}

- (void)onConversionDataFail:(NSError *)error{
    [Apphud addAttributionWithData:@{@"error" : error.localizedDescription} from:ApphudAttributionProviderAppsFlyer identifer:AppsFlyerTracker.sharedTracker.getAppsFlyerUID callback:^(BOOL result){}];    
}
public class AppsFlyerListener : MonoBehaviour, IAppsFlyerConversionData
{
    public void Initialize()
    {
        AppsFlyer.initSDK({developerKey}, {appId}, this);
        AppsFlyer.startSDK();
    }
    public void onConversionDataSuccess(string conversionData)
    {
        string id = AppsFlyer.getAppsFlyerId();
        var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(conversionData);
        ApphudSDK.AddAttribution(ApphudAttributionProvider.appsFlyer, data, id);
    }
    public void onConversionDataFail(string error)
    {
        ApphudSDK.AddAttribution(ApphudAttributionProvider.appsFlyer, new Dictionary<string, object>{
            {"error", error }
        }, AppsFlyer.getAppsFlyerId());
    }
}

Set AppsFlyer ID manually if needed

To improve attribution accuracy, you may explicitly set the AppsFlyer ID right before initiating a purchase. This helps ensure the ID is attached even if AppsFlyer’s delegate callback was not triggered.

/// In addition to delegate methods, also set AppsFlyer ID manually:
Apphud.setAttribution(
    data: ApphudAttributionData(rawData: [:]),
    from: .appsFlyer,
    identifier: AppsFlyerLib.shared().getAppsFlyerUID(),
    callback: nil
)

// Then proceed with your purchase flow (Apphud or custom implementation)
Apphud.purchase(product) { result in
    // handle result
}
val uid = AppsFlyerLib.getInstance().getAppsFlyerUID(this)
Apphud.addAttribution(ApphudAttributionProvider.appsFlyer, null, uid)

Forward Google ODM info to AppsFlyer (iOS)

What is ODM info. Google's On-Device Conversion Measurement (ODM) is a privacy-preserving way to attribute iOS conversions to Google Ads without relying on the IDFA. AppsFlyer's Integrated Conversion Measurement (ICM) uses this on-device signal, passed as odm_info. See Google's overview here.

When Apphud is the source of your subscription events, the odm_info value has to reach AppsFlyer through Apphud's S2S payload. Starting with Apphud iOS SDK 4.4.0, your app can fetch odm_info from Google's on-device SDK and pass it to Apphud — Apphud then attaches it to the subscription events it forwards to AppsFlyer.

📘

Prerequisites

  • Apphud iOS SDK 4.4.0+.
  • In AppsFlyer, enable Advanced Data Sharing for the Google Ads integration (off by default) so ICM/ODM is active for your account.
  • Your app links the GoogleAdsOnDeviceConversion SDK (via Firebase iOS SDK) and obtains the odm_info value.

Pass the value to Apphud:

Apphud.setAttribution(
    data: ApphudAttributionData(rawData: ["odm_info": "1234"]),
    from: .google,
    callback: nil
)
📘

Note

This applies to iOS only.

Collect Device Identifiers (required)

iOS: Call setDeviceIdentifiers(idfa: String?, idfv: String?) method immediately after the SDK initialization. If the advertising identifier (IDFA) is not available, pass only the IDFV.
When IDFA becomes available, you can call setDeviceIdentifiers(idfa: String?, idfv: String?) again.

Android: Call Apphud.collectDeviceIdentifiers() method after the SDK initialization.
When targeting Android 13 and above, you must also declare AD_ID permission in the manifest file.

For more details, refer to Device Identifiers guide.

Request IDFA Consent (required)

Starting iOS 14.5 access to IDFA requires user consent. You should request IDFA manually using AppTrackingTransparency framework and pass it to Apphud. Read more here.

Events Cheat Sheet

This is a list of some events and their parameters that are being sent to AppsFlyer.

📘

Note

You can read more about subscription events here and parameters here.

Trial

Trial period started

Default event name: apphud_trial_started

Parameters:

  • af_content_id: String

Successful conversion from trial period to regular subscription

Default event name: apphud_trial_converted

Parameters:

  • af_content_id: String
  • af_revenue: Float
  • af_currency: String

Failed conversion from trial period to regular subscription

Default event name: apphud_trial_expired

Parameters:

  • af_content_id: String
  • reason: String (iOS)

Cancellations

Trial Canceled

Default event name: apphud_trial_canceled

Parameters:

  • af_content_id: String

Subscription Canceled

Default event name: apphud_subscription_canceled

Parameters:

  • af_content_id: String

Autorenew disabled (Deprecated)

Default event name: apphud_autorenew_disabled

Parameters:

  • af_content_id: String

Autorenew enabled

Default event name: apphud_autorenew_enabled

Parameters:

  • af_content_id: String

Introductory Offer

Introductory offer started

Default event name: apphud_intro_started

Parameters:

  • af_content_id: String
  • af_revenue: Float
  • af_currency: String
  • offer_type: String (iOS)
  • unit: String (iOS)
  • units_count: Integer (iOS)

Introductory offer renewed

Default event name: apphud_intro_renewed

Parameters:

  • af_content_id: String
  • af_revenue: Float
  • af_currency: String
  • offer_type: String (iOS)
  • unit: String (iOS)
  • units_count: Integer (iOS)

Successful conversion from introductory offer to regular subscription

Default event name: apphud_intro_converted

Parameters:

  • af_content_id: String
  • af_revenue: Float
  • af_currency: String
  • offer_type: String (iOS)

Failed conversion from introductory offer to regular subscription or failed renewal

Default event name: apphud_intro_expired

Parameters:

  • af_content_id: String
  • reason: String (iOS)
  • offer_type: String (iOS)

Refund during the introductory offer

Default event name: apphud_intro_refunded

Parameters:

  • af_content_id: String
  • af_revenue: Float
  • af_currency: String
  • reason: String (iOS)
  • offer_type: String (iOS)

Regular

Subscription started

Default event name: apphud_subscription_started

Parameters:

  • af_content_id: String
  • af_revenue: Float
  • af_currency: String

Subscription renewed

Default event name: apphud_subscription_renewed

Parameters:

  • af_content_id: String
  • af_revenue: Float
  • af_currency: String

Subscription expired

Default event name: apphud_subscription_expired

Parameters:

  • af_content_id: String
  • reason: String (iOS)

Subscription refunded

Default event name: apphud_subscription_refunded

Parameters:

  • af_content_id: String
  • af_revenue: Float
  • af_currency: String
  • reason: String (iOS)

Promo Offer

Promotional offer started

Default event name: apphud_promo_started

Parameters:

  • af_content_id: String
  • offer_id: String (iOS)
  • af_revenue: Float
  • af_currency: String
  • offer_type: String (iOS)
  • unit: String (iOS)
  • units_count: Integer (iOS)

Promotional offer renewed

Default event name: apphud_promo_renewed

Parameters:

  • af_content_id: String
  • offer_id: String (iOS)
  • af_revenue: Float
  • af_currency: String
  • offer_type: String (iOS)
  • unit: String (iOS)
  • units_count: Integer (iOS)

Successful conversion from a promotional offer to a regular subscription

Default event name: apphud_promo_converted

Parameters:

  • af_content_id: String
  • offer_id: String (iOS)
  • af_revenue: Float
  • af_currency: String
  • offer_type: String (iOS)

Failed conversion from promotional offer to regular subscription or failed renewal

Default event name: apphud_promo_expired

Parameters:

  • af_content_id: String
  • offer_id: String (iOS)
  • reason: String (iOS)
  • offer_type: String (iOS)

Refund during the promotional offer

Default event name: apphud_promo_refunded

Parameters:

  • af_content_id: String
  • offer_id: String (iOS)
  • af_revenue: Float
  • af_currency: String
  • reason: String (iOS)
  • offer_type: String (iOS)

Other Events

Non-renewing purchase

Default event name: apphud_non_renewing_purchase

Parameters:

  • af_content_id: String
  • af_revenue: Float
  • af_currency: String

Non-renewing purchase refunded

Default event name: apphud_non_renewing_purchase_refunded

Parameters:

  • af_content_id: String
  • af_revenue: Float
  • af_currency: String
  • reason: String (iOS)

Billing Issue

Default event name: apphud_billing_issue

Parameters:

  • af_content_id: String

Billing issue Resolved

Default event name: [Apphud] billing_issue_resolved

Parameters:

  • af_content_id: String

Troubleshooting

I can’t see my events in AppsFlyer or not all events are shown.

AppsFlyer displays events in the “Events” tab based on the app installation date.
For instance, if you select a specific date range in AppsFlyer, it will show all events that occurred for users who installed the app within that date range.
However, in "Activity > Activity Summary > In-App Events," AppsFlyer shows all events that occurred within the selected date range, regardless of the app installation date.

Events are in the Pending State

Ensure you are collecting attribution data from the app. The attribution data must include appsflyer_id, which is essential for the integration. If appsflyer_id is missing, we won't be able to send the event.

For example, if you added attribution integration to an app that was already live, some users may still be on older app versions that do not send attribution data. These users cannot be matched until they update to the version that includes correct attribution handling. As a result, events triggered by these users (purchases, renewals, etc.) will hang in the Pending State. If the user updates the app and reopens it within the attribution expectation window (30 days from the event occurrence), Apphud will receive appsflyer_id, and the event will be sent automatically.

As this may lead to attribution delays or loss if the app is not updated promptly, we recommend encouraging users to update to the latest app version that supports attribution. You can automate this process using Apphud Rules to send push notifications to users on outdated versions.

Events are in the Skipped State

Sandbox events are not sent by default. To send Sandbox events, you need to provide a Test App ID in the Integration page. You can use the same Test App ID, but note that doing so will mix sandbox purchases with production purchases, leading to inaccurate data.

If some production events are in the Skipped State, check the status of these events on your integration page.


Did this page help you?