Birthday & Anniversary Automations in AcelleMail — Date-Triggered Sends

Send a happy-birthday discount on the day, a year-since-signup gift on the anniversary, a renewal reminder 30 days before expiry. AcelleMail's date-relative automation trigger handles all of these — this guide walks the setup with screenshots.

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 AutomationAutomations. The index lists every automation in this account with its current state:

Automations index

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

Trigger picker — pick what starts the automation

For birthday emails:

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

The trigger picker shows the date-relative option:

Trigger picker — pick "Date relative"

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:

Configured flow — date trigger + email step

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:

  1. Trigger: Date relative
  2. Date field: created_at (the built-in signup-date)
  3. Schedule: On the date + Yearly
  4. 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:

  1. Trigger: Date relative
  2. Date field: subscription_expires_at
  3. Schedule: Before the date, offset = 30 days
  4. Repeat: One-time per subscriber per renewal cycle
  5. Email step: "Your subscription renews in 30 days — confirm payment method"

Then a 7-day reminder:

  1. Trigger: Date relative
  2. Field: subscription_expires_at
  3. Schedule: Before, offset 7 days
  4. Email: "Your subscription renews next week"

And a 1-day final notice:

  1. Trigger: Date relative
  2. Field: subscription_expires_at
  3. Schedule: Before, offset 1 day
  4. 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

17 条评论

7 条评论

  1. y.yamamoto
    How do you handle subscribers who join mid-sequence (e.g. via API)? Do they start at step 1 or pick up at a current point?
    1. admin
      Good question — and one that comes up often enough we should add an FAQ section. Short answer: yes for the common case; the exception is when you're running custom plugins that override the default behavior.
  2. linhvu.dev
    The visual flow diagram is exactly what I needed. Our welcome series has been a mess of forgotten branches — going to redo it tonight using this as the template.
  3. sobrien.kw
    always test the end of the sequence first, not the start. most testing focuses on email 1 but the longest-tenure subscribers are at the end and that's where bugs surface.
  4. tnovak.cz
    We do almost exactly this but with one tweak — we use the 'goal' node to exit subscribers from the sequence early when they complete a target action. Saves us sending to people who already did the thing
    1. admin
      Thanks for the numbers. Worth pulling into a follow-up post on volume-tier sizing.
  5. ravi.kumar.del…
    Built a 9-email welcome series last quarter using this pattern. Took 4 days end-to-end. Open rate on email 1 is 62%, drops to 28% by email 9 — which is actually higher engagement than our broadcast list. Highly recommend the format.
    1. admin (已编辑)
      Solid case study material here. If you're open to it, we'd love to write this up as a blog post — happy to credit you anonymously or otherwise.
    2. admin (已编辑)
      thanks for sharing. the pattern you describe is exactly the use case we built that feature for — glad it landed for you.
  6. lequan.saigon
    solid walkthrough. the conditional-branching example especially — most automation guides kip that and you end up rebuilding from scratch.
    1. admin
      Appreciate it. If anything in this needs updating, ping us — we revisit articles every few months.
  7. lucas.bernard.…
    What's the max number of steps in a sequence before performance becomes a concern? Asking because we have a 14-step nurture and I'm wondering if it's overkill.
    1. admin
      Yes, that pattern is supported. The undocumented bit is the order — config:cache MUST come after the migration, not before. Updating the docs to make that explicit.
    2. admin (已编辑)
      Right — for RDS specifically, you can change wait_timeout via the parameter group without a reboot if its set as 'dynamic'. Most defaults are.
    3. admin (已编辑)
      There's no built-in way today. Two workarounds: (1) cron + custom script polling the API every N minutes, (2) webhook-driven if your event source supports it. Most operators go with #2.
    4. admin (已编辑)
      we tested this with up to 1m subscribers on a $40/mo vps. past that you start needing query optimization. below that, the defaults are fine
    5. admin (已编辑)
      yes — strict alignment requires the From: domain to match exactly. Subdomain-level (`bounce.example.com` vs `example.com`) passes relaxed but fails strict. Mst operators run relaxed; the rare strict-DMARC setups need explicit subdomain DKIM configuration

More in Automation