Which subscriptions is this for? This recipe is for subscriptions kept in your own site’s database, for example a membership app you built that charges through your own Stripe account. If you bill clients with Hiveku subscriptions, you don’t need it: Hiveku already emails an upcoming charge notice three days before each card renewal and a receipt after each charge, and retries declined cards on its own. To add your own steps to those, see Hiveku subscriptions below.
subscriptions (or customers) table in your site’s database with a renewal_date column, and email sending set up. See Send Emails.
The Flow at a Glance
7 days out
Gentle heads-up
3 days out
Upgrade/downgrade options
1 day out
Last chance to change
Day of
Receipt or payment retry
Step 1: Create the Workflow
1
Open Workflows
Go to Workflows and click New Workflow. Name it
Subscription Renewal Reminders.2
Add a Scheduled Trigger
Add a Scheduled Trigger. Set Frequency to Daily, set the Hour and Minute (for example 8:00), and pick your Timezone. The schedule runs in UTC unless you change it.
Step 2: Query Upcoming Renewals
Add a Run SQL Query step pointed at your site’s database. Each tier uses its own query so you can send different messages. Here’s the 7-day query as a template:Step 3: Send Tiered Reminder Emails
Different days, different tone:- 7 days out: “Your plan renews next week”. Casual and reassuring. Include the plan name and renewal amount, with one soft call to action: “Manage your subscription.”
- 3 days out: “Renewing in 3 days”. Introduce upgrade and downgrade options, and highlight what a higher tier adds.
- 1 day out: “Your subscription renews tomorrow”. The last chance to change anything. Put the link to update payment details up front.
- Day of: either a “Thanks for renewing” receipt, or a “Renewal failed: update your payment details” notice, depending on the charge result.
1
Loop over the rows
After each query, add a For Each (Loop) step. In List to loop over, reference the query’s rows (typing
{{ lists what earlier steps produced). The steps on its Each item exit run once per row and read the row as {{ forEach.item }}.2
Send the email
On Each item, add a Send Email step. Set To to the row’s email column, for example
{{ forEach.item.user_email }}, and write the Subject and body for that tier.3
Personalize per plan
Monthly customers get different messaging than annual ones, and high-tier customers deserve a more personal touch. Branch with a Condition (If) step, or split the tiers into separate workflows.
4
Mark the reminder as sent
After the email, still inside the loop, add a Run SQL Query step that sets the flag. Pass the row’s id in Params (JSON array) instead of typing Params (JSON array):
{{...}} into the SQL:["{{ forEach.item.id }}"]Step 4: Handle Failed Renewals
A failed payment is often an expired or replaced card rather than a decision to leave, so it’s worth a follow-up. If your site charges through your own Stripe account, build a second workflow for this:1
Create a new workflow
Name it
Renewal Recovery.2
Add an Inbound Webhook trigger
Add an Inbound Webhook trigger and copy its URL. Point a Stripe webhook for the
invoice.payment_failed event at that URL. See Webhook Patterns for protecting it. The event’s data arrives under trigger.output.payload.3
Send a recovery email
Add a Send Email step. Subject: “Your payment didn’t go through.” Body: plain and calm. Explain what happened, link to where they update their payment details, and say what happens if they don’t.
4
Follow up
Add a Wait Until step (for example 2 days), then another Send Email with a little more urgency, and repeat once or twice. Use Wait Until rather than Delay, which is for waits under 15 minutes. Before each follow-up, check your own database with a Run SQL Query and a Condition (If) step, so a customer who has already paid isn’t chased. What happens to an account that never pays is up to your app.
Loyalty Incentives
Tenure is a retention lever. Use it in your renewal emails:- “You’ve been with us 1 year: here’s 10% off next month”
- “Celebrating 6 months: unlocked priority support”
- “Year 2 with Acme: we upgraded you to the new Pro features at no extra cost”
created_at in your subscription record and branch the email on thresholds.
Personalize by Plan
One-size-fits-all reminders get unsubscribes. Branch your messaging:- Monthly vs annual: annual customers need less hand-holding but bigger-deal framing (“your annual plan renews in 30 days”)
- Free trial converting to paid: emphasize what they’ll lose if they cancel vs what they’ve already set up
- High-tier: consider a personal email from a human, not a templated one
- At-risk (low usage): combine the renewal reminder with a “need help getting started?” offer
Hiveku subscriptions
If you bill clients with Hiveku subscriptions, Hiveku sends these on its own:- An Upcoming charge email three days before each card renewal.
- A receipt after each charge, or the invoice by email when there’s no card.
- Automatic retries of a declined card: 3 days later, then 7 days after that.
subscription.created: a client was subscribed.subscription.renewed: a new period was billed, either charged to the card or emailed as an invoice.subscription.past_due: a renewal charge was declined.subscription.cancelled: the subscription ended.
The Run SQL Query steps in this recipe read your site’s own database, not Hiveku subscriptions, and there’s no trigger for “renews in N days”. No workflow step pauses or cancels a Hiveku subscription: do that from the subscription’s page.
Verify It Worked
1
Create a test subscription
Insert a test row with
renewal_date = now() + interval '7 days'.2
Trigger the workflow
Wait for the next scheduled run, or ask an AI agent connected to your account to run the workflow once for real. The editor’s Test Run is a dry run: sending and saving steps are simulated, and read steps use your live data.
3
Confirm the email arrives
Check the test inbox. Verify the subject, tone and links render correctly, and confirm the
renewal_reminder_7d_sent flag was set.4
Reset and test other tiers
Update the test row to
now() + interval '3 days', clear the flag, and run again.Troubleshooting
No reminder went out
No reminder went out
Check that the workflow shows Enabled in the editor. Then check the
renewal_reminder_*_sent flag on the target row: if it’s already set, the query filters the row out. To re-test, clear the flag and run again.Wrong tier fired (7-day instead of 3-day)
Wrong tier fired (7-day instead of 3-day)
Date math is tricky. Make sure your
BETWEEN ranges don’t overlap: the 7-day window should end exactly where the 3-day window begins. Also check that your database’s timezone matches the Timezone on the Scheduled Trigger.Customer says 'my card was charged without warning'
Customer says 'my card was charged without warning'
The 7-day email is the one most often missed: spam folder, busy week, address change. Send a day-of confirmation too, even for successful renewals, so they have at least one message they can search their inbox for.
Renewals failing after email sends fine
Renewals failing after email sends fine
If the email workflow runs but Stripe payments aren’t going through, the issue is at the payment layer, not the email layer. Keep email sending and payment retries independent, so each succeeds or fails on its own. Check your Stripe API logs and webhook configuration.
Duplicate emails on the same day
Duplicate emails on the same day
Usually a sent-flag race. Make sure the flag update happens before the next workflow run, or use a uniqueness constraint like
UNIQUE(subscription_id, reminder_tier, date) on a reminders log table.What’s Next?
Add Stripe
Wire up subscriptions and payment processing on your site
Webhook Patterns
Handle Stripe and other third-party webhooks reliably
Hiveku Subscriptions
Plans, renewals and the emails Hiveku sends for you