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

# Node Basics

> Node structure, transitions, tools, and variables.

You can edit nodes in either the visual node editor or the JSON editor. To create a node in the visual editor, click **Add Node** in the lower-left corner.

For how the global **role instruction** fits with each node’s **task instruction**, see [Role and task instructions](/guides/scenarios/role-and-task-instructions).

## Name

Each node name must be unique within the scenario.

The node name is used as the identifier for:

* `initial_node` (the scenario start node)
* any function `transition_to` targets

When you rename a node in the node editor, Akapulu Labs also updates:

* `initial_node` if that node is currently the start node
* any function transitions that point to that node

The first node you create becomes the start node by default. A scenario has one start node at a time.

**JSON**

```json theme={null}
{
  "initial_node": "Greeting",
  "nodes": {
    "Greeting": {
      ...
    },
    ...
  }
}
```

**UI**

Edit the node title directly to rename it.

To set any node as the start node (other than the first one created), click the **3 dots** in the top-right of that node, then click **Set as start node**.

## Global role instruction

Scenarios use one global `role_instruction` at the top level.

Use `role_instruction` for long-lived identity, tone, boundaries, and behavior that should apply across the full conversation.

**JSON**

```json theme={null}
{
  "initial_node": "Greeting",
  "role_instruction": "You are a friendly Akapulu Labs onboarding assistant. Keep responses concise because they will be converted to audio.",
  "nodes": {
    "Greeting": {
      ...
    }
  }
}
```

**UI**

In the scenario editor, click the **hamburger** icon on the left to open the left-hand nav menu. That panel includes a text area where you edit the global **role instruction**.

## Task instruction

`task_instruction` defines what the assistant should do in the current node.

Each node must include one non-empty `task_instruction`.

Recommended pattern:

* Put long-lived persona and behavior rules in the global `role_instruction`
* Use `task_instruction` in each node for node-specific goals and instructions

**JSON**

```json theme={null}
{
  "role_instruction": "You are a friendly Akapulu Labs onboarding assistant. Keep responses concise because they will be converted to audio.",
  "nodes": {
    "Greeting": {
      "task_instruction": "Greet the user and gather what they want to build.",
      ...
    },
    "Planning": {
      "task_instruction": "Help the user plan their project based on gathered requirements.",
      ...
    }
  }
}
```

**UI**

The task instruction is the text input on the node labeled **Task Instruction**.

### Runtime variables in instructions

You can embed **`{{runtime.*}}`** placeholders in the global **`role_instruction`** and in any node’s **`task_instruction`**. Akapulu Labs substitutes them from the **`runtime_vars`** your server sends on [connect](/api-reference/conversations/connect), so the model sees session-specific text without you duplicating scenarios per customer.

<Note>
  `secret` and `llm` template variables are **not** supported in `role_instruction` or `task_instruction`. Use **`{{runtime.*}}`** for per-session variables; keep sensitive variables in endpoint headers with `{{secret.*}}`, and use **`{{llm.*}}`** only in HTTP endpoint templates. See [Templates and Variables](/guides/endpoints/templates-and-variables).
</Note>

**Example**

```json theme={null}
{
  "role_instruction": "You are a support agent for {{runtime.company_name}}. The caller’s account id is {{runtime.user_id}}.",
  "nodes": {
    "Ticket triage": {
      "task_instruction": "Greet {{runtime.display_name}} and ask which product line they need help with before looking anything up.",
      ...
    }
  }
}
```

Your connect request should include matching keys in `runtime_vars`, for example `company_name`, `user_id`, and `display_name`.

<Note>
  **Web SDK:** If using the [Akapulu Labs Web SDK](/web-sdk/overview), in your connect route, call **`akapulu.connectConversation`** from **`@akapulu/server`** with a **`runtime_vars`** object whose keys match your **`{{runtime.*}}`** placeholders. See [Customize Conversation UI](/guides/conversations/customize-conversation-ui) for a complete server-side example.
</Note>

## Functions

Functions are tool calls available to the assistant while the node is active.

Allowed function types:

* `transition`
* `http`
* `rag`
* `vision`

Function rules:

* function name is required and must be unique within the node
* function name can only use letters, numbers, underscores, and hyphens
* function description is required

Type-specific requirements:

* `transition` requires `transition_to`
* `http` requires `endpoint_id`
* `rag` requires `knowledge_base_id`
* `vision` has no additional required ID field

Function description guidance:

* this is the instruction shown to the model for when and why to call the tool
* be explicit about the trigger, expected use, and what the tool does

**JSON**

```json theme={null}
{
  "nodes": {
    "Greeting": {
      "functions": [
        {
          "name": "transition_to_planning_phase",
          "description": "Once project scope is clear, move to planning phase.",
          "type": "transition",
          "transition_to": "Planning Phase"
        },
        {
          "name": "Akapulu_RAG",
          "description": "Access information on the Akapulu Labs platform.",
          "type": "rag",
          "knowledge_base_id": "<KNOWLEDGE_BASE_ID>"
        }
      ],
      ...
    },
    "Planning Phase": {
      "task_instruction": "Help the user plan their project on Akapulu Labs.",
      "functions": [
        {
          "name": "create_project_brief",
          "description": "Create a project brief in your external planning system.",
          "type": "http",
          "endpoint_id": "<HTTP_ENDPOINT_ID>"
        },
        {
          "name": "inspect_user_whiteboard",
          "description": "Analyze a camera frame when the user shares a whiteboard or sketch.",
          "type": "vision"
        }
      ],
      ...
    },
    ...
  }
}
```

**UI**

Click **Add function** on the node to open the function modal, then choose the function type:

* [`transition`](/guides/tools/transition-tools)
* [`rag`](/guides/knowledge-bases/overview)
* [`vision`](/guides/vision/vision-tool-overview)
* [`http`](/guides/endpoints/create-endpoint)

In the modal:

* enter a function `name`
* enter a function `description`
* select type-specific targets from the dropdowns (saved HTTP endpoint for `http`, knowledge base for `rag`)

To set `transition_to` in the UI for any function, drag the probe on the right side of the function to the desired node.

## Require function call

`require_function_call` makes the assistant **call at least one function** on the next model turn in this node instead of answering with only plain text.

<Note>
  **Constraint:** When `require_function_call` is `true`, the node must define **at least one** function in `functions`.
</Note>

**JSON**

```json theme={null}
{
  "nodes": {
    "Booking": {
      "task_instruction": "The user already gave date, time, and visit type in intake. Call scheduling to create the appointment from that information, then confirm out loud.",
      "require_function_call": true,
      "functions": [
        {
          "name": "create_appointment",
          "description": "Create the appointment using the date, time, and visit type the user provided earlier.",
          "type": "http",
          "endpoint_id": "<HTTP_ENDPOINT_ID>",
          "transition_to": "Confirmation"
        }
      ]
    }
  }
}
```

**UI**

Open **Advanced Settings** on the node, then set **Force Function Call** to yes. While this is on, the function section on the node shows a **Call required** indicator.

<Note>
  **Loop protection:** If `require_function_call` is enabled and the assistant gets stuck repeatedly calling tools without replying in text, Akapulu Labs automatically relaxes the forced-call requirement after 5 consecutive tool-only turns for that session/node. After that, the assistant can either call a tool or reply in plain text to prevent infinite loops.
</Note>

## Respond immediately

`respond_immediately` controls whether the assistant speaks immediately after entering the node.

* `true` (default): the assistant responds right away
* `false`: the assistant waits for user input before responding

**JSON**

```json theme={null}
{
  "nodes": {
    "Greeting": {
      "respond_immediately": false,
      ...
    },
    ...
  }
}
```

**UI**

Open the **Advanced Settings** dropdown, then toggle **Respond Immediately** on or off.

## End after bot response

`end_after_bot_response` controls whether the conversation ends after the bot finishes its first utterance in that node.

Use this for terminal nodes like wrap-up, goodbye, or confirmation nodes where the assistant should speak once and then end the conversation.

When `end_after_bot_response` is `true`, interruptions are disabled for that node so the bot can finish its closing response before the conversation ends.

**JSON**

```json theme={null}
{
  "nodes": {
    "End": {
      "task_instruction": "Thank the user, summarize next steps briefly, and end the conversation.",
      "end_after_bot_response": true
    }
  }
}
```
