Email API

Appointment Reminder Emails via API: 24h, 1h and Time Zone Handling

Send appointment reminder emails from your booking app: the 24-hour and 1-hour schedule, calendar links, reschedules, and getting time zones right every time.

Reminder emails cut no-shows for clinics, salons, tutors, consultants and anyone else who books time. The logic is simple until time zones and reschedules enter the picture, at which point a reminder for "3:00 PM" sent to someone who thinks the appointment is at 4:00 PM does more harm than no reminder at all. Here is a schedule and an implementation that stays correct.

Quick answer

Send a confirmation immediately on booking, a reminder 24 hours before, and a short reminder 1 hour before (email, or SMS if you have it). Store every appointment as an instant in UTC plus the IANA time zone of the location, render times in the recipient's zone with the zone name spelled out, and cancel any queued reminder when the appointment moves.

Store time correctly first

  • Store starts_at as a UTC timestamp and location_tz as an IANA zone such as Africa/Nairobi or America/Toronto. Never store "3pm" or a UTC offset; offsets change with daylight saving.
  • Store the customer's zone separately if the appointment is remote (a video call between London and Lagos has two correct local times).
  • Render with a real library: Intl.DateTimeFormat in JavaScript, zoneinfo in Python, Carbon in PHP. Do not add hours by hand.
  • Always print the zone name in the email: "Thu 21 Aug, 3:00 PM (EAT, Nairobi)". Ambiguity is the enemy.
  • Reminder scheduling is done in UTC: remind_at = starts_at minus 24 hours, regardless of zones.

The three messages

  1. Confirmation, sent on booking: service, provider, date and time with zone, location or video link, what to bring, how to reschedule or cancel, and an .ics calendar link so the appointment lands in their calendar in the right zone.
  2. 24-hour reminder: the same facts in fewer words, one line for reschedule and cancel. This is the reminder that prevents most no-shows.
  3. 1-hour reminder: two sentences. "Your appointment with Dr Okafor is at 3:00 PM (EAT) today at 12 Kimathi Street." For video calls, the join link.

The send request

Build the local time string server-side with the customer's zone, then send:

const when = new Intl.DateTimeFormat("en-GB", {
  dateStyle: "full", timeStyle: "short", timeZone: appt.customerTz
}).format(appt.startsAt);

const res = await fetch("https://api.oqumail.com/api/v1/emails", {
  method: "POST",
  headers: { "Authorization": "Bearer " + process.env.OQUMAIL_API_KEY, "Content-Type": "application/json" },
  body: JSON.stringify({
    from: "bookings@yourdomain.com",
    to: appt.customerEmail,
    subject: "Reminder: " + appt.service + " tomorrow at " + when,
    html: "<p>Your " + appt.service + " with " + appt.provider + " is on <strong>" + when + " (" + appt.customerTz + ")</strong>.</p><p>" + appt.locationLine + "</p><p><a href=\"" + appt.manageUrl + "\">Reschedule or cancel</a></p>",
    text: "Your " + appt.service + " with " + appt.provider + " is on " + when + " (" + appt.customerTz + "). Manage: " + appt.manageUrl
  })
});

Scheduling and cancellation

Do not create delayed jobs at booking time for reminders days away; a reschedule then leaves a stale job behind. Instead run a scheduler every 5 minutes that selects appointments whose starts_at falls in the window (now + 24h) to (now + 24h + 5min) and have not yet had reminder_24h_sent_at set, sends, and stamps the timestamp. Do the same for the 1-hour window. When an appointment moves, clear the sent timestamps so the new time gets fresh reminders, and send a "your appointment has been moved" email immediately. When it is cancelled, mark it and the scheduler naturally skips it.

Pitfalls that cause missed appointments

  • Rendering times in the server's zone. A Frankfurt server telling a Nairobi customer "1:00 PM" when it is 3:00 PM locally.
  • Sending the 1-hour reminder at 2 a.m. for an early appointment. Fine by email; do not do it by SMS.
  • Reminders for cancelled appointments because the job was queued at booking time.
  • Calendar links without a time zone in the .ics (use TZID or UTC with a Z suffix).
  • A noreply@ sender. "Running 10 minutes late" is a reply people send, and the front desk needs to see it.

Sending reliably from your domain

Reminders are worthless if they arrive after the appointment, so they must not be deferred by the receiver. Send from bookings@ on your own domain with SPF, DKIM and DMARC passing, and check the delivery log when a customer says they got nothing. OquMail sets up the DNS records with live verification when you add your domain, gives you the bookings@ mailbox for replies alongside the send API, and records the receiving server's SMTP response for each reminder on the free plan.

Common questions

Should the reminder ask for confirmation?

A "Confirm" button that hits a signed URL is useful for clinics with waitlists; unconfirmed slots can be offered to others. Do not make confirmation mandatory or people will be marked no-show for ignoring an email.

Email or SMS for the 1-hour reminder?

SMS is read faster and is better for the same-day nudge. If you only have email, still send it; many people have mail notifications on their phone.

How do I handle a customer with no time zone on file?

Default to the location's zone for in-person bookings and ask for the zone during booking for remote ones. Show both zones in the email if they differ.

Free business email on your own domain

OquMail gives you up to 15 mailboxes on your domain — free — with guided SPF/DKIM/DMARC, webmail, IMAP/SMTP for any mail app, and a send API. Most teams are live in under fifteen minutes. Start at oqumail.com.

Get started free

Ready for business email on your domain?

Up to 15 free mailboxes, guided DNS, webmail, and a transactional API — start in minutes.

Create your free workspace