Bot Flows — Visual Chatbot Builder¶
Bot Flows (/flows) is ConnectGain's visual bot builder: a drag-and-drop canvas for automated conversation flows across WhatsApp and every other channel. With 46 node types and a rich set of triggers, flows can answer inquiries, collect information, create deals and tasks, move contacts through lifecycle stages, call external APIs, run AI/RAG answering, log to spreadsheets, and hand off to a human agent when needed.
The visual flow builder.
New here? Skim Concepts → The builder UI → Triggers, then jump to the node you need or to End-to-end scenarios.
Parity + backward-compatibility. ConnectGain's builder covers every node, trigger, and use case offered by respond.io's Workflows — and adds deals, tasks, RAG, voice, multi-LLM, and cross-turn merge on top. The builder is also versioned additively: new nodes, triggers, and fields are always optional, so flows built in older versions keep running unchanged.
Contents¶
- Concepts
- The builder UI
- Triggers — starting a flow
- Variables & dynamic text
- Pause / resume (waiting for the user)
- Node reference
- Multilingual flows
- Deploying, activating & publishing
- Testing & debugging
- Flow analytics
- API integration
- End-to-end scenarios
- Best practices
- Troubleshooting
Concepts¶
| Term | Meaning |
|---|---|
| Flow | A chatbot/automation graph. Lives in bot_flows; has a status of Draft or Published. |
| Node | One step (send a message, branch, call an API, create a deal…). 37 types across 6 categories. |
| Edge | A connection from one node's output handle to another node's input. Defines execution order/branches. |
| Trigger node | The required entry point. Defines what event starts the flow and which channel it listens to. |
| Session | Per-conversation state (bot_sessions) holding variables and the current pause point. |
| Variable | A named value in the session ({{name}}). Set by inputs, API calls, AI, or Set Variable. |
| Deployment | Publishing compiles the flow into a runnable workflow and activates its webhook. |
How many flows you can have. Every plan includes 3 bot flows, counted across Draft and Published. Archived flows do not count — archiving frees the slot and keeps the work, so you can park a seasonal flow and build a new one. The
/flowspage shows your usage next to Create Flow, and Create Flow is disabled once you are at the limit. Need more? Contact support: allowances are raised per organization, and several customers already run more than three.
How a flow actually runs: an inbound message arrives on a channel (WhatsApp, Messenger, …) → ConnectGain hands the event to the published flow, which executes node-by-node → node actions (sending messages, CRM writes) call back into ConnectGain. A flow only runs when it is Published and active.
The builder UI¶
- Canvas — drag-and-drop, zoom/pan, snap-to-grid. Connect nodes by dragging from a node's output handle to another node's input.
- Add-node palette — grouped by category (Triggers, Messaging, AI & RAG, Logic, Data, CRM). Each node shows an icon + one-line description.
- Node inspector — click a node to configure it. The inspector also lists available variables from upstream nodes (computed by walking the edge graph backwards).
- Node search / jump — quickly find and focus a node in large flows.
- Flow settings — name, description, status.
- Deploy / Publish — compiles and activates the flow. Undeploy takes it offline.
- Test — sample-webhook tester + preview.
- Flows list paginates at 50 per page.
Triggers — starting a flow¶
Every flow needs at least one Trigger node. Configure its event and (for message events) a channel filter.
Trigger events¶
Conversation & messaging
message.received— inbound customer message (the most common trigger)message.sent— an outbound message was sentconversation.created/conversation.assigned/conversation.status.changed- Broadcast Response (
broadcast.response) — the contact replied within 72h of receiving a campaign. Payload carriescampaign_id,campaign_name. - Manual Shortcut (
manual.shortcut) — an agent launches the flow from the inbox ⚡ Shortcuts menu.
Contact
contact.created/contact.assignedcontact.updated— any contact edit. Set the optional Field to watch (e.g.lead_score) to fire only when that field changes (Field Updated).- Tag Updated (
contact.tag.updated) — contact tags changed. - Lifecycle Updated (
contact.lifecycle.updated) — the contact's lifecycle stage changed.
Deals & tasks
deal.created/deal.stage.changed/deal.updated/deal.assignedtask.created/task.updated/task.completed/task.overdue
Other
- Call Ended (
call.ended) — a call recording arrived (carries call id, contact, duration). - Incoming Webhook (HTTP POST) — an external system (your POS, ordering platform, Zapier — anything that can send an HTTP request) calls your flow's webhook URL. Send
phonein the body and ConnectGain finds or creates the contact and their WhatsApp conversation for you. - Zoom Recording Completed (
zoom.recording.completed)
Incoming Webhook runs at most once per contact per day. If the external system sends the same customer again on the same day — a retry, a duplicate event, a job that fires more often than it should — the flow is not started again. The call still succeeds (200) and the response says "skipped": true with "reason": "once_per_contact_per_day", so nothing on the sender's side breaks. "Day" follows your organisation's timezone (Settings → Organization). This protects you from paying for the same WhatsApp template twice for one visit. If the same customer genuinely needs the flow several times a day, switch off Run at most once per contact per day on the Trigger node. Runs from the Channel Simulator are never limited, so you can test freely.
Channel filtering (message triggers)¶
- Channel —
all, or one ofWHATSAPP_LITE, WHATSAPP_CLOUD, FB_MESSENGER, INSTAGRAM, TELEGRAM, TIKTOK, SHOPIFY_INBOX, BULK_EMAIL, LINKEDIN. - Channel account — pin to one connected account (e.g. only your main WhatsApp Cloud number), or
all. Prevents a flow meant for one number firing on another.
Multiple triggers in one flow¶
Add several Trigger nodes to a single flow and each starts its own branch from the same webhook; events are routed to the matching branch by event type. Use this to share setup (variables, lookups) across related events.
Inbox Shortcuts (agent-launched)¶
Build a flow with a Manual Shortcut trigger and publish it. A ⚡ Shortcuts menu appears in the inbox conversation header; selecting your flow runs it for the current conversation, passing the conversation/contact context. The webhook URL is never exposed to the browser — launches go through the org-validated trigger-shortcut-flow endpoint.
Variables & dynamic text¶
Reference any session value in any text field with handlebars: {{variable}} or dot-paths {{contact.first_name}}.
Autocomplete & typo warnings. In any variable-aware field, typing {{ opens a picker of the variables actually available at that point in the flow — trigger/system vars, the outputs of steps before this one (labelled with the node that produces them), and variables set on other branches. Use ↑/↓ then Enter/Tab (or click) to insert. If a field references a {{token}} that no step produces (a typo like {{recipt_url}}), a small amber note appears under the field so you catch it before publishing rather than getting empty text at run time. The Node Inspector panel lists the same tokens as draggable chips.
Where variables come from
- The trigger payload:
{{message.text}},{{contact.id}},{{contact.first_name}},{{contact.phone}},{{conversation.id}} - Collect Input / Quick Reply (collect mode) — the user's answer
- Set Variable, HTTP Request (
responseVariable), Database Query/Memory (outputVariable), AI Response (response), AI Agent (agentResponse)
Rules to remember
- Missing variable → the literal
{{token}}is left unchanged (never blanked). This is what makes interpolation safe to add to old flows. - Dot-paths are static —
{{order.id}}works; nested/dynamic keys like{{texts.{{lang}}.greeting}}do not. Compute the final flat value first (Set Variable / Database Query), then reference it. - Numeric helper:
{{counter}} + 1increments in text fields. - Store user input, API responses, and computed values, then reuse them in conditions and in message templates.
Dates & the current time¶
There is no generic {{now}} text variable — flows can't stamp the current date into an arbitrary message field. Use one of these instead:
- Resolve relative dates from what the user says → use the AI Agent. The AI Agent (and RAG agent) are automatically given the real current date/time at run time, so "book me in tomorrow at 3 PM", "next Monday", "this Friday at 10 AM" resolve to a correct absolute ISO 8601 datetime you can pass to the Scheduler. Set the agent's Timezone (for dates) field to your local IANA zone (e.g.
Africa/Cairo) so relative dates land on the correct local day. This is injected at Publish time — re-publish a flow after changing the timezone or after this feature was deployed. (See UC-FLOW-019.) - Branch on the current time → use the Date & Time node. It evaluates now (in a chosen timezone) against business-hours windows and takes the In hours / Out of hours output. Use it for open/closed routing, not for emitting a date string.
- Compute a date server-side → use the Scheduler node. It returns real, bookable slots (
available_slots) computed from your availability rules — no date maths in the flow.
Pause / resume (waiting for the user)¶
Two nodes pause the flow and wait for the next inbound message: Quick Reply and Collect Input. This is fundamental to conversational bots.
- When a pause node runs, the session stores
current_node_idand what it's awaiting (awaiting_buttons/awaiting_input_variable). - The customer's next message resumes the flow at that exact point — Quick Reply matches a button (by number
1/2, exact title, payload, orbtn_0id), Collect Input captures free text into the variable. - If a reply doesn't match any button, the flow restarts from the trigger (so a stray "hi" re-greets rather than dead-ends).
- Bot flows pause for the user automatically; they only stop on an explicit human handoff (
ai_handoff_at).
Chaining tip: enable Collect into variable on Quick Reply to collapse its many per-button outputs into a single "answered" output. You can chain several Quick Replies and send one summary at the end, instead of 15-edge spaghetti.
Node reference¶
Each node lists its purpose, key config, the variables it outputs, and notes. Configure everything in the node inspector. All text fields accept {{variables}}.
Messaging nodes¶
Send Message¶
Sends a text/template message on the chosen channel.
- Key config:
channelAccountId,message(supports{{vars}}),messageSource=custom | freeformOrTemplate | quickReply | waCloudTemplate | llm. For multilingual templates:templateNameBase+localizedBodies(see Multilingual). - Outputs:
messageId,status(sent/failed). - Notes:
freeformOrTemplatesmart-send picks freeform inside the 24h window, else a Meta template. Respects the WhatsApp 24-hour customer-service window.
Send Media¶
Sends an image, video, audio, or document.
- Key config:
mediaType(image | video | audio | document),mediaUrl(URL or{{var}}),caption. - Outputs:
messageId. - Notes: provider media URLs expire — prefer re-hosted/Storage URLs.
Quick Reply¶
Sends a prompt with tappable buttons.
- Key config:
message,buttons[](title,payload), and optionally Collect into variable (collectAsVariable,variableName,saveMode=index | title | payload). Per-language localized message + button labels are supported. - Outputs: the chosen button as
{variableName}(collect mode), orselectedValue/selectedLabel; one output handle per button otherwise. - Notes: WhatsApp Cloud and the Channel Simulator render ≤3 as buttons, 4–10 as a list, more as numbered text. Other channels use numbered text. Pauses the flow. See Multilingual.
Collect Input¶
Prompts the user and waits for a validated reply.
- Key config:
inputType(text | number | email | phone | date | choice),prompt,variableName. - Outputs:
{variableName}(validated/coerced). - Notes: Pauses the flow until the user replies.
Logic nodes¶
If Condition¶
Branches on one or more comparisons.
- Key config:
field(or{{var}}),conditions[]of{ operator, value },combinator=and | or. - Operators: equals, not equals, contains, does not contain, starts with, ends with, regex,
>,>=,<,<=, is empty, is not empty. - Outputs:
true/falsehandle.
Switch¶
Multi-way branch — one handle per case + default.
- Key config:
inputVariable(e.g.{{intent}}or{{lang}}),cases[]of{ value, label }. - Outputs: the matched case, or
default.
Loop¶
Iterates over an array, or repeats a fixed / variable number of times.
- Modes:
array(default) orcount. - Array mode:
arrayPathto a session array (e.g.api_result.body.items); body runs per item. - Count mode:
countis a literal (5) or a variable ({{guest_count}}/guest_count). Body runs N times with{{index}}/{{item}}as0..N-1by default (capped at 100). Enable 1-based index to use1..Ninstead. - 1-based index: optional toggle;
{{index}}(and count-mode{{item}}) become 1-based. Array path placeholders{index}still use the internal 0-based cursor. - Outputs:
loop(body) anddone(after the last iteration). Body exits auto-advance — no back-edge required.
Delay¶
Waits before continuing.
- Key config: duration (seconds/minutes/hours/days).
Date & Time¶
Branches on whether now is inside configured business-hours windows.
- Key config:
timezone(IANA),businessHours[]of{ day, startTime, endTime }. - Outputs: In hours / Out of hours. An empty schedule always takes In hours.
Merge¶
Waits for multiple branches and combines them.
- Key config:
inputCount(2–6),mode=combineAll | combineByPosition | append. - Outputs:
mergedpayload with fields from every branch. Use after a fan-out so downstream sees all results together.
Jump To¶
Redirects execution to another node (loops, skip-ahead, reusable sections).
- Key config:
targetNodeId. - Notes: transparent at deploy time (compiles away); cycle-guarded. Great for converging language/branch paths back to a shared tail.
Trigger Flow (Sub-flow)¶
Starts another published flow for the same contact.
- Key config:
targetFlowId,passVariables(default on),waitForCompletion. - Notes: build reusable sub-flows (e.g. a Booking flow) once and call them from many parents.
Error Handler¶
Catches errors from upstream nodes.
- Key config:
fallbackMessage. - Outputs:
errorMessage, and anerror/successbranch.
End Flow¶
Terminates the flow. Optional final message.
AI & RAG nodes¶
All LLM nodes need an LLM API key. Enter it in the node, or rely on your organization's key (Settings → AI Provider). An AI node with no resolvable key blocks activation — the deploy fails with a clear message instead of silently killing the trigger. Google's AI Response node can run key-in-URL, but the AI Agent node needs a real credential.
AI Response¶
Calls an LLM with a system prompt + user input, returns text.
- Key config:
llmProvider(openai | google | anthropic | groq | deepseek | mistral | ollama),model,prompt(supports{{vars}}),userInput(e.g.{{message.text}}),temperature. - Google Gemini models: Gemini 3.1 Pro, Gemini 3.5 Flash, Gemini 2.5 Pro, Gemini 2.5 Flash.
- Outputs:
response,tokensUsed.
AI Agent¶
A LangChain tools-agent that autonomously picks tools to answer.
- Key config:
llmProvider/llmModel,systemPrompt,temperature,memoryEnabled+memoryWindowSize(1–50), Timezone (for dates), and tools:databaseQuery,updateContact,createContact,createDeal,addTag,httpRequest(give the HTTP tool a base URL + description), andscheduler(appointment booking — see below). - Outputs:
agentResponse. - Appointment Scheduler tool: enable Scheduler and pick a Calendar (event type) — without a calendar the tool is not deployed. It gives the agent five real tools the model can call by name:
get_slots,create_booking,get_bookings,reschedule_booking,cancel_booking(each a separate tool so the model doesn't have to reason about anoperationargument). To reschedule/cancel, the agent passes abooking_id(fromget_bookings) or the patient's phone/email — the system acts on their single upcoming booking, or returns the list to disambiguate. - Database tool: when enabled it deploys as separate tools per allowed operation —
get_records, and (if allowed)create_record/update_records/delete_records. Reads are paginated and trimmed to keep the agent's context small (it can request specificcolumnsand apage); writes require a filter to scope them. - Models: Google Gemini options include Gemini 3.1 Pro and Gemini 3.5 Flash (plus 2.5 Pro/Flash), alongside OpenAI, Anthropic, Groq, DeepSeek, Mistral, and Ollama.
- Current date awareness: the agent is automatically told the current date/time at run time (see Dates & the current time), so it resolves "tomorrow at 3 PM" / "next Monday" correctly and outputs ISO 8601. Set Timezone to your local IANA zone (e.g.
Africa/Cairo). - Notes: its system prompt interpolates
{{variables}}. Keeps per-conversation chat memory. Tools call back into ConnectGain securely.
AI Agent v2¶
An agent that runs a short tool loop, then answers in a fixed JSON shape you define. Use it when the reply must carry structured fields (a route number, a chosen product, collected customer details) that later nodes switch on. It runs on Google Gemini (default) or Anthropic Claude.
- Key config: AI provider + model, System prompt, Temperature, Memory + window size (1–50), Cache system prompt, Max iterations (tool-loop turns per message), Per-tool budget / turn (how many times any one tool may be called in a single message), Tools after first call (below), Output variable (default
agent_response), and a Structured output schema (JSON Schema the reply must match). Tools are switched on per node. - Outputs: the parsed JSON in the output variable —
{{agent_response.customer_text}},{{agent_response.route}}, and any field your schema names. - Tools after first call: Hide after first success (recommended) — once a tool has returned something in this message, the agent writes its answer on the next step instead of searching again. That is one AI call for the lookup and one for the reply. Keep available every turn lets the agent call tools again in the same message; expect roughly one extra AI call per customer message, and a plainer fallback reply when it runs out of budget without answering. Leave it on Hide unless a flow genuinely needs several different lookups for one message.
- Known-state position: where the fields the agent has already collected (name, phone, chosen product…) are shown to the model each turn. Before the conversation (default) is unchanged behaviour. After the conversation (cache-friendly) lets the earlier turns of a long conversation be cached by Gemini instead of billed again on every reply — same answers, fewer tokens on conversations of more than a few messages.
- Stored history: what the agent remembers of its own earlier replies. Full reply (default) keeps everything, including the structured fields behind each reply. Customer text only (smaller) keeps just the text the customer saw; the collected fields are carried separately, so nothing is forgotten and each later turn is cheaper.
- Anthropic Claude: pick Anthropic Claude as the provider and choose Claude Opus 5.5 (default), Claude Sonnet 5.5 or Claude Haiku 5.5. Claude runs on your own Anthropic account, so paste your Anthropic API key into the node — it is required, stored encrypted, and never shown again (leave the field blank later to keep it). Temperature does not apply to Claude. The Structured output schema is enforced on Claude too; the dialog tells you if yours uses something Claude can't enforce (for example an object that accepts any keys), in which case Claude is only instructed to follow it. Tools that use Gemini behind the scenes (Smart Query, semantic product search) still use your organization's Gemini key; without one, product search falls back to keyword matching. Gemini nodes are unaffected by this option.
- Notes: the system prompt interpolates
{{variables}}. Keeps per-conversation chat memory. Every call is metered on the AI usage & cost page, Claude calls included.
Classify Intent¶
AI intent detection — usually feeding a Switch.
- Key config: the input text + the candidate intents.
- Outputs: the detected intent (switch on
{{intent}}).
Image Compare¶
Recognises the product in a photo the customer sends and tells you whether you sell it. Usually placed after a message trigger, with a reply on each branch.
- How it works: the AI looks at the photo and identifies the product, finds the closest products in your Products catalog, then compares the photo with those products' own catalog photos to make sure it is the same item (not just the same kind of item). Only active products available on the conversation's channel are considered.
- Branches: Available (the product is in your catalog) · Not available (not in your catalog, not a product photo, or — by default — out of stock) · No image (the customer didn't send a readable image) · Error (the AI could not run, for example no Gemini key in Settings → AI Provider). Connect at least Available and Not available; the builder warns if either is empty.
- Key config: Image source (blank = the image in the customer's current message, or a
{{variable}}holding an image URL), Save result as (defaultimage_match), Minimum confidence (default 70%), Out of stock counts as not available (default on), Compare with catalog photos (default on — more precise, one extra AI call), and an optional AI model. - Outputs:
{{image_match.outcome}},{{image_match.available}},{{image_match.product.name}}(also.price,.currency,.sku,.image_url),{{image_match.confidence}}, and what the AI saw in{{image_match.detected.name}}. A reply such as "Yes, {{image_match.product.name}} is available for {{image_match.product.price}} {{image_match.product.currency}}" works on the Available branch. - Notes: it does not suggest alternative products. Products without a photo are still matched, on name and description only. Uses your organization's Gemini key and is metered on the AI usage & cost page.
RAG System¶
Full Retrieval-Augmented Generation: agent + embeddings + vector retriever + an upload pipeline for your knowledge files.
- Key config: LLM + embedding providers/models, knowledge base, uploaded files (ingested to the vector store on deploy).
- Notes: zero-hallucination answering grounded in your documents.
Voice Agent¶
Opens a Gemini Live voice call. Realtime audio needs a channel that can hold a WebSocket (web widget, simulator); messaging channels send the fallback text instead.
- Key config:
agentId,prompt,voice. - Outputs:
transcript,summary.
Data nodes¶
Set Variable / Get Variable¶
Store / read a value in flow- or global-scoped state.
- Set:
variableName,value(supports{{vars}}),scope(flow | global). - Get:
variableName→ value into context.
Transform Data¶
Runs a JS expression and stores the result.
- Key config:
inputVariable,expression(e.g.input.toUpperCase),outputVariable.
HTTP Request¶
Calls an external API and stores the response.
- Key config:
url(supports{{vars}}),method,headers(JSON),body(JSON),responseVariable. - Outputs:
{responseVariable}.status,{responseVariable}.body.
Database Query¶
Runs a parameterised query against an allow-listed table and stores rows.
- Key config: table/operation,
filters/data(JSON, support{{vars}}),columns,limit(≤500),page(0-based pagination),outputVariable. - Pagination: reads return
pageandhas_more(plusnext_pagewhen there's more); passpageto fetch the next batch of rows. - Allow-listed tables:
contacts, deals, tasks, conversations, messages, bot_sessions, companies, notes, products. - Scoping to the current customer:
{{contact_id}}and{{conversation_id}}are always available insidefilters,searchanddataand mean the contact and conversation the flow is running for — e.g.{"contact_id": "{{contact_id}}"}reads only this customer's rows, and{"conversation_id": "{{conversation_id}}", "direction": "OUTBOUND"}only what was already sent in this chat. Use them to check "did this contact already get X?" before sending it again. If your flow sets its own variable with one of these names, yours wins.
Cart¶
Keeps a shopping cart for the customer while they chat, so they can pick several products before checking out.
- Actions: Add item, Set quantity (0 removes the item), Remove item, Clear cart, and View, which only recalculates the totals.
- Key config:
Cart variable(defaultcart), plusProduct id(or a product name as fallback) andQuantity, both of which accept{{vars}}. Products are looked up in your catalog. Under the advanced settings you can point it at another table and choose which columns hold the id, name, price and currency. - Outputs: after every action the node updates
{{cart}}(the items),{{cart_count}}(how many items in total),{{cart_total}}(price × quantity, summed),{{cart_currency}}and{{cart_text}}, a numbered list you can drop straight into a Send Message. If you rename the cart variable, for example tofood_cart, these become{{food_cart_count}},{{food_cart_total}}and so on. Give two carts different names and you can run them side by side in one flow. - Branches:
ok,empty(the action worked and the cart is now empty),not_found(the product isn't in your catalog or the cart),mixed_currency(the product's currency differs from what is already in the cart),invalid_qty,error.
Memory¶
Per-session / per-contact / org key-value memory, or CRUD on allow-listed tables.
- Key config:
mode(memory | database); memory mode:operation(set/get/append/delete/clear),scope(session/contact/org),key,value,ttlSeconds; database mode mirrors Database Query. - Outputs:
{outputVariable},success.
Google Sheets¶
Appends a row to a Google Sheet.
- Key config: spreadsheet id or URL + tab name, ordered cell values (each supports
{{var}}). - Notes: uses the organization's connected Google account (reconnect Google once to grant Sheets access).
CRM & conversation nodes¶
Update Contact¶
Sets fields on the current contact. Key config: fields.
Add Tag¶
Adds or removes a tag on the contact. Key config: tags, mode = add | remove (supports {{vars}}).
Update Lifecycle¶
Moves the contact to a lifecycle stage and fires the Lifecycle Updated event. Key config: stageId. Stages are defined in Settings → Lifecycle Stages.
Create Deal / Update Deal¶
Create a deal (title, value, stageId) → dealId; or update an existing deal (dealId, fields).
- Line items: optional
products[]of{ name, quantity, price }(support{{vars}}); deal value is computed automatically when left blank. - Attachments (optional): attach files to the new deal. Enter a file URL, a comma/newline-separated list of URLs, or a
{{variable}}captured upstream (string, array, or{url}objects). Externalhttps://URLs are allowed; only local or private-network addresses are blocked. Downloaded files land in the deal's Attachments (same place as the manual upload button). Max 10 files, 50 MB each. - Attach customer-sent media: tick "Attach media the customer sent" to automatically pull images/files the customer sent in this conversation (choose how many recent inbound media messages, 1–10) — no variable wiring needed.
Scheduler¶
Books appointments against your calendars (event types) from Scheduling. Pick an Operation and a Calendar (or override the calendar with a {{variable}}), and save the result to a variable.
- Operations: Get Available Slots (
get_slots), Get Bookings by phone/email (get_bookings), Create Booking (create_booking), Reschedule Booking (reschedule_booking), Cancel Booking (cancel_booking). - Get Available Slots options: Days ahead to search (1–120); Window — Next available days (default; the earliest 3 days that actually have openings), Today, Tomorrow, or Nearest (soonest); a Specific date (
YYYY-MM-DDor a{{variable}}) for one day's full list; and a Page number to fetch more when the result reportshas_more. Slots come back as compact start-times (withtotal_available/showingcounts) to keep replies short. - Create Booking config:
guestName,guestPhone,guestEmail,startDateTime(ISO, supports{{selected_slot}}), optional note, timezone. - Reschedule / Cancel: pass a Booking ID (from Get Bookings, e.g.
{{contact_bookings.0.id}}) or fall back to the contact's phone/email — it acts only when there is a single upcoming booking. Reschedule takes a new start time; cancel takes an optional reason. - Outputs: the chosen
outputVariable(defaults:available_slots,contact_bookings,new_booking,rescheduled_booking,cancelled_booking). - Tip: to let the AI resolve "tomorrow at 3 PM" into a real slot, use the AI Agent with its Scheduler tool instead of hard-wiring dates (see AI Agent).
Create Task / Update Task / Auto Assign Task¶
- Create Task — create a task (
title,assigneeId?,dueDate?) →taskId. - Update Task — patch an existing task (title, description, priority, completed, due date, assignee). Defaults Task ID to
{{task.id}}, so a flow triggered bytask.created/task.updated/task.completed/task.overduecan update that same task without looking it up. You can also point at a UUID or an upstream Create Task variable. For description, choose Replace (default) or Append to keep the existing text and add the new content after it. - Auto Assign Task — auto-assign by availability/round-robin (only agents whose profile allows auto-assigned tasks).
Assign Conversation¶
Assigns the conversation to an agent (specific, round-robin, or availability-based; only agents whose profile allows auto-assigned conversations).
Add Comment¶
Adds an internal note to the contact's activity timeline (not sent to the customer). Supports {{vars}}.
Close Conversation / Open Conversation¶
Mark the conversation closed, or reopen it (e.g. close after resolution; reopen when the customer replies).
Handoff to Agent¶
Transfers the conversation to a human, preserves context, and stops the bot for that conversation so the bot won't talk over the agent.
After handoff decides what happens when the customer writes again:
- Wait for a human agent (default) — the bot stays off until someone on your team resumes it from the inbox (the bot icon on the chat). Use this whenever the customer asked for a person, or an agent needs to finish the job.
- Let the bot resume on the customer's next message — the bot picks the flow up again as soon as the customer replies, even while an agent is assigned. Use it only for side-effect escalations where the bot stays the main channel and the agent is looped in to help in the background (for example, a specialist notified about a booking while the bot keeps answering).
Multilingual flows¶
One flow can answer each customer in their own language. The short version:
- Capture the language into a
langsession variable (a Quick Reply language-picker with Save = payload, or a Set Variable from{{contact.language}}). - Author localized Quick Reply bodies + button labels (per-language editor in the node). Default content is the fallback.
- For template sends, use Send Message
templateNameBase+localizedBodies. - Need different paths per language (not just text)? Switch on
{{lang}}and converge with Jump To.
Full step-by-step, data shapes, and fallback rules: Building Multilingual Bot Flows.
Deploying, activating & publishing¶
A flow's status is Draft (in development), Published (active and running), or Archived (disabled).
Your plan's flow allowance counts Published flows only. Draft and Archived flows are always free, so there's no limit on how many ideas you sketch out — the cap only applies when you publish. If publishing is refused because you're at your limit, unpublish or archive a flow you no longer need running to free a slot, or contact support to raise the limit.
- Add a Trigger node and save the flow (deploy refuses without a trigger).
- Click Deploy / Publish. ConnectGain first runs a pre-publish check (see below), then compiles the graph into a runnable workflow, (re)creates it, and activates it.
- On success the flow is Published and its webhook is live; on failure the toast shows the reason.
Pre-publish check. Clicking Publish scans the flow and, if it finds anything, shows a panel listing issues in plain language — a Problem (missing/duplicate trigger, disconnected node) or a Heads-up (AI node with no API key, Scheduler tool without a calendar, empty message/prompt/buttons, or a {{variable}} no step produces). Each item has a fix hint and a Show button that jumps to the node on the canvas. The check is advisory — you can always Publish anyway, so it never blocks a flow that would have published before.
What activation means: the flow engine only serves a flow's production webhook while the workflow is active. If a node is invalid (classically, an AI node with no LLM key), activation is refused — and the deploy surfaces that error and leaves the flow Draft instead of pretending success. Fix the named node and redeploy.
Undeploy takes the workflow offline and returns the flow to Draft.
Testing & debugging¶
- Sample-webhook tester — fire a representative event into the flow without a real customer message.
- Run log — every run is recorded node by node (inputs, outputs, errors), so a misbehaving flow can be traced step by step.
- Quick activation check — POST to the flow's webhook URL:
404 → workflow inactive (activation failed); 200 → active.
- Session inspection — bot_sessions holds the live variables and current_node_id for a conversation; useful when a pause/resume misbehaves.
Flow analytics¶
Track how flows perform in aggregate and per node.
Flow performance: execution count, success rate, average completion time, drop-off points, and user-engagement metrics.
Node analytics: per-node execution count, success/failure rates, average processing time, and bottleneck identification.
API integration¶
Flows are built, published and managed in the Bot Builder; there is no public API for creating them. To pause or resume a bot on a live conversation programmatically, use the Bot Control API. A flow's Webhook trigger and its HTTP Request node are the ways to connect a flow to your own systems — see Triggers and the node reference.
End-to-end scenarios¶
A. Welcome + route¶
Trigger message.received (channel all) → Send "Hi {{contact.first_name}}! How can we help?" → Quick Reply [Sales, Support, Hours] (collect into topic) → Switch on {{topic}} → Sales path / Support path / Date & Time (hours) path.
B. FAQ deflection with auto-close¶
Trigger → Classify Intent → Switch on {{intent}} → Send the matching answer → Quick Reply "Did that help? Yes/No" (collect resolved) → If resolved contains "yes" → Add Comment "Auto-resolved" → Close Conversation; else Handoff to Agent.
C. Lead capture → deal¶
Trigger → Collect Input name → Collect Input email → Update Contact (fields from the answers) → Create Deal ("New web lead", stage = New) → Google Sheets append row → Send confirmation.
D. Business-hours routing¶
Trigger → Date & Time (Mon–Fri 09:00–17:00, Africa/Cairo): In hours → Assign Conversation + "An agent will be right with you"; Out of hours → "We're closed, we'll reply at 9 AM" → Collect Input (their question) → Add Comment.
E. Multilingual support bot¶
Trigger → Quick Reply language picker (save payload → lang) → localized Quick Reply menu (one node, per-language body + buttons) → Switch on the choice → localized answers. See Multilingual.
F. Campaign reply engagement¶
Trigger Broadcast Response → Add Tag "Campaign Engaged" → Send "Thanks for replying to {{campaign_name}}!" → Quick Reply "Book a demo? Yes/No" → on Yes, Trigger Flow → Booking sub-flow.
G. Post-call follow-up¶
Trigger Call Ended → Send "Thanks for the call!" → Add Comment "Follow-up sent after {{duration_seconds}}s" → Create Task "Send proposal" (due +2 days).
H. Lifecycle onboarding¶
Trigger Lifecycle Updated → If lifecycle_stage equals "Customer" → Send onboarding checklist → Create Task "Onboarding call".
I. AI knowledge agent¶
Trigger → RAG System (your docs) answers grounded questions; fall back to Handoff to Agent when confidence is low or the user asks for a human.
J. Agent one-click playbook¶
Trigger Manual Shortcut → Send pricing pack → Add Tag "Pricing Sent" → Update Lifecycle → Opportunity. Launch it from the inbox ⚡ Shortcuts menu.
More ready-to-build recipes: Bot Flow Automation Recipes.
Best practices¶
Channel Simulators¶
Flows → Simulator is a sandboxed WhatsApp-style channel for testing flows without connecting a real number. An organization can run as many simulators as it needs, but one is usually enough: each test conversation runs exactly one bot flow, chosen when you start it, so several flows — and several testers — can share one simulator without disturbing each other.
- Pick the simulator from the dropdown in the page header; the ⋮ menu creates, renames, and deletes them. Deleting a simulator also deletes its test conversations.
- New test conversation asks which flow the conversation runs. That choice belongs to the conversation, not the simulator: conversation A can run one flow while conversation B, in the same simulator, runs another, and starting a new conversation never changes which bot answers in an existing one. The list shows each conversation's flow, and the banner above the chat names the flow of the one that is open.
- The chosen flow runs regardless of its own trigger channel pins — choosing it is the intent, so there is no need to pin a flow's trigger node to the simulator just to test it.
- Subflows need no setup. A Trigger Flow node dispatches its target by ID, and a
webhook.receivedflow can still resume inside the conversation that started it, from the customer's next reply. - A flow may carry several trigger nodes (a webhook entry point for subflow dispatch plus a message entry point for customers). It runs on whichever of them matches the inbound — a flow that can start from a message does, even when its webhook trigger comes first in the flow.
- A Gemini Live voice call started from a conversation runs that conversation's flow only, so the call button appears only when that flow has a Voice call requested trigger.
- Only published (deployed) flows can reply; drafts are listed but marked as not yet testable.
- Test in Simulator in the Flow Builder opens the simulator with a new conversation already running that flow.
- Conversations started before this behaviour keep answering with the flow that first ran in them. One that never ran a flow follows the simulator's older settings: the flow attached to it, or, with none attached, every published flow that targets the simulator channel. Start a new conversation to test one flow on its own.
Public simulator links¶
Owners and admins can select a Channel Simulator conversation and choose Create Share Link. The URL opens an interactive, no-login test chat branded with the organization name.
Each browser receives its own isolated simulator conversation: visitors cannot see one another's messages or the admin's source transcript. Public sessions allow conversational replies only; CRM changes, bookings, knowledge-base reads, database actions, subflow dispatch, and external sends are blocked. Links are revocable from the same dialog.
- Always offer an exit to a human (Handoff) on any branch a bot can't resolve.
- Keep flows focused. Extract reusable pieces into sub-flows and call them with Trigger Flow.
- Name variables clearly (
rating_call,lead_score) and prefer collecting into variables over wide button fan-outs. - Use Jump To to converge branch/language tails instead of duplicating nodes.
- Pin the channel/account on triggers when a flow is meant for one number, so it doesn't fire elsewhere.
- Test after every deploy — confirm the workflow activated (toast or curl check) before relying on it.
- Localize content, branch only on flow shape — most multilingual needs are content, handled in-node.
AI usage & cost (Gemini)¶
Owners and admins can open Bot Flows → AI Usage (/flows/ai-usage) to see estimated Gemini token usage and cost per flow for the current month.
- Metered today: Google/Gemini AI Response, AI Agent, and RAG System nodes, plus trigger voice transcription (STT).
- Costs are estimates from published model rates applied to actual Gemini
usageMetadata(input, output, and thinking tokens). After redeploying Google AI Agent/RAG flows, every LLM call is metered viagemini-meter(including tool-loop steps). If the org uses BYOK Gemini, Google bills your Google account — ConnectGain still shows the estimate for visibility. - After metering was enabled on the platform, redeploy (re-publish) each flow that uses those Google AI nodes. Older deployments will not appear on the dashboard.
Troubleshooting¶
| Symptom | Likely cause / fix |
|---|---|
| AI Usage dashboard stays empty | Flow still on an old deployment that calls Gemini directly. Re-publish the flow, then send a test message through an AI Response (Google) node. |
| Flow never runs for a message | Workflow not active. The trigger is a channel POST to the flow's webhook, which is only served while the flow is active. Redeploy and read the toast; curl the webhook (404 = inactive). |
| "Deploy failed: AI Agent node is missing the LLM API key…" | Enter the LLM key in the AI Agent node (or set the platform key). Google AI Agent needs a real credential, not key-in-URL. |
| Deploy reports success but bot never replies | The flow may be Draft after a failed activation — check status and the deploy toast for the blocking node. |
| Flow re-greets instead of resuming | The user's reply didn't match any awaiting button — check button titles/payloads, or use Collect-into-variable. |
{{var}} shows literally in a message |
The variable wasn't set upstream (missing tokens are left as-is by design). Verify the producing node ran and the name matches. |
| Wrong language sent | Ensure a lang/language variable is set before the localized node; missing/blank translations fall back to Default. |
| Bot talks over a human agent | A Handoff stops the bot; ensure your handoff path runs before further bot messages, and keep After handoff on Wait for a human agent unless the bot really should take over again. |
| Bot restarts after a handoff while an agent is chatting | The Handoff node is set to Let the bot resume on the customer's next message. Switch it to Wait for a human agent and publish the flow again. |
See also¶
- Building Multilingual Bot Flows — per-language content
- Bot Flow Automation Recipes — ready-to-build use cases
- Automations · Templates · Sequences
- Inbox · Complete API Reference · Bot Control API
- ConnectGain AI chatbot overview · Bot Flows · AI Agent — feature overviews on the ConnectGain website
ConnectGain — omnichannel inbox, CRM & automation for WhatsApp, Messenger, Instagram, Telegram and more. Open the app · Docs home