revenuecat-integration

RevenueCat Grace Period Push Notifications with Pushlane

Use Pushlane and RevenueCat billing issue events to notify subscribers during grace period, stop on renewal, and hand off cleanly at expiration.

A subscription grace period is an awkward moment for the person paying. Their renewal failed, the store may still be retrying the charge, and they may still have full access. From inside the app, nothing appears broken. Then access can disappear later for a problem they never noticed.

A RevenueCat grace period push notification should close that information gap. It should not threaten immediate lockout, claim the subscription has expired, or send someone through a win-back campaign while their entitlement remains active. With Pushlane, the practical trigger is RevenueCat's BILLING_ISSUE event, and the flow should stop when a RENEWAL confirms recovery or hand off when EXPIRATION confirms access ended.

This guide explains the states, the current Pushlane capabilities, and a conservative flow you can ship without overstating what either system knows.

Grace period is not the same as billing retry

RevenueCat's billing issues and grace periods guide distinguishes the payment failure from the access state. A billing issue means a charge attempt failed. A grace period is a configured interval in which the subscription remains active and the user retains the entitlement while collection continues.

The stores implement the lifecycle differently:

  • Apple says an enabled Billing Grace Period lets subscribers keep access while Apple attempts to recover the payment. Without it, paid service can pause during billing retry. See Apple's Billing Grace Period setup.
  • Google Play can move a failed renewal through grace period and then account hold. During grace, access continues; during account hold, Google says entitlement access should be blocked. See the official Play subscription lifecycle.

So "billing retry" and "grace period" are not interchangeable. Retry describes continued collection attempts. Grace describes continued entitlement access during some portion of that process.

Four events or states also need to stay separate:

  • BILLING_ISSUE: a charge attempt failed. RevenueCat says this does not mean the subscription expired.
  • CANCELLATION: auto-renewal was disabled, or a cancellation/refund condition occurred. For a normal voluntary cancellation, access usually continues through the paid period.
  • Grace period: an active entitlement continues temporarily after a billing issue. It is a state represented by a timestamp in the RevenueCat payload, not a standalone webhook event type.
  • EXPIRATION: access ended. Recovery messaging after this point is win-back, not grace-period dunning.

That vocabulary determines whether your push is accurate when it reaches the lock screen.

What RevenueCat sends and what Pushlane exposes

RevenueCat sends BILLING_ISSUE when an attempt to charge the subscriber fails. Its current event field reference includes grace_period_expiration_at_ms on billing issue events. The value can be null when no grace period applies. RevenueCat sends RENEWAL if payment is recovered and EXPIRATION if the entitlement eventually ends.

Pushlane currently maps those lifecycle webhooks to:

  • revenuecat.billing_issue
  • revenuecat.renewal
  • revenuecat.expiration

The Pushlane worker records a billing issue as an in_grace entitlement state internally and bridges the billing issue into the flow engine. The flow event includes product_id, store, environment, period_type, expiration_at_ms, purchase and revenue fields, and entitlement ids when RevenueCat supplies them.

One current limitation matters: Pushlane does not project RevenueCat's grace_period_expiration_at_ms into flow properties. There is also no separate grace_period_started or grace_period_ended Pushlane event. Do not configure a delay that claims to land a fixed number of hours before the grace deadline based on expiration_at_ms; that is the transaction expiration field, not the dedicated grace-period expiration field.

Until that property is exposed, use the billing issue as an immediate signal, keep follow-up timing conservative, and let RENEWAL or EXPIRATION determine the outcome.

Build the recovery flow in Pushlane

Connect RevenueCat first using /docs/revenuecat. Use the same stable user id for RevenueCat's App User ID and Pushlane.identify, because that is how the server-side billing event reaches the correct registered device.

Then create the flow:

  1. Use revenuecat.billing_issue as the entry trigger.
  2. Add an is_production branch so sandbox billing tests cannot notify real production audiences.
  3. Optionally branch on store, product_id, or entitlement_ids when the remediation path differs.
  4. Send one prompt notification soon after the billing issue arrives.
  5. Wait for a conservative interval that fits your shortest configured grace window, without claiming it is synchronized to the exact deadline.
  6. Exit when revenuecat.renewal arrives.
  7. If revenuecat.expiration arrives, stop grace-period language and hand off to an expiration win-back flow.

RevenueCat describes webhook delivery as at least once, while Pushlane deduplicates each RevenueCat event.id. A retried billing webhook therefore does not create a second entry for the same event.

The first notification can be simple:

We could not renew your subscription. Your access may still be active, but your payment method needs attention.

The phrase "may still be active" is deliberately cautious. The presence of a BILLING_ISSUE alone does not prove a grace period exists, and Pushlane does not currently expose the dedicated grace deadline to let the flow verify it.

If your store and product configuration guarantee grace for the audience entering a specific branch, you can be more direct:

We could not renew Premium. You still have access for now. Review your payment method to avoid an interruption.

That claim should come from your actual store configuration, not from a generic template.

Choose a destination the user can act on

A billing notification without a useful destination creates anxiety and no resolution. The best link depends on the store and your app.

For Apple subscriptions, your app can explain the issue and direct the user toward Apple's subscription or payment-management surface. For Google Play, Google documents subscription management links and in-app purchase flows in its subscription guidance. Do not imply that Pushlane updates payment credentials itself. Pushlane sends the lifecycle notification; Apple, Google, or your billing provider owns the payment method and collection attempt.

Before shipping, test the link for an authenticated user, an expired session, and someone opening after the subscription state changed. The screen should refresh entitlement state instead of rendering the original warning forever.

Stop on recovery

RevenueCat sends RENEWAL when a retry succeeds or a lapsed subscriber resubscribes. That event is the cleanest stop signal for the dunning flow. Once it arrives, the person should not receive another "fix your payment" push.

You may choose to send a short confirmation:

Your subscription is active. No further action is needed.

Often silence is better. The renewed entitlement and restored app experience already confirm success. A confirmation is most useful when the earlier message created uncertainty or when access visibly changed.

Do not use UNCANCELLATION as a universal payment recovery signal. RevenueCat defines it as re-enabling a non-expired cancelled subscription. That is a voluntary auto-renew decision, not necessarily recovery from a failed charge.

Hand off at expiration

If collection is not recovered and the entitlement ends, RevenueCat sends EXPIRATION. At that point, copy such as "keep your access" is no longer accurate. The user needs a reactivation path.

End the billing issue flow and let revenuecat.expiration start a separate sequence:

Your Premium access has ended. Open the app to review your subscription options.

The separate RevenueCat expiration event push guide covers that handoff in depth. Keeping the flows separate also prevents one long automation from mixing active grace messaging with post-expiration win-back copy.

Pushlane's current expiration flow properties do not expose RevenueCat's expiration_reason, so an expiration-triggered flow cannot reliably say that billing failure caused the lapse. Keep the expiration message cause-neutral unless your app verifies the state elsewhere.

Handle cancellation without mixing messages

RevenueCat may send a CANCELLATION with cancel_reason=BILLING_ERROR when a billing problem is detected. It also sends cancellation for voluntary churn and some refund cases. Pushlane bridges revenuecat.cancellation, but it does not currently expose cancel_reason as a flow property.

That means revenuecat.cancellation is not a safe substitute for the billing issue trigger inside Pushlane. A cancellation flow that assumes every event represents a user's choice will misread billing failures. A billing flow that assumes every cancellation represents failed payment will misread voluntary churn.

Use revenuecat.billing_issue for payment messaging. Use revenuecat.cancellation only for copy that remains true without knowing the reason, or wait until Pushlane exposes the reason field before branching into cause-specific paths.

Frequency should follow usefulness

The payment problem is important, but notifications cannot make the store retry faster. Start with one prompt message. Add a reminder only if your configured grace window leaves time to act and the reminder carries honest new urgency.

Because the exact grace deadline is not currently available in Pushlane flows, avoid "last day" or "expires tonight" copy. A relative delay after BILLING_ISSUE is not proof of time remaining. Store rules, product configuration, webhook timing, and recovery attempts can all affect the state.

Track flow entries, push sends, opens, and subsequent revenuecat.renewal or revenuecat.expiration events. Define the observation window before reviewing results. A renewal after a push is useful operational evidence, but it is not automatically proof that the push caused recovery. Use a control group when your volume supports one instead of publishing a borrowed recovery benchmark.

Test the lifecycle, not just the endpoint

RevenueCat's dashboard TEST webhook verifies authentication and connectivity. Pushlane acknowledges it but does not persist it or enter a flow. A green test response therefore does not prove your billing branches or stop conditions.

Use sandbox subscription testing to verify:

  • The App User ID matches the user identified in Pushlane.
  • revenuecat.billing_issue enters the intended flow.
  • Sandbox events follow the non-production branch.
  • The first push opens the correct payment-help destination.
  • revenuecat.renewal stops later billing reminders.
  • revenuecat.expiration ends grace language and enters the win-back flow.
  • A webhook retry with the same event id does not duplicate the sequence.

Pushlane's iOS APNs path is currently proven end to end. Android clients register FCM tokens, but Pushlane does not yet claim end-to-end Android delivery. Scope the production rollout accordingly.

Make the uncertain moment clearer

The person in a possible grace period is not yet a churned subscriber. They are someone whose payment failed and who may still have time to prevent an interruption. Treating them that way produces better copy and cleaner automation.

Trigger on revenuecat.billing_issue, explain the issue without overstating entitlement state, stop on revenuecat.renewal, and switch to win-back only on revenuecat.expiration. Most importantly, do not pretend Pushlane currently has the dedicated grace deadline in its flow properties. A modest flow built on known events is more trustworthy than a precise-looking countdown built on the wrong timestamp.