---
title: "Receive attribution data in your app"
description: "Access campaign attribution data in your app after Adapty matches an install to a campaign."
---

When Adapty matches an install to a campaign, it returns attribution data to your app in the `onInstallationDetailsSuccess` callback. Use this data to personalize the user experience based on the channel or campaign that drove the install.

The attribution data is returned as a nested `attribution` object inside the `payload` field. It contains the following fields:

| Field | Description |
|---|---|
| `channel` | Acquisition channel (e.g. `facebook`, `tiktok`, `google`, `organic`) |
| `campaign_id` | Campaign identifier |
| `campaign_name` | Campaign name |
| `adset_id` | Ad set / ad group identifier |
| `adset_name` | Ad set / ad group name |
| `ad_id` | Ad / creative identifier |
| `ad_name` | Ad / creative name |

All fields are optional. For organic installs or when attribution could not be determined, the `payload` field does not include the `attribution` object.

To read attribution data in your app:

<Tabs groupId="current-os" queryString>
<TabItem value="swift" label="Swift" default>

```swift showLineNumbers
Adapty.delegate = self

nonisolated func onInstallationDetailsSuccess(_ details: AdaptyInstallationDetails) {
    guard
        let payloadDict = details.payload?.dictionary,
        let attribution = payloadDict["attribution"] as? [String: Any]
    else { return }

    let channel = attribution["channel"] as? String
    let campaignName = attribution["campaign_name"] as? String
    let adName = attribution["ad_name"] as? String

    print("Channel: \(channel ?? "organic")")
}
```

</TabItem>

<TabItem value="android" label="Kotlin">

```kotlin showLineNumbers
Adapty.setOnInstallationDetailsListener(object : OnInstallationDetailsListener {
    override fun onInstallationDetailsSuccess(details: AdaptyInstallationDetails) {
        val payloadStr = details.payload ?: return
        runCatching {
            val payload = JSONObject(payloadStr)
            val attribution = payload.optJSONObject("attribution") ?: return

            val channel = attribution.optString("channel")
            val campaignName = attribution.optString("campaign_name")
            val adName = attribution.optString("ad_name")

            println("Channel: $channel")
        }.onFailure(Throwable::printStackTrace)
    }
})
```

</TabItem>

<TabItem value="rn" label="React Native">

```typescript showLineNumbers
adapty.addEventListener('onInstallationDetailsSuccess', details => {
    try {
        if (!details.payload) return;
        const payload = JSON.parse(details.payload);
        const attribution = payload.attribution;
        if (!attribution) return;

        const channel = attribution.channel;
        const campaignName = attribution.campaign_name;
        const adName = attribution.ad_name;

        console.log('Channel:', channel ?? 'organic');
    } catch (error) {
        console.error('Error parsing payload:', error);
    }
});
```

</TabItem>

<TabItem value="flutter" label="Flutter">

```dart showLineNumbers
Adapty().onUpdateInstallationDetailsSuccessStream.listen((details) {
  final payloadStr = details.payload;
  if (payloadStr == null) return;

  final payload = json.decode(payloadStr) as Map<String, dynamic>;
  final attribution = payload['attribution'] as Map<String, dynamic>?;
  if (attribution == null) return;

  final channel = attribution['channel'] as String?;
  final campaignName = attribution['campaign_name'] as String?;
  final adName = attribution['ad_name'] as String?;

  print('Channel: ${channel ?? 'organic'}');
});
```

</TabItem>

</Tabs>