Go live
Going live · dedicated WhatsApp number for AI agent · WhatsApp business verification API · WhatsApp message templates API · iMessage dedicated line · go live WhatsApp APIConnect a dedicated sender, use a live key and check production delivery.
Going live means sending from a dedicated sender of your own with a live key; your agent’s code does not change. The examples below assume FLOW_MESSAGING_KEY holds your live key (fk_live_...).
export TELEGRAM_BOT_TOKEN=... # the token from @BotFather; never commit it
curl https://api.flow.engineer/v0/senders \ -H "Authorization: Bearer $FLOW_MESSAGING_KEY" \ -H "Content-Type: application/json" \ -d "{\"channel\": \"telegram\", \"telegram_bot_token\": \"$TELEGRAM_BOT_TOKEN\"}"| Channel | How you go live today |
|---|---|
| Telegram | Self-serve: connect your own bot with a live key (below). |
| iMessage | Flow’s shared line works with a live key once your account has it (the free trial your first sign-in at flow.engineer starts opens it, and a paid plan keeps it). For a line of your own, buy Dedicated in the dashboard (Settings → Usage and billing → Choose a plan); Flow reviews the line, then sets it up, usually within a day. Lines reply after the person writes first; writing first comes with the Outbound add-on on dedicated lines. |
| Self-serve: connect a number in your own WhatsApp Business Account with a live key (below), or in the dashboard under Connections › WhatsApp. The Flow team can also connect one for you. |
Checklist
Section titled “Checklist”Get a live key
Sign in to the dashboard with a Flow account, open Developers › API keys, switch to Live, and make an fk_live_... key (test keys take one call, see Keys and sign-in). Keep it on your server only, in its own variable (for example FLOW_MESSAGING_LIVE_KEY) so it does not replace your test key. Your own Telegram bot and your own WhatsApp number are self-serve; a dedicated iMessage line comes with the Dedicated plan, bought in the dashboard.
Get a dedicated sender
Telegram: POST /v0/senders with your bot’s token; it is active at once. WhatsApp: POST /v0/senders with your number’s IDs, token and app secret (see WhatsApp); it is active at once. iMessage: buy Dedicated in the dashboard; once Flow has reviewed and set up the line, it appears in GET /v0/senders with your live key (POST /v0/senders with "channel": "imessage" answers 501 not_implemented with any key; build meanwhile on simulated conversations of that channel).
Register your webhook with the live key
Webhook endpoints and their signing secrets belong to one mode: your test endpoint never receives live events. POST /v0/webhook_endpoints again with the live key and store the new whsec_... secret where your live deployment reads it; the test endpoint keeps its own secret.
Switch the key
Set FLOW_MESSAGING_KEY to the live key and deploy. Watch message.failed and sender.status_changed for the first days.
Your own number
Section titled “Your own number”Connect a number in your own WhatsApp Business Account, under your own Meta app, with a live key: POST /v0/senders, or Connections › WhatsApp in the dashboard. The Flow team can also connect a number for you.
First, in Meta Business Manager:
- Create a Meta app of type Business with the WhatsApp product, and switch it to Live.
- Add the phone number to your WhatsApp Business Account, with an approved display name, and add a payment method in WhatsApp Manager (Meta bills its fees to you directly).
- Create a system user (Admin), assign it your WhatsApp Business Account (full control) and the app, and generate a token that never expires with
whatsapp_business_messagingandwhatsapp_business_management.
Then connect the number. Keep the token and the app secret (App settings > Basic) in environment variables, never in code or a chat:
export WHATSAPP_ACCESS_TOKEN=... # the system user token; never commit itexport WHATSAPP_APP_SECRET=... # your Meta app's App secret
curl https://api.flow.engineer/v0/senders \ -H "Authorization: Bearer $FLOW_MESSAGING_KEY" \ -H "Content-Type: application/json" \ -d "{\"channel\": \"whatsapp\", \"whatsapp_phone_number_id\": \"106540352242922\", \"whatsapp_business_account_id\": \"102290129340398\", \"whatsapp_access_token\": \"$WHATSAPP_ACCESS_TOKEN\", \"whatsapp_app_secret\": \"$WHATSAPP_APP_SECRET\"}"Use your number’s phone number ID (WhatsApp Manager, or App Dashboard > WhatsApp > API Setup), not the phone number itself. Add whatsapp_registration_pin (six digits) only when the number is not registered for the Cloud API yet, and display_name if you like.
- Answer:
200with the senderactiveand, only in this answer,setup: enter itscallback_urlandverify_tokenin your Meta app (App Dashboard > WhatsApp > Configuration) and subscribe thewebhook_fieldsthere, following itssteps. Flow has already pointed this number’s webhooks at Flow (your other numbers keep theirs); the settings in your app make its field subscriptions apply. The verify token is shown only in this answer. - New token: send the number again the same way. It stays the same sender, with the same verify token.
- Disconnect:
DELETE /v0/senders/{sender_id}deletes the stored token and app secret and retires the sender. Nothing changes in your Meta app: remove Flow’s callback URL there yourself. - Numbers the Flow team connected are updated and disconnected by the Flow team (
403 permissionwithchannel_codewhatsapp_number_managed, and501 not_implementedonDELETE).
Numbers, quality and fees
Section titled “Numbers, quality and fees”- Numbers: a number in your business’s own WhatsApp Business Account, or a number Flow provides.
- Quality and tiers: the sender shows WhatsApp’s
quality_rating(green,yellow,red) and its messaging tier inlimits.whatsapp_tier. Replies to people who wrote first do not use the tier, but those people still count toward your plan’s people (Pricing). - Fees: on a number Flow provides, WhatsApp’s own per-message fees (charged by Meta) are passed through. With your own WhatsApp Business Account, Meta bills its fees to your business directly, so add a payment method in WhatsApp Manager. See Pricing.
WhatsApp templates
Section titled “WhatsApp templates”A template is a message WhatsApp approved in advance. On WhatsApp you need one to message someone first, or to write more than 24 hours after their last message. Templates are created on your WhatsApp number, with a realistic sample for each placeholder:
curl https://api.flow.engineer/v0/templates \ -H "Authorization: Bearer $FLOW_MESSAGING_KEY" \ -H "Content-Type: application/json" \ -d '{ "sender": "snd_01JB8ZC3K5M7P9R1T3V5X7Z9B1", "name": "order_ready", "language": "en", "category": "utility", "components": [ { "type": "body", "text": "Hi {{1}}, your {{2}} is ready for pickup.", "examples": ["Asha", "cake"] }, { "type": "buttons", "buttons": [ { "type": "url", "text": "See order", "url": "https://shop.example.com/orders/{{1}}", "example": "A-1042" } ] } ] }'The answer is 201 with the template, pending until WhatsApp reviews it, its components as you sent them:
{ "id": "tpl_01JB8ZC3K5M7P9R1T3V5X7Z9B1", "sender": "snd_01JB8ZC3K5M7P9R1T3V5X7Z9B1", "name": "order_ready", "language": "en", "category": "utility", "status": "pending", "components": [ { "type": "body", "text": "Hi {{1}}, your {{2}} is ready for pickup.", "examples": ["Asha", "cake"] }, { "type": "buttons", "buttons": [ { "type": "url", "text": "See order", "url": "https://shop.example.com/orders/{{1}}", "example": "A-1042" } ] } ], "created_at": "2026-10-24T09:30:00Z"}- Always give samples. WhatsApp reviews a template with them. Put
exampleson the body and on a text header, one value per placeholder in order ({{1}}first), andexampleon aurlbutton whose link ends in{{1}}: the end of the link, which Flow shows WhatsApp in place. Left out, Flow sends placeholder samples (x1,x2, …), which WhatsApp often rejects or moves to another category. - Sample limits: exactly one per placeholder; 1 to 60 characters in a header and 1 to 1024 in a body; no line breaks, tabs or more than four spaces in a row. The url sample has no spaces, and the link with it in place is at most 2000 characters.
- Placeholders in a template you create through Flow are numbered
{{1}},{{2}}, … with none skipped. A header has at most one, a footer none, and a url button’s link only{{1}}, at its end. A template that breaks these rules or the sample limits gets400 invalid_request, and so do samples on a footer, a buttons part, a media header or text without placeholders. - Names use lowercase letters, digits and underscores. A name and language pair is unique per sender.
categoryisutility(updates about something the person asked for),marketingorauthentication; it sets WhatsApp’s fee.- You receive
template.status_changedwhen WhatsApp approves, rejects (withrejection_reason) or pauses it. - In the dashboard, open your WhatsApp number under Connections › WhatsApp, then its Templates section: its New template form always sends its sample values. The list says where each template was made, when Flow last read them from WhatsApp, and has Sync now.
- Send it with
POST /v0/conversations/{conversation_id}/template(or start a conversation withPOST /v0/messagesand"kind": "template"), with values for its placeholders, the url button’s by its position (from 0):
{ "template_id": "tpl_01JB8ZC3K5M7P9R1T3V5X7Z9B1", "language": "en", "params": { "body": ["Asha", "cake"], "buttons": [{ "index": 0, "value": "A-1042" }] } }Templates made in WhatsApp Manager
Section titled “Templates made in WhatsApp Manager”On your own WhatsApp Business Account you can also make templates in Meta’s WhatsApp Manager. Flow reads them by itself: when the number connects, when Meta reviews a template Flow did not know, and every 6 hours. To read them now, for example right after Meta approved one:
curl https://api.flow.engineer/v0/templates/sync \ -H "Authorization: Bearer $FLOW_MESSAGING_KEY" \ -H "Content-Type: application/json" \ -d '{"sender": "snd_01JB8ZC3K5M7P9R1T3V5X7Z9B1"}'The answer is 200 with what changed in Flow’s list:
{ "sender": "snd_01JB8ZC3K5M7P9R1T3V5X7Z9B1", "synced_at": "2026-10-30T09:00:00Z", "added": 1, "updated": 0, "disabled": 0, "skipped": [ { "name": "spring_cards", "language": "en", "reason": "It has a CAROUSEL part, which Flow does not send yet." } ]}addedcounts templates Flow did not have,updatedthose whose words, category or status changed, anddisabledthose Meta no longer has (Flow keeps them asdisabled).skippedlists the ones Flow cannot send yet, with why.- Flow never changes or deletes anything at Meta. To change a synced template’s words, edit it in WhatsApp Manager; the next sync takes them.
- Every template has
source,flow(made through the API or the dashboard) orwhatsapp_manager, andsynced_at, when Flow last read it from Meta. Both are sent the same way. GET /v0/templates?sender=snd_...on your own WhatsApp number also answers"sync": { "synced_at": "2026-10-30T09:00:00Z", "error": null, "error_at": null }. When a read from Meta failed after the last success,errorsays why in one line anderror_atwhen.- Only your own WhatsApp number syncs: a Telegram, iMessage or SMS sender gets
422 unsupported_content, a shared sandbox sender403 permission, and Meta refusing the read502 channel_error.
Named variables. A template made in WhatsApp Manager may name its variables. Give their values in params.body in the order the body first names each one, one value per name. For the Example Market template listing_answer, body About {{listing}}: {{answer}}, body[0] fills {{listing}} and body[1] fills {{answer}}:
{ "template_id": "tpl_01JB8ZD4M6P8R0T2V4X6Z8B0C2", "language": "en", "params": { "body": ["the oak table", "it is still available"] } }The person reads “About the oak table: it is still available”. A wrong number of values answers 400 invalid_request with param content.params.body and the order in its hint: Give exactly 2 values in params.body, in this order: body[0] = {{listing}}, body[1] = {{answer}}.
iMessage: a dedicated line
Section titled “iMessage: a dedicated line”- Get one: Buy Dedicated (SMS/RCS fallback and call forwarding included), and, if you want your agent to write first, the $40 Outbound add-on, in the dashboard (Settings → Usage and billing → Choose a plan); Flow reviews the line, then sets it up, usually within a day. (Pricing). A new line takes up to 250 different people a day and ramps to 1,000 over its first 14 days. It is connected to your app and listed in
GET /v0/senders?channel=imessagewith your live key. Flow’s shared line needs no setup: once your account has it (the free trial, kept on a paid plan), a live key sees it too, with your app’s live join message. - The person writes first: starting a conversation with a new contact is refused with
403 permission, unless writing first is switched on for the line (imessage.can_initiate). Writing first comes with the Outbound add-on on dedicated lines, within a warm-up: 2 new conversations a day, growing to 40 a day over the line’s first 21 days. - Included, and the one add-on (Pricing): every iMessage line, Flow’s shared line included, sends by SMS or RCS from the same number to contacts who cannot get iMessage; a dedicated line also forwards phone calls. Outbound (writing first) is the one add-on, on dedicated lines. Group chats people add the line to reach your app as
groupconversations; they are for dedicated lines only: Flow’s shared line ignores group chats. - Get people to write: the sender’s
address.linkis the line’s opt-in link (when it has one): it opens Messages with the line and a prefilled text. Publish it on your site, receipts or QR codes. See Build an iMessage agent.
Telegram: your own bot
Section titled “Telegram: your own bot”Create a bot with @BotFather and send its token: POST /v0/senders with channel: "telegram" and telegram_bot_token. It is connected and active at once. See Build a Telegram agent.
- New token: after revoking a token in @BotFather, send the new one the same way. The bot stays the same sender; one Telegram rejected meanwhile is
flaggeduntil you do. Messages queued while it is flagged wait at most 72 hours, then fail withoutside_window(channel_codequeued_too_long). - Disconnect:
DELETE /v0/senders/{sender_id}removes the bot’s webhook and token and retires the sender. - One app per bot: connecting a bot to another app moves it there and retires the old sender.
Before you launch
Section titled “Before you launch”- Handle
outside_window,new_contact_limitandsender_throttled(see Errors). Know the sending limits and abuse signals: the same first message to many new contacts throttles a sender for 24 hours. - Deduplicate webhook deliveries on the event
id, and use event IDs as idempotency keys. - Subscribe your webhook only to the event types you use.
- Message content and media are kept for your plan’s retention period (30 days by default); store anything you need longer yourself.
Related
Section titled “Related”Thanks, we read every comment.