> This page is for version v2026-02-09 (default).
> For other versions, use one of these documentation indexes:
> - v2026-02-09 (default): https://docs.extend.ai/2026-02-09/llms.txt
> - v2025-04-21: https://docs.extend.ai/2025-04-21/llms.txt
> - v2024-12-23: https://docs.extend.ai/2024-12-23/llms.txt

> ## Documentation Index
> Fetch the complete documentation index at: https://docs.extend.ai/llms.txt
> Use this file to discover all available pages before exploring further.
>
> ## API version
> The current API version is `2026-02-09`, served at the site root (no version prefix in URLs).
> If this page URL contains `/2025-04-21/` or `/2024-12-23/`, you are reading an older API version.
> Prefer the current docs at https://docs.extend.ai/llms.txt unless the user explicitly needs that older version.
> Do not treat older-version pages as the source of truth for new integrations.

# Validation Step

> Configure VALIDATION steps with code and semantic blocks using workflows.create, workflows.update, and workflowVersions.create, or Extend Studio. Read pass/fail results from workflowRuns.retrieve.

Validation steps run one or more checks against data produced by earlier workflow steps. Use them to enforce required fields, compare values, assess whether extracted data is plausible, or send a run to a different downstream step when a check fails.

> **Note**
>
> The Validation step described here replaces the legacy formula-based Rule Validation step for new workflows. Existing Rule Validation steps continue to work, but Composer chat does not create or edit them.

## How validation works

A Validation step contains an ordered list of **blocks**. Every block is one of two types:

| Block type   | Best for                                                                     | Result                                                       |
| ------------ | ---------------------------------------------------------------------------- | ------------------------------------------------------------ |
| **Code**     | Required fields, comparisons, formats, totals, and other deterministic rules | JavaScript returns `true`, `false`, or `[boolean, "reason"]` |
| **Semantic** | Plausibility, consistency, meaning, tone, and other judgment calls           | An LLM returns a pass or fail result with reasoning          |

An unconfigured Validation step has no blocks and passes every run. A code or semantic execution error fails the step instead of treating the block as an ordinary invalid result.

## Via the API

Add a step with `type: "VALIDATION"` to the workflow's `steps` array. Define checks in `config.blocks` and connect the two outcomes with `next[].result`.

### Define the blocks and routes

This step assumes an upstream extractor named `extract_invoice` produces `invoice_number` and `vendor_name`. The code block checks a required field; the semantic block evaluates the vendor name.

```json
{
  "name": "validate_invoice",
  "type": "VALIDATION",
  "config": {
    "blocks": [
      {
        "name": "Required invoice number",
        "blockType": "code",
        "code": {
          "language": "javascript",
          "code": "const number = context.outputs.extract_invoice.invoice_number.value; return [number != null && number !== '', 'Invoice number is required'];"
        }
      },
      {
        "name": "Plausible vendor",
        "blockType": "semantic",
        "semantic": {
          "promptText": "The vendor name {{ outputs.extract_invoice.vendor_name.value }} must be a plausible company name, not a placeholder."
        }
      }
    ]
  },
  "next": [
    { "step": "send_response", "result": "pass" },
    { "step": "review_invoice", "result": "fail" }
  ]
}
```

| Field                          | How to use it                                                                                                          |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| `name`                         | A unique step name, from 1 to 255 characters. Use this name in upstream `next[].step` entries.                         |
| `config.blocks`                | Up to 100 code or semantic blocks. All blocks must pass to take the `pass` route. An empty array passes every run.     |
| `blocks[].id`                  | Optional on input; generated when omitted. Keep the IDs returned by the API when editing existing blocks.              |
| `blocks[].name`                | Required display name for the block.                                                                                   |
| `blocks[].blockType`           | `"code"` or `"semantic"`. Include the corresponding `code` or `semantic` object.                                       |
| `blocks[].code`                | Set `language: "javascript"` and a non-empty `code` string. Python is accepted in configuration but is not executable. |
| `blocks[].semantic.promptText` | Non-empty instructions. Insert upstream values with `{{ outputs.<step>.<field>.value }}`.                              |
| `next[].result`                | `"pass"` when every block is valid, or `"fail"` when any block is invalid. `step` names the downstream step.           |

Code references upstream values through `context.outputs`; semantic templates use `outputs` without the `context.` prefix. Both use the upstream step's public `name`. See [Code blocks](#code-blocks) for single-file and collected-file context shapes, and [Semantic blocks](#semantic-blocks) for template syntax.

### Save, deploy, and read results

1. Include this definition in the complete `steps` array when calling [Create Workflow](/api-reference/endpoints/workflow/create-workflow) (`workflows.create`) or [Update Workflow](/api-reference/endpoints/workflow/update-workflow) (`workflows.update`). Include the trigger, parse, upstream extraction, and downstream steps, and connect the upstream step to `validate_invoice`. Updating `steps` replaces the entire draft step list.
2. To preserve an existing Validation step's blocks, keep its step name and omit `config`. Providing `config.blocks` replaces its block list; `[]` clears the list.
3. [Create a Workflow Version](/api-reference/endpoints/workflow/create-workflow-version) (`workflowVersions.create`) to deploy the draft. If you pass `steps` to this endpoint, it deploys those definitions directly without modifying the draft.
4. [Run the workflow](/api-reference/endpoints/workflow/create-workflow-run), then [retrieve the run](/api-reference/endpoints/workflow/get-workflow-run) (`workflowRuns.retrieve`). Find the `stepRuns` entry with `stepType: "VALIDATION"` and `step.name: "validate_invoice"`.

The step's `result` contains `valid` and a `blocks` array. Each block result includes `id`, `name`, and `valid`, with optional `reasoning` and `error`. `result` is nullable for a step run without a result. An ordinary invalid result takes the `fail` route; an execution error fails the step instead of following that route.

See [Validation Step in the step reference](/workflows/configuring-workflows#validation-step) for the configuration contract and the [Validation pattern](/workflows/configuring-workflows#validation) for a complete workflow definition.

## Via Extend Studio

### Build validations with Composer chat

Open the workflow you want to edit in Extend Studio and open **Composer** in the workflow builder. You need workflow edit access for Composer to change the draft.

![Workflow builder with Validation blocks in the center and Composer chat on the right, including the prompt box, approval mode, and Review and Undo controls](/_fern-img/de725e4f5f995240d2b9a2b628e2f6b1f69c7266edf9ed8b146478da7beb5d5a.webp)

1. Start a new chat with **+**, or continue the current conversation. Composer works on the workflow displayed in the builder.
2. Use the mode selector below the message box to choose **Ask for approval** or **Edit automatically**. The first mode lets you accept changes before they are applied; the second applies draft changes while Composer works.
3. Describe the upstream step, required fields, whether each check should use code or semantic validation, and the destinations for valid and invalid runs. Composer can inspect upstream variables before writing the blocks.

```text
After extract_invoice, add a Validation step named validate_invoice. Add a code block that fails when invoice_number or vendor_name is missing. Route valid runs to send_response and invalid runs to review_invoice. Create those downstream steps if they do not exist.
```

4. Inspect the proposed update with **Review**. Confirm the block contents, upstream references, and **If All Valid** / **If Any Invalid** connections. Accept the proposal if you chose **Ask for approval**; use **Undo** to revert an applied change.
5. Refine the step in the same chat. Composer can add, edit, remove, and reorder individual blocks without replacing the rest of the step.

```text
Add a semantic validation to validate_invoice that checks whether the vendor name is plausible. Put it after the required-fields check.
```

6. Select the Validation node and **Configure** to inspect the generated code and semantic blocks. Run a representative document, inspect each block's result, and deploy the workflow when the routes and checks behave as intended.

Use the chat history menu to reopen a saved conversation and continue refining the same workflow. See [Composer chat](/general/composer-chat) for permissions and history.

![Composer chat history menu showing saved workflow conversations and the search box for reopening a previous chat](/_fern-img/fe6f1f3f38663696f5a82d6ba4cab56cb22bfb4ca3992986ea5a5868822a1830.webp)

### Add and configure a Validation step manually

1. Add a **Validation** node to the workflow and connect an upstream step to it.
2. Connect the **If All Valid** and **If Any Invalid** outputs to their downstream steps. For example, send valid runs to a Webhook Response and invalid runs to Human Review.
3. Select **Configure** on the node.
4. Add a block, give it a descriptive name, and choose **Code** or **Semantic**.
5. Use the Variables panel to browse or drag in data from upstream steps.
6. Add more blocks as needed and drag them into the order you want. Changes save automatically.

The incoming connection controls when the Validation step runs. A block can reference any supported upstream output, even when that upstream step is not directly connected to the Validation node.

### Code blocks

Code blocks run JavaScript in a sandbox with a read-only `context` object. The editor offers completion and type checking for the variables available at that point in the workflow.

For a single-file workflow, access upstream step data through `context.outputs`:

```javascript
const invoiceNumber = context.outputs.extract_invoice.invoice_number.value;
const invoiceDate = context.outputs.extract_invoice.invoice_date.value;

if (invoiceNumber == null || invoiceDate == null) {
  return [false, "Invoice number and date are required"];
}

return true;
```

You can also read workflow-run metadata through `context.metadata`. Classification outputs expose `label` and `confidence` directly rather than under `.value`.

> **Note**
>
> When a Validation step runs after a Collect step, `context.outputs.<step>` is an array with one entry for each file that ran that step. These arrays are not index-aligned with `context.files`, because routing may skip a step for some files. Use `context.outputs.<step>` for aggregate checks and `context.files[i].outputs.<step>` when you need to associate an output with a specific file. A step skipped for a file is `undefined`, so check for its presence or use optional chaining before reading its fields. The Variables panel reflects the correct shape for the current workflow.

### Semantic blocks

Describe what must be true in natural language. Use semantic validation when the decision requires interpretation rather than an exact calculation.

For example:

```text
The vendor name {{ outputs.extract_invoice.vendor_name.value }} must be a plausible company name, not a placeholder or a person's name.
```

Type `{{` or drag a variable from the Variables panel to insert an upstream value. Variables inside the templates are formatted and queried using [JMESPath](https://jmespath.org/); they are not the formulas used by legacy Rule Validation steps.

### View run results

Open a completed workflow run and select the Validation step. The result shows the overall pass or fail status, the number of code and semantic checks, and the status or reasoning for each evaluated block. If a code block errors, the run identifies the block that failed so you can inspect its configuration.