> This page is for Cloud Call Center.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.hams.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.hams.ai/_mcp/server.

# Call flows

> Build what happens to a call from the moment it arrives to the moment it ends.

A call flow is a diagram of the path of a call. For example, play a greeting, offer a menu, send the caller to a queue, and fall back to voicemail. You draw the flow on a canvas. You do not write rules.

Only administrators can open call flows. Go to **Call Flows** at `/call-center/call-flows`.

![The call flow builder with the IVR menu node selected: its prompt, the keys a caller can press, and where each key leads.](/_fern-img/0334fd85513e961736a6bdb073d3b20ffcd42f10e0bbeb2412728649da9041dc.webp)

## The call flow list

The list shows every call flow on the account. Each row shows the name, direction, status, who created the flow, and who last edited it. The list still names a member who has left the account.

Search by name, or filter by status. Open a flow to edit it. The row menu has **Publish**, **Stop**, **Resume**, and **Delete**.

![The call flow list: the status, the last editor, and the creator of each flow, and the row menu with Stop on a published flow.](/_fern-img/ff22d19777ad9dbfdfe92ce8d4ca75f28617949b189e680a1aeeca9413f8624d.webp)

## Build a call flow

Drag nodes from the palette onto the canvas. Connect the nodes. Set each node in the side panel. The palette has 3 categories: **Interaction**, **Routing**, and **Flow control**.

Every flow starts at one **New call** trigger. The system answers the call when a node needs it. A node that plays audio or collects digits answers the call when it runs. A routing node answers the call when the far side connects.

A flow control node, such as **HTTP request**, does not answer the call. To answer the call at a point that you choose, add an **Answer** node. See [Answer](#answer).

Each node has one connector for each outcome it can end with. The flow follows the connector that matches what happened. If an outcome has no connector, the call ends.

To copy a node with its settings, select the node. Copy and paste it. To pan, drag the empty canvas. To zoom, scroll. To add a node right after a connector, click the connector.

## Draft, published, and stopped

Every flow has a draft and a published version. You edit the draft. The published version answers calls.

An edit changes the draft only. The builder marks the flow as **Unpublished changes**. Use the **Draft** and **Published** toggle in the builder to look at either version.

**Publish** checks the draft and copies it over the published version. The change applies to the next call that arrives. A call that is already in the old version finishes in the old version.

You cannot undo a publish. To go back, publish the earlier draft again. Keep a copy of the draft that you want to return to.

**Stop** takes a published flow out of service. Calls to its DIDs arrive as if the DID has no flow. The fallback of the DID handles those calls. The system keeps the published version.

**Resume** puts a stopped flow back in service. You do not need to publish again. If you publish a stopped flow, the flow also resumes.

A new flow is a blank draft with nothing published. If you point a DID at the flow before the first publish, calls arrive and nothing runs. Publish the flow first.

### What publish checks

The builder marks each unfinished item. Publish fails until you fix every marked item. These are the most common failures:

* A node has an empty setting that it needs. Examples are a menu with no sound, a dial node with no extension, or a queue node with no queue.
* A node uses a sound that is not ready yet.
* A branch has no conditions, or a condition has no variable, operator, or value.
* A **Jump** node targets another **Jump** node, or its **Max loops** value is not from 1 to 10.
* A node refers to a queue, extension, trunk, sound, or voice agent that you deleted.
* A `{{ }}` placeholder or a condition uses a `call.*` variable that does not exist.
* A **Matches regex** condition has a pattern that the system does not support. See [Branch](#branch).

> **Note**
>
> Publish refuses a flow that uses an unknown `call.*` variable. The draft accepts the variable, and publish then reports it.

## Variables

A flow keeps state in 2 namespaces. Use `{{ }}` placeholders to read a value in a setting, for example `{{ call.caller_number }}`.

* `call.*` holds read-only facts about the call.
* `vars.*` holds values that the flow writes. An HTTP request node, a Collect digits node, an IVR menu node, and a Set variable node write to `vars.*`.

A variable name starts with a letter or an underscore. It contains only letters, digits, and underscores. The system uses the prefix `$` for its own variables.

### Call variables

A call that is in progress has some of these values only. For example, `ended_at` and `outcome` are empty until the call ends. An after-call sequence sees the final values. See [After-call sequences](/cloud-call-center/call-flows-after-call).

In an after-call sequence, `call.outcome` is the view of the flow. For a call that entered a queue, it can differ from call history. The flow does not know what the queue did.

| Variable                  | Type          | When it has a value                     | Meaning                                                                                                    |
| ------------------------- | ------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `call.id`                 | text          | Always                                  | The ID of the call. It is the same ID that the API and webhooks use.                                       |
| `call.flow_id`            | text          | Always                                  | The ID of the call flow that runs.                                                                         |
| `call.direction`          | text          | Always                                  | Always `inbound`. Every call that runs a flow enters through a DID.                                        |
| `call.caller_number`      | text          | Always                                  | The number that called.                                                                                    |
| `call.did`                | text          | Always                                  | The DID that the caller dialed.                                                                            |
| `call.domain`             | text          | Always                                  | The SIP domain of the flow.                                                                                |
| `call.outcome`            | text          | After the call ends                     | The call outcome: `answered`, `no_answer`, `busy`, `abandoned`, `canceled`, `failed`, or `voicemail`.      |
| `call.answered`           | true or false | Always                                  | `true` when a person or a voice agent answered the call.                                                   |
| `call.status`             | text          | After the call ends                     | The state of the flow run. You can read it in a placeholder. The builder does not offer it as a condition. |
| `call.started_at`         | time          | Always                                  | When the call started, in ISO 8601 format.                                                                 |
| `call.answered_at`        | time          | After the call is answered              | When someone answered the call.                                                                            |
| `call.ended_at`           | time          | After the call ends                     | When the call ended.                                                                                       |
| `call.duration_seconds`   | number        | Always                                  | The seconds since the call started. For a live call, the count runs to the present.                        |
| `call.talk_seconds`       | number        | After the call is answered              | The seconds since someone answered the call.                                                               |
| `call.agent.extension`    | text          | After an agent connects                 | The extension of the agent on the call.                                                                    |
| `call.agent.name`         | text          | After an agent connects                 | The name of the agent.                                                                                     |
| `call.agent.user_id`      | text          | After an agent connects                 | The ID of the agent user in Cloud Call Center.                                                             |
| `call.agent.omni_user_id` | number        | After an agent connects                 | The ID of the same agent in Omni Channel.                                                                  |
| `call.last_node`          | object        | After the first node ends               | The last node that ended. It has `id`, `name`, and `type`. For example, `call.last_node.name`.             |
| `call.last_outcome`       | text          | After the first node ends               | The outcome of the last node.                                                                              |
| `call.voicemail_left`     | true or false | Always                                  | `true` when the caller left a voicemail message.                                                           |
| `call.transfer_count`     | number        | Always                                  | How many times the call went through a transfer.                                                           |
| `call.queue_id`           | text          | After the call enters a queue           | The ID of the queue that took the call last.                                                               |
| `call.hangup_by`          | text          | Always                                  | `caller` when the caller ended the call. Otherwise `flow`. During a live call, the value is `flow`.        |
| `call.redirected_from`    | text          | When a trusted trunk reports a redirect | The number that redirected the call to you. Empty when the call was not redirected.                        |
| `call.redirect_reason`    | text          | When a trusted trunk reports a redirect | Why the call was redirected. See [Redirected calls](#redirected-calls).                                    |

The call outcome `canceled` means that the call ended before it connected. The outcome `answered` covers a call that a voice agent handled.

### Redirected calls

A carrier or PBX can forward a call to you. The trunk reports the number that forwarded the call and the reason. The trunk reports them only when **Trust identity headers** is on. See [Trunks](/cloud-call-center/settings/trunks#trust-identity-headers).

`call.redirected_from` holds the number that forwarded the call. `call.redirect_reason` holds one of these values:

| Value            | Builder label        |
| ---------------- | -------------------- |
| `user-busy`      | **Busy**             |
| `no-answer`      | **No answer**        |
| `unavailable`    | **Unavailable**      |
| `unconditional`  | **Always forwarded** |
| `time-of-day`    | **Time of day**      |
| `do-not-disturb` | **Do not disturb**   |
| `deflection`     | **Deflected**        |
| `follow-me`      | **Follow me**        |
| `out-of-service` | **Out of service**   |
| `away`           | **Away**             |
| `unknown`        | **Unknown**          |

If the carrier sends a reason that is not in this list, the value is `unknown`. The builder offers these values in a list when you branch on `call.redirect_reason`.

> **Note**
>
> A condition compares a boolean variable to the lowercase text `true` or `false`. The builder offers these values in a list. A value such as `True` never matches.

## Node reference

### Interaction

| Node                      | What it does                                                                                                  |
| ------------------------- | ------------------------------------------------------------------------------------------------------------- |
| **Answer**                | Answers the call and does nothing else.                                                                       |
| **Play sound**            | Plays a sound or prompt to the caller.                                                                        |
| **IVR menu**              | Plays a prompt. The caller presses a digit, and the flow branches on the digit.                               |
| **Collect digits**        | Collects digits from the caller, for example an account number.                                               |
| **Voicemail**             | Sends the caller to the shared mailbox of the account to leave a message.                                     |
| **Send WhatsApp message** | Sends a WhatsApp message to the caller. See [After-call sequences](/cloud-call-center/call-flows-after-call). |

### Routing

| Node                    | What it does                                                                           |
| ----------------------- | -------------------------------------------------------------------------------------- |
| **Dial extension**      | Calls one extension and connects the caller when it answers.                           |
| **Ring group**          | Rings 1 to 10 agents and connects the first agent that answers.                        |
| **Queue**               | Puts the caller in a queue to wait for an agent.                                       |
| **External trunk call** | Sends the call out on a trunk to another PBX, an outside number, or an AI voice agent. |
| **AI voice agent**      | Hands the call to an AI voice agent.                                                   |

### Flow control

| Node                         | What it does                                                                   |
| ---------------------------- | ------------------------------------------------------------------------------ |
| **Business hours**           | Branches on whether the call arrives inside a weekly schedule.                 |
| **Branch**                   | Branches on conditions that you define.                                        |
| **Distribute by percentage** | Splits calls across branches by percentage.                                    |
| **Jump**                     | Continues at an earlier node. **Max loops** sets how many times, from 1 to 10. |
| **HTTP request**             | Calls an external API during the call and saves values from the response.      |
| **Set variable**             | Writes values into variables that later nodes can read.                        |
| **Hang up**                  | Ends the call.                                                                 |

The nodes **Wait** and **Run workflow** run only in an after-call sequence. The node **Answer** runs only during the call.

### Answer

The node answers the call. The node has no settings. It has 1 outcome, **Done**. The canvas summary of the node is **Pick up the call**.

Use the node when a flow control node comes first and the caller must be on the line. For example, answer the call, and then call an API with an **HTTP request** node. Without the **Answer** node, the call stays unanswered while those nodes run.

![A call flow that answers the call and then calls an API.](/_fern-img/b05b00b82046511657cb82e4cdcb798b1474fdbd56c449e699f5faf66b99df5f.webp)

### Play sound

Plays 1 sound to the caller. The node has 1 outcome, **Done**. The sound must be ready before you can publish.

### IVR menu

The menu plays its prompt and waits for 1 key. Each key that you add becomes a connector, for example **Press 1** and **Press 2**. A key with no connector ends the call.

The node has 2 more connectors for other cases. **No input** applies when the caller presses no key before the timeout. The default timeout is 6 seconds. **Invalid key** applies when the caller presses a key that you did not add.

Set **Retries on silence** to replay the prompt that number of times before the flow takes **No input**. To keep the pressed key, set **Save digit to**, for example `menu_choice`. A later Branch node can use `{{ vars.menu_choice }}`.

### Collect digits

The node collects a string of digits into a variable that you name, for example `account_number`. Read the value later as `{{ vars.account_number }}`.

Set the minimum and maximum digits (1 to 32). Set the wait for the first digit (1 to 60 seconds). Set the wait between digits (1 to 30 seconds). You can also set a finish key. The finish key ends the entry early, and the system does not save it.

Only the keys `0` to `9` count as digits. The entry ends at the maximum, at the finish key, or when the wait between digits ends.

If the caller types too few digits or no digit, the node spends 1 retry (0 to 3) and replays the prompt. When the retries end, the flow takes **Failed**. Otherwise the flow takes **Success**.

The node checks the length only. It does not check the shape of the value. To check that an account number is real, connect **Success** to an HTTP request node that looks up the number in your own system. Then branch on the answer.

Turn on **Treat as sensitive** for a PIN or a card number. Call history then hides the value. The next nodes still receive the real digits.

### Branch

The node tests its branches from top to bottom. The call takes the first branch whose conditions match. If no branch matches, the call takes **Else**. A branch uses **Match all conditions** or **Match any condition**. A condition has a variable, an operator, and a value. The operator list has 11 operators. One of them is **Matches regex**.

**Matches regex** tests the value of the variable against a regular expression. The system uses the RE2 syntax. A pattern matches when it finds a match anywhere in the value. Use `^` and `$` to match the whole value.

These rules apply to **Matches regex**:

* A condition can have up to 10 patterns. The condition matches when any pattern matches.
* A pattern can have up to 256 characters.
* The system tests only the first 1024 characters of the value.
* `\d`, `\w`, `\s`, and `\b` match ASCII characters only.
* `$` matches only at the end of the value, not before a final line break.
* The system does not support a backreference or a lookaround.

Publish fails when a pattern breaks one of these rules. The builder shows that the pattern is not a supported regular expression.

### Distribute by percentage

Splits calls across branches by whole-number percentage. Use it to test 2 flows against each other or to share load.

![The Distribute by percentage node open: the VIP and general split and the remainder branch.](/_fern-img/f34b68f84e9acd1d3280ef4a2dec2a82a6788bcfe2ba6fc1990e9257fdc05104.webp)

Click **Add branch** and give each branch a name and a percentage from 1 to 100. The total of all branches must not be more than 100. The **Remainder** branch gets the rest of the calls. The canvas shows the percentage on each branch.

The system picks the branch for each call with a fixed calculation on the call. If the node runs again for the same call, the call takes the same branch.

### Voicemail

Sends the caller to the shared mailbox of the account. The node has no settings. The outcomes are **Done** and **Failed**. A caller who leaves a message has usually hung up when the node ends, so the builder hides both connectors. You can show a connector when you need it.

To send a call to the mailbox of one agent, set **Offer voicemail if unanswered** on a **Dial extension** node.

### HTTP request

The node calls a URL that you choose during the call. The caller hears nothing while the request runs. Use an endpoint that answers fast.

Choose the method. Add headers and a JSON body. Choose an authentication mode: **None**, **Bearer token**, or **Basic auth**. The token and the body can use variables. Basic credentials cannot use variables.

The body goes out as `application/json`. To send a different type, add a `Content-Type` header.

Map values from the JSON response into variables by path. You can also save the HTTP status code in a variable. A `2xx` reply takes **Success**. Any other reply, a timeout, or an unreachable host takes **Failure**. The node does not retry.

Later nodes read the saved variables as `{{ vars.name }}`. They can also read the call variables, for example `{{ call.id }}`.

The node sends an `Idempotency-Key` header on every request. If you set your own `Idempotency-Key` header, the node keeps yours. The system can repeat a request. Your endpoint can use the key to drop the repeat.

Each request shows on the timeline of the call as a **Web request** event. The event name in the API is `http.request`. The event shows the method, the URL, the headers, the request body, the response body, the status, and the duration. The system cuts bodies at 4 KB and masks secrets.

### External trunk call

The node sends the call out on one of your trunks to a destination that you enter. Set a ring timeout of 5 to 120 seconds. Add up to 10 custom SIP headers. The node branches on how the call ends: **Answered**, **No answer**, **Busy**, or **Failed**.

A fifth outcome, **Transfer not recognized**, applies when the far end asks to transfer the call to a destination that the account does not know. The destination is not one of your extensions and not one of your queues.

The flow then takes that branch. The number that the far end asked for is in `{{ vars.$transfer_target }}`. You can play a message, look up the number, or dial it on another trunk.

### AI voice agent

The node picks one of your voice agents as the destination. The agent talks to the caller. The node has 2 outcomes: **Completed** and **Failed**.

The flow takes **Completed** when the conversation ends. The flow takes **Failed** when the agent does not exist or does not answer. It also takes **Failed** when **Answer calls with this agent** is off for the agent.

A transfer is not an outcome. When the agent transfers the caller, the flow stops and the call goes to the queue or the extension that the agent chose. See [Voice agents](/cloud-call-center/voice-agents).

## After-call sequences

A call flow can have a second part that runs after the caller hangs up. Use it to send a WhatsApp message, wait, call an API, or start a workflow. See [After-call sequences](/cloud-call-center/call-flows-after-call).

## Related

* [After-call sequences](/cloud-call-center/call-flows-after-call) explains what a flow does after the call ends.
* [Voice agents](/cloud-call-center/voice-agents) explains how to give an AI agent a phone voice.
* [DIDs](/cloud-call-center/settings/dids) explains how to point a number at a flow.
* [Queues](/cloud-call-center/settings/queues) explains queue behavior, members, and hold music.
* [Trunks](/cloud-call-center/settings/trunks) explains how a trunk reports the number that redirected a call.
* [Sounds](/cloud-call-center/settings/sounds) explains where the prompts for the **Play sound** node come from.
* [Call history and recordings](/cloud-call-center/call-history) explains how to see the timeline of a call.