> ## Documentation Index
> Fetch the complete documentation index at: https://documents.xobito.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Bot Flow Nodes Reference

> Every node available in the Xobito Bot Flow builder — what it does, every parameter, every limit, and a JSON example.

This page is the per-node reference for the [Bot Flow builder](/automations/bot-flows). 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.

<Note>
  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.
</Note>

<Tip>
  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.
</Tip>

## Graph shape

Every flow is stored as a single JSON object with two keys — `nodes` and `edges` — following Vue Flow's native format:

```json theme={null}
{
  "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_type`        | string | yes         | `lead` or `customer`                                                             |
| `reply_type`      | string | yes         | `1`, `2`, `3`, or `4`                                                            |
| `trigger`         | string | conditional | Comma-separated keywords. Required for reply types 1 and 2; ignored for 3 and 4. |
| `reply_type_text` | string | no          | Human-readable label — display only, not used in matching.                       |

### The four reply types

| ID  | Meaning                                                                    |
| --- | -------------------------------------------------------------------------- |
| `1` | On exact match — incoming message equals a keyword                         |
| `2` | When message contains — incoming message contains any keyword as substring |
| `3` | On first message — contact is messaging your business for the first time   |
| `4` | Fallback — fires when no other bot caught this message                     |

### Example

```json theme={null}
{
  "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](/automations/bot-flows)

***

## 2. Text Message

* **Internal type:** `textMessage`
* **Purpose:** send a plain text reply.
* **Output handles:** 1.

### Parameters

| Field        | Type   | Required | Limit               |
| ------------ | ------ | -------- | ------------------- |
| `reply_text` | string | yes      | Max 1000 characters |

Merge fields (`{contact_first_name}` etc.) are supported inside `reply_text`.

### Example

```json theme={null}
{
  "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

| Field        | Type   | Required                | Limit                       |
| ------------ | ------ | ----------------------- | --------------------------- |
| `reply_text` | string | yes                     | Body of the message         |
| `button1`    | string | at least 1 of the three | Max 20 characters           |
| `button2`    | string | optional                | Max 20 characters           |
| `button3`    | string | optional                | Max 20 characters           |
| `bot_header` | string | optional                | Short header above the body |
| `bot_footer` | string | optional                | Short footer below the body |

### Example

```json theme={null}
{
  "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:

```json theme={null}
[
  { "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                                                           |
| ------------ | ------ | -------- | --------------------------------------------------------------- |
| `header`     | string | optional | Max 60 characters                                               |
| `valueText`  | string | yes      | Body. Max 1000 characters                                       |
| `buttonText` | string | yes      | Button label. Max 20 characters                                 |
| `buttonLink` | string | yes      | A valid URL, or a `{variable}` placeholder that resolves to one |
| `footer`     | string | optional | Short footer                                                    |

### Example

```json theme={null}
{
  "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_text`                       | string | yes      | Body of the message      |
| `buttonText`                       | string | yes      | Label on the list button |
| `sections`                         | array  | yes      | Max 10 sections          |
| `sections[i].title`                | string | yes      | Section heading          |
| `sections[i].items`                | array  | yes      | Max 10 items per section |
| `sections[i].items[j].id`          | string | yes      | Internal ID for the item |
| `sections[i].items[j].title`       | string | yes      | Item title               |
| `sections[i].items[j].description` | string | optional | Item description         |
| `bot_header`                       | string | optional | Header above the body    |
| `bot_footer`                       | string | optional | Footer below the body    |

<Warning>
  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.
</Warning>

### Example

```json theme={null}
{
  "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

| Field            | Type   | Required | Values                                                       |
| ---------------- | ------ | -------- | ------------------------------------------------------------ |
| `media_type`     | string | yes      | `image`, `video`, `audio`, `document`, or `sticker`          |
| `media_url`      | string | yes      | Public URL to the file                                       |
| `media_caption`  | string | optional | Caption — ignored for some media types (e.g. audio, sticker) |
| `media_filename` | string | optional | Suggested filename (useful for documents)                    |

### Example — document

```json theme={null}
{
  "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

```json theme={null}
{
  "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_latitude`  | number | yes      | Between -90 and 90   |
| `location_longitude` | number | yes      | Between -180 and 180 |
| `location_name`      | string | yes      | Max 100 characters   |
| `location_address`   | string | optional | Any length           |

### Example

```json theme={null}
{
  "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

| Field                   | Type   | Required | Notes                                |
| ----------------------- | ------ | -------- | ------------------------------------ |
| `contacts`              | array  | yes      | At least one entry                   |
| `contacts[i].firstName` | string | yes      |                                      |
| `contacts[i].lastName`  | string | optional |                                      |
| `contacts[i].phone`     | string | yes      | Phone number in international format |
| `contacts[i].email`     | string | optional |                                      |
| `contacts[i].company`   | string | optional |                                      |
| `contacts[i].title`     | string | optional | Job title                            |

### Example

```json theme={null}
{
  "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                                                        |
| ---------------- | ------ | -------- | ---------------------------------------------------------------------- |
| `requestUrl`     | string | yes      | Absolute URL, relative URL, or a `{variable}` placeholder              |
| `requestMethod`  | string | yes      | `GET`, `POST`, `PUT`, `PATCH`, or `DELETE`                             |
| `requestFormat`  | string | yes      | `JSON` or `Form`                                                       |
| `requestHeaders` | array  | optional | Array of `{name, value, isCustom}` objects. Common headers are preset. |
| `requestBody`    | array  | optional | Array of `{key, value}` pairs                                          |

<Warning>
  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.
</Warning>

<Warning>
  API keys and tokens you put into `requestHeaders` are stored inside the flow's JSON in your database. Treat the flow config as sensitive.
</Warning>

### Example — POST JSON

```json theme={null}
{
  "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_assistant` | string | yes      | The ID of a pre-configured AI assistant — chosen from a dropdown of the assistants defined on the workspace |

### Example

```json theme={null}
{
  "id": "10",
  "type": "aiAssistant",
  "position": { "x": 250, "y": 1240 },
  "data": {
    "output": [{
      "personal_assistant": "5"
    }]
  }
}
```

<Note>
  If the AI Assistant module is disabled, the builder offers **9** action nodes instead of 10 — everything above except this one.
</Note>

***

# 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](/automations/template-bots) 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               | `trigger`         | Always the first node                      |
| 2  | Text Message          | `textMessage`     |                                            |
| 3  | Button Message        | `buttonMessage`   | Branches the flow                          |
| 4  | Call To Action        | `callToAction`    | URL button                                 |
| 5  | List Message          | `listMessage`     | Max 10 items total                         |
| 6  | Media Message         | `mediaMessage`    | image / video / audio / document / sticker |
| 7  | Location              | `locationMessage` |                                            |
| 8  | Contact Card          | `contactMessage`  | One or more vCards                         |
| 9  | API Request           | `webhookApi`      | Fire-and-forget                            |
| 10 | AI Personal Assistant | `aiAssistant`     | Only if AI module enabled                  |
| —  | Condition Node        | `conditionNode`   | Internal, not in builder                   |
| —  | Template Message      | `templateMessage` | Internal, not in builder                   |
| —  | Reaction Message      | `reactionMessage` | Internal, not in builder                   |

## What to read next

<CardGroup cols={2}>
  <Card title="Bot Flows overview" icon="diagram-project" href="/automations/bot-flows">
    How flows run, how triggers work, and how to activate them.
  </Card>

  <Card title="Automations overview" icon="sliders" href="/automations/overview">
    Compare Bot Flows to Message Bots and Template Bots.
  </Card>
</CardGroup>
