Skip to main content
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

FieldTypeRequiredValues / limits
rel_typestringyeslead or customer
reply_typestringyes1, 2, 3, or 4
triggerstringconditionalComma-separated keywords. Required for reply types 1 and 2; ignored for 3 and 4.
reply_type_textstringnoHuman-readable label — display only, not used in matching.

The four reply types

IDMeaning
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

FieldTypeRequiredLimit
reply_textstringyesMax 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."
    }]
  }
}

3. Button Message

  • 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

FieldTypeRequiredLimit
reply_textstringyesBody of the message
button1stringat least 1 of the threeMax 20 characters
button2stringoptionalMax 20 characters
button3stringoptionalMax 20 characters
bot_headerstringoptionalShort header above the body
bot_footerstringoptionalShort 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

FieldTypeRequiredLimit
headerstringoptionalMax 60 characters
valueTextstringyesBody. Max 1000 characters
buttonTextstringyesButton label. Max 20 characters
buttonLinkstringyesA valid URL, or a {variable} placeholder that resolves to one
footerstringoptionalShort 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

FieldTypeRequiredLimit
reply_textstringyesBody of the message
buttonTextstringyesLabel on the list button
sectionsarrayyesMax 10 sections
sections[i].titlestringyesSection heading
sections[i].itemsarrayyesMax 10 items per section
sections[i].items[j].idstringyesInternal ID for the item
sections[i].items[j].titlestringyesItem title
sections[i].items[j].descriptionstringoptionalItem description
bot_headerstringoptionalHeader above the body
bot_footerstringoptionalFooter 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" }
          ]
        }
      ]
    }]
  }
}

6. Media Message

  • Internal type: mediaMessage
  • Purpose: send an image, video, audio clip, document, or sticker.
  • Output handles: 1.

Parameters

FieldTypeRequiredValues
media_typestringyesimage, video, audio, document, or sticker
media_urlstringyesPublic URL to the file
media_captionstringoptionalCaption — ignored for some media types (e.g. audio, sticker)
media_filenamestringoptionalSuggested 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

FieldTypeRequiredLimit
location_latitudenumberyesBetween -90 and 90
location_longitudenumberyesBetween -180 and 180
location_namestringyesMax 100 characters
location_addressstringoptionalAny 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"
    }]
  }
}

8. Contact Card

  • Internal type: contactMessage
  • Purpose: send one or more vCard contacts the customer can save directly to their phone.
  • Output handles: 1.

Parameters

FieldTypeRequiredNotes
contactsarrayyesAt least one entry
contacts[i].firstNamestringyes
contacts[i].lastNamestringoptional
contacts[i].phonestringyesPhone number in international format
contacts[i].emailstringoptional
contacts[i].companystringoptional
contacts[i].titlestringoptionalJob 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

FieldTypeRequiredValues / limits
requestUrlstringyesAbsolute URL, relative URL, or a {variable} placeholder
requestMethodstringyesGET, POST, PUT, PATCH, or DELETE
requestFormatstringyesJSON or Form
requestHeadersarrayoptionalArray of {name, value, isCustom} objects. Common headers are preset.
requestBodyarrayoptionalArray 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

FieldTypeRequiredValues
personal_assistantstringyesThe 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

#NodeInternal typeNotes
1TriggertriggerAlways the first node
2Text MessagetextMessage
3Button MessagebuttonMessageBranches the flow
4Call To ActioncallToActionURL button
5List MessagelistMessageMax 10 items total
6Media MessagemediaMessageimage / video / audio / document / sticker
7LocationlocationMessage
8Contact CardcontactMessageOne or more vCards
9API RequestwebhookApiFire-and-forget
10AI Personal AssistantaiAssistantOnly if AI module enabled
Condition NodeconditionNodeInternal, not in builder
Template MessagetemplateMessageInternal, not in builder
Reaction MessagereactionMessageInternal, not in builder

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.