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

# Nodes

> The three node types in an Asteroid workflow: start, workflow, and output.

A node is one step of a workflow. Three node types exist.

| Node   | What it does                                                  | API, SDK and MCP `type` | `settings.yaml` `type` |
| ------ | ------------------------------------------------------------- | ----------------------- | ---------------------- |
| Start  | Marks where an execution begins.                              | `start`                 | `start`                |
| Agent  | Reads instructions and acts in the environment.               | `iris`                  | `agent`                |
| Output | Ends the execution and returns an outcome label and a result. | `output`                | `output`               |

When you call the API, the SDK, or the MCP tools, send the value in the third column. In a workflow's `settings.yaml`, write the value in the fourth.

Nodes are joined by [transitions](/concepts/transitions).

***

## Start node

Every graph has exactly one start node. The builder seeds it when you create a workflow.

A start node makes no decision. It holds one outgoing transition, and that transition must point at a node that is not an output node. An execution always begins here.

***

## Agent node

An agent node is where the work happens. You write instructions in plain language. The model carries them out in a real browser or on a real desktop.

<CardGroup cols={2}>
  <Card title="Navigate" icon="compass" horizontal>Move to and through pages</Card>
  <Card title="Fill forms" icon="list" horizontal>Enter and submit structured data</Card>
  <Card title="Use files" icon="file" horizontal>Read, upload, or download files</Card>
  <Card title="Extract data" icon="trending-up" horizontal>Pull values off a page</Card>
</CardGroup>

### Instructions

The instruction block is the core of an agent node. Four parts make it reliable:

| Part             | What to write                                        |
| ---------------- | ---------------------------------------------------- |
| Goal             | What this node accomplishes.                         |
| Ordered steps    | The actions, in sequence.                            |
| Edge cases       | Variations the workflow will meet.                   |
| Success criteria | What the workflow can observe when the step is done. |

```text theme={null}
Your goal is to submit the consultation form for the current patient:
1. Click "New Consultation"
2. Fill the form fields with the provided data
3. Upload any provided attachments
4. Click "Create"

Edge cases:
- If a modal appears, close it before continuing
- If the page asks for confirmation, accept it

Success criteria:
Once you see the "Consultation Submitted" banner, the task is complete.
```

<Card title="Write good instructions" icon="pen-line" href="/build/instructions" horizontal>Patterns that hold up across runs</Card>

### Variables in instructions

Write `{{.name}}` in a node's instructions to drop a value in at run time.

```text theme={null}
Navigate to the patient search page:
1. Enter {{.patient_name}} into the search bar
2. Select the matching result
3. Fill {{.diagnosis}}
4. Click Submit
```

A variable name must start with a letter or an underscore. After that it may hold letters, digits, and underscores only.

* Valid: `{{.user_name}}`, `{{.email}}`, `{{.api_key}}`
* Invalid: `{{.user-name}}`, `{{.my@var}}`, `{{.1st_item}}`

Reach into nested JSON with a dot path:

```text theme={null}
User: {{.user.name}} ({{.user.email}})
```

```json theme={null}
{
  "inputs": {
    "user": { "name": "Jane Smith", "email": "jane@example.com" }
  }
}
```

The values come from the [inputs](/concepts/inputs-and-outputs) your code sends on the execute call.

### Model

Each node picks its own model. Match the model to the difficulty of that step.

| Model               | Pick it for                                |
| ------------------- | ------------------------------------------ |
| `asteroid-fast`     | Simple, fast interactions. Lowest latency. |
| `asteroid-balanced` | The default. Balanced speed and accuracy.  |
| `asteroid-max`      | Complex flows that need deep reasoning.    |

### Capabilities

Every agent node declares three switches: `browser_use`, `computer_use`, and `ask_user_question`. They decide which tools the node gets. The [environment](/concepts/environments) can override two of them.

<Card title="What a node can do" icon="toggle-right" href="/concepts/node-capabilities" horizontal>The three switches and how the environment gates them</Card>

### Script

By default an agent node works turn by turn: the model reads the page, picks a tool, and acts. Set the node's **Script** field to run a stored Playwright script first instead.

Set `script_filepath` to a relative `.js` path and the node becomes a scripted node. Clear the field and it goes back to working turn by turn.

Scripts live in the workflow's persistent `shared/` tree. The runtime resolves the path inside the node's own shared directory:

```
shared/<node-slug>/<script_filepath>
```

* The runtime derives `<node-slug>` from the node's display name. It lowercases the name and strips punctuation. You never write the slug, so renaming a node never breaks the path.
* You author `script_filepath` itself. It is stored with a `./` prefix, as in `./scripts/find_patient.js`.
* Name the file after the action, in `lower_snake_case`. The directory already says which node it belongs to. Avoid `scripts/main.js` and `scripts/index.js`.

A node named "Find Patient" with `script_filepath: ./scripts/find_patient.js` resolves to `/home/agent/shared/find_patient/scripts/find_patient.js`.

<Card title="Workflow filesystem" icon="folder-open" href="/concepts/filesystem" horizontal>How `shared/` keeps files across runs of the same workflow</Card>

#### Passing data into a script

Values that change from run to run reach the script as `args`. Declare an input schema on the node, then read each value by name.

```javascript theme={null}
/**
 * @param {object} args
 * @param {string} args.patient_name - Patient to file the note against.
 * @param {string} args.date_of_birth - Date of birth, as shown in the EHR.
 */
module.exports = async ({ page, args }) => {
  await page.fill('#username', '##USERNAME##');
  await page.fill('#password', '##PASSWORD##');
  await page.click('button[type="submit"]');

  await page.fill('#patient-name', args.patient_name);
  await page.fill('#dob', args.date_of_birth);
  await page.click('#save');

  return { saved: true };
};
```

A scripted node that declares an input schema must use the `async ({ page, args }) => { ... }` signature. The plain `async (page) => { ... }` form fails validation.

<Tip>Document every argument with a JSDoc `@param` block. A failed script hands the node back to the workflow. That JSDoc is then the only description of the arguments the workflow can see.</Tip>

Secrets take a separate path. A `##CREDENTIAL##` token is replaced at the tool boundary, straight from the credential store, and never passes through the model. See [Agent profiles](/concepts/profiles).

#### What a script returns

| Return value   | Effect                                                                   |
| -------------- | ------------------------------------------------------------------------ |
| An object      | Each top-level key becomes an output variable that later nodes can read. |
| A string       | Wrapped under a single `script_output` variable.                         |
| A thrown error | Treated as a failure. `script_failure_action` decides what happens next. |

#### If a script fails

<CardGroup cols={2}>
  <Card title="fallback_to_ai" icon="sparkles" horizontal>The default. The failure context joins the model's turn, and the workflow recovers using the instructions.</Card>
  <Card title="cancel_execution" icon="ban" horizontal>The execution is cancelled at once, with reason `script_failed`. This also fires when the script file is missing.</Card>
</CardGroup>

<Tip>Ask [Astro](/build/from-your-coding-agent) which steps are worth scripting. It writes and tests the scripts for you.</Tip>

#### How a scripted node ends

The script runs against the same live session the model would have used. Then the runtime checks, in order:

1. A [selector transition](/concepts/transitions) matches right after the script. The runtime takes it and skips the model.
2. The script succeeded and the node has exactly one outgoing transition. The runtime takes it and skips the model.
3. Neither holds. The runtime calls the model with the script's output as context, and the workflow decides what to do next.

### How an agent node ends

An agent node ends when one of its outgoing transitions fires. It never ends on its own.

<Warning>
  **Give every agent node a failure path.**

  Connect at least one transition from every agent node to an output node that handles failure.

  Without it, a missing element or an unexpected page has nowhere to go. The execution then cannot report what went wrong.
</Warning>

### Agent node reference

In a workflow's `settings.yaml`, an agent node is `type: agent`. Every agent node declares `type`, `capabilities`, and `model`.

| Field                   | Value                                                                                                  |
| ----------------------- | ------------------------------------------------------------------------------------------------------ |
| `capabilities`          | Required. The `browser_use`, `computer_use`, and `ask_user_question` booleans.                         |
| `model`                 | Required. One of `asteroid-fast`, `asteroid-balanced`, or `asteroid-max`.                              |
| `script_filepath`       | Optional. A relative `.js` path inside the node's own shared directory.                                |
| `script_failure_action` | Optional. `fallback_to_ai` (the default) or `cancel_execution`. Only read alongside `script_filepath`. |

```yaml theme={null}
label: "Submit Form"
type: agent
capabilities:
  browser_use: true
  computer_use: false
  ask_user_question: true
model: asteroid-balanced
transitions:
  - to: success_output
    type: ai
  - to: failure_output
    type: ai
  - to: confirmation_page
    type: selector
    name: "Confirmation Visible"
    selectors:
      - ".confirmation-banner"
```

A scripted node that falls back to the workflow:

```yaml theme={null}
label: "Find Patient"
type: agent
capabilities:
  browser_use: true
  computer_use: false
  ask_user_question: true
model: asteroid-balanced
script_filepath: ./scripts/find_patient.js
transitions:
  - to: patient_found
    type: ai
  - to: not_found
    type: ai
```

A scripted node that cancels the execution when the script fails:

```yaml theme={null}
label: "Login"
type: agent
capabilities:
  browser_use: true
  computer_use: false
  ask_user_question: true
model: asteroid-balanced
script_filepath: ./scripts/login.js
script_failure_action: cancel_execution
transitions:
  - to: dashboard
    type: ai
```

The node's instructions live in an `instructions.md` file in the same directory.

***

## Output node

An output node ends the execution. It closes the environment, sets the outcome label, and returns the structured result to your code.

Every graph needs at least one output node. Most graphs have several: one per way the execution can finish.

### Outcome labels

An outcome label answers one question: how did this execution finish? It is the value your code branches on.

An output node lists the labels it can produce.

```yaml theme={null}
label: "Result"
type: output
outcomes:
  - success
  - failure
```

Labels are lowercase, and each node holds between 1 and 20 of them. The full rules are on the page below.

### Result schema

An output node can also return structured data. You define the shape as a JSON Schema, and the workflow fills it in. Your code reads it at `executionResult.result`.

The schema lives in an `output-schema.json` file next to the node's `settings.yaml`.

<Card title="Inputs and outputs" icon="arrow-right-left" href="/concepts/inputs-and-outputs" horizontal>Label rules, schema rules, and the exact response your code receives</Card>

***

## Related

<CardGroup cols={2}>
  <Card title="Workflows are graphs" icon="workflow" href="/concepts/graphs" horizontal>How nodes fit together into a whole workflow</Card>
  <Card title="Transitions" icon="git-branch" href="/concepts/transitions" horizontal>The edges that join nodes</Card>
  <Card title="Build in the platform" icon="table-properties" href="/build/in-the-platform" horizontal>Add and configure nodes in the visual builder</Card>
  <Card title="Environments" icon="server" href="/concepts/environments" horizontal>Where a node's work actually runs</Card>
</CardGroup>
