Scheduled reminder in the recipient's timezone

You know the recipient's IANA timezone (e.g. America/New_York) and you want "tomorrow at 9 AM their time".

Client-side — compute UTC

import { Temporal } from "@js-temporal/polyfill";

function nextNineAmInTz(tz: string): string {
  const now = Temporal.Now.zonedDateTimeISO(tz);
  const target = now
    .with({ hour: 9, minute: 0, second: 0, millisecond: 0 })
    .add({ days: now.hour < 9 ? 0 : 1 });
  return target.toInstant().toString(); // ISO 8601 UTC
}

const scheduled_at = nextNineAmInTz("America/New_York");

Send

await mr.emails.send({
  from: "reminders@yourdomain.com",
  to: ["recipient@example.com"],
  subject: "Your reminder",
  html: "<p>Here it is.</p>",
  scheduled_at, // "2026-04-23T13:00:00.000Z" (9 AM ET in April)
});

Response:

{
  "id": "msg_01HN...",
  "status": "scheduled",
  "scheduled_at": "2026-04-23T13:00:00.000Z"
}

Cancel if plans change

curl -X DELETE https://api.sendoka.com/api/v1/emails/msg_01HN... \
  -H "Authorization: Bearer $SENDOKA_API_KEY"

Returns 409 NOT_SCHEDULED if the cron already picked it up.

Use native recipient-timezone scheduling

If you don't want to compute UTC yourself, use the send_at_local form:

{
  "from": "reminders@yourdomain.com",
  "to": ["recipient@example.com"],
  "subject": "Your reminder",
  "html": "<p>Here it is.</p>",
  "send_at_local": "09:00",
  "recipient_timezone": "America/New_York"
}

Sendoka computes the next occurrence of 09:00 in the given tz and stores scheduled_at accordingly. See api/timezone-schedules.md.

Gotchas

  • DST transitionsTemporal handles these; naive Date math doesn't. Use it.
  • Minute granularity — the cron runs once per minute. A message scheduled for 09:00:45 fires at the 09:01 tick.
  • Past times — Sendoka treats scheduled_at within the next 1 second as immediate (not scheduled). Far past is rejected with VALIDATION_ERROR.