Event reminders β
Calendar events stay useful only when people show up to them. Reminders turn the calendar into a push β a WhatsApp message a day before the youth retreat, an email the morning of the leaders' meeting, an SMS one hour before the prayer call starts.
Reminders aren't configured per event. They're configured once per organization, and every event with a future date and a matching channel goes through the same pipeline.
How the config works β
There's exactly one event_reminder_config row per org. Open Settings β Reminders to edit it.
event_reminder_config(
id, organization_id, is_enabled,
reminder_offsets_hours integer[],
channel_slugs text[],
title_template text,
body_template text,
created_at, updated_at
)Two arrays do the work:
reminder_offsets_hoursβ when to send.{24}is a single reminder 24 hours before the event.{24, 1}is two reminders: a day before and an hour before.{168, 24, 1}is three reminders: a week before, a day before, and an hour before.channel_slugsβ how to send. Pick fromemail,sms,whatsapp,push,in_app. The default is{push, in_app}which never costs you message credits.
The two arrays multiply. If reminder_offsets_hours = {24, 1} and channel_slugs = {email, whatsapp}, every event triggers four notifications: an email and a WhatsApp message 24 hours before, then again one hour before.
Send-X-hours-before semantics β
Offsets are absolute hours, not "the morning of" or "the day before." A 24-hour offset for an event at 7 PM Sunday sends at 7 PM Saturday β not 9 AM Sunday morning. If you want morning-of reminders, set the offset to roughly the gap between morning and the event time (10β12 hours for a Sunday evening event).
A few common patterns:
| Use case | Offsets |
|---|---|
| Day-before nudge only | {24} |
| Day-before + hour-before | {24, 1} |
| Week-before + day-before for big events | {168, 24} |
| Just-in-time prayer call | {0.25} (15 min β but integer-only, so use 1) |
WARNING
The column is integer[]. Sub-hour offsets aren't supported. Round up β a 0-hour offset would compete with the event itself.
Channels β
Each channel slug maps to the configured channel in your Messaging settings. To send WhatsApp reminders, you need an active WhatsApp channel set up under Messaging β Channels; to send SMS, an SMS channel; to send email, an email channel.
If a channel slug is in the config but no working channel exists for it, the reminder for that slug silently fails. The other channels still go out. Watch the Notifications β Logs screen if reminders go quiet β failed channels show up there with the failure reason.
The two free-of-cost channels are worth highlighting:
pushβ browser and PWA push notifications. Members who installed the app or subscribed in their browser get these without spending message credits.in_appβ bell-icon notifications. Visible to anyone who logs in.
Most orgs run {push, in_app, whatsapp} β push for cheap reach, WhatsApp for the people who didn't install the app.
Title and body templates β
Two text fields carry the reminder copy:
title_templateβ defaults toUpcoming:.body_templateβ defaults toon at.
Variables available in both templates:
β the event's title (or override title from an exception).β formatted in the org's timezone.β the start time, or blank if the event has none.β prefixed with " at " when present, otherwise blank.
Keep titles short β SMS and push limit visible characters. The body can be longer for email and WhatsApp.
Deduplication β
Recurring events repeat. Without deduplication you'd send the same reminder once per occurrence per offset per channel β quickly into thousands of messages. The event_reminders_sent table prevents that:
event_reminders_sent(
id, event_id, occurrence_date,
organization_id, channel_slug, offset_hours,
sent_at
)The cron that dispatches reminders checks (event_id, occurrence_date, channel_slug, offset_hours) against this table before sending. If the row exists, the reminder is skipped. If it doesn't, the reminder goes out and the row gets inserted in the same transaction.
The implication: editing a recurring event doesn't re-send already-sent reminders, and rerunning the cron after a failure won't double-send the ones it already succeeded on.
Turning reminders off β
Set is_enabled = false on the config row and the cron skips your org entirely. Useful when you're seeding a test org, migrating from another platform, or testing event creation without spamming your members.
You can also turn off reminders per-channel by removing the slug from channel_slugs. Removing whatsapp mid-week means the next reminder pass skips WhatsApp for everyone, even for events already on the schedule.
What gets reminded β
Every non-deleted event with a future occurrence inside the next scheduled offset gets a reminder. That includes:
- One-off events whose
event_dateis in the future. - Each future occurrence of a recurring event, expanded from the RRULE.
- Events that fall on a date with an override β the reminder uses the override title, time, and location.
- Events the exception hasn't cancelled. Cancelled occurrences are skipped.
Birthdays don't currently go through event_reminder_config β they have a separate workflow trigger in the messaging module.