RevenueCat sends webhooks in response to events that occur in your app. Here these event types are defined, as well as the data contained in each webhook.

# Event Types

Webhook Event TypeDescriptionApp StorePlay StoreAmazonStripePromo
`TEST`Test event issued through the RevenueCat dashboard.βœ…βœ…βŒβŒβŒ
`INITIAL_PURCHASE`A new subscription has been purchased.βœ…βœ…βœ…βœ…βŒ
`RENEWAL`An existing subscription has been renewed or a lapsed user has resubscribed.βœ…βœ…βœ…βœ…βŒ
`CANCELLATION`A subscription or non-renewing purchase has been cancelled or refunded. Note that in the event of refunds, a subscription's auto-renewal setting may still be active. See [cancellation reasons](πŸ”—ο»Ώ) for more details.βœ…βœ…βœ…βœ…βœ…
`UNCANCELLATION`A non-expired cancelled subscription has been re-enabled.βœ…βœ…βœ…βŒβŒ
`NON_RENEWING_PURCHASE`A customer has made a purchase that will not auto-renew.βœ…βœ…βœ…βœ…βœ…
`SUBSCRIPTION_PAUSED`The subscription has set to be paused at the end of the period. Please note: You should not revoke access when receiving a `SUBSCRIPTION_PAUSED` event, but only when receiving an `EXPIRATION` event (which will have the [expiration reason](πŸ”—ο»Ώ) `SUBSCRIPTION_PAUSED`)βŒβœ…βŒβŒβŒ
`EXPIRATION`A subscription has expired and access should be removed. If you have [Platform Server Notifications](πŸ”—ο»Ώ) configured, this event will occur as soon as we are notified (within seconds to minutes) of the expiration. If you do not have notifications configured, delays may be approximately 1 hour.βœ…βœ…βœ…βœ…βœ…
`BILLING_ISSUE`There has been a problem trying to charge the subscriber. This does not mean the subscription has expired. Can be safely ignored if listening to `CANCELLATION` event + `cancel_reason=BILLING_ERROR`.βœ…βœ…βœ…βœ…βŒ
`PRODUCT_CHANGE`A **subscriber** has changed the product of their subscription. This does not mean the new subscription is in effect immediately. See [Managing Subscriptions](πŸ”—ο»Ώ) for more details on updates, downgrades, and crossgrades.βœ…βœ…βŒβœ…βŒ
`TRANSFER`A transfer of transactions and entitlements was initiated between one App User ID(s) to another.βœ…βœ…βœ…βœ…βŒ
`SUBSCRIBER_ALIAS`**Deprecated**. A new `app_user_id` has been registered for an existing subscriber.ο»Ώ

# Events Format

Webhook events are serialized in JSON. The body of a `POST` request to your server will contain the serialized event, as well as the API version.


# Common Fields

FieldTypeDescriptionPossible Values
`id`StringUnique identifier of the event.ο»Ώ
`app_id`StringUnique identifier of the app the event is associated with. Corresponds to an app within a project. This value will soon be visible in the app's configuration page in project settings.ο»Ώ
`event_timestamp_ms`IntegerThe time that the event was generated. Does not necessarily coincide with when the action that triggered the event occurred (purchased, cancelled, etc).ο»Ώ
`app_user_id`StringLast seen app user id of the subscriber.ο»Ώ
`original_app_user_id`StringThe first app user id used by the subscriber.ο»Ώ
`aliases`Arrayk:parameAll app user ids ever used by the subscriber.ο»Ώ


When looking up users from the webhook in your systems, make sure to search both the `original_app_user_id` and the `aliases` array.


If we have to retry a webhook for any reason, the retry will have the same `id` and `event_timestamp_ms` of the first attempt.

# Subscription Lifecycle Events Fields

FieldTypeDescriptionPossible Values
`product_id`StringProduct identifier of the subscription. Please note: For Google Play products set up in RevenueCat after February 2023, this identifier has the format `<subscription_id>:<base_plan_id>`ο»Ώ
`entitlement_ids`Arrayk:parameEntitlement identifiers of the subscription.It can be `NULL` if the `product_id` is not mapped to any entitlements.
`entitlement_id`StringDeprecated. See `entitlement_ids`.Deprecated. See `entitlement_ids`.
`period_type`StringPeriod type of the transaction.`TRIAL`, for free trials. `INTRO`, for introductory pricing. `NORMAL`, standard subscription. `PROMOTIONAL`, for subscriptions granted through RevenueCat. `PREPAID`,for Play Store prepaid transactions .
`purchased_at_ms`IntegerTime when the transaction was purchased. Measured in milliseconds since Unix epochο»Ώ
`grace_period_expiration_at_ms`Integer**Only available for** `BILLING_ISSUE` **events.** The time that the grace period for the subscription would expire. Measured in milliseconds since Unix epoch. Use this field to determine if the user is currently in a grace period.It can be `NULL` if subscription does not have a grace period.
`expiration_at_ms`IntegerExpiration of the transaction. Measured in milliseconds since Unix epoch. Use this field to determine if a subscription is still active.It can be `NULL` for non-subscription purchases.
`auto_resume_at_ms`IntegerThe time when an Android subscription would resume after being paused. Measured in milliseconds since Unix epoch. ** Only available for Play Store subscriptions and** `SUBSCRIPTION_PAUSED` **events.**ο»Ώ
`store`StringStore the subscription belongs to.`AMAZON` `APP_STORE` `MAC_APP_STORE` `PLAY_STORE` `PROMOTIONAL` `STRIPE`
`environment`StringStore environment.`SANDBOX` `PRODUCTION`
`is_trial_conversion`Boolean**Only available for** `RENEWAL` **events**. Whether the previous transaction was a free trial or not.`true` or `false`
`cancel_reason`String**Only available for** `CANCELLATION` **events**. See [Cancellation and Expiration Reasons](πŸ”—ο»Ώ).`UNSUBSCRIBE` `BILLING_ERROR` `DEVELOPER_INITIATED` `PRICE_INCREASE` `CUSTOMER_SUPPORT` `UNKNOWN`
`expiration_reason`String**Only available for** `EXPIRATION` **events**. See [Cancellation and Expiration Reasons](πŸ”—ο»Ώ).`UNSUBSCRIBE` `BILLING_ERROR` `DEVELOPER_INITIATED` `PRICE_INCREASE` `CUSTOMER_SUPPORT` `UNKNOWN`
`new_product_id`StringProduct identifier of the new product the subscriber has switched to. **Only available for App Store subscriptions and** `PRODUCT_CHANGE` **events**.ο»Ώ
`presented_offering_id`String**Not available for apps using legacy entitlements.** The identifier for the offering that was presented to the user during their initial purchase.Can be `NULL` if the purchase was made using purchaseProduct instead of purchasePackage or if the purchase was made outside of your app or before you integrated RevenueCat.
`price`DoubleThe USD price of the transaction.Can be `NULL` if the price is unknown, and `0` for free trials. Can be negative for refunds.
`currency`StringThe ISO 4217 currency code that the product was purchased in.`USD`, `CAD`, etc. Can be `NULL` if the currency is unknown.
`price_in_purchased_currency`DoubleThe price of the transaction in the currency the product was purchased in.Can be `NULL` if the price is unknown, and `0` for free trials. Can be negative for refunds.
`tax_percentage`DoubleThe estimated percentage of the transaction price that was deducted for taxes (varies by country and store).Can be `NULL` if the tax percentage is unknown.
`commission_percentage`DoubleThe estimated percentage of the transaction price that was deducted as a store commission / processing fee.Can be `NULL` if the commission percentage is unknown.
`takehome_percentage`DoubleDEPRECATED: The estimated percentage of the transaction price that will be paid out to developers after commissions, but before VAT and DST taxes are taken into account. We recommend using tax_percentage and commission_percentage to calculate proceeds instead. [Learn more here](πŸ”—ο»Ώ).ο»Ώ
`subscriber_attributes`Map of attribute names to attribute objects. For more details see the [subscriber attributes guide](πŸ”—ο»Ώ).ο»Ώ
`transaction_id`StringTransaction identifier from Apple/Amazon/Google/Stripe.ο»Ώ
`original_transaction_id`String`transaction_id` of the original transaction in the subscription from Apple/Amazon/Google/Stripe.ο»Ώ
`is_family_share`BooleanIndicates if the user made this purchase or if it was shared to them via [Family Sharing](πŸ”—ο»Ώ).`true` or `false` Always false for non-Apple purchases.
`transferred_from`Arrayk:parame**This fields is only available when `type` is set to `TRANSFER`.** App User ID(s) that transactions and entitlements are being taken from, and granted to `transferred_to`.ο»Ώ
`transferred_to`Arrayk:parame**This field is only available when `type` is set to `TRANSFER`.** App User ID(s) that are receiving the transactions and entitlements taken from `transferred_from`.ο»Ώ
`country_code`StringThe ISO 3166 country code that the product was purchased in. The two-letter country code (e.g., US, GB, CA) of the app user's location (this country code is derived from the last seen request from the SDK for the subscriber.)`US`, `CA`, etc.
`offer_code`String**This field is not available when `type` is set to `SUBSCRIBER_ALIAS` or `TRANSFER`.** The offer code that the customer used to redeem the transaction. Available for App Store and Play Store. For App Store this property corresponds to the [`offer_code_ref_name`](πŸ”—ο»Ώ). For Play Store this corresponds to the [`promotionCode`](πŸ”—ο»Ώ).Can be null if no offer code was used for this product.


To get the RevenueCat event `id` from a Subscription Lifecycle webhook, simply make an API call to our GET `/subscribers`endpoint with the `app_user_id` after receiving the webhook and look for the latest purchase in the [subscription](πŸ”—ο»Ώ)/[non-subscription](πŸ”—ο»Ώ) object.

Determine trial and subscription duration

To get a trial or subscription's duration from a webhook, you can subtract purchased_at_ms from expiration_at_ms and you will get the duration of the trial in milliseconds.

# Cancellation and Expiration Reasons

ReasonDescriptionApp StorePlay StoreAmazonWebPromo
`UNSUBSCRIBE`Subscriber cancelled voluntarily. **This event fires when a user unsubscribes, not when the subscription expires.**βœ…βœ…βœ…βœ…βŒ
`BILLING_ERROR`Apple, Amazon, or Google could not charge the subscriber using their payment method. The `CANCELLATION` event with cancellation reason `BILLING_ERROR` is fired as soon as the billing issue has been detected. The `EXPIRATION` event with expiration reason `BILLING_ERROR` is fired if the grace period (if set up) has ended without recovering the payment, and the customer should lose access to the subscription.βœ…βœ…βœ…βŒβŒ
`DEVELOPER_INITIATED`Developer cancelled the subscription.βœ…βœ…βŒβŒβœ…
`PRICE_INCREASE`Subscriber did not agree to a price increase.βœ…βŒβŒβŒβŒ
`CUSTOMER_SUPPORT`Customer received a refund from Apple support, a Play Store subscription was refunded through RevenueCat, an Amazon subscription was refunded through Amazon support, or a web subscription was refunded. Note that this does not mean that a subscription's autorenewal preference has been deactivated since refunds can be given without cancelling a subscription. You should check the current subscription status to check if the subscription is still active.βœ…βœ…βœ…βœ…βŒ
`UNKNOWN`Apple did not provide the reason of the cancellation.βœ…βŒβŒβŒβŒ
`SUBSCRIPTION_PAUSED`The subscription expired because it was paused (only `EXPIRATION` event)βŒβœ…βŒβŒβŒ