> For the complete documentation index, see [llms.txt](https://docs.amply.tools/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.amply.tools/recipes/post-trial-recovery.md).

# Post-trial recovery

Catch users the first time they open the app after their trial ended and show a recovery paywall instead of the default one.

**Use this when** your product has a free trial and you see a measurable cohort that lapses without converting. **Don't use this when** the app has no trial, or when lapsed users should be treated identically to free users — this recipe only pays off if the recovery paywall differs from the default.

## Goal

A user opens the app on day 8, the trial ended on day 7, and the default paywall appears as it always has. You have one shot to re-sell them and you are using the same creative that did not convert them on day 1. This recipe detects the trial-ended state, fires a dedicated recovery flow with a different offer, and stops bothering them once they either convert or clearly churn.

## Setup

### In the dashboard

* Create a campaign `post-trial-recovery`.
* Trigger: session start.
* Conditions: `trial_status = "expired"` and `subscription_status = "free"` and `sessions_since_trial_end < 5`.
* Action: fire `yourapp://paywall/recovery` — a paywall that leads with a discounted annual offer or a one-time coupon.
* Frequency cap: one show per session, maximum three total.
* Create a follow-up campaign `post-trial-recovery-final` with the same trigger but `sessions_since_trial_end >= 5` and a sharper offer. Cap it at one show, ever.
* End state: when the user subscribes, set `subscription_status = "pro"`. Both campaigns stop matching.

### In the app (engineering hand-off)

* When your billing library reports trial expiration, update the user attributes:

  ```kotlin
  amply.setCustomProperty("trial_status", "expired")
  amply.setCustomProperty("subscription_status", "free")
  amply.setCustomProperty("trial_ended_at", trialEndDate)
  ```
* Track a session counter that increments only while `trial_status = "expired"`:

  ```kotlin
  amply.setCustomProperty("sessions_since_trial_end", count)
  ```
* Register the `yourapp://paywall/recovery` deeplink to present the recovery screen.

## How it runs

1. User installs, starts a 7-day trial. `trial_status = "active"`.
2. Day 7: billing library fires trial-ended. App sets `trial_status = "expired"`, `subscription_status = "free"`, `sessions_since_trial_end = 0`.
3. Day 8: user opens the app. Session starts. Counter is now `1`. `post-trial-recovery` matches. Amply fires the recovery deeplink. User sees the 40%-off annual offer instead of the default paywall.
4. User dismisses. Counter becomes `2`, `3`, `4` over the next few sessions. Campaign continues to match (counter stays below 5), capped at three total shows.
5. On the session where the counter reaches 5, `post-trial-recovery` no longer matches and `post-trial-recovery-final` does. A last-chance offer appears.
6. User subscribes. App sets `subscription_status = "pro"`. Neither campaign matches from this session onward.

## Metrics to watch

* Recovery paywall view-to-purchase rate. Compare against default paywall conversion for the same cohort.
* Days-to-conversion after trial end. Shorter is better.
* Share of trial-ended users who ever return. If this is <20%, the recovery window is wasted; move the effort to push or email.
* Long-term retention of recovered subscribers versus first-session subscribers. A bad recovery offer buys expensive churn.

## Related

* [Paywall versioning](/recipes/paywall-versioning.md) — swap the recovery paywall variant itself
* [Reactivation after inactivity](/recipes/reactivation-after-inactivity.md) — the same pattern for users with no trial signal
* [Campaigns](/user-guide/campaigns.md) — frequency caps and session-start triggers
* [User attributes](/concepts/user-attributes.md) — modeling trial and subscription state
* [User attributes how-to](/developer-guide/user-attributes.md) — writing the attributes from the app
* [AI-assisted integration](/getting-started/ai-assisted-integration.md) — describe this campaign in plain language and have your AI assistant build it
