Channels are two-way
Your agent doesn’t only reply — it can initiate. A scheduled job can send a reminder, a webhook can confirm a payment, a tool can follow up hours later. Outbound messages flow through the Channels API, and every one is recorded to the recipient’s conversation thread so your agent stays coherent across the whole exchange.How continuity works
There’s no durable “wait for reply” machinery to manage. The model is simple:- Your agent sends a message (from a tool, job, webhook, or trigger). The message is recorded to the recipient’s thread.
- Hours or days later, the user replies on that channel.
- The reply wakes your agent with the same thread loaded — it sees the message it sent and continues naturally.
A user is identified by their Lua
userId, and one user ↔ agent pair shares one conversation thread. Send on WhatsApp, get a reply on WhatsApp — the agent has the full history either way. You don’t manage sessions or state machines for this; the thread is the memory.Three ways to send
Pick the one that matches what you have and where you want the message to go.Channels.send(...) — pick the channel explicitly
Choose exactly which channel and recipient. Works from any context, can reach cold recipients (a phone number or email with no prior conversation), and records to the thread. This is the general-purpose proactive send.
user.send(...) — reach the user you already have
If you’ve already loaded a
User instance, user.send([...]) delivers to that user’s active conversation. Simplest when you just want to message the user you’re working with, on the channel they’re already on.Templates.whatsapp.send(...) — bulk template send
Send an approved WhatsApp template to one or many phone numbers by channel ID. Best for campaigns and batch notifications. See the Templates API.
Which should I use?
| You want to… | Use |
|---|---|
| Message the current user, on the channel they’re on | user.send([...]) |
| Choose a specific channel (e.g. always WhatsApp) for a known user | Channels.send with to.userId |
| Reach a phone/email with no prior conversation | Channels.send with to.phoneNumber / to.email |
| Start or re-open a WhatsApp conversation (window closed) | Channels.whatsapp.sendTemplate |
| Blast an approved template to many numbers | Templates.whatsapp.send |
The WhatsApp 24-hour window
WhatsApp only allows free-form business messages within 24 hours of the user’s last inbound message. This is a Meta rule, not a Lua one — and it shapes how proactive WhatsApp sends behave.- Window open (user messaged within 24h) →
Channels.send({ channel: 'whatsapp', ... })delivers immediately (delivered: true). - Window closed → free-form isn’t allowed. Two options:
- Send an approved template with
Channels.whatsapp.sendTemplate. Templates are allowed any time and are how you start or re-open a conversation. - Let
Channels.sendqueue it (the default). The message is held, the recipient is shown a short opt-in prompt, and the queued text is delivered once they re-engage. The call returnsqueued: true(not yetdelivered).
- Send an approved template with
US (+1) recipients: the opt-in prompt used by the queue path is a marketing-category template, which Meta may not deliver to US numbers. For reliable US outreach outside the window, send an approved utility template directly with
Channels.whatsapp.sendTemplate.Per-channel constraints
Windows and policies differ by channel:| Channel | Proactive send | Constraint |
|---|---|---|
| Free-form (in-window) or template | 24-hour window; templates required outside it | |
| SMS | Free-form any time | No window, but subject to carrier opt-out (STOP/HELP/START) and regional rules |
| Free-form any time | No window | |
| Web chat | Free-form to a known user | Delivered to the user’s web widget |
| Teams / Instagram / Messenger | Free-form to a known user | Warm-only — the user must have an existing conversation with your agent |
Scheduled outbound
There’s no separate “campaign” primitive — scheduled outreach is just a job that callsChannels.send:
Components in proactive messages
Proactive messages support the same response formatting components (the::: blocks) as inline replies — lists, links, images, actions — on channels that render them. The web chat widget renders the full component set; messaging channels render what their platform supports (text, media), and fall back to text otherwise.
Next steps
Channels API
Full reference for send, sendTemplate, and email.send
Channel Capabilities
Per-channel limits and sender resolution
Proactive Send Recipe
Schedule outreach with defineJob + Channels.send
Jobs API
Schedule recurring and one-off work

