> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.onesignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Paid plan subscription renewal & expiry Journey

> Win back users who turn off auto-renew and remind them before their paid plan expires, using a Custom Event entry trigger plus date-relative Wait Until steps.

Apps with paid plans have two high-value moments worth automating: when a user **turns off auto-renew** (intent to churn) and the days **leading up to expiration** (last chance to retain). This guide combines both into a single Journey.

This is a **hybrid** Journey. The entry is **event-driven** (the user turns off auto-renew), while the reminder timing is **date-relative** (a fixed number of days before a stored expiration date). This page shows how [Event-driven Journeys](./event-driven-journeys) and [Time Operators](./time-operators) work together.

**What you will build**

* Paid plan or subscription data sent to OneSignal: an `auto_renew_disabled` Custom Event and a `plan_expires_at` timestamp Tag
* A win-back email that fires the moment auto-renew is turned off
* Reminder emails at **T-3 days**, **T-2 days**, and **day-of** expiration
* Separate messaging for monthly vs yearly plans
* Exit rules so users who re-enable auto-renew or renew stop receiving messages

<Note>
  This Journey mixes a Custom Event entry trigger with segment-based Wait Until steps. Review [Journey settings](./journeys-settings) and [Journey actions](./journeys-actions) first if you are new to Journeys.
</Note>

***

## Before you start: what you need in OneSignal

Time-based reminders depend on OneSignal knowing each user's expiration date and auto-renew state. If your app does not already send that data, a developer must send it once (via SDK, API, or an integration such as Mixpanel or Amplitude) before the Journey can work.

You need two pieces of data:

| Data                  | Type                             | Purpose                                                                              |
| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------ |
| `plan_expires_at`     | **Unix timestamp Tag** (seconds) | Drives the T-3 / T-2 / day-of reminder timing via [Time Operators](./time-operators) |
| `auto_renew_disabled` | **Custom Event**                 | Starts the Journey the moment a user turns off auto-renew                            |

Optionally, add a `plan_tier` Tag (for example `monthly` or `yearly`) if you want different messaging per plan.

**The `plan_expires_at` Tag is only required if you want OneSignal to calculate the reminder timing.** If your backend already detects when a plan is approaching expiry, you can skip the timestamp Tag and the reminder segments: send a Custom Event (for example, `plan_expiring`) at each reminder point and trigger the message from that event instead. Use the Tag-based approach when OneSignal owns the timing, and the event-based approach when your backend owns it.

<Note>
  There is no special "future date" field in OneSignal. A **Tag that stores a Unix timestamp** is what enables date-relative segments. Time Operators compare that stored timestamp against the current time. See [Time Operators](./time-operators).
</Note>

***

## Setup

### Step 1: Send paid plan data to OneSignal

Send the expiration date as a Tag and the auto-renew-off action as a Custom Event.

<Tabs>
  <Tab title="Frontend SDK">
    ```javascript Set the expiration Tag (and optional plan tier) theme={null}
    OneSignal.User.addTags({
      plan_expires_at: 1793404800, // Unix timestamp in seconds
      plan_tier: "yearly"
    });
    ```

    ```javascript Track auto-renew turned off theme={null}
    OneSignal.User.trackEvent("auto_renew_disabled", {
      plan_tier: "yearly"
    });
    ```

    See [Data Tags](./mobile-sdk-reference#data-tags) and [Custom Events](./mobile-sdk-reference#custom-events) in the SDK reference.
  </Tab>

  <Tab title="REST API">
    ```json Update User API: set the expiration Tag theme={null}
    {
      "properties": {
        "tags": {
          "plan_expires_at": 1793404800,
          "plan_tier": "yearly"
        }
      }
    }
    ```

    ```json Create Custom Events API: auto-renew turned off theme={null}
    {
      "events": [
        {
          "name": "auto_renew_disabled",
          "properties": { "plan_tier": "yearly" },
          "external_id": "ID_OF_THE_USER"
        }
      ]
    }
    ```

    See the [Update User API](/reference/update-user) and [Create Custom Events API](/reference/create-custom-events).
  </Tab>
</Tabs>

<Warning>
  Store `plan_expires_at` in **seconds**, and update it whenever the paid plan renews or the date changes. A stale expiration date will send reminders at the wrong time.
</Warning>

<Check>
  OneSignal now knows when each user's paid plan expires and when they turn off auto-renew, the two signals this Journey depends on.
</Check>

### Step 2: Create your email templates

Create the templates you will use in the Journey. A typical set is:

1. **Auto-renew off**: what they will lose if they don't renew
2. **T-3 days**: reminder that the paid plan ends in 3 days
3. **T-2 days**: 2 days left
4. **Day-of**: final notice

You can personalize with the entry event's properties (for example `{{ journey.first_event.properties.plan_tier }}`). See [Personalize with Custom Events](./personalization-custom-event) and [Liquid syntax](./using-liquid-syntax).

### Step 3: Create the reminder segments

Skip this step if your backend detects expiration itself and sends a Custom Event (for example, `plan_expiring`) at each reminder point. In that case, trigger the reminder messages from that event instead of segments.

Reminder timing comes from segments built on the `plan_expires_at` timestamp Tag using [Time Operators](./time-operators). Create one segment per reminder window, for example:

* **Expiring in 3 days**: `plan_expires_at` is within the next 3 days
* **Expiring in 2 days**: `plan_expires_at` is within the next 2 days
* **Expiring today**: `plan_expires_at` is within the next 1 day

See [Time Operators](./time-operators) for the exact operator configuration, since the comparison is relative to the current time.

<Check>
  You now have segments that a user moves into automatically as their expiration date approaches.
</Check>

### Step 4: Build the Journey

Create a Journey (see [Journeys](./journeys-overview)).

#### Entry and exit rules

**Entry rule:** Custom Event `auto_renew_disabled`. The user enters the moment they turn off auto-renew. See [Custom Event entry rules](./journeys-settings#custom-events).

**Exit rule:** Add an exit condition so users who change their mind stop receiving reminders. Use a `plan_renewed` or `auto_renew_enabled` Custom Event as an **exit rule**, so anyone who renews or re-enables auto-renew leaves the Journey immediately. See [Exit when Custom Event condition occurs](./journeys-settings#exit-when-custom-event-condition-occurs).

#### Journey steps

<Steps>
  <Step title="Send the win-back email">
    Add a **Message** step using your "auto-renew off" template. This fires immediately on entry, while intent is highest.
  </Step>

  <Step title="Wait Until: expiring in 3 days">
    Add a **Wait Until** step with the condition **user enters the "Expiring in 3 days" segment**. When they enter it, send the T-3 email.
  </Step>

  <Step title="Repeat for T-2 and day-of">
    Add Wait Until steps for the "Expiring in 2 days" and "Expiring today" segments, each followed by its reminder email.
  </Step>

  <Step title="Branch by plan (optional)">
    If monthly and yearly subscribers need different copy, add a Yes/No or segment branch on the `plan_tier` Tag and use plan-specific templates. Alternatively, run two Journeys, one per plan tier.
  </Step>
</Steps>

<Warning>
  **Always add an expiration branch to every Wait Until step.** A Wait Until without an expiration branch will hold a user **indefinitely** if the condition is never met (for example, if their expiration date is more than 3 days out when they enter, or the Tag is missing). Set an expiration on each Wait Until and decide whether the user should continue through the Journey or exit. Without this, users can get permanently stuck partway through the flow. See [Wait Until](./journeys-actions#wait-until).
</Warning>

<Check>
  Users now enter when they turn off auto-renew, receive a win-back email immediately, then get timed reminders as their expiration date approaches, and exit early if they renew.
</Check>

***

## FAQ

### Can I build this without developer help?

Only if OneSignal already has the data. The reminder timing needs each user's expiration date stored as a `plan_expires_at` timestamp Tag, and the entry trigger needs an `auto_renew_disabled` Custom Event. If those are not already being sent, a developer must send them once via the [SDK](./mobile-sdk-reference), [API](/reference/create-custom-events), or an [integration](./integrations). After that, building and editing the Journey is self-serve.

### Should the reminders use segments (Time Operators) or a Custom Event?

Use **segments with [Time Operators](./time-operators)** when OneSignal owns the timing: store the expiration date as the `plan_expires_at` Tag and let OneSignal compute the reminder windows. Use a **Custom Event** when your backend owns the timing and fires the reminder moment itself (for example, a `plan_expiring` event), in which case you do not need the timestamp Tag or the reminder segments. Choose the Tag-based approach if you already store the expiration date and want to avoid extra backend events.

### What is the "future date" tag the AI assistant mentioned?

There is no separate "future date" tag type. It refers to a **Tag storing a Unix timestamp** (like `plan_expires_at`). Time Operators compare that stored timestamp against the current time to build "expiring in N days" segments.

### Why did my users get stuck in the Journey?

Almost always a **Wait Until step without an expiration branch**. If the user never enters the segment the step is waiting for, they wait forever. Add an expiration to every Wait Until and choose whether to continue or exit the user. See [Journey actions](./journeys-actions#wait-until).

### How do I handle monthly vs yearly subscribers?

Add a `plan_tier` Tag and branch on it inside the Journey, or run a separate Journey per plan tier. Both approaches let you tailor copy and timing per plan.

<Info>
  Need help?

  Chat with our Support team or email `support@onesignal.com`

  Please include:

  * Details of the issue you're experiencing and steps to reproduce if available
  * Your OneSignal App ID
  * The External ID or Subscription ID if applicable
  * The URL to the message you tested in the OneSignal Dashboard if applicable
  * Any relevant [logs or error messages](/docs/en/capturing-a-debug-log)

  We're happy to help!
</Info>
