Node Types
Workflows are built from 14 node types. Each performs a specific function and can pass data to downstream nodes via shared variables.
trigger
The entry point for every workflow. At least one trigger node is required.
| Field | Description |
|---|---|
triggerType | Currently only message_received is supported |
When a message arrives, the trigger node sets the following built-in variables and starts the workflow:
| Variable | Value |
|---|---|
message | The user’s message text |
user_id | Platform-specific user identifier |
user_name | User’s display name (if available) |
channel_id | Platform-specific channel identifier |
platform | telegram, discord, whatsapp, slack, or custom |
{
"id": "trigger_1",
"type": "trigger",
"data": {
"triggerType": "message_received",
"label": "On Message"
}
}llm_call
Sends a prompt to the bot’s configured LLM and stores the response.
| Field | Required | Description |
|---|---|---|
prompt | Yes | The prompt to send. Supports {{variable}} interpolation. |
outputVariable | No | Variable name to store the LLM response. |
label | No | Display label for the node. |
The response object contains text, tokensInput, and tokensOutput. Access the response text downstream with {{outputVariable.text}}.
{
"id": "classify_1",
"type": "llm_call",
"data": {
"prompt": "Classify this customer message into exactly one category: billing, refund, other.\n\nMessage: {{message}}\n\nRespond with only the category name.",
"outputVariable": "intent",
"label": "Classify Intent"
}
}LLM call nodes use the bot’s configured provider and model. Temperature is set to 0.3 for more deterministic outputs.
Use case: Intent classification, data extraction, response generation, summarization, translation.
logic
Conditional branching. Routes execution to different downstream nodes based on conditions.
if_else
Evaluates a condition and follows the true or false edge.
{
"id": "check_1",
"type": "logic",
"data": {
"logicType": "if_else",
"condition": "{{intent.text}} == billing",
"label": "Is Billing?"
}
}Connect edges with sourceHandle:
{ "source": "check_1", "target": "billing_node", "sourceHandle": "true" },
{ "source": "check_1", "target": "other_node", "sourceHandle": "false" }switch
Matches a variable against multiple cases.
{
"id": "switch_1",
"type": "logic",
"data": {
"logicType": "switch",
"variable": "intent",
"cases": ["billing", "support", "general"],
"label": "Route by Intent"
}
}Connect edges with sourceHandle: "case_billing", sourceHandle: "case_support", etc. Unmatched values follow sourceHandle: "case_default".
wait
Pauses execution until the next user message. Currently a placeholder for future multi-turn workflow support.
See Variables & Conditions for the full condition syntax reference.
mcp_tool
Calls any MCP tool from the bot’s workspace.
| Field | Required | Description |
|---|---|---|
toolName | Yes | Fully qualified tool name (e.g., stripe__get_payment) |
arguments | No | JSON string with {{variable}} interpolation. Default: {} |
outputVariable | No | Variable name to store the tool result. |
The result is truncated to 3000 characters and stored in outputVariable.
{
"id": "lookup_1",
"type": "mcp_tool",
"data": {
"toolName": "orders__get_order",
"arguments": "{\"order_id\": \"{{order_id}}\"}",
"outputVariable": "order_data",
"label": "Look Up Order"
}
}If the tool name is not found in the workspace, or if the arguments JSON is malformed, the node returns an error object in the output variable rather than throwing an exception. Always check the result before using it in downstream nodes.
Use case: Query databases, process payments, create tickets, search knowledge bases, call any external API exposed through an MCP server.
send_message
Sends a message to the user.
| Field | Required | Description |
|---|---|---|
message | No | Message text with {{variable}} interpolation. If omitted, sends the last AI response. |
useTemplate | No | Whether to treat the message as a template (default: true). |
{
"id": "respond_1",
"type": "send_message",
"data": {
"message": "Your order #{{order_id}} is currently {{order_data.status}}. Shipped on {{order_data.shipped_date}}.",
"label": "Send Order Status"
}
}If no message is provided, the node sends the value of ctx.variables.ai_response (the most recent LLM call output).
A workflow can have multiple send_message nodes. All messages are collected and sent in order when the workflow completes.
action
Performs side effects that don’t produce user-facing messages.
set_variable
Sets a variable in the execution context.
{
"type": "action",
"data": {
"actionType": "set_variable",
"variable": "greeting",
"value": "Hello, {{user_name}}!",
"label": "Set Greeting"
}
}end_conversation
Stops the workflow and closes the conversation.
{
"type": "action",
"data": {
"actionType": "end_conversation",
"label": "End"
}
}human_handoff
Pauses the workflow and notifies a human reviewer. The reviewer receives the conversation context, the escalation reason, and approve/reject controls. See Human Handoffs for the complete guide.
{
"type": "action",
"data": {
"actionType": "human_handoff",
"reason": "Refund request over $500 for order {{order.id}}",
"label": "Escalate to Finance"
}
}The workflow pauses at this point. When the reviewer approves, execution resumes on the approved path. When rejected, execution follows the rejected path.
create_ticket
Creates a support ticket notification and continues.
{
"type": "action",
"data": {
"actionType": "create_ticket",
"subject": "Issue with order {{order_id}}",
"label": "Create Ticket"
}
}The create_ticket action sends a notification message. For creating actual tickets in external systems (Zendesk, Jira, etc.), use an mcp_tool node connected to the appropriate MCP server.
loop
Iterates over arrays or a fixed count.
for_each
Iterates over an array variable.
| Field | Description |
|---|---|
loopType | for_each |
arrayVariable | Name of the variable containing the array |
itemVariable | Variable name for the current item (default: item) |
maxIterations | Maximum iterations (capped at 50) |
{
"type": "loop",
"data": {
"loopType": "for_each",
"arrayVariable": "order_items",
"itemVariable": "current_item",
"maxIterations": 10,
"label": "Process Each Item"
}
}Connect edges with sourceHandle: "loop_body" (for the loop body) and sourceHandle: "loop_done" (for after the loop completes).
Use case: Process each item in a shopping cart, send a message for each search result, iterate over a list of tickets.
count
Iterates a fixed number of times.
| Field | Description |
|---|---|
loopType | count |
iterations | Number of iterations |
itemVariable | Variable name for the current index (default: index) |
while
Iterates while a condition is true.
| Field | Description |
|---|---|
loopType | while |
condition | Condition string (same syntax as logic nodes) |
code_block
Runs JavaScript code in a sandboxed context.
| Field | Description |
|---|---|
code | JavaScript code to execute |
Available in the sandbox:
| Object | Available |
|---|---|
ctx.variables | Read/write workflow variables |
ctx.message | User’s message text |
ctx.user_id | User identifier |
JSON, Math, Date, String, Number, Array, Object | Yes |
parseInt, parseFloat, isNaN | Yes |
encodeURIComponent, decodeURIComponent | Yes |
fetch, require, import | No (blocked) |
{
"type": "code_block",
"data": {
"code": "const items = ctx.variables.order_items || []; ctx.variables.total = items.reduce((sum, item) => sum + item.price, 0); ctx.variables.item_count = items.length;",
"label": "Calculate Total"
}
}Use case: Data transformation, calculations, string manipulation, filtering arrays, formatting output, business logic that does not require external calls.
Code blocks run in a sandboxed environment. They cannot make network requests or access the filesystem. Do not expose code block configuration to untrusted users.
auth_gate
Runs a multi-turn identity verification flow inside the conversation. The bot prompts the user for their email or phone number, sends a one-time code via one of the supported providers, and verifies the code before allowing the workflow to continue.
| Field | Description |
|---|---|
provider | resend, ses, twilio, msg91, or custom_http |
contact_type | email or phone |
outputVariable | Variable to store the verified identity |
State machine:
{
"type": "auth_gate",
"data": {
"provider": "resend",
"contact_type": "email",
"outputVariable": "verified_identity",
"label": "Verify Customer Email"
}
}Connect edges with sourceHandle: "verified" and sourceHandle: "failed":
{ "source": "auth_1", "target": "proceed_node", "sourceHandle": "verified" },
{ "source": "auth_1", "target": "deny_node", "sourceHandle": "failed" }After verification, {{verified_identity}} contains the verified email or phone number.
Use case: Verify identity before accessing account data, processing refunds, or changing settings. Required for any workflow that handles sensitive personal information.
Supported providers:
- Resend — Email OTP via Resend API
- SES — Email OTP via Amazon SES
- Twilio — Phone OTP via Twilio SMS
- MSG91 — Phone OTP via MSG91
- Custom HTTP — Your own OTP endpoint
schedule_message
Sends a message at a future time.
| Field | Description |
|---|---|
message | Message text with {{variable}} interpolation |
delay_minutes | Minutes from now to send the message |
scheduled_time | ISO 8601 timestamp for exact scheduling |
{
"type": "schedule_message",
"data": {
"message": "Reminder: Your appointment is in 1 hour. Reply to confirm or cancel.",
"delay_minutes": 1380,
"label": "24h Reminder"
}
}Use case: Appointment reminders, follow-up messages after support interactions, daily digests, shift notifications.
send_proactive
Sends a message to a different channel or user than the one who triggered the workflow.
| Field | Description |
|---|---|
message | Message text with {{variable}} interpolation |
target_channel_id | The channel/user to send to |
target_platform | The platform to send on (can differ from trigger platform) |
{
"type": "send_proactive",
"data": {
"message": "Alert: Customer {{user_name}} reported a critical issue. Details: {{message}}",
"target_channel_id": "ops-alerts-channel",
"target_platform": "slack",
"label": "Notify Ops Channel"
}
}Use case: Notify a team channel when a customer reports an issue, alert on-call engineers, send summaries to management channels, cross-platform notifications.
delegate_to_bot
Calls another bot, passing the current message and context. The target bot processes the message using its own workspace and tools, and the response is returned to the current workflow.
| Field | Description |
|---|---|
target_bot_id | The bot ID to delegate to |
outputVariable | Variable to store the delegated bot’s response |
context | Optional JSON context to pass to the target bot |
{
"type": "delegate_to_bot",
"data": {
"target_bot_id": "bt_billing_specialist",
"outputVariable": "specialist_response",
"context": "{\"customer_id\": \"{{verified_identity}}\"}",
"label": "Route to Billing Bot"
}
}Maximum 3 delegation hops to prevent infinite loops. See Bot Teams & Delegation for architecture patterns and examples.
Use case: Reception bot routes to specialist bots (billing, technical support, HR). Each specialist has its own workspace with domain-specific tools.
error_handler
Catches errors from upstream nodes and provides a fallback path.
{
"type": "error_handler",
"data": {
"label": "Handle Errors",
"fallback_message": "Something went wrong while processing your request. A support agent will follow up shortly."
}
}If a node throws an error, the error is recorded in the execution log and the workflow continues following the error_handler’s edges.
Use case: Send a graceful fallback message instead of leaving the user with no response. Log the error for debugging.
parallel
A structural node for fan-out execution. When the graph walker encounters a node with multiple outgoing edges, it follows all of them in breadth-first order. The parallel node is a visual marker for this behavior.
{
"type": "parallel",
"data": { "label": "Fan Out" }
}Connect multiple outgoing edges from the parallel node to execute multiple paths simultaneously:
{ "source": "parallel_1", "target": "lookup_order" },
{ "source": "parallel_1", "target": "lookup_account" },
{ "source": "parallel_1", "target": "check_inventory" }Use case: Fetch data from multiple MCP tools at the same time, send notifications to multiple channels, run independent processing steps in parallel.
Node Type Summary
| Type | Inputs | Outputs | Side Effects |
|---|---|---|---|
trigger | Platform message | Built-in variables | None |
llm_call | Prompt template | outputVariable with text, tokens | LLM API call |
logic | Condition/variable | Edge routing | None |
mcp_tool | Tool name, arguments | outputVariable with tool result | MCP tool call |
send_message | Message template | None | Sends message to user |
action | Action-specific fields | Variable (set_variable) | Depends on action type |
loop | Array/count/condition | itemVariable per iteration | None |
code_block | JavaScript code | Via ctx.variables | None |
auth_gate | Provider, contact type | outputVariable with identity | Sends OTP |
schedule_message | Message, delay | None | Queues future message |
send_proactive | Message, target | None | Sends to different channel |
delegate_to_bot | Target bot ID | outputVariable with response | Calls another bot |
error_handler | Fallback config | None | Fallback message |
parallel | None | None | Fan-out marker |