This page is the per-node reference for the Bot Flow builder . Each entry lists the node’s purpose, its full parameter set, any validation rules, and a minimal JSON example of how the node looks inside a flow’s stored flow_data graph.
There are 10 node types usable inside the builder: one Trigger plus nine action nodes. A further three node types exist in the source but are not exposed in the builder today — they’re documented at the end of this page for completeness.
You rarely need to look at the raw JSON. Xobito’s builder writes it for you. We include examples so that if you ever inspect a flow’s stored graph — for backup, for support, or for an import — you know exactly what you’re looking at.
Graph shape
Every flow is stored as a single JSON object with two keys — nodes and edges — following Vue Flow’s native format:
{
"nodes" : [
{
"id" : "1" ,
"type" : "trigger" ,
"position" : { "x" : 250 , "y" : 0 },
"data" : { "output" : [{ "reply_type_text" : "" , "reply_type" : "" , "rel_type" : "" , "trigger" : "" }] },
"label" : "Start Trigger" ,
"initialized" : true
}
],
"edges" : [
{
"id" : "e1-2" ,
"source" : "1" ,
"target" : "2" ,
"sourceHandle" : null ,
"animated" : true ,
"type" : "button"
}
]
}
Each node has an id (string), a type (one of the names below), a position, and a data.output array that holds its properties.
Each edge links a node’s output handle to the next node’s input. A Button Message has three output handles — button-0, button-1, button-2 — referenced by sourceHandle.
Active nodes
1. Trigger
Internal type: trigger
Purpose: the flow’s entry point. Every flow has exactly one Trigger, and it’s always the first node. You cannot delete it; you configure its rules to decide when the flow runs.
Output handles: 1 — the Trigger can connect to exactly one next node.
Parameters
Field Type Required Values / limits rel_typestring yes lead or customerreply_typestring yes 1, 2, 3, or 4triggerstring conditional Comma-separated keywords. Required for reply types 1 and 2; ignored for 3 and 4. reply_type_textstring no Human-readable label — display only, not used in matching.
The four reply types
ID Meaning 1On exact match — incoming message equals a keyword 2When message contains — incoming message contains any keyword as substring 3On first message — contact is messaging your business for the first time 4Fallback — fires when no other bot caught this message
Example
{
"id" : "1" ,
"type" : "trigger" ,
"position" : { "x" : 250 , "y" : 0 },
"data" : {
"output" : [{
"rel_type" : "customer" ,
"reply_type" : "2" ,
"reply_type_text" : "when message contains" ,
"trigger" : "hi,hello,hey"
}]
},
"label" : "Start Trigger" ,
"initialized" : true
}
Back to overview
2. Text Message
Internal type: textMessage
Purpose: send a plain text reply.
Output handles: 1.
Parameters
Field Type Required Limit reply_textstring yes Max 1000 characters
Merge fields ({contact_first_name} etc.) are supported inside reply_text.
Example
{
"id" : "2" ,
"type" : "textMessage" ,
"position" : { "x" : 250 , "y" : 120 },
"data" : {
"output" : [{
"reply_text" : "Hi {contact_first_name}! Thanks for reaching out."
}]
}
}
Internal type: buttonMessage
Purpose: send a text message with up to three quick-reply buttons. This is how flows branch — each button is a separate output handle.
Output handles: up to 3 — button-0, button-1, button-2.
Parameters
Field Type Required Limit reply_textstring yes Body of the message button1string at least 1 of the three Max 20 characters button2string optional Max 20 characters button3string optional Max 20 characters bot_headerstring optional Short header above the body bot_footerstring optional Short footer below the body
Example
{
"id" : "3" ,
"type" : "buttonMessage" ,
"position" : { "x" : 250 , "y" : 260 },
"data" : {
"output" : [{
"reply_text" : "How can we help today?" ,
"button1" : "Pricing" ,
"button2" : "Support" ,
"button3" : "Hours" ,
"bot_header" : "Menu" ,
"bot_footer" : "Tap one of the options below"
}]
}
}
Branching
Connect an edge from each button handle to a different downstream node:
[
{ "id" : "e3-4" , "source" : "3" , "sourceHandle" : "button-0" , "target" : "4" },
{ "id" : "e3-5" , "source" : "3" , "sourceHandle" : "button-1" , "target" : "5" },
{ "id" : "e3-6" , "source" : "3" , "sourceHandle" : "button-2" , "target" : "6" }
]
4. Call To Action
Internal type: callToAction
Purpose: send a message with a single URL button — a button that opens a web link when tapped.
Output handles: 1.
Parameters
Field Type Required Limit headerstring optional Max 60 characters valueTextstring yes Body. Max 1000 characters buttonTextstring yes Button label. Max 20 characters buttonLinkstring yes A valid URL, or a {variable} placeholder that resolves to one footerstring optional Short footer
Example
{
"id" : "4" ,
"type" : "callToAction" ,
"position" : { "x" : 250 , "y" : 400 },
"data" : {
"output" : [{
"header" : "Your invoice" ,
"valueText" : "Tap below to download your latest invoice." ,
"buttonText" : "Download" ,
"buttonLink" : "https://example.com/invoice/{contact_id}" ,
"footer" : "Xobito Billing"
}]
}
}
5. List Message
Internal type: listMessage
Purpose: send a WhatsApp list picker. Customers tap the list button, see grouped sections, and pick one item.
Output handles: 1.
Parameters
Field Type Required Limit reply_textstring yes Body of the message buttonTextstring yes Label on the list button sectionsarray yes Max 10 sections sections[i].titlestring yes Section heading sections[i].itemsarray yes Max 10 items per section sections[i].items[j].idstring yes Internal ID for the item sections[i].items[j].titlestring yes Item title sections[i].items[j].descriptionstring optional Item description bot_headerstring optional Header above the body bot_footerstring optional Footer below the body
WhatsApp limits the total items across all sections to 10, not 10 per section. If you have three sections you can spread 10 items across them however you like.
Example
{
"id" : "5" ,
"type" : "listMessage" ,
"position" : { "x" : 250 , "y" : 540 },
"data" : {
"output" : [{
"reply_text" : "Pick a topic" ,
"buttonText" : "Topics" ,
"bot_header" : "Help center" ,
"bot_footer" : "Choose one" ,
"sections" : [
{
"title" : "Billing" ,
"items" : [
{ "id" : "bill_invoice" , "title" : "Invoice" , "description" : "Download latest invoice" },
{ "id" : "bill_change" , "title" : "Change plan" , "description" : "Upgrade or downgrade" }
]
},
{
"title" : "Support" ,
"items" : [
{ "id" : "sup_tech" , "title" : "Technical issue" },
{ "id" : "sup_account" , "title" : "Account help" }
]
}
]
}]
}
}
Internal type: mediaMessage
Purpose: send an image, video, audio clip, document, or sticker.
Output handles: 1.
Parameters
Field Type Required Values media_typestring yes image, video, audio, document, or stickermedia_urlstring yes Public URL to the file media_captionstring optional Caption — ignored for some media types (e.g. audio, sticker) media_filenamestring optional Suggested filename (useful for documents)
Example — document
{
"id" : "6" ,
"type" : "mediaMessage" ,
"position" : { "x" : 250 , "y" : 680 },
"data" : {
"output" : [{
"media_type" : "document" ,
"media_url" : "https://example.com/files/guide.pdf" ,
"media_caption" : "Your setup guide" ,
"media_filename" : "xobito-setup.pdf"
}]
}
}
Example — image
{
"data" : {
"output" : [{
"media_type" : "image" ,
"media_url" : "https://example.com/images/welcome.jpg" ,
"media_caption" : "Welcome aboard, {contact_first_name}!"
}]
}
}
7. Location
Internal type: locationMessage
Purpose: send a location pin the customer can tap to open in their maps app.
Output handles: 1.
Parameters
Field Type Required Limit location_latitudenumber yes Between -90 and 90 location_longitudenumber yes Between -180 and 180 location_namestring yes Max 100 characters location_addressstring optional Any length
Example
{
"id" : "7" ,
"type" : "locationMessage" ,
"position" : { "x" : 250 , "y" : 820 },
"data" : {
"output" : [{
"location_latitude" : 19.076 ,
"location_longitude" : 72.8777 ,
"location_name" : "Our office" ,
"location_address" : "Bandra Kurla Complex, Mumbai, India"
}]
}
}
Internal type: contactMessage
Purpose: send one or more vCard contacts the customer can save directly to their phone.
Output handles: 1.
Parameters
Field Type Required Notes contactsarray yes At least one entry contacts[i].firstNamestring yes contacts[i].lastNamestring optional contacts[i].phonestring yes Phone number in international format contacts[i].emailstring optional contacts[i].companystring optional contacts[i].titlestring optional Job title
Example
{
"id" : "8" ,
"type" : "contactMessage" ,
"position" : { "x" : 250 , "y" : 960 },
"data" : {
"output" : [{
"contacts" : [
{
"firstName" : "Sales" ,
"lastName" : "Team" ,
"phone" : "+441234567890" ,
"email" : "sales@xobito.com" ,
"company" : "Xobito" ,
"title" : "Sales"
}
]
}]
}
}
9. API Request
Internal type: webhookApi
Purpose: call an external HTTP endpoint mid-flow. Use it to push flow context to your own system, or fetch data to log against the contact.
Output handles: 1.
Parameters
Field Type Required Values / limits requestUrlstring yes Absolute URL, relative URL, or a {variable} placeholder requestMethodstring yes GET, POST, PUT, PATCH, or DELETErequestFormatstring yes JSON or FormrequestHeadersarray optional Array of {name, value, isCustom} objects. Common headers are preset. requestBodyarray optional Array of {key, value} pairs
The response from the API is not currently piped back into the flow as a variable — the request is fire-and-forget from the flow’s point of view. Use API Request to send data out; use your own system to act on it.
API keys and tokens you put into requestHeaders are stored inside the flow’s JSON in your database. Treat the flow config as sensitive.
Example — POST JSON
{
"id" : "9" ,
"type" : "webhookApi" ,
"position" : { "x" : 250 , "y" : 1100 },
"data" : {
"output" : [{
"requestUrl" : "https://api.example.com/v1/tickets" ,
"requestMethod" : "POST" ,
"requestFormat" : "JSON" ,
"requestHeaders" : [
{ "name" : "Content-Type" , "value" : "application/json" , "isCustom" : false },
{ "name" : "Authorization" , "value" : "Bearer sk_live_xxx" , "isCustom" : true }
],
"requestBody" : [
{ "key" : "contact_id" , "value" : "{contact_id}" },
{ "key" : "phone" , "value" : "{contact_phone_number}" },
{ "key" : "source" , "value" : "whatsapp" }
]
}]
}
}
10. AI Personal Assistant
Internal type: aiAssistant
Purpose: hand the conversation over to an AI assistant you’ve configured in the AI Assistant module. From this point, the assistant takes over the conversation until a human intervenes.
Output handles: 1.
Availability: only appears in the builder when the AI Assistant module is enabled on the workspace.
Parameters
Field Type Required Values personal_assistantstring yes The ID of a pre-configured AI assistant — chosen from a dropdown of the assistants defined on the workspace
Example
{
"id" : "10" ,
"type" : "aiAssistant" ,
"position" : { "x" : 250 , "y" : 1240 },
"data" : {
"output" : [{
"personal_assistant" : "5"
}]
}
}
If the AI Assistant module is disabled, the builder offers 9 action nodes instead of 10 — everything above except this one.
Internal / future nodes
These node types exist in the source code but are not currently exposed in the builder menu . They’re documented here because you may see them referenced in older flows or during support sessions. Don’t rely on them in new flows — they may change shape before they ship.
Condition Node
Internal type: conditionNode
Purpose: evaluate a condition (contains, equals, starts with, ends with, or regex) and branch accordingly.
Status: source exists but not wired into the builder . Branching today is done with Button Message handles.
Template Message
Internal type: templateMessage
Purpose: send an approved WhatsApp template from inside a flow (which would allow a flow to respond outside the 24-hour session window).
Status: source exists but not in the builder menu. Use a Template Bot instead for now.
Reaction Message
Internal type: reactionMessage
Purpose: react to the customer’s last message with an emoji.
Status: source exists but not in the builder menu.
Node type summary
# Node Internal type Notes 1 Trigger triggerAlways the first node 2 Text Message textMessage3 Button Message buttonMessageBranches the flow 4 Call To Action callToActionURL button 5 List Message listMessageMax 10 items total 6 Media Message mediaMessageimage / video / audio / document / sticker 7 Location locationMessage8 Contact Card contactMessageOne or more vCards 9 API Request webhookApiFire-and-forget 10 AI Personal Assistant aiAssistantOnly if AI module enabled — Condition Node conditionNodeInternal, not in builder — Template Message templateMessageInternal, not in builder — Reaction Message reactionMessageInternal, not in builder
What to read next
Bot Flows overview How flows run, how triggers work, and how to activate them.
Automations overview Compare Bot Flows to Message Bots and Template Bots.