# Follow-ups on WhatsApp

A follow-up is a dated commitment on a channel with a reason attached, and `whatsapp` is one of the channels `followups.py` supports. Connect will schedule one and act on it when it is due — but what may be sent at that moment depends on Meta's 24-hour window. Inside it, the message Connect wrote. Outside it, only the approved template the workspace chose for follow-ups, sent under Meta's category for that template.

- **Status:** Available
- **Audience:** both
- **Channels:** whatsapp
- **In the app:** #/follow-ups, #/whatsapp
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/whatsapp/whatsapp-follow-up/

## What a follow-up is

Follow-ups are one queue across every channel: `email`, `whatsapp`, `sms`, `phone`, `any`, and `task`. Each row carries a due time, the channel it should happen on, the person it concerns and the reason it exists. The reason is not decoration — it is what makes the message when it is due read like a continuation rather than a fresh approach.

The `task` channel is the useful contrast. A `task` row is work for a human; no drain ever sends it, and it appears on the follow-ups screen as "For a person to do". A WhatsApp one is different: it is something Connect intends to send, subject to every gate that governs any outbound message.

## The window problem, stated plainly

WhatsApp does not let a business write freely to a person at an arbitrary moment. The window is opened by the person's own message and runs 24 hours; outside it a business may only send a template Meta has approved. That constraint is Meta's. Connect cannot extend it, does not pretend to, and checks it locally before the network call.

So a WhatsApp chase due tomorrow morning has two possible outcomes that both count as correct behaviour. If the window is still open, the message goes as Connect wrote it. If it has closed, the engine falls back to the follow-up template the workspace nominated in the channel's automation settings — and if no template is set, or the one named is not on the list synced from Meta, the send is refused with that reason rather than attempted.

> **Careful** Do not schedule a WhatsApp follow-up on the assumption that a long-dated one will behave like an email. The further out the due date, the more likely the window has closed by the time it arrives. Where the timing is genuinely important, an `email` or `phone` follow-up is the more reliable promise.

## What happens when one comes due

1. The due time passes and the queue picks the row up.
   - Result: Connect composes the message using the reason recorded on the row and everything it knows about the relationship.
2. The autonomy rule for `whatsapp` is applied at the narrowest scope.
   - Result: Under `ask_before_send` it lands in Needs You rather than going out; under `draft_only` it is written and kept.
3. Suppression, do-not-contact and any `block:whatsapp` directive are checked.
   - Result: A person who has opted out is not messaged, and the follow-up closes rather than being retried against the same refusal.
4. `whatsapp.dispatch` prices the message on Meta's category and asks the autonomy policy.
   - Result: A template Meta classes as marketing is treated as sales outreach however bland the wording is, because Meta requires opt-in for it and charges roughly 7.5 times a utility template. A workspace that has not enabled automatic sales follow-ups does not get one by accident.
5. Meta is asked to send.
   - Result: A message id records the send with evidence. A refusal — commonly the window, an unapproved template, or the hourly cap — is recorded with its reason, and the commitment is visible as unfinished business rather than silently dropped.

The last point is the one worth insisting on. A promise Connect could not keep is surfaced, not hidden, on the same principle described in [Preventing a voice agent from promising a call-back it cannot keep](/research/callback-promises/).

## Choosing the channel for a promise

**Use `whatsapp`** — When the conversation is live on WhatsApp and the follow-up is soon — a quote to send this afternoon, a confirmation after a delivery.
**Use `email`** — When the follow-up is days or weeks out, or carries an attachment, or needs to be findable later.
**Use `any`** — When the commitment is to the person rather than to a channel, and Connect should use whatever is open.
**Use `task`** — When the next action is genuinely a human's — a call from the owner, a site visit, a decision.

[Follow-ups in Connect](/docs/follow-ups/) covers the queue itself: how rows are created, who can see them, and how they close.

## Questions

### Can Connect send a template message to reopen a closed window?

Only one Meta has approved. Templates are synced from Meta with their names, languages, categories and approval states; a name that is unknown or not approved is refused rather than attempted. Approval itself happens at Meta, outside Connect.

### Has a WhatsApp follow-up ever actually gone out from production?

No. No WhatsApp number is connected in production, so nothing outbound has reached Meta from the live build. Scheduling, the window rule, the category decision and the refusal paths are proven; the send itself is proven against a stubbed Meta in `test_whatsapp_platform_v1`.

### Does a refused follow-up disappear?

No. The refusal and its reason are recorded and the commitment stays visible, which is what lets somebody pick it up on another channel.

### Can a customer's reply cancel a scheduled follow-up?

A follow-up completes when the thing it was about is done. If a customer answers the question the follow-up existed to chase, close it — from the follow-ups screen or by asking the [Connect Assistant](/docs/assistant/), which has tools for both creating and completing them.

## Related

- [WhatsApp in Connect](https://connectbyjbrh.com/docs/whatsapp/)
- [Replying on WhatsApp](https://connectbyjbrh.com/docs/whatsapp/replying/)
- [What WhatsApp cannot do here](https://connectbyjbrh.com/docs/whatsapp/whatsapp-limits/)
- [A WhatsApp follow-up, end to end](https://connectbyjbrh.com/docs/workflows/whatsapp-follow-up-execution/)
- [Follow-ups in Connect](https://connectbyjbrh.com/docs/follow-ups/)
- [Preventing a voice agent from promising a call-back it cannot keep](https://connectbyjbrh.com/research/callback-promises/)

## What this page is based on

- `backend/app/whatsapp_engine.py` — `_due_followups`, `tick`, `schedule_message`
- `backend/app/whatsapp.py` — `_category_for`, `dispatch`, `set_automation`
- `docs-source/sources/GENERAL.md` §7 — follow-ups and their channels
- Connect capability registry (docs-source/facts.py) — `WHATSAPP`
