Blog · · 6 min read

How to remind customers before their payment card expires with n8n and AI

Step-by-step guide to having n8n check your recurring payers in Google Sheets every morning, spot cards about to expire and use AI to draft a clear reminder in Gmail before the charge fails.

In a gym, academy, small SaaS or any business with recurring dues the customer's card expires… and the next charge bounces. You find out late, the customer thinks they are still current and you end up chasing card updates after hours.

In this guide you'll build an n8n workflow that checks a recurring-payers log in Google Sheets every morning, spots cards expiring in 30 or 7 days and uses AI to draft a clear reminder in Gmail. You review the draft and send it.

This is not the same as a failed recurring card payment reminder: there the charge already failed; here you warn before, so they update the card and the payment does not drop. It also does not replace a subscription renewal reminder: that one is about the plan ending; this one focuses only on the card's expiry date.

What you're going to build

A five-step workflow that runs on its own every day:

  1. Daily trigger: a Schedule Trigger node runs at 09:15.
  2. Read recurring payers: a Google Sheets node reads your "Recurring cards" sheet.
  3. Days-until-expiry filter: a Code node keeps only rows expiring in ~30 or ~7 days that have not yet had that stage's reminder.
  4. AI reminder: a Basic LLM Chain writes a warm email asking them to update the card, with a portal link when you have one.
  5. Draft and log: Gmail creates the draft and Google Sheets records the stage in reminder_sent.

What you need

  • An n8n instance (cloud or installed free on your computer).
  • Google Sheets and Gmail connected in n8n.
  • An OpenAI or other AI API key, with a monthly spend limit set in the provider's dashboard.
  • A "Recurring cards" sheet with columns: customer, email, plan, expires (YYYY-MM, or YYYY-MM-01), last4 (optional), portal_link (optional), notes and reminder_sent (empty or 1 / 2).

You can fill the sheet by pasting a Stripe, Lemon Squeezy or gateway export (many show month/year of expiry), or by hand when you set up a recurring payer. Never store the full card number: month/year and, if you want, the last 4 digits only. This guide assumes the rows are already in Sheets.

Step 1: prepare the recurring-cards sheet

Keep expires in a consistent format (2026-11 or 2026-11-01). Leave only active payers; archive cancelled or already updated rows. Put the last four digits in last4 when your gateway shows them (helps the customer recognise the card). Put a customer-portal or "update payment method" URL in portal_link when you have one; leave it blank otherwise. Use notes for useful preferences ("prefers WhatsApp", "travelling until Friday").

Step 2: trigger and read

Add a daily Schedule Trigger at 09:15 (check that the workflow timezone in Settings is correct for you). Connect it to Google Sheets → Get Row(s) on your "Recurring cards" sheet.

Step 3: filter cards that are about to expire

Add a Code node (Run Once for All Items) with this code:

const today = new Date(); today.setHours(0, 0, 0, 0);
const out = [];
for (const i of $input.all()) {
  const c = i.json;
  if (!c.email || !c.expires) continue;
  // Accepts YYYY-MM or YYYY-MM-DD; card expires on the last day of that month
  const parts = String(c.expires).trim().split(/[-/]/);
  const y = Number(parts[0]), m = Number(parts[1]);
  if (!y || !m) continue;
  const endOfMonth = new Date(y, m, 0); // day 0 of next month = last day of month m
  endOfMonth.setHours(0, 0, 0, 0);
  const days = Math.round((endOfMonth - today) / 86400000);
  if (days < 0 || days > 35) continue; // already expired or too far out
  const already = Number(c.reminder_sent || 0);
  let stage = null;
  if (already < 1 && days <= 30 && days >= 25) stage = 1; // ~30 days
  else if (already <= 1 && days <= 7 && days >= 0) stage = 2; // ~7 days (or less)
  if (!stage) continue;
  out.push({ json: { ...c, days, stage, expires_on: endOfMonth.toISOString().slice(0, 10) } });
}
return out;

Each card gets at most two reminders: one ~a month ahead and one in the final week. If nothing is due, the workflow ends without spending AI tokens.

Step 4: draft the reminder with AI

Add a Basic LLM Chain with a small, cheap model (for example gpt-4o-mini) and this prompt:

You manage billing for [BUSINESS NAME]. Write a short email to
{{ $json.customer }} letting them know that the card on their {{ $json.plan }} plan
expires on {{ $json.expires }} ({{ $json.days }} days left).
Last 4 digits (mention them if present; if empty, do not invent): {{ $json.last4 }}
Link to update the card (include it if present; if empty, do not invent one): {{ $json.portal_link }}
Notes: {{ $json.notes }}
- Warm, professional tone, max 6 sentences.
- Explain you are writing so the next charge does not fail; do not alarm or blame.
- If portal_link exists, invite them to update the card in one click; otherwise ask them to reply to this email.
- Do not invent amounts, discounts, cut-off dates or card details that are not in the data.
Return only the email body.

The facts come from your sheet; the AI only sets the tone. If your gateway already sends its own expiry email, use this flow only for a polite second reminder or for customers who do not get that email.

Step 5: create the draft and mark the reminder

Connect a Gmail → Create Draft node with recipient {{ $('Code').item.json.email }}, subject Your card expires soon · {{ $('Code').item.json.plan }} and the AI text as the body. Drafts let you review before sending, just like failed recurring card reminders or subscription renewal reminders.

Then a Google Sheets → Update Row node that looks up by email + expires and writes the stage into reminder_sent.

Once you trust the wording, you can switch Create Draft to Send.

How to test it

  1. Add a test row with your own email and an expires value ending in ~28 days, with reminder_sent empty.
  2. Run the workflow by hand and check that a draft appears and reminder_sent becomes 1.
  3. Run it again: no new draft should be created (outside the stage-1 window).
  4. Change expires so ~5 days remain (leaving reminder_sent at 1) and confirm the second reminder is prepared.

Cost and good practices

  • Cost: each reminder costs well under a tenth of a cent on the AI API. Set a monthly spend limit in the provider's dashboard.
  • Privacy: never put the full PAN or CVV in Sheets; month/year and last 4 are enough.
  • Don't nag: at most ~2 reminders per expiry cycle; if they do not update, log it and be ready for the failed charge flow.
  • Don't invent links: if portal_link is empty, the prompt asks them to reply to the email.
  • Catch failures: add an error workflow so you know if Gmail or Sheets fails, as in n8n error handling.
  • Combine it with subscription renewal reminders when the plan is also about to end, and with failed recurring card reminders if, despite the warning, the charge is still declined.

Start with ready-made templates

If you'd rather skip the basic n8n setup, download the WhatsApp agent for free or check out the AgentKiosk pack: 8 ready-to-import agents in English and Spanish, each with workflow, prompt and guide, ready to combine with these card-expiry reminders.