Sending rules
Sending rules · WhatsApp 24 hour window API · WhatsApp template message API · iMessage rate limits · WhatsApp messaging limits · avoid WhatsApp number ban · iMessage spam limitsUnderstand reply windows, new-contact budgets, pacing and send refusals.
Every send, whether over HTTP, the live stream or a webhook reply, passes one send gate that applies the channel’s rules before anything leaves Flow; this page lists those rules. The gate also refuses what the channel does not take: a kind, field or value it cannot show answers 422 unsupported_content, and nothing is sent (Message kinds).
import { OutsideWindowError, template } from "@flow-engineer/messaging";
// WhatsApp:try { await conversation.send("Your table is ready.");} catch (err) { if (err instanceof OutsideWindowError) { // More than 24 hours since they last wrote. Only a template may go. await conversation.send(template("tpl_01JB8ZC3K5M7P9R1T3V5X7Z9B1", "en", { body: ["Table 12"] })); } else throw err;}Rule 1: is the conversation open?
Section titled “Rule 1: is the conversation open?”| Channel | You may send free-form content when… | Otherwise |
|---|---|---|
| the contact’s last message is less than 24 hours old (the window) | only an approved template; anything else gets 409 outside_window | |
| iMessage | the contact has written to this line before | a new contact is refused with 403 permission, unless the line may write first (imessage.can_initiate) |
| Telegram | the contact has started your bot | Telegram refuses the message itself |
| Sandbox senders | the contact joined through your app with its join code | refused |
On WhatsApp, conversation.window_open_until (on the conversation and on every event) says when the window closes. Subscribe to conversation.window_closing to hear about it 1 hour before.
Rule 2: starting a conversation spends budget
Section titled “Rule 2: starting a conversation spends budget”Writing to someone first (POST /v0/messages) spends the sender’s new-contact budget:
- Per sender: a number of new contacts per day and per hour, in
sender.limits. Defaults: WhatsApp 50 a day and 10 an hour, SMS 1,000 and 200, iMessage 4 an hour. - Warm-up: a new sender that writes first starts with a small daily budget that grows day by day while it is
warming_up. - WhatsApp: also the number’s messaging tier (
sender.limits.whatsapp_tier). - Telegram: a bot can write only to people who started it, so in practice its budget is not the limit.
- iMessage: lines reply only, so writing first gets
403permission, unless writing first is switched on for the line (imessage.can_initiate). It is available on request for dedicated lines: the Flow team switches it on with a warm-up. If the channel still refuses a start, the send ends inmessage.failedwithoutside_window.
When the budget is used up the send fails with 429 new_contact_limit and retry_after in seconds. A message to someone who already has an open conversation with that sender goes into it and spends nothing.
Rule 3: abuse protection
Section titled “Rule 3: abuse protection”The gate watches for signs that a sender is being used for unwanted messages:
- the same text going to many new contacts;
- a high share of new conversations that get no reply;
- people blocking the sender;
- a drop in WhatsApp’s quality rating.
The defaults: the same first message (text or caption, ignoring case and spacing) to 20 new contacts within an hour; of at least 20 starts from the last 72 hours that are more than a day old, 80% or more never answered; 10 contacts blocking the sender within 24 hours. Templates and media without a caption do not count as the same text. iMessage lines have stricter limits of their own.
When one trips, the sender is throttled for 24 hours (the start that tripped it still goes): until throttled_until, starting conversations fails with 429 sender_throttled, while replies into existing conversations still go. You receive sender.status_changed with a one-sentence reason, and another when the sender recovers by itself.
Rule 4: pacing and order
Section titled “Rule 4: pacing and order”- Each sender has a sending rate (pacing): only so many messages a second, from all your conversations together (a Telegram bot: 20 a second, bursts of 40). A send over it is refused with
429rate_limitedandretry_after; wait that long and retry with the same idempotency key. - At most one message per conversation is in flight at a time, and messages go out in the order you sent them.
Write agents that respect the gate
Section titled “Write agents that respect the gate”- Reply, don’t broadcast. Agents that answer people who wrote first never meet rules 2 and 3.
- Know the channel before you send. Every event carries
conversation.channel, each endpoint’s page lists the channels it works on, and on WhatsAppconversation.window_open_untilsays whether the window is open. Send what the channel takes; a422names what it refused (param) and what to send instead (hint). - Retry only what clears by itself.
new_contact_limit,sender_throttledandrate_limitedcarryretry_after;outside_windowwill not clear until the person writes again, so wait for them to message the line (iMessage), or send a template instead (WhatsApp). - Watch
sender.status_changed. It is the early warning before a channel acts against a number.
Related
Section titled “Related”- Senders: statuses and limits.
- Going live: templates and dedicated numbers.
- Rate limits: request limits per API key, which are separate from the gate.
Thanks, we read every comment.