Reply-only: what it forbids, and where the constraint actually lives
Reply-only means the system never messages anyone first. What that forbids, where the constraint lives in code rather than in a policy page, how much of that code exists today, and why both platforms already work this way.
Reply-only means one thing: mesej never messages anyone first. Everything else follows, including the capabilities listed below that the product does not have. This page lists what the constraint forbids, shows where it is enforced in code rather than in a policy page, says how much of that code exists today, and quotes the platform rules that already work the same way.
What reply-only forbids
Each is a route the code does not have rather than a setting it declines to use. How much of that code exists is the third section down:
- No first message. Nothing in the product takes a phone number, a chat id or a list from you and writes to somebody. The one function that mints a send destination takes a conversation’s own identity and is named for the single caller allowed to use it, so an originating send would have to be written on purpose rather than switched on.
- No broadcast, no campaign, no reactivation of a cold customer list.
- No follow-up once a conversation has gone quiet past its permission window, including one you type yourself. On WhatsApp a closed window takes away your ability to write freely exactly as it takes away the agent’s.
- No review request, and no appointment reminder, to anyone outside an open window. Reminder timing is computed by a pure scheduler with no ability to send anything.
A build gate here enforces the wording too: the claim is that it never messages anyone first, not that it merely answers. A review ask is not an answer, so the second version would stop being true the moment one shipped.
A policy and an architecture are different claims
A tool can describe itself as inbound-only and mean it sincerely. A policy is a sentence in documentation, held in place by whoever ships the next release, and it lasts as long as the incentive behind it. So the question worth putting to any vendor is what would have to be rewritten to break it. If the answer is a toggle in an admin panel, the promise is a preference.
Where the constraint lives in this codebase
Two mechanisms, both checkable by reading the repository.
No send function takes a raw destination. The type a send accepts is a
branded ChannelTarget carrying a unique symbol, so no plain object satisfies
it structurally, and it has one constructor: target_from_conversation, which
is meant to be called with a conversation’s own persisted identity, meaning the
channel a message arrived on and the contact who wrote it. Be exact about how
strong that is, because the overstated version was here until this pass: the
constructor takes plain strings and brands them, so a phone number typed into a
form could be handed to it by a caller determined to do so. What the brand buys
is that no forged object satisfies the type, and that the constructor is named
for its one legal caller, so a wrong call site shows up in a diff. The
enforcement test named for this is specified as: “No exported function accepts a
phone number, WhatsApp ID or Telegram chat ID as a send destination.” What
tests/send_guard.test.ts checks today is narrower than that sentence: it scans
the guard function’s own source text for the words phone_number, chat_id and
recipient, which a parameter rename would pass.
Permission is the first check in the guard, before anything becomes visible outside the system. The guard reads a permission clock and refuses outright once it has passed. Two details carry weight. The permission clock is a separate field from the billing clock and separate functions read them, so a free 72-hour billing window cannot quietly grant 72 hours of permission to write. And an owner reply typed in the console runs through the same check as an agent reply, so the window applies to a human exactly as it applies to the model. Tests drive the guard through a closed window, an owner reply into a closed window, an over-plan owner reply that must still go, and a live billing window against a dead permission window.
How much of this exists, stated exactly
Claiming an architectural guarantee for code that does not exist would be worse than writing nothing, so:
| Part | Status on 19 September 2026 |
|---|---|
| The branded target and its one constructor | Written, with tests |
| The send guard, permission checked first | Written as a pure function over injected effects, with tests |
| The WhatsApp and Telegram senders | Written, called by nothing but a test |
| The conversation object that would own the send | Deployed, storing state and running its alarm. It does not send |
| Webhook route, queues, the path from an inbound message to a reply | Absent |
So the mechanism exists and the product around it does not. No channel is connected, so nothing here answers a customer yet. Our architecture document names 38 enforcement tests. Searching the test tree for those names on 2026-09-19 finds 11 of them and not the other 27, and most of the missing ones cannot be written yet because they assert properties of a send path that is unfinished. This page said roughly sixteen and seventeen until that count was actually run, which did not even add up. Publishing the mechanism now is not a claim that it is done. It is checkable now, and a constraint retrofitted later is a different thing from one the send path was designed around.
Telegram already enforces it, and charges nothing
On Telegram the constraint costs us nothing, because the platform does it for us. From Telegram’s introduction for developers:
Bots can’t start conversations with users. A user must either add them to a group or send them a message first. People can search for your bot’s username or start a chat via its unique t.me/bot_username link.
The discovery route named there is the only outbound shape Telegram allows: a link somebody else chooses to click. Telegram removes the unwanted message while leaving the wanted ones free, and says so in its FAQ: “bots are able to message their users at no cost”, subject to published throughput limits such as “In a single chat, avoid sending more than one message per second.” Its paid tier buys throughput to users who already started a chat, never the first message.
On WhatsApp, breaking it costs money and needs approval
Meta does not prohibit the first message. It prices it and reviews it, which comes to the same thing for a product that does not want to send one. The Service messages page defines the permission:
When a WhatsApp user messages you or calls you, a 24-hour timer called a customer service window starts. If the user messages or calls you again before the timer expires, the timer resets to 24 hours.
Inside it a business writes freely, and Meta notes on the same page that “service messages do not require pre-approval”. Outside it, the Business Messaging Policy is blunt: “Outside the 24-hour customer service window, you may only send messages via approved Message Templates”. The pricing update page puts the consequence in one line: “Thus non-template messages can only be used to respond - not reach out - to users”.
Two costs sit on that template route, and they are why we do not build it. Approval is one: Meta’s Template fundamentals page says “Templates are automatically reviewed upon creation or after editing”, and a template that fails review “cannot be sent in template messages”. Money is the other: on Meta’s pricing page, “You are only charged when a template message is delivered”, and “Template messages are the only message type that can be sent outside of a customer service window, to reach out to users.” Those are Meta’s rules and Meta’s prices on Meta’s platform, quoted as platform mechanics and not as anything we offer.
So reaching out buys approval and pays per message, and replying needs neither. That is a poor reason to adopt the constraint and a fair reason to believe somebody who has.
What we have not verified
We read documentation, not behaviour. We have not run a send at a stranger against either provider to see what error comes back, and we are not going to describe an error message we have not seen. Nothing above has been exercised by a real customer message, because no channel is connected to exercise it.
Reply-only also says nothing about reply quality, and the two are easy to conflate. A system that never initiates can still answer badly, which is why the reply text is gated separately, and why Google moderates the replies businesses publish and exposes a reason in its API when it rejects one. If you are comparing tools that write for you, what the category can and cannot do is more useful than who claims restraint, and answering an unhappy customer is a craft problem either way.
The checkable version of reply-only is short. No send takes a raw destination, one constructor mints a target and is named for the only caller allowed to use it, and permission is the first thing the guard reads. Today that mechanism has nothing wired to it, and saying so is part of the claim.
Sources
- Meta, WhatsApp Business Platform, Service messages Read 19 September 2026.
- WhatsApp Business Messaging Policy Read 19 September 2026.
- Meta, WhatsApp Business Platform, Pricing Read 19 September 2026.
- Meta, WhatsApp Business Platform, Upcoming pricing updates for Meta Business Agent, service and utility messages Read 19 September 2026.
- Meta, WhatsApp Business Platform, Template fundamentals Read 19 September 2026.
- Telegram, Bots: An introduction for developers Read 19 September 2026.
- Telegram, Bots FAQ Read 19 September 2026.