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

# Step types

> Every step type an automation can run, the fields each one takes, and when to reach for it.

An automation is a trigger and a list of steps. Every step shares the same handful
of fields, and each type adds its own.

The field reference on this page is generated from the automation engine, so it is
always what the validator actually enforces.

## Shared fields

Every step takes these, whatever its type.

<ParamField path="id" type="string" required>
  Identifies the step within the automation. Later steps read its output as `{{steps.<id>.*}}`.
</ParamField>

<ParamField path="type" type="string" required>
  Which kind of step this is, from the table below.
</ParamField>

<ParamField path="description" type="string">
  One line on what this step does, shown to the merchant in the run timeline.
</ParamField>

<ParamField path="name" type="string">
  Display name. Defaults to the step id.
</ParamField>

<ParamField path="next" type="string">
  Id of the step to run after this one. Omit to end the automation.
</ParamField>

<ParamField path="timeout_seconds" type="integer">
  Abandon the step if it has not finished within this long.
</ParamField>

## Choosing a step

| Type                                | Use it to                                      |
| ----------------------------------- | ---------------------------------------------- |
| [`action`](#action)                 | Call a connection or a Colossal operation      |
| [`choice`](#choice)                 | Branch on a condition                          |
| [`transform`](#transform)           | Reshape data into a typed object               |
| [`map`](#map)                       | Repeat steps once per item in a list           |
| [`parallel`](#parallel)             | Run groups of steps at the same time           |
| [`delay`](#delay)                   | Wait for a duration, or until a moment         |
| [`wait_for_event`](#wait-for-event) | Pause until something happens elsewhere        |
| [`approval`](#approval)             | Ask a person before continuing                 |
| [`ai_reasoning`](#ai-reasoning)     | Decide something rules cannot express          |
| [`insights`](#insights)             | Ask a question of the merchant's commerce data |
| [`builder`](#builder)               | Change the storefront                          |
| [`end`](#end)                       | Stop the automation early                      |

## Run an action

`type: action`

Calls one action on a connection, or a Colossal operation. This is the step that
does most of the work in most automations.

Action ids are `connection/action`. Search the available actions rather than
guessing an id, and read the action's arguments before filling `parameters`.

<ParamField path="action" type="string" required>
  What to run, as 'connection/action' - for example 'shopify/getOrders' or 'colossal/http\_request'. Search the available actions rather than guessing an id.
</ParamField>

<ParamField path="parameters" type="object">
  Arguments for the action. Values may be '\{\{...}}' template expressions.
</ParamField>

<ParamField path="retry_policy" type="object">
  Overrides the default retry behaviour for this action.
</ParamField>

```json theme={null}
{
  "id": "notify_team",
  "type": "action",
  "description": "Post the order to Slack",
  "action": "slack/send_message",
  "parameters": {
    "channel": "#orders",
    "text": "New order from {{customer_email}}"
  }
}
```

## Branch on a condition

`type: choice`

Tests conditions in order and runs the first branch that matches.

Two shapes cover nearly everything. In **branch form** the matched branch does the
conditional work and `default_steps` is the else. In **guard form** the branch
catches the failure case and ends the automation, leaving `default_steps` empty so
the rest of the automation stays flat instead of nesting inside each check.

`default_steps` is a sibling of `choices`, never a key inside a `choices` entry.

<ParamField path="choices" type="array" required>
  Branches tested in order. The first match wins.
</ParamField>

<ParamField path="default_steps" type="array">
  The else path, run when no choice matched. A key on the choice step itself, a sibling of 'choices' - never inside a choices entry.
</ParamField>

```json theme={null}
{
  "id": "check_value",
  "type": "choice",
  "description": "Is the total more than $100?",
  "choices": [
    {
      "condition": { "type": "greater_than", "field": "totalPrice", "value": 100 },
      "label": "Yes",
      "steps": [
        {
          "id": "flag",
          "type": "action",
          "action": "colossal/http_request",
          "parameters": { "url": "https://example.com/flag" }
        }
      ]
    }
  ],
  "default_steps": []
}
```

A `condition` is a `type`, the `field` it reads and the `value` it compares against.
`field` is a data reference without `{{ }}` around it - a trigger field by its bare name,
or a `steps.<id>.<field>` path - while `value` is a literal or a `{{...}}` template.

| `type`                                          | Matches when                                                                        |
| ----------------------------------------------- | ----------------------------------------------------------------------------------- |
| `equals` / `not_equals`                         | The values are the same. Compare against `null` to test that a lookup found nothing |
| `equals_ignore_case` / `not_equals_ignore_case` | The same, ignoring case. Use these on anything a person typed                       |
| `greater_than` / `less_than`                    | The field is above or below a number                                                |
| `contains` / `not_contains`                     | The array or string holds the value                                                 |

Anything else is rejected, so read a merchant's "is not empty" as one of these rather
than inventing an operator for it.

A `label` is the answer to the question in the step's `description`, and it is one
word. `Yes` and `No` cover almost every choice; only a choice routing several
conditions to several outcomes takes anything else, and then it names where the
branch goes, like `New` or `VIP`.

## Reshape data

`type: transform`

Reshapes data from the trigger and earlier steps into a new object, validated
against `output_schema`. Set `source` to run the mapping once per item in a list
instead of once in total.

<ParamField path="output_schema" type="object">
  JSON Schema for the object this step produces.
</ParamField>

<ParamField path="output_schema_type" type="string">
  Named output type, for a storefront event that must return a known shape.
</ParamField>

<ParamField path="mapping" type="object" required>
  Output field name to a '\{\{...}}' template expression.
</ParamField>

<ParamField path="source" type="string">
  Template expression resolving to a list, to map over instead of running once. The step then produces a list, and its 'mapping' reads the current element through 'iteration.item', as in '\{\{iteration.item.email}}'.
</ParamField>

```json theme={null}
{
  "id": "summarise",
  "type": "transform",
  "output_schema": {
    "type": "object",
    "properties": {
      "valid": { "type": "boolean" },
      "amount": { "type": "number" }
    }
  },
  "mapping": {
    "valid": "{{steps.lookup.is_valid}}",
    "amount": "{{steps.lookup.total | int}}"
  }
}
```

See [data access](/automations/data-access) for the template syntax and the pipes
available in `mapping`.

## Repeat over a list

`type: map`

Runs a group of steps once per item in a list. Inside them the current item is
`{{iteration.item.*}}`.

<ParamField path="source" type="string" required>
  Template expression resolving to the list to walk, such as '\{\{steps.fetch.items}}'.
</ParamField>

<ParamField path="steps" type="array" required>
  Steps run once per item. Inside them the current item is '\{\{iteration.item.\*}}'.
</ParamField>

<ParamField path="max_items" type="integer" default="100">
  Stop after this many items.
</ParamField>

```json theme={null}
{
  "id": "each_item",
  "type": "map",
  "source": "{{steps.get_order.line_items}}",
  "steps": [
    {
      "id": "restock",
      "type": "action",
      "action": "colossal/http_request",
      "parameters": { "url": "https://example.com/restock/{{iteration.item.sku}}" }
    }
  ]
}
```

## Run branches at the same time

`type: parallel`

Runs several groups of steps at the same time. Use it when the groups do not
depend on each other.

<ParamField path="branches" type="array" required>
  Groups of steps that run at the same time. Every branch takes a 'name', which is how its steps' outputs are addressed afterwards.
</ParamField>

<ParamField path="wait_for_all" type="boolean" default="true">
  Wait for every branch before continuing. When false, continue as soon as the first finishes.
</ParamField>

```json theme={null}
{
  "id": "gather",
  "type": "parallel",
  "description": "Look up the customer and the inventory at once",
  "branches": [
    {
      "name": "customer",
      "steps": [
        {
          "id": "load",
          "type": "action",
          "action": "colossal/get_customer",
          "description": "Load the customer",
          "parameters": { "customer_uid": "{{customer_uid}}" }
        }
      ]
    },
    {
      "name": "stock",
      "steps": [
        {
          "id": "levels",
          "type": "action",
          "action": "shopify/getInventoryLevels",
          "description": "Read stock",
          "parameters": {}
        }
      ]
    }
  ]
}
```

Each branch keeps its own results, so a later step reads one through both names:
`{{steps.gather.customer.load.email}}`. A step inside a branch reads its
siblings by bare id, as anywhere else. A `map` nests differently, by index:
`{{steps.<map_id>.<i>.<step_id>.<field>}}`.

## Wait for a set time

`type: delay`

Waits before continuing. Give either a `duration` or an `until`, not both.

<ParamField path="duration" type="string">
  How long to wait, in minutes, hours, days or weeks, such as '30 minutes' or '7 days'. Seconds are not supported.
</ParamField>

<ParamField path="until" type="string">
  Wait until a moment in time instead of for a duration: an ISO 8601 timestamp, or a template expression resolving to one.
</ParamField>

```json theme={null}
{
  "id": "wait_a_day",
  "type": "delay",
  "duration": "1 day",
  "next": "follow_up"
}
```

## Wait for an event

`type: wait_for_event`

Pauses until a named event arrives, or the timeout passes.

<Warning>
  Not executable yet. A definition using `wait_for_event` is rejected when you create
  it, so design around it only once this notice is gone.
</Warning>

<ParamField path="event" type="string" required>
  Event to wait for, as the connection's own event id, such as 'shopify/orders/paid'.
</ParamField>

<ParamField path="timeout" type="string" default="30 days">
  Give up after this long, such as '30 days'.
</ParamField>

## Ask a person to approve

`type: approval`

Pauses and asks a person to approve before the automation carries on. Reach for
this before anything that spends money or contacts a customer in a way that is
hard to undo.

<ParamField path="title" type="string" required>
  What the merchant is being asked to approve.
</ParamField>

<ParamField path="message" type="string">
  The detail they need to decide.
</ParamField>

<ParamField path="preview_url" type="string">
  A page showing what will change, opened from the approval.
</ParamField>

## Let an agent decide

`type: ai_reasoning`

Decides something that rules cannot express, and returns a structured result
matching `output_schema`. Give it the narrowest job that works, and let
deterministic steps act on what it returns.

<ParamField path="prompt" type="string" required>
  What to decide, and the data to decide it from.
</ParamField>

<ParamField path="output_schema" type="object" required>
  JSON Schema the answer must match, so later steps can rely on it.
</ParamField>

<ParamField path="max_iterations" type="integer" default="10">
  How many tool-calling rounds the agent may take.
</ParamField>

<ParamField path="timeout_minutes" type="integer" default="3">
  Give up on the step after this long.
</ParamField>

<ParamField path="enabled_tools" type="array">
  Allowlist of Tools the agent can call. Selectors: bare names for built-ins (e.g. 'get\_order\_details'), 'app\_\_{integration}**{action}' for a connection's tools, 'app**{integration}**\*' or 'colossal**{service}\_\_\*' for wildcards. Empty list = reasoning only (no tool calls).
</ParamField>

<ParamField path="tool_safety" type="object">
  Per-app write-action approval policy. Currently a no-op: write tools are temporarily disabled on ai\_reasoning steps, so no calls reach this field. Kept for forward compatibility.
</ParamField>

<ParamField path="skills" type="array">
  Names of reusable skills the agent can load on demand for craft guidance (e.g. content writing). Each name resolves to a backend skill; the agent reads its full instructions only when relevant. Empty = no extra skills.
</ParamField>

```json theme={null}
{
  "id": "classify",
  "type": "ai_reasoning",
  "description": "Decide whether the order looks fraudulent",
  "prompt": "Given the order below, decide whether it looks fraudulent and say why.\n\n{{steps.get_order}}",
  "output_schema": {
    "type": "object",
    "properties": {
      "suspicious": { "type": "boolean" },
      "reason": { "type": "string" }
    },
    "required": ["suspicious", "reason"]
  }
}
```

## Ask a question about your data

`type: insights`

Asks a question of the merchant's commerce data and returns a structured answer.
Use it for questions about orders, products and customers rather than wiring the
queries by hand.

<ParamField path="question" type="string" required>
  The question to ask of the project's commerce data, in plain language.
</ParamField>

<ParamField path="output_schema" type="object" required>
  JSON Schema the answer must match.
</ParamField>

<ParamField path="max_iterations" type="integer" default="10">
  How many analysis rounds the agent may take.
</ParamField>

<ParamField path="timeout_minutes" type="integer" default="5">
  Give up on the step after this long.
</ParamField>

## Build a storefront page

`type: builder`

Changes the storefront from inside an automation.

<ParamField path="prompt" type="string" required>
  What to change about the storefront.
</ParamField>

<ParamField path="timeout_minutes" type="integer" default="15">
  Give up on the build after this long.
</ParamField>

## Stop the automation

`type: end`

Stops the automation before its last step. Most automations do not need one; a
guard branch usually does.

<ParamField path="status" type="string" default="success">
  How the automation finished.
</ParamField>

<ParamField path="message" type="string">
  Why it finished this way, shown in the run timeline.
</ParamField>

```json theme={null}
{
  "id": "stop",
  "type": "end",
  "status": "success",
  "message": "Nothing to do for this order"
}
```

## Next steps

* [Data access](/automations/data-access). The `{{...}}` syntax every step shares
* [Overview](/automations/overview). How automations are triggered and run
