Why date-based automations earn their spot#
Birthday and anniversary emails are reliably the highest-converting transactional sends most senders run — 5-10× the click-through rate of standard newsletters. They're personal, they're expected, they coincide with a high-affinity moment.
The setup is a one-time configuration; once it's running, every subscriber whose date matches fires the email automatically. No manual scheduling.
What you need on the list#
Before building the automation, the list must have the date field you want to trigger on. Three common patterns:
| Use case |
Field name |
When you collect it |
| Birthday |
birthday (Date type) |
Signup form, optional field |
| Year-since-signup anniversary |
(uses built-in created_at) |
Automatic |
| Renewal reminder |
subscription_expires_at |
API push from your billing system |
| Loyalty milestone |
first_purchase_at |
API push from ecommerce |
For birthday, add a Date field via Audience → [list] → Fields → New field → name birthday, type Date, optional.
To collect the value on signup, edit the signup form (Audience → [list] → Signup forms → [form] → Edit) and add the birthday field with the date picker. Keep it optional — required birthday on signup tanks conversion.
For existing subscribers with no birthday, fill via import or via your account UI — you'll only fire emails for subscribers where the field is non-empty.
Build the birthday automation#
Open the visual automation builder#
In AcelleMail's left sidebar, click Automation → Automations. The index lists every automation in this account with its current state:

Click New automation in the top-right toolbar. The trigger picker opens:

For birthday emails:
- Trigger: Date relative in the trigger picker
- Date field:
birthday
- Schedule: On the date (offset = 0 days) — fires at midnight in subscriber's timezone if known, or in the campaign's default timezone
- Repeat: Yearly — re-fires every year on the same month/day
The trigger picker shows the date-relative option:

After the trigger, drop in the email step:
- Subject:
Happy birthday, {{ subscriber.first_name | default('friend') }}!
- Body: a short personal note + a discount code or gift
The configured flow:

Save. Going forward, every day at midnight AcelleMail evaluates every subscriber's birthday field; matches fire the email.
Build the anniversary automation#
Same pattern, different field:
- Trigger: Date relative
- Date field:
created_at (the built-in signup-date)
- Schedule: On the date + Yearly
- Email step: thank-you for being-a-subscriber-N-years note
Subject: It's been a year, {{ subscriber.first_name | default('friend') }} — thank you 🎉
Body: One year ago today you joined us. As a thank-you, here's a code for 20% off your next order: ANNIV20.
For "two years since signup", run a second automation with offset = -730 days from created_at + 1095 = checks "exactly 3 years ago". (Or use the created_at Date-relative trigger and a condition WHERE year_diff(created_at, now()) == 2.)
Build the renewal-reminder automation#
For subscription / contract renewals, fire 30 days before expiry:
- Trigger: Date relative
- Date field:
subscription_expires_at
- Schedule: Before the date, offset = 30 days
- Repeat: One-time per subscriber per renewal cycle
- Email step: "Your subscription renews in 30 days — confirm payment method"
Then a 7-day reminder:
- Trigger: Date relative
- Field:
subscription_expires_at
- Schedule: Before, offset 7 days
- Email: "Your subscription renews next week"
And a 1-day final notice:
- Trigger: Date relative
- Field:
subscription_expires_at
- Schedule: Before, offset 1 day
- Email: "Tomorrow: your subscription renews automatically"
Common UI signals + fixes#
| Symptom |
Likely cause |
UI fix |
| Birthday emails never fire |
birthday field empty for most subscribers |
Confirm field is collected on signup; back-fill via import for existing subscribers |
| Birthday email fires on the wrong day |
Timezone mismatch — birthday stored without TZ, sent in app timezone |
See Advanced: timezone handling |
| Subscriber gets birthday email twice in the same year |
Duplicate automation, both with same trigger |
Open automations index — confirm only one is enabled |
| Renewal reminder fires AFTER expiry |
subscription_expires_at was updated late by billing system |
Move trigger to billing-system-side webhook instead of pulling from AcelleMail's field |
| Some subscribers get the email at 3am their time |
Default timezone is server TZ, not subscriber TZ |
Add per-subscriber timezone field; see Advanced |
When to skip the birthday automation#
If your industry doesn't fit (B2B SaaS where birthday feels invasive, regulated industries where personalization triggers compliance review), skip it. The "year-since-signup" anniversary is a safer alternative that earns most of the engagement without the personal-data ick factor.
Advanced: timezone-aware date triggers, lookahead jobs, and lead-time logic
The date-relative trigger evaluates dates in a single timezone — by default the AcelleMail server's timezone (typically UTC). For senders with a global audience, you want each subscriber to receive their birthday email at a reasonable local hour.
Per-subscriber timezone field:
Add a timezone field (Type = Text) to the list. Populate it via signup form (geolocate via IP) or via API push from your app's user model.
Automation logic with timezone:
The AcelleMail date-relative trigger handles the matching itself; for per-subscriber timezone offsetting, the cleaner path is to schedule the trigger to evaluate at 09:00 in each subscriber's local time using AcelleMail's per-recipient send-time-optimization feature (if your install supports it).
If not, the workaround: run 24 automations, one per hour, each scoped to subscribers in that hour's timezone:
Automation 1: trigger 09:00 server time; condition timezone = 'America/Los_Angeles'
Automation 2: trigger 10:00 server time; condition timezone = 'America/Denver'
Automation 3: trigger 11:00 server time; condition timezone = 'America/Chicago'
Automation 4: trigger 12:00 server time; condition timezone = 'America/New_York'
Automation 5: trigger 13:00 server time; condition timezone = 'America/Halifax'
...
Tedious to set up; runs forever. Most senders simplify to 4 buckets (Americas / EMEA / APAC / IST) and accept the imprecision.
Lookahead via API for high-precision renewals:
For subscription renewals where the field source is your billing system (Stripe, Chargebee), the cleanest pattern is to fire the email from the billing system's webhook rather than from AcelleMail's date trigger:
Stripe webhook 'invoice.upcoming' fires 30 days before renewal
→ your backend receives the webhook
→ calls AcelleMail API: trigger a "renewal_reminder_30d" custom event
→ AcelleMail automation triggered by event = renewal_reminder_30d sends the email
Advantage: Stripe's invoice.upcoming is authoritative; AcelleMail's stale subscription_expires_at might not have been updated yet if your billing-to-AcelleMail sync runs nightly.
Bulk back-fill birthdays from an existing system:
If you're migrating from a system that already has birthdays stored, push them in via the API:
# CSV file: email,birthday (YYYY-MM-DD)
while IFS=, read email bday; do
curl -X PATCH "https://acellemail.com/api/v1/subscribers" \
-H "Authorization: Bearer $ACELLE_TOKEN" \
-d "{
\"email\": \"$email\",
\"list_uid\": \"$ACELLE_LIST_UID\",
\"fields\": {\"birthday\": \"$bday\"}
}"
done < birthdays.csv
Lead-time math for renewal cycles:
If subscription_expires_at = 2026-12-31 and your trigger says "30 days before", that fires on 2026-12-01 at the timezone the trigger runs in. For annual renewals, set the automation to One-time per subscriber per year (or whatever your cycle is) — AcelleMail tracks the per-subscriber last-run timestamp so it won't double-fire within the cycle window.
If you change the subscription_expires_at mid-cycle (e.g. customer upgrades and the date resets), the automation re-evaluates and may fire the reminder again from the new date. Test this carefully if your billing flow updates the field frequently.
Multi-step birthday flow (day-of + 3-day follow-up if no purchase):
Date relative on birthday → email "Happy birthday, here's 20% off"
Wait 3 days
Condition: did NOT use discount code
→ email "Your birthday discount expires in 24h"
The Wait step + condition step are standard automation primitives. Many senders see the highest birthday ROI on the expiry reminder, not the day-of email.
Related articles#