{
  "slug": "subscription-renewal-reminder-email",
  "title": "Subscription renewal reminder email: template and code",
  "url": "https://www.courier.com/recipes/subscription-renewal-reminder-email",
  "markdown": "https://www.courier.com/recipes/subscription-renewal-reminder-email.md",
  "stage": "Monetization",
  "purpose": "Remind annual customers before they renew, so the charge never comes as a surprise.",
  "summary": "A subscription renewal reminder tells annual customers their plan is about to renew, so the charge never surprises them. It emails the billing contact 30 days ahead with the date and amount, then emails the account admin and leaves an Inbox note a week ahead. Canceling or switching plans stops the reminders.",
  "channels": [
    "email",
    "inbox"
  ],
  "shape": "journey",
  "shows": [
    "A 23-day wait between the two reminders",
    "An email to a billing contact who isn't the app user",
    "Cancel when the plan changes"
  ],
  "data": [
    {
      "name": "profile.first_name",
      "type": "string",
      "example": "Dana",
      "note": "From the profile, the account admin. \"there\" is the fallback when it's missing"
    },
    {
      "name": "billing_name",
      "type": "string",
      "example": "Sam"
    },
    {
      "name": "billing_email",
      "type": "email",
      "example": "billing@example.com",
      "note": "Where the 30-day email goes"
    },
    {
      "name": "plan_name",
      "type": "string",
      "example": "Pro"
    },
    {
      "name": "amount",
      "type": "integer",
      "example": "58800",
      "note": "In cents, as Stripe sends it. The templates show it as dollars"
    },
    {
      "name": "renews_at",
      "type": "string",
      "example": "2026-11-15T17:00:00Z",
      "note": "An ISO timestamp. The templates format it as a date in the recipient's timezone"
    }
  ],
  "steps": [
    {
      "step": "1",
      "when": "When the journey starts, 30 days before the plan renews",
      "channels": "Email",
      "templates": [
        "templates/renewal-30d-email.json"
      ]
    },
    {
      "step": "2",
      "when": "23 days after the first email, 7 days before the plan renews",
      "channels": "Email, Inbox",
      "templates": [
        "templates/renewal-7d-email.json",
        "templates/renewal-7d-inbox.json"
      ]
    }
  ],
  "files": {
    "journey": "journey.json",
    "templates": [
      "templates/renewal-30d-email.json",
      "templates/renewal-7d-email.json",
      "templates/renewal-7d-inbox.json"
    ],
    "setup": [
      "setup.ts"
    ],
    "start": [
      "start.ts",
      "start.py"
    ],
    "stop": [
      "stop.ts",
      "stop.py"
    ]
  },
  "docs": [
    "https://www.courier.com/docs/journeys/nodes/delay",
    "https://www.courier.com/docs/journeys/nodes/send",
    "https://www.courier.com/docs/journeys/nodes/cancel",
    "https://www.courier.com/docs/journeys/invoke"
  ],
  "related_recipes": [
    "https://www.courier.com/recipes/expiring-card-reminder-email",
    "https://www.courier.com/recipes/dunning-email-sequence",
    "https://www.courier.com/recipes/subscription-cancellation-email"
  ],
  "agent_prompt": "Add renewal reminders for annual plans to this app, sent through Courier: an email to the billing contact 30 days before renewal, then an email and in-app note to the account admin 7 days before, stopped if the customer cancels or switches plans. Follow the steps at https://www.courier.com/recipes/subscription-renewal-reminder-email.md and use the Courier skill for SDK details.\n\n1. Create the journey and its three templates in my Courier workspace by running setup.ts from that page with COURIER_API_KEY from our environment. Save the journey ID it prints as COURIER_RENEWAL_JOURNEY_ID. Replace the placeholder billing link (https://app.acme.com/billing) in the templates with ours.\n2. Find where this codebase handles billing and add a daily job that finds annual subscriptions renewing in 30 days (or use the billing provider's upcoming-renewal webhook, if it fires 30 days out). Start the journey for each one, with the admin's ID. Don't start it when the plan begins: the first email sends right away, so it has to start at the 30-day mark. Pass the fields in the Data fields table from our own data.\n3. Find where a subscription is canceled or changed to another plan and cancel the journey with the token renewal-<admin user ID>.\n4. Match this codebase's language, file layout, and error handling. Never hardcode the API key.\n5. When you're done, start one run for a test user and show me the run's steps. The first email sends right away and the run then waits 23 days, so cancel the test run once you've checked it.",
  "status": {
    "state": "passing",
    "last_tested": "2026-10-19"
  }
}
