Send now, deliver later.
Set an exact time or a relative delay on any message, up to 30 days out, and cancel it any time before it fires. No cron job, no worker process.
Two ways to say when
Every reminder you have ever wanted from a script needed a process that stayed alive until the moment arrived. Not any more.
Absolute times
Give an exact timestamp in RFC3339, Unix time, or a plain date and time — whichever your language makes easy.
Relative delays
Or give a relative delay: 30m, 2h, or bare seconds. Handy when the trigger is "two hours after this script started".
Your timezone, not UTC
Times without an offset are read in your account timezone, so 09:00 means nine where you are.
List and cancel
List everything pending and cancel any of it before delivery. A race with the dispatcher resolves cleanly, never halfway.
Encrypted until delivery
Content sits encrypted at rest until it is delivered, attachments included. A cancelled message takes its file with it.
A schedule is a promise
Priority and plan resolve at scheduling time. A message promised as critical arrives as critical, whatever changed since.
Three steps, one extra field
Add one field
Take a message you already send and add send_at with a time, or delay with an offset.
Keep the tid
You get back a tid and the resolved delivery time in your timezone and in UTC.
Cancel if needed
List pending messages any time, and cancel with a DELETE if plans change.
How scheduled delivery works
Add send_at or delay to a normal create-message request. Instead of sending immediately, PocketAlert stores an encrypted snapshot of the message and hands it to an internal dispatcher, which delivers it at the right moment as an ordinary push. The delivered message then appears in your history like any other.
You get back a 201 with "scheduled": true, the resolved delivery time in both your timezone and UTC, and a tid — keep that one, it is what you cancel with.
What it replaces
Certificate expiry reminders, maintenance windows, a nudge two hours after a migration starts to go and check the row counts, a follow-up the morning after a deploy. All of that used to need a cron entry or a worker process that had to survive until the moment arrived. Now it is one extra field on a request you were already making.
Edge cases, handled
A past send_at returns 400, with sixty seconds of tolerance for clock skew — inside that window the message is simply sent now. Overdue messages after a restart are delivered late rather than dropped. Attachments upload when you schedule and are linked on delivery.
Questions, answered
Add send_at with an exact time or delay with a relative offset to any message. send_at accepts RFC3339, a Unix timestamp, or a plain YYYY-MM-DD HH:MM; delay accepts durations like 30m and 2h. Send one or the other, never both.
Thirty days. Anything further out is rejected rather than silently clamped.
Your account timezone. A send_at of 2026-08-15 09:00 means nine in the morning where you are, not in UTC. Add an offset explicitly if you want to be unambiguous.
List pending messages with GET /v1/messages/scheduled and cancel one with DELETE /v1/messages/scheduled/{tid}. If delivery has already started you get a 409 instead of a half-cancelled message. Cancelling also deletes any attachment.
It is delivered late, not dropped. If the service restarts past a delivery time, the message goes out on the next tick. The content sits encrypted at rest with your key from the moment you schedule it.
Scheduled delivery is a paid feature, and a free-plan request using send_at or delay returns 403 rather than being silently sent immediately. Priority and plan are resolved when you schedule, so a message scheduled as critical still arrives as critical.
Delete the cron job. Keep the reminder.
One extra field on a message you were already sending, and the reminder shows up when it matters instead of now.