---
id: "integrations/attribution/appstack"
title: "Appstack"
description: "With our Appstack integration you can:"
permalink: "/docs/integrations/attribution/appstack"
slug: "appstack"
version: "current"
original_source: "docs/integrations/attribution/appstack.mdx"
---

> **AI agents:** This is the Markdown version of a RevenueCat documentation page. For the complete documentation index, see [llms.txt](https://www.revenuecat.com/docs/llms.txt).

With our Appstack integration you can:

- Send RevenueCat subscription events directly to your Appstack webhook endpoint (optional).
- Attribute subscription revenue to campaigns tracked by Appstack using the Purchases SDK.
- Identify users in Appstack using the `$appstackId` subscriber attribute.

The SDK integration (steps 1–2) and the webhook event forwarding (step 3) are independent. You can use the SDK attribution mapping without configuring webhook event forwarding, or enable both to get attribution mapping and event forwarding.

### Event Forwarding Integration at a Glance

| Includes Revenue | Supports Negative Revenue | Sends Sandbox Events | Includes Customer Attributes | Sends Transfer Events | Optional Event Types |
| :--------------: | :-----------------------: | :------------------: | :--------------------------: | :-------------------: | :------------------: |
|        ✅        |            ✅             |          ❌          |              ❌              |          ✅           |          ❌          |

## 1. Install the Appstack SDK

Before RevenueCat can integrate with Appstack, your app must be running the Appstack SDK. Refer to the Appstack developer documentation for the latest installation instructions.

## 2. Send attribution data to RevenueCat

The Appstack integration uses a reserved subscriber attribute to associate RevenueCat events with users in Appstack if the integration is configured to send events.

| Key           | Description                                     | Required |
| :------------ | :---------------------------------------------- | :------- |
| `$appstackId` | The unique user identifier assigned by Appstack | ✅       |

Call `await AppstackAttributionSdk.shared.getAttributionParams()` (iOS) or `AppstackAttributionSdk.getAttributionParams()` (Android) and pass the result to `setAppstackAttributionParams()`. On iOS, `getAttributionParams()` is async and must be awaited. This single call sets `$appstackId`, campaign attribution attributes (`$mediaSource`, `$campaign`, `$adGroup`, `$ad`, `$keyword`), click IDs (`fbclid`, `gclid`, `ttclid`, `wbraid`, `gbraid`), and device identifiers when `appstack_id` is present. You do not need to call `collectDeviceIdentifiers()` separately.

`setAppstackAttributionParams()` also automatically syncs the attributes to the RevenueCat backend and fetches fresh offerings before returning. This ensures Appstack-based targeting is applied before you display a paywall — the returned offerings already reflect the user's Appstack attribution data.

**Swift**

```swift
import AdSupport
// ...
Purchases.configure(withAPIKey: "public_sdk_key")
// ...

// Retrieve attribution params from the Appstack SDK
let base = await AppstackAttributionSdk.shared.getAttributionParams() ?? [:]
let params: [String: Any]
if let id = AppstackAttributionSdk.shared.getAppstackId() {
    params = base.merging(["appstack_id": id]) { _, new in new }
} else {
    params = base
}

// Forward to RevenueCat — syncs attributes and fetches fresh offerings
// so Appstack-based targeting is applied before the callback returns.
Purchases.shared.attribution.setAppstackAttributionParams(params) { offerings, error in
    // Use `offerings` to present the correct paywall for this user
}
```

**Kotlin**

```kotlin
// ...
Purchases.configure(this, "public_sdk_key")
// ...

// Retrieve attribution params from the Appstack SDK
val base = AppstackAttributionSdk.getAttributionParams() ?: emptyMap()

// Merge the locally-cached Appstack ID as a safety net in case the
// network-based getAttributionParams() didn't return it.
val params = AppstackAttributionSdk.getAppstackId()?.let {
    base + ("appstack_id" to it)
} ?: base

// Forward to RevenueCat — syncs attributes and fetches fresh offerings
// so Appstack-based targeting is applied before the callback returns.
Purchases.sharedInstance.setAppstackAttributionParams(
    params,
    object : SyncAttributesAndOfferingsCallback {
        override fun onSuccess(offerings: Offerings) {
            // Use `offerings` to present the correct paywall for this user
        }
        override fun onError(error: PurchasesError) { /* handle error */ }
    }
)
```

**Swift (async/await)**

```swift
import AdSupport
// ...
Purchases.configure(withAPIKey: "public_sdk_key")
// ...

// Retrieve attribution params from the Appstack SDK
let base = await AppstackAttributionSdk.shared.getAttributionParams() ?? [:]
let params: [String: Any]
if let id = AppstackAttributionSdk.shared.getAppstackId() {
    params = base.merging(["appstack_id": id]) { _, new in new }
} else {
    params = base
}

// Forward to RevenueCat — syncs attributes and fetches fresh offerings
// so Appstack-based targeting is applied before the await returns.
do {
    let offerings = try await Purchases.shared.attribution.setAppstackAttributionParams(params)
    // Use `offerings` to present the correct paywall for this user
} catch {
    // handle error
}
```

**Kotlin (Coroutines)**

```kotlin
// ...
Purchases.configure(this, "public_sdk_key")
// ...

// Retrieve attribution params from the Appstack SDK
val base = AppstackAttributionSdk.getAttributionParams() ?: emptyMap()

// Merge the locally-cached Appstack ID as a safety net in case the
// network-based getAttributionParams() didn't return it.
val params = AppstackAttributionSdk.getAppstackId()?.let {
    base + ("appstack_id" to it)
} ?: base

// Forward to RevenueCat — syncs attributes and fetches fresh offerings
// so Appstack-based targeting is applied before the suspend function returns.
try {
    val offerings = Purchases.sharedInstance.awaitSetAppstackAttributionParams(params)
    // Use `offerings` to present the correct paywall for this user
} catch (e: PurchasesException) {
    // handle error
}
```

:::tip\[Recommended: merge the local Appstack ID as a safety net]
`getAttributionParams()` fetches attribution data over the network and should include the `appstack_id`. However, transient network issues can occasionally cause the ID to be missing from the response. To guarantee it is always present, the snippets above merge in the value from `getAppstackId()`, which reads the ID from local storage and does not depend on the network. If `getAttributionParams()` already returned the ID, the local value is a no-op; if it didn't, the local fallback ensures RevenueCat still receives it.
:::

Set these attributes after configuring the Purchases SDK and before the first purchase occurs.

This is enough for RevenueCat to start storing attribute subscription revenue information from Appstack.

:::danger\[Device identifiers with iOS App Tracking Transparency (iOS 14.5+)]
If you are requesting the App Tracking permission through ATT to access the IDFA, call
`setAppstackAttributionParams()` again after the customer grants permission, passing
the latest params from `AppstackAttributionSdk.shared.getAttributionParams()`.
:::

:::info\[Import AdSupport Framework (iOS)]
The AdSupport framework is required to collect the IDFA on iOS.
:::

![Import AdSupport Framework](https://www.revenuecat.com/docs_images/integrations/attribution/import-adsupport-framework.png)

## 3. Configure the Appstack integration in RevenueCat to forward events to Appstack

1. Navigate to your project settings in the RevenueCat dashboard and choose **Appstack** from the Integrations menu.

![Integration setup](https://www.revenuecat.com/docs_images/integrations/setup-integrations.png)

2. Enter your **Webhook URL**. The endpoint provided by Appstack to receive RevenueCat events.
3. Enter the **Authorization Header**. The token provided by Appstack to authenticate incoming webhook requests.

:::success\[You're all set!]
RevenueCat will begin forwarding subscription events to your Appstack webhook endpoint.
:::

## Events sent to Appstack

The following RevenueCat event types are forwarded to Appstack:

| RevenueCat Event      |
| :-------------------- |
| Initial Purchase      |
| Renewal               |
| Cancellation          |
| Uncancellation        |
| Non-Renewing Purchase |
| Subscription Paused   |
| Expiration            |
| Billing Issue         |
| Product Change        |
| Transfer              |
| Subscriber Alias      |
