Pushlane: RevenueCat Expiration Push Notifications
Build an honest Pushlane win-back flow from RevenueCat expiration events, with separate paths for expired trials, paid subscriptions, and billing failures.
An expiration event is not an early warning. It is confirmation that access has ended. That makes a RevenueCat expiration event push notification fundamentally different from a cancellation save or a billing reminder. The person receiving it may have deliberately left, may have reached the end of a trial, or may have lost access after a failed renewal. Your job is not to pretend those situations are the same. It is to acknowledge what happened and offer a relevant next step.
Pushlane turns RevenueCat's EXPIRATION webhook into the flow event revenuecat.expiration. You can use that event to start a mobile push flow without adding purchase-state tracking to the app. This guide shows how to build the flow, split expired trials from paid subscriptions, and avoid sending win-back copy while someone still has access.
What RevenueCat expiration actually means
RevenueCat defines EXPIRATION as the point when a subscription has expired and the associated access should be removed. Its official event types and fields reference also notes that an expiration may follow several paths, including a subscription reaching the end of its cancelled term, an unrecovered billing issue, the end of a grace period, or a refund.
That definition gives expiration a precise place in the lifecycle:
- Cancellation means auto-renewal was turned off or a purchase was cancelled or refunded. In the common voluntary cancellation case, paid access continues until the current period ends.
- Billing issue means an attempt to charge the subscriber failed. RevenueCat explicitly says this does not itself mean the subscription expired.
- Grace period is a window after a failed payment during which the subscriber may retain access while the store retries collection.
- Expiration means the entitlement has ended. This is the moment for reactivation or win-back, not a warning that access might end later.
There are edge cases behind CANCELLATION, especially refunds, so do not infer entitlement state from that event alone. RevenueCat exposes cancel_reason on cancellation events and expiration_reason on expiration events for teams processing the raw webhook. Pushlane's current flow bridge does not expose either reason as a flow property. It does expose the event type, product, store, period type, environment, entitlement ids when present, and relevant timestamps. Build only on the properties that are actually available.
Why the expiration push should be restrained
The user has already crossed a boundary. They no longer have the paid entitlement, and your push appears on a device that may not have opened the app recently. A message that says "your subscription is about to end" is late and inaccurate. A message that says "your payment failed" assumes a cause you cannot currently branch on in Pushlane. A message that says "we miss you" may be harmless, but it gives the person no concrete reason to return.
A useful expiration push does three things:
- States the current condition without drama.
- Names one piece of value the person can regain.
- Opens the app at a place where reactivation makes sense.
For example:
Your Premium access has ended. Reopen the app to review your options and restore access.
That copy works when you cannot confidently identify the expiration cause. If your product or period branch gives you more context, make the message more specific without inventing intent.
Build the Pushlane flow
First connect RevenueCat to Pushlane using the RevenueCat integration guide or the current /docs/revenuecat setup. The identity requirement matters more than any copy choice: the RevenueCat App User ID must equal the id passed to Pushlane.identify. Without that shared id, Pushlane cannot match the server-side subscription event to the user's registered device.
In the Pushlane builder:
- Create a flow with
revenuecat.expirationas the entry event. - Add a branch on
period_typeto separateTRIALfromNORMALorINTROperiods. - If useful, branch on
product_id,store, orentitlement_idsto keep the offer and destination relevant. - Add one push shortly after entry.
- Add a short wait and, only when your flow logic can confirm that no recovery event arrived, consider one follow-up.
- Stop the sequence when a
revenuecat.renewal,revenuecat.initial_purchase, or other event that represents restored access arrives for your implementation.
Pushlane deduplicates RevenueCat webhooks by event.id, so RevenueCat retries do not enter the same expiration flow twice. RevenueCat documents webhook delivery as at least once in its webhook guide, which is why that idempotency matters.
Use an is_production branch if your production flow should exclude sandbox events. Pushlane records and bridges sandbox lifecycle events too, which is useful for testing but unsafe to ignore when a flow is activated.
Split expired trials from expired paid subscriptions
An expired trial user and a lapsed paid subscriber have different histories with the product. Pushlane exposes RevenueCat's period_type on the event, so the first useful branch is straightforward.
Expired trial
The trial user may never have reached the feature that makes the subscription worthwhile. Repeating the paywall headline is unlikely to resolve that. Point to the smallest valuable action they can still understand from the notification.
Your Premium trial has ended. See what you can unlock before you decide.
The destination should let the person review the offer and product value without pretending access remains active. For broader trial timing and pre-expiration messaging, use the trial expiry push notification guide. Those reminders belong before expiration; this message belongs after it.
Expired paid subscription
A former paid subscriber already knows the core product. Their best reason to return may be continuity, saved work, or meaningful improvements since they left.
Your Premium access has ended. Your saved projects are waiting when you are ready to return.
Only mention retained data if your app genuinely keeps it and your retention policy allows the claim. If a specific entitlement or product id maps to a different experience, branch accordingly rather than using one generic message for every plan.
Do not confuse voluntary and involuntary churn
The hardest limitation in the current Pushlane flow is causal segmentation. RevenueCat's raw expiration webhook can contain expiration_reason, including values such as UNSUBSCRIBE and BILLING_ERROR. Pushlane currently validates and persists the webhook, but its synthetic flow event does not project expiration_reason. You therefore cannot honestly create a Pushlane branch that says "billing failure" versus "user chose to leave" from the expiration trigger alone.
You can still design the surrounding flows correctly:
- Start payment recovery on
revenuecat.billing_issue, while expiration has not necessarily happened. - Start a voluntary cancellation save on
revenuecat.cancellation, while the person commonly still has access. - Start generic win-back on
revenuecat.expiration, once access has ended. - End stale payment or cancellation messaging when
revenuecat.renewalorrevenuecat.uncancellationindicates recovery.
This is why lifecycle flows should work together. The four subscription push flows guide maps the handoffs in more detail.
Timing and frequency
Triggering from the webhook means "after RevenueCat reports expiration," not necessarily the exact millisecond the store changed state. RevenueCat says EXPIRATION usually arrives within seconds to minutes when platform server notifications are configured, but may be delayed without them. Treat the webhook as the operational trigger and avoid copy such as "just now" that depends on exact delivery timing.
Start with one message. A second push needs a distinct reason to exist, such as a meaningful product update or a clearly different value proposition. Repeating the same subscription offer is not a strategy. Push permission is durable only while the person believes notifications remain useful.
Measure outcomes your app can observe: flow entries, sends, opens, and subsequent RevenueCat recovery events. Do not assign every later purchase to the push merely because it happened after a notification. Use a holdout or an experiment when volume supports it, and choose the attribution window before reading results. Pushlane supports flow experiments, but your own traffic determines whether the result is informative.
Test the full path before activation
RevenueCat provides dashboard test webhooks, but its TEST event only verifies that the authenticated endpoint responds. Pushlane acknowledges that event without entering a flow. To test the actual expiration automation, use a RevenueCat sandbox subscription lifecycle and inspect both systems.
Check that:
- RevenueCat delivered the webhook successfully.
- The event appears as
revenuecat.expirationfor the expected Pushlane user. - Your
is_productionbranch prevents an unintended production send during sandbox testing. - Trial and paid periods take the intended branch.
- The deep link opens a valid reactivation surface for a user without the entitlement.
- A later renewal or purchase stops any remaining win-back steps.
On iOS, Pushlane's APNs delivery path is the currently proven end-to-end path. Android clients can register FCM tokens, but Pushlane does not currently claim end-to-end Android delivery. Keep that platform boundary explicit when planning the audience for this flow.
Treat expiration as a state, not a verdict
Expiration tells you access ended. It does not tell you that the user disliked the app, that a payment failed, or that a discount will bring them back. The best RevenueCat expiration event push notification respects that uncertainty.
Use revenuecat.expiration to begin a small, cause-neutral win-back flow. Split trial and paid histories with period_type, personalize only from properties Pushlane actually exposes, and stop when access returns. The result is less aggressive than a generic churn blast and much more useful to the person receiving it.