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

# Conversation flow (under the hood)

> The node graph the agent runs on: global node, conversation stages, pathways, variable extraction and webhooks. The flow canvas is hidden for now - you configure everything in the agent editor.

<Note>
  **The flow canvas is currently hidden in the app.** The **Workflow** tab in
  the agent editor is not shown on any plan - you configure agents entirely in
  the [agent editor](/en/voice-agent/agent-editor). This page explains what
  happens underneath, and where the settings that used to live on the canvas
  are configured today.
</Note>

## What the flow is

The flow (workflow) is a graph of connected nodes representing the stages
of a conversation. Pathways (edges) determine when the agent moves between
stages - transitions are chosen by the LLM based on how the conversation
unfolds.

The graph still runs: AI generation and the industry templates build it for
you, and edits made in the agent editor are written straight into it. You
don't have to look at it for the agent to work.

## Node types and where they are configured today

| Node | What it does | Where you set it today |
| - | - | - |
| **Global Node** | A shared prompt attached to every stage: company context, tone, objection handling, rules. One per agent. | **Role** tab → system prompt |
| **Start Call** | Starts the conversation and defines the greeting. One per agent. | **Role** tab → first message and "who speaks first" |
| **Agent node** | A conversation stage with its own prompt; outgoing edges are pathways the LLM can choose. | Describe the stages in the system prompt; templates create them for you |
| **End Call** | The last utterance right before hanging up. One per agent. | **Settings** tab → **End Call Phrases**, plus the **End Call** tool |
| **Webhook** | Sends a JSON payload to a given address after the call. | **Integrations** tab → **Post-call Webhook** or **Custom Webhook** |

The toggles that used to sit on the nodes are now in the agent editor's
**Settings** tab: **Voicemail Detection** (formerly "Detect Voicemail" on
Start Call) and **Interruption Sensitivity** (formerly "Allow Interruption").
The global prompt no longer has a toggle - the system prompt from the
**Role** tab reaches every stage.

## Variable extraction

The agent can extract structured data from the conversation (first name,
budget, preferred date). You define it in the **Variables** tab, in the
**Data collected by the assistant** card - name, type and description. It
lands in `gathered_context` and is available in webhooks, SMS and
integrations. Details: [Template variables](/en/voice-agent/template-variables).

## The post-call webhook payload

The **Post-call Webhook** integration sends a fixed `call.completed` event
after every finished call (JSON, signed with an `X-Brosbots-Signature`
header):

```json theme={null}
{
  "event": "call.completed",
  "sent_at": "2026-09-19T10:15:00+00:00",
  "data": {
    "workflow_run_id": 12345,
    "workflow_id": 42,
    "workflow_name": "Clinic reception",
    "duration_seconds": 184,
    "initial_context": { "first_name": "Anna" },
    "gathered_context": { "rsvp": "yes" },
    "recording_url": "recordings/12345.wav",
    "transcript_url": "transcripts/12345.txt",
    "recording_download_url": "https://...",
    "transcript_download_url": "https://..."
  }
}
```

| Field | Meaning |
| - | - |
| `duration_seconds` | Call length in seconds |
| `recording_download_url` / `transcript_download_url` | HTTPS links to the recording (WAV) and transcript (text). They don't expire and need no API key; they stop working when you delete the call or a retention policy removes the file. `null` for chat conversations |
| `recording_url` / `transcript_url` | Storage keys of the files, **not URLs**. Kept for existing integrations; download through the `*_download_url` fields |

The webhook goes out right after the call, often before the recording is
saved. The link works anyway: opened a few seconds later, it leads to the
file. Anyone holding the link can listen to the recording - treat it as
personal data.

### SMS replies (`sms.received`)

The same integration can also receive replies to SMS your agent sent. Enter
`sms.received` in the **Events** step of the webhook wizard (or the
integration's **Events** field in the catalog). Without it the endpoint
only receives `call.completed`, as before.

```json theme={null}
{
  "event": "sms.received",
  "sent_at": "2026-10-01T10:15:03+00:00",
  "data": {
    "sms_id": 981,
    "from_number": "+48600100200",
    "to_number": "+48510000777",
    "text": "Yes, I confirm the appointment",
    "workflow_id": 42,
    "workflow_run_id": 12345,
    "received_at": "2026-10-01T10:15:01+00:00"
  }
}
```

`workflow_run_id` and `workflow_id` are set when the reply answers an SMS sent
during or after a call (within the last 7 days). Otherwise they are `null`.

### Other events

These also have to be switched on: in the wizard's **Events** step or in the
**Events** field (comma-separated), e.g. `sms.received, campaign.completed`.
Setting up a webhook, picking agents and checking the signature:
[Webhooks](/en/integrations/webhooks).

| Event | When | Main `data` fields |
| - | - | - |
| `call.started` | a call begins (phone, widget, test) | `workflow_run_id`, `workflow_id`, `workflow_name`, `direction`, `phone_number`, `channel`, `campaign_id`, `started_at` |
| `campaign.completed` | a campaign has called every contact | a summary (`contacts`, `calls`, `answered`, `answer_rate`, `talk_minutes`, `avg_duration_seconds`, `by_disposition`) and `rows`: one per contact (`phone_number`, `name`, `disposition`, `duration_seconds`, `attempts`, `last_called_at`, `workflow_run_id`, `collected`) |
| `balance.low` | the balance reaches the warning level or goes below zero | `balance`, `in_debt`, `minutes_left` (`null` in the daily warning) |
| `lead.dropped` | a lead the agent did not call (no credit, busy channels, a limit) | `lead_id`, `workflow_id`, `workflow_name`, `source`, `phone_number`, `name`, `status`, `reason`, `attempts`, `created_at` |

`call.started` is sent without waiting for your endpoint, so it never delays
the greeting. `rows` in `campaign.completed` holds at most 2000 rows
(`rows_truncated: true` when there are more); the full report is on the
campaign page as CSV and at `GET /api/v1/campaigns/{id}/report`.

<Note>
  Agents created earlier may still carry a **Webhook node** in their graph with
  its own templated payload (`{{workflow_run_id}}`, `{{initial_context.*}}`,
  `{{gathered_context.*}}`, `{{recording_url}}`, `{{transcript_url}}`,
  `{{cost_info.call_duration_seconds}}`; `{{recording_url}}` and
  `{{transcript_url}}` are storage keys, not links). Those nodes still fire, but with the
  canvas hidden you cannot add new ones or edit existing ones - set up new
  webhooks under **Integrations**.
</Note>

## Pathways and tool calls

Transitions between nodes are implemented as tool calls made by the LLM -
a pathway description works like a tool description. That is why the tool
descriptions you write in the **Tools** tab should be clear and actionable:
they are what the LLM decides on. The word **"tool"** is reserved and should
not appear in your own tool names.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.