Remindlo: SMS and email reminders that send themselves

A product I build and operate myself. Appointments arrive from the business's calendar, reminders go out by SMS and email on schedule, and every message is accounted for, including the ones the network failed to deliver.

Problem
Service businesses lose customers between visits: the MOT, the dental check-up or the vaccination falls due and nobody reminds them. Doing it by hand means a spreadsheet and an afternoon of texts.
What runs
Calendar pollers every 5 to 10 minutes, a campaign engine that decides who is due, SMS through two providers, a daily job that rolls recurring services forward, a monthly recap by email.
How it is watched
A delivery receipt on every SMS, browser errors in Sentry, a nightly backup that is restored and compared before it is kept, and a known-issues list for what the system still gets wrong.
Stack
React, Supabase (Postgres with row-level security, Deno Edge Functions, pg_cron), Twilio and Telnyx, Resend, Stripe, Cloudflare Workers.
Code
Private repository. A walkthrough is available on request.
Remindlo campaigns page with three SMS campaigns: pre-visit reminders 14, 7 and 3 days before, a thank-you one day after the visit, and reactivation 330 days after
The three campaigns a workspace starts with, here for a tyre service: reminders before the visit is due, a thank-you the day after, and a reactivation message 330 days later. Each is a list of day offsets and a template.

The problem

A garage, a clinic or a salon earns most of its money from people who have been before. The next visit is predictable: twelve months after the MOT, six months after the check-up. The reminder that brings them back is the part that gets skipped, because it depends on someone remembering to send it.

Remindlo sends automatic SMS and email reminders for service businesses. I built it and I run it, so this page is the same kind of automation I build for clients, seen from the operator's side.

What it does

  • Reads appointments from where the business already keeps them: Google Calendar, Outlook and Microsoft 365, iCloud or any ICS feed. A Google Calendar add-on works from inside the calendar.
  • Runs campaigns defined as offsets: N days before the next visit is due, or N days after the last one. The scheduling lives in Postgres triggers, so a change to a visit recalculates the next send in the same transaction.
  • Rolls recurring services forward every night: when an annual service date passes, the next one is set a year later and the reminders follow.
  • Sends SMS through Twilio, or through Telnyx for North American numbers, each provider the other's fallback. Replies and STOP messages come back through signed webhooks.
  • Has a public REST API, webhooks and an MCP server, so other systems and AI assistants can add contacts and send messages.

How it stays under control

  • Every SMS gets a delivery receipt. The status is the network's answer, and a reminder sent to a landline shows up as failed instead of as done.
  • A message is sent once, even when it is requested twice. Four parts of the system can ask for the same send. The sender claims the message with one conditional database update before calling the provider, and a caller that loses the claim treats it as "not mine" rather than as an error.
  • Anything that must happen once per signup happens in the database, not in the browser, so a closed tab cannot leave a half-created account.
  • The backup is tested every night. The database is dumped, checked for truncation, loaded into an empty database and compared table by table, then encrypted and stored separately.
  • Public numbers have a source. The figure on the site is 89.0% of reminders confirmed delivered by the network: 719 messages that had a receipt, out of 753 sent by 93 businesses, measured on 3 October 2026. The method and the failures behind it are published on Remindlo's evidence page.

Sending exactly once

UPDATE messages
   SET claimed_at = now()
 WHERE id = $1
   AND status = 'queued'
   AND (claimed_at IS NULL
        OR claimed_at < now() - interval '5 minutes')
RETURNING id;

-- no row back: another caller owns this send
-- answer 200 {"skipped": true}, not an error
The claim the SMS sender takes before it calls a provider. Two requests for the same message race on one row, and only one gets it back. A claim older than five minutes can be taken again, so a crashed send is retried rather than lost.

Screenshots

Remindlo dashboard with a needs-attention list: calendar syncing from Outlook, two campaigns running, ten contacts in no campaign and fourteen with no upcoming visit
The dashboard of a demo workspace. The first block lists what will stop reminders going out, such as contacts in no running campaign or with no visit date, each with a link to fix it.

What a business could reuse from this

Any business with recurring visits, renewals or due dates: services, inspections, memberships, contracts. The same parts apply to client work: read the calendar or system the business already uses, keep the schedule in the database, and record what actually happened to every message.

Want something like this, built around your process?

Tell me what happens today and how often. A few sentences are enough.

Describe a process