Event Types and Fields
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 Type | Description | App Store | Play Store | Amazon | Stripe | Promo |
---|---|---|---|---|---|---|
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. Note that, for the case of subscription refunds, this event is only fired if the latest subscription period of a subscription is refunded, refunds for earlier subscription periods do not trigger this event. | ✅ | ✅ | ✅ | ✅ | ✅ |
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. Please note: The webhook will only be sent for the destination user despite us displaying this event in both customer histories because the event body is exactly the same for both users. | ✅ | ✅ | ✅ | ✅ | ❌ |
SUBSCRIPTION_EXTENDED | An existing subscription has been extended (the expiration date of the current subscription period has been pushed back to the future). This event is fired when a Apple App Store or Google Play Store subscription is extended through the store's API. On the Google Play Store, this event can also sometimes fire when Google defers charging for a renewal by less than 24 hours (for unknown reasons). In this case, you will receive a SUBSCRIPTION_EXTENDED webhook, followed by either a RENEWAL or BILLING_ISSUE webhook within the next 24 hours. | ✅ | ✅ | ✅ | ✅ | ❌ |
TEMPORARY_ENTITLEMENT_GRANT | RevenueCat was temporarily unable to validate a purchase with the respective store and has granted a short-term entitlement to the customer. This event is sent in exceptional situations (for example, a partial app store outage) and is used to avoid customers making a purchase but not getting access to their entitlement. The expiration date of the entitlement is always at most 24 hours in the future. Once the exceptional situation has been resolved and RevenueCat can validate the purchases, a regular INITIAL_PURCHASE event will be sent. If the purchase can't be validated, an EXPIRATION event with the same transaction_id is sent. Please note: because this event type is dispatched in cases of limited connectivity with the store servers, it contains less information than a regular purchase event. The following fields are guaranteed to be present: app_user_id , purchased_at_ms , expiration_at_ms , event_timestamp_ms , product_id , entitlement_ids , store , transaction_id (note: the transaction_id might be different from the store's transaction_id present in a subsequent INITIAL_PURCHASE event; depending on the store, the product_id might be different from the product_id present in the subsequent INITIAL_PURCHASE event). | ✅ | ✅ | ✅ | ✅ | ❌ |
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.
- Code
{
"api_version": "1.0",
"event": {
"aliases": [
"yourCustomerAliasedID",
"yourCustomerAliasedID"
],
"app_id": "yourAppID",
"app_user_id": "yourCustomerAppUserID",
"commission_percentage": 0.3,
"country_code": "US",
"currency": "USD",
"entitlement_id": "pro_cat",
"entitlement_ids": [
"pro_cat"
],
"environment": "PRODUCTION",
"event_timestamp_ms": 1591121855319,
"expiration_at_ms": 1591726653000,
"id": "UniqueIdentifierOfEvent",
"is_family_share": false,
"offer_code": "free_month",
"original_app_user_id": "OriginalAppUserID",
"original_transaction_id": "1530648507000",
"period_type": "NORMAL",
"presented_offering_id": "OfferingID",
"price": 2.49,
"price_in_purchased_currency": 2.49,
"product_id": "onemonth_no_trial",
"purchased_at_ms": 1591121853000,
"store": "APP_STORE",
"subscriber_attributes": {
"$Favorite Cat": {
"updated_at_ms": 1581121853000,
"value": "Garfield"
}
},
"takehome_percentage": 0.7,
"tax_percentage": 0.3,
"transaction_id": "170000869511114",
"type": "INITIAL_PURCHASE"
}
}
Common Fields
Field | Type | Description | Possible 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 , SUBSCRIPTION_EXTENDED |
id | String | Unique identifier of the event. | |
app_id | String | Unique 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 | Integer | The 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 | String | Last seen app user id of the subscriber. | |
original_app_user_id | String | The first app user id used by the subscriber. | |
aliases | Array | All app user ids ever used by the subscriber. | |
subscriber_attributes | Array | All attributes set for the customer, including common reserved attributes such as $idfa , $gpsAdId , $campaign , $keyword , etc. |
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
Field | Type | Description | Possible Values |
---|---|---|---|
product_id | String | Product 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 | Array | Entitlement identifiers of the subscription. It can be NULL if the product_id is not mapped to any entitlements. | |
entitlement_id | String | Deprecated. See entitlement_ids . | Deprecated. See entitlement_ids . |
period_type | String | Period 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 | Integer | Time 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 the subscription does not have a grace period. | |
expiration_at_ms | Integer | Expiration 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 | Integer | The 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 | String | Store the subscription belongs to. | AMAZON , APP_STORE , MAC_APP_STORE , PLAY_STORE , PROMOTIONAL , STRIPE . |
environment | String | Store 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 | String | Product 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 | Double | The USD price of the transaction. Can be NULL if the price is unknown, and 0 for free trials. Can be negative for refunds. | |
currency | String | The ISO 4217 currency code that the product was purchased in. Can be NULL if the currency is unknown. | USD , CAD , etc. |
price_in_purchased_currency | Double | The 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 | Double | The 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 | Double | The 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 | Double | DEPRECATED: 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 | Map of attribute names to attribute objects. For more details see the customer attributes guide. | |
transaction_id | String | Transaction 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 | Boolean | Indicates if the user made this purchase or if it was shared to them via Family Sharing. Always false for non-Apple purchases. | true or false . |
transferred_from | String[] | This field 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 | String[] | 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 | String | The 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 | 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. | |
renewal_number | Integer | The number of renewals that this subscription has already gone through. Always starts at 1. Trial conversions are counted as renewals. is_trial_conversion is used to signify whether a transaction was a trial conversion. |
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.
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
Reason | Description | App Store | Play Store | Amazon | Web | Promo |
---|---|---|---|---|---|---|
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 (RevenueCat Billing or Stripe Billing) 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 for the cancellation. | ✅ | ❌ | ❌ | ❌ | ❌ |
SUBSCRIPTION_PAUSED | The subscription expired because it was paused (only EXPIRATION event). | ❌ | ✅ | ❌ | ❌ | ❌ |