Skip to main content

Analytics integrations

Adapty sends all subscription events to analytical services, such as Amplitude, Mixpanel, and AppMetrica. We can also send events to your server using webhook integration. The best thing about this is that you don't have to send any of the events, we'll do it for you. Just make sure to configure the integration in the Adapty Dashboard.

Adapty supports the integration with the following 3d-party analytics services:

note

Don't see your analytics provider?

Let us know! Write to the Adapty support and we'll consider adding it.

Event properties

Webhook events are sent in JSON format. All events follow the same structure, but their fields vary based on the event type, store, and your specific configuration.

PropertyTypeDescription
profile_iduuidAdapty user ID.
currencystrLocal currency (defaults to USD).
price_usdfloatProduct price before Apple/Google cut. Revenue.
proceeds_usdfloatProduct price after Apple/Google cut. Net revenue.
net_revenue_usdfloatNet revenue (income after Apple/Google cut and taxes) in USD. Can be empty.
price_localfloatProduct price before Apple/Google cut in local currency. Revenue.
proceeds_localfloatProduct price after Apple/Google cut in local currency. Net revenue.
transaction_idstrA unique identifier for a transaction such as a purchase or renewal.
original_transaction_idstrThe transaction identifier of the original purchase.
purchase_dateISO 8601 dateThe date and time of product purchase.
original_purchase_dateISO 8601 dateThe date and time of the original purchase.
environmentstrCan be Sandbox or Production.
vendor_product_idstrProduct ID in the Apple App Store, Google Play Store, or Stripe.
base_plan_idstrBase plan ID in the Google Play Store or price ID in Stripe.
event_datetimeISO 8601 dateThe date and time of the event.
storestrCan be app_store or play_store.
trial_durationstrDuration of a trial period in days. Sent in a format " days" , for example, "7 days".
cancellation_reasonstr

A reason why the user canceled a subscription.

Can be

iOS & Android

voluntarily_cancelled, billing_error, refund

iOS

price_increase, product_was_not_available, unknown

Android

new_subscription_replace, cancelled_by_developer

subscription_expires_atISO 8601 dateThe Expiration date of subscription. Usually in the future.
consecutive_paymentsintThe number of periods, that a user is subscribed to without interruptions. Includes the current period.
rate_after_first_yearboolBoolean indicates that a vendor reduces cuts to 15%. Apple and Google have 30% first-year cut and 15% after it.
promotional_offer_idstrID of promotional offer as indicated in the Product section of the Adapty Dashboard
store_offer_categorystrCan be introductory or promotional.
store_offer_discount_typestrCan be free_trial, pay_as_you_go or pay_up_front.
paywall_namestrName of the paywall where the transaction originated.
paywall_revisionintRevision of the paywall where the transaction originated. The value is set to 1.
developer_idstrDeveloper (SDK) ID of the placement where the transaction originated.
ab_test_namestrName of the A/B test where the transaction originated.
ab_test_revisionintRevision of the A/B test where the transaction originated. The value is set to 1.
cohort_namestrName of the audience to which the profile belongs to.
profile_event_iduuidUnique event ID that can be used for deduplication.
store_countrystrThe country sent to us by the store.
profile_ip_addressstrProfile IP (can be IPv4 or IPv6, with IPv4 preferred when available). It is updated each time IP of the device changes.
profile_countrystrDetermined by Adapty, based on profile IP.
profile_total_revenue_usdfloatTotal revenue for the profile, refunds included.
variation_iduuidUnique ID of the paywall where the purchase was made.
access_level_idstrPaid access level ID
is_activeboolBoolean indicating whether paid access level is active for the profile.
will_renewboolBoolean indicating whether paid access level will be renewed.
is_refundboolBoolean indicating whether transaction is refunded.
is_lifetimeboolBoolean indicating whether paid access level is lifetime.
is_in_grace_periodboolBoolean indicating whether profile is in grace period.
starts_atISO 8601 dateDate and time when paid access level starts for the user.
renewed_atISO 8601 dateDate and time when paid access will be renewed.
expires_atISO 8601 dateDate and time when paid access will expire.
activated_atISO 8601 dateDate and time when paid access was activated.
billing_issue_detected_atISO 8601 dateDate and time of billing issue.
profile_has_access_levelBoolA boolean that indicates whether the profile has an active access level (Webhook only).

Each event has the following properties:

transaction_id, original_transaction_id, purchase_date, original_purchase_date, environment, vendor_product_id, event_datetime, store.

In addition, some events have additional properties. For the events subscription_refunded and non_subscription_purchase_refunded, it is mandatory to provide the values of price_usd and proceeds_usd as additional properties.

Event NameProperties
subscription_initial_purchaseprice_usd, proceeds_usd, subscription_expires_at, consecutive_payments, rate_after_first_year, trial_duration
subscription_renewedprice_usd, proceeds_usd, subscription_expires_at, consecutive_payments, rate_after_first_year, trial_duration
subscription_cancelledcancellation_reason, trial_duration
trial_startedsubscription_expires_at, trial_duration
trial_convertedprice_usd, proceeds_usd, subscription_expires_at, consecutive_payments, rate_after_first_year, trial_duration
trial_cancelledcancellation_reason, trial_duration
non_subscription_purchaseprice_usd, proceeds_usd
billing_issue_detectedsubscription_expires_at, trial_duration
entered_grace_periodsubscription_expires_at, trial_duration

Event example

Json
{
"price_usd": 9.99,
"proceeds_usd": 6.99,
"transaction_id": "1000000628581600",
"original_transaction_id": "1000000628581600",
"purchase_date": "2020-02-18T18:40:22.000000+0000",
"original_purchase_date": "2020-02-18T18:40:22.000000+0000",
"environment": "Sandbox",
"vendor_product_id": "premium",
"event_datetime": "2020-02-18T18:40:22.000000+0000",
"store": "app_store"
}

Adapty sends events to your server and 3rd party analytical systems.

profile_ip_address property is synchronized with the current device IP. Each time the Adapty servers receive info from the SDK, the IP will be updated if it differs from the one we have on record.

Setting the profile's identifier

Set the profile's identifier for the selected analytics using .setIntegrationIdentifier method. For example, for Amplitude integration, you can set either amplitudeUserId or amplitudeDeviceId. For Mixpanel integration, you have to set mixpanelUserId. When these identifiers are not set, Adapty will use customerUserId instead. If the customerUserId is not set, we will use our internal profile ID.

warning

Avoiding duplication

Don't forget to turn off sending subscription events from devices and your server to avoid duplication

Disabling external analytics for a specific customer

You may want to stop sending analytics events for a specific customer. This is useful if you have an option in your app to opt-out of analytics services.

To disable external analytics for a customer, use updateProfile() method. Create AdaptyProfileParameters.Builder object and set the corresponding value to it.
When external analytics is blocked, Adapty won't be sending any events to any integrations for the specific user. If you want to disable an integration for all users of your app, just turn it off in Adapty Dashboard.

let builder = AdaptyProfileParameters.Builder()
.with(analyticsDisabled: true)

Adapty.updateProfile(parameters: builder.build())

Disable collection of advertising identifiers

You can disable IDFA collection by using the idfaCollectionDisabled property. Make sure you call it before .activate() method.

// In your AppDelegate class:
import Adapty

let configurationBuilder =
AdaptyConfiguration
.builder(withAPIKey: "PUBLIC_SDK_KEY")
.with(idfaCollectionDisabled: true) // set to `true`

Adapty.activate(with: configurationBuilder.build()) { error in
// handle the error
}