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
`type`String[Type of the event](🔗).`TEST` `INITIAL_PURCHASE` `NON_RENEWING_PURCHASE` `RENEWAL` `PRODUCT_CHANGE` `CANCELLATION` `BILLING_ISSUE` `SUBSCRIBER_ALIAS` `SUBSCRIPTION_PAUSED` `UNCANCELLATION` `TRANSFER`
`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)