> ## Documentation Index
> Fetch the complete documentation index at: https://flow.engineer/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Sending rules

> Understand 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`](https://flow.engineer/docs/unified-messaging-api/errors/unsupported_content), and nothing is sent ([Message kinds](https://flow.engineer/docs/unified-messaging-api/concepts/content-types.md)).

```ts TypeScript
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?

| Channel         | You may send free-form content when...                                | Otherwise                                                                                                                                                                         |
| --------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| WhatsApp        | the contact's last message is less than 24 hours old (the **window**) | only an approved `template`; anything else gets `409` [`outside_window`](https://flow.engineer/docs/unified-messaging-api/errors/outside_window)                                  |
| iMessage        | the contact has written to this line before                           | a new contact is refused with `403` [`permission`](https://flow.engineer/docs/unified-messaging-api/errors/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

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 `403` [`permission`](https://flow.engineer/docs/unified-messaging-api/errors/permission), 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 in `message.failed` with `outside_window`.

When the budget is used up the send fails with `429` [`new_contact_limit`](https://flow.engineer/docs/unified-messaging-api/errors/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

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`](https://flow.engineer/docs/unified-messaging-api/errors/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

* 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 `429` [`rate_limited`](https://flow.engineer/docs/unified-messaging-api/errors/rate_limited) and `retry_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

* **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 WhatsApp `conversation.window_open_until` says whether the window is open. Send what the channel takes; a `422` names what it refused (`param`) and what to send instead (`hint`).
* **Retry only what clears by itself.** `new_contact_limit`, `sender_throttled` and `rate_limited` carry `retry_after`; `outside_window` will 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

* [Senders](https://flow.engineer/docs/unified-messaging-api/concepts/senders.md): statuses and limits.
* [Going live](https://flow.engineer/docs/unified-messaging-api/guides/going-live.md): templates and dedicated numbers.
* [Rate limits](https://flow.engineer/docs/unified-messaging-api/concepts/rate-limits.md): request limits per API key, which are separate from the gate.
