How to Schedule WhatsApp Messages
You want to send a campaign first thing in the morning, an appointment reminder a day ahead, or a payment notice at the start of the month — but you won't be at the keyboard when it should go out. That is exactly what scheduled (future-dated) WhatsApp messages are for: prepare the send now, pick a future date and time, and let the system handle the rest. This article covers what scheduling is good for, how it works under the hood (BullMQ delay), why "scheduled for 9 PM" does not mean "sent at exactly 9 PM", the time-zone trap, and how cancelling works.
1. What scheduling actually solves
Scheduled sending turns "write and send" into "write and tell it when to send". In practice it delivers three big wins:
- Reaching people at the right moment: A message delivered when the recipient is most likely to read it (say 8 PM) beats one you typed at midnight and that gets buried by morning.
- Hands-off operations: A reminder 24 hours before an appointment, a notice when a parcel ships, a fee announcement on the first of the month — all planned in advance and sent even when you are away.
- Spreading a long list over time: The cleanest way to send a large list is not all at once, but paced across days — and scheduling is how you do it.
Scheduling is one of the most-used triggers behind automated WhatsApp messages: instead of one click in the panel, sends fire on a calendar.
2. How it works under the hood (BullMQ delay)
The system does not hand your message to WhatsApp immediately. Instead, every send becomes a job added to a BullMQ queue running on Redis. Scheduling simply means giving that job a delay: "become ready to run X milliseconds from now".
The flow looks like this:
| Step | What happens |
|---|---|
| 1. Schedule in the panel | You write the message and pick recipients plus a target date and time. |
| 2. Server computes | The gap between the target time and now (the delay) is calculated; the record is written to Supabase. |
| 3. Enqueue | The job is added to the whatsapp queue with that delay; it sits in a "delayed" state until it elapses. |
| 4. Time arrives | BullMQ promotes the job to ready; the worker picks it up. |
| 5. Worker sends | The worker (the only sender) applies the anti-ban rules and delivers the message. |
Key distinction: the server never sends. It only validates and enqueues; the sole sender is the worker. That is why your computer can be switched off — the worker on the server fires your scheduled message when the time comes.
The queue lives in Redis with AOF persistence enabled. Even if the server restarts, delayed jobs are not lost; the queue resumes where it left off.
3. "Scheduled for 9 PM" ≠ "sent at exactly 9 PM"
This is the most common misconception. The time you pick sets when the message becomes ready in the queue — not the exact second it goes out. The gap comes from the anti-ban regime that protects your number:
| Anti-ban rule | Value | Effect on timing |
|---|---|---|
| Wait between messages | 45–90 s (67–135 s for media) | Each message in a list queues after the previous one. |
| Burst break | 8–12 min every 15 messages | Long lists get breaks inserted between batches. |
| Time window | 05:00–23:00 (local) | Anything scheduled outside it waits for the window to open. |
| Recipient cooldown | 4 hours per number | A number messaged recently is deferred. |
So: a single message scheduled for 9 PM goes out within seconds. But a 500-recipient list scheduled for 9 PM does not all leave at 9 PM — it disperses at roughly 25–30 messages/hour and rolls into the next day if needed. This is by design, not a bug: sending 500 messages at once would take your number straight to a ban.
The window trap: Messages scheduled between 23:00 and 05:00 do not go out then; the system defers them to the next open window (after 05:00). Don't schedule for the middle of the night expecting instant delivery. For ban signals and safe pacing, see rules to avoid a ban.
4. Why the time zone (TZ) is critical
The sneakiest scheduling bug is the time zone. When you enter "9 PM", that time is interpreted in the server's time zone. If the server runs on UTC by default, the send you thought was 9 PM local may actually try to fire at 9 PM UTC — which for Istanbul is midnight, landing straight inside the night window and getting deferred.
The fix is clear: pin the time zone on the server (and the worker container) to your local zone:
- Set the
TZ=Europe/Istanbulenvironment variable (or your own zone). - Both the 05:00–23:00 window and the time you schedule are then evaluated against the same local clock; the drift disappears.
- If you message recipients in other countries, remember the "best time" is planned against the server's clock, not the recipient's.
Quick check: to confirm the server clock is right, schedule a single test message for a few minutes ahead. If it arrives at the expected minute, your time zone is correct.
5. Cancelling a scheduled message
After scheduling a future send you may change your mind or want to edit the content. Cancelling requires two things done together, because the record lives in two places:
- Database record (Supabase): The send is soft-deleted — marked cancelled rather than fully removed. That keeps an audit trail of what you had planned.
- Queue job (BullMQ): The pending delayed job in Redis is also removed. This step is critical: if you only flag the database and leave the queue job, the worker may still try to run it when its time comes.
When you cancel from the panel the system does both at once; you never have to touch the queue yourself. If you want to change the content, the cleanest path is to cancel the old plan and create a new send with the new time.
6. Best times to send
The real power of scheduling is placing the message when it is most likely to be read. There is no universal rule, but industry experience points to these tendencies:
| Scenario | Suggested timing |
|---|---|
| Appointment reminder | 24 hours and/or 2 hours before the appointment |
| Campaign / announcement (B2C) | Evening, 7–9 PM |
| Corporate / B2B notice | Weekdays, 10–11 AM or 2–4 PM |
| Fee / payment reminder | First business day of the month, around 10 AM |
| Event reminder | The evening before + the morning of the event |
Lunch (noon–1 PM) and very late hours usually underperform. For ready-made copy matched to each slot, see bulk message templates; for the appointment scenario in depth, see appointment reminders.
Ethics and law: Scheduling makes it easy to hit the right hour, but it does not remove the consent requirement. Only schedule for people who have consented to hear from you, and include an opt-out line in every marketing message. Unsolicited bulk messaging violates WhatsApp's terms and data-protection law (GDPR, and KVKK/electronic-communication rules in Turkey). See consent-compliant messaging.
Frequently asked questions
Can I schedule a WhatsApp message for a future date?
Yes. You give each send a date and time; the job waits in the queue until then (BullMQ delay) and the worker sends it when the moment arrives. Minutes, hours, days or weeks ahead all work.
Will a message scheduled for 9 PM go out at exactly 9 PM?
Roughly. Scheduling sets when the job becomes ready; the exact send is governed by the anti-ban timing. For one message the gap is seconds; for a long list it can be minutes or hours.
How do I cancel a scheduled message?
When you cancel from the panel, the record is soft-deleted and the pending BullMQ job is removed. So even when its time comes, it will not be sent.
Why does the time zone matter?
The time you pick is interpreted in the server's time zone. Set TZ=Europe/Istanbul (or your zone) on the server; otherwise UTC shifts your send by hours.
Schedule your messages for the right moment
Prepare the future-dated send, set the time zone correctly, and let the system handle the rest. Free, open source, on your own server.
Open the panel →