Consent and STOP
You may not message just anyone. In the US, a business may text first only someone who agreed to it, and anyone can text STOP to stop all messages. WhatsApp needs the person’s opt-in, and lets you write freely only within 24 hours of their last message. On Telegram and iMessage the person writes first. People also write STOP, “stop texting me” or “unsubscribe” in the middle of a conversation. Your agent must not answer a STOP with a cheerful reply.- On Flow Messaging: the send gate applies each channel’s rule to every send. On SMS, a start needs the contact’s recorded
consentunless they texted first; STOP (and STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPT OUT, OPTOUT) opts the contact out of all your SMS numbers until they text START, and Flow sends the keyword reply itself. You still receive the message; a send to an opted-out contact answers403permissionwithcontact_opted_out. Do not retry it. - On another channel: your SMS provider may block sends after STOP, but check what it does and which words it treats as STOP. Record consent before you text anyone first. Make your agent recognise STOP and its variants, send nothing more, and record the opt-out where every part of your system that sends can see it.
Quiet hours
A text at 3 a.m. wakes people up. Messages you start (reminders, follow-ups) should wait for the daytime in the person’s time zone. A reply to someone who just wrote to you is fine at any hour.- On Flow Messaging: on SMS, a send to a contact who has not texted in the last hour respects the sender’s quiet hours (by default 21:00 to 08:00 in the contact’s time zone, worked out from the area code). Inside them it answers
409outside_windowwithquiet_hoursandretry_after, and a queued message is held until morning instead of sent at night. - On another channel: keep a time zone per person (from their area code, or ask), and check it in every job that sends without being asked. Check the rules that apply to your messages and your users.
Message length and splitting
A long, formatted answer reads well in a web chat and badly on a phone. One SMS holds 160 plain characters, or 70 if the text has any emoji or non-Latin letter; a longer text goes as several segments, each billed, and some phones show them out of order. Markdown is shown as raw asterisks and pound signs on SMS. On chat apps, one wall of text is harder to read than two or three short messages.- On Flow Messaging:
reply()splits your model’s text into chat-sized bubbles at paragraph and sentence ends, and turns Markdown into plain text on channels without formatting. See Streaming replies. On SMS a text is capped at 1530 characters and 10 segments. - On another channel: ask the model for short answers, strip Markdown for SMS, and split long answers at paragraph or sentence ends yourself (the bubble rule works for any channel). If you send Observe traces, report each part you actually sent as its own
flow.agent_reply.
Duplicates and retries
The same message can reach your code twice: webhooks are delivered at least once, providers retry when your server answers slowly, and people send the same thing again when no reply comes. Your send can be retried too. Without care, the person gets two answers, or the same answer twice.- On Flow Messaging: deduplicate on the event’s
id(see delivery rules), and send every reply with anIdempotency-Keybuilt from it, so a retried send never sends twice. See Idempotency. - On another channel: deduplicate on your provider’s message id before you run the agent, and use your provider’s idempotency feature on sends if it has one. Answer your provider’s webhook at once and run the agent afterwards, so a slow model does not trigger a retry. Set
flow.message_idon your traces, so Observe stores the person’s message once even when your code ran twice (it still records every reply you actually sent).
People who come back days later
People answer a question three days later with “yes”, or start a new topic in an old thread. Your agent needs the history to know what “yes” means, and it must not treat old context as current (“your order is on its way” from last week). Observe counts a conversation as one run of messages: after 48 hours with no messages, the next one starts a new conversation. Billing counts the same way.- On Flow Messaging: one person on one sender is one conversation object, however long the gap. Load the recent history with
GET /v1/conversations/{conversation_id}/messages, and check message times when context may be stale. On WhatsApp, more than 24 hours after the person’s last message you can send only a template. - On another channel: keep your own history per thread (for SMS, per pair of your number and theirs), with times. Send the same
flow.conversation_idfor the whole thread; Observe splits it at 48-hour gaps itself.
Photos, voice notes and other media
People send photos, screenshots, voice notes, stickers, contact cards and locations, often with no text at all. An agent that ignores them answers as if nothing was said. Media can also be sensitive: a photo of a prescription or an ID card.- On Flow Messaging: every message has a typed content type.
contentText()gives you the text, a caption, a voice note’s transcript or a button’s label. When your reply holds something a channel cannot show,fallback: "auto"sends the closest thing and reports what was shown. - On another channel: decide what your agent does with each kind: read it, ask for text, or say it cannot open it. Never write media or its links to your logs. On the traces route, Observe grades the text of each turn; a message with only a photo has no text to grade.
Latency and typing
On a phone, ten seconds of silence feels broken. People write again (see duplicates, above) or leave. Tools and long model calls are the usual cause.- On Flow Messaging:
reply()keeps the typing indicator on while your model writes and sends each bubble as soon as it is ready. For a slow tool call, wrap the work inevent.conversation.responding(...). Webhooks must answer within 10 seconds: run the agent after answering, or read events from the stream. See Streaming replies. - On another channel: SMS has no typing indicator. For work that takes more than a few seconds, send a short “Looking that up” first, and keep the final answer short.