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

# Node schemas

> Every field the API accepts on each workflow node, the shared types they use, the edge shape, and the topology rules a graph must satisfy.

This page is transcribed from the API's request schema. It lists the fields a public caller may set on each node of a [workflow](/concepts/workflows), with their type, whether they are required, and their default. The machine-readable form is the OpenAPI document at [https://api.anyformat.ai/schema/](https://api.anyformat.ai/schema/). Each node's guide page explains what the fields do and when to use them.

A workflow body is `{ name, description, nodes, edges }`. Every node carries an `id` (unique within the graph) and a `type` (the discriminator). The API rejects unknown keys on any node with `400 VALIDATION_ERROR`, so a field that is not on this page is not accepted, even if the app shows it.

Write only the fields you want to set. A `GET` echoes every node with every default filled in (for example `"cache": true` on a Parse node you created with only `id` and `type`), and every edge with `"branch": null` where you set none. That echoed shape is what `PATCH` accepts back.

## parse

Reads the document and turns it into text, tables and layout. Every workflow has exactly one. Guide: [Parse](/guides/nodes/parse).

| Field                | Type                                         | Required | Default    | Description                                                                                                                                                                                     |
| -------------------- | -------------------------------------------- | -------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                 | string                                       | yes      |            | Stable identifier for this node within the graph.                                                                                                                                               |
| `type`               | `"parse"`                                    | yes      |            |                                                                                                                                                                                                 |
| `mode`               | `standard` \| `agentic` \| `lite` \| `flash` | no       | `standard` | The parse tier. `lite` is shown as **Fast** in the app.                                                                                                                                         |
| `prompt_hint`        | string \| null                               | no       | `null`     | Free-form hint that biases the parse output. Standard, agentic and flash only; ignored in lite.                                                                                                 |
| `figure_enhancement` | boolean                                      | no       | `false`    | Standard mode: extract structured data from charts and images. Extra processing per figure. Ignored in agentic and lite.                                                                        |
| `cache`              | boolean                                      | no       | `true`     | Reuse the parsed output when the same file was already parsed with the same mode and settings. A hit skips the whole parse. Set `false` to force a fresh parse.                                 |
| `effort`             | `low` \| `mid` \| `accurate`                 | no       | `mid`      | Agentic mode only: quality and cost preset.                                                                                                                                                     |
| `scanned_pages`      | `ocr` \| `skip` \| `fail`                    | no       | `ocr`      | Flash mode only: what to do with a page whose text layer produced no words. `ocr` parses it like any other page, `skip` serves it blank and flags it, `fail` raises and names the page numbers. |

A field that belongs to another tier is accepted and ignored. A `GET` also echoes `"ocr_effort": "high"`: a retired lite-tier knob, accepted and ignored, kept on the wire for compatibility. Do not set it.

```json theme={null}
{ "id": "parse_1", "type": "parse", "mode": "agentic", "effort": "accurate" }
```

Echoed by `GET`:

```json theme={null}
{ "id": "parse_1", "type": "parse", "mode": "agentic", "prompt_hint": null, "figure_enhancement": false, "cache": true, "effort": "accurate", "ocr_effort": "high", "scanned_pages": "ocr" }
```

## extract

Pulls the fields of a schema out of the parsed document, each with a value, a confidence and evidence. Guide: [Extract](/guides/nodes/extract).

| Field                     | Type                                             | Required | Default    | Description                                                                                                                                                           |
| ------------------------- | ------------------------------------------------ | -------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                      | string                                           | yes      |            | Stable identifier for this node within the graph.                                                                                                                     |
| `type`                    | `"extract"`                                      | yes      |            |                                                                                                                                                                       |
| `mode`                    | `standard` \| `agentic` \| `lite`                | no       | `standard` | The extraction tier. `lite` is shown as **Fast** in the app.                                                                                                          |
| `extraction_schema`       | [ExtractionSchema](#extractionschema-and-fields) | yes      |            | The fields this node extracts. At least one.                                                                                                                          |
| `lookup_files`            | string\[]                                        | no       | `[]`       | URIs of the [Smart Lookup](/guides/nodes/smart-lookup) reference files stored on the node. Read it back from `GET`; write `lookup_file_uploads` to add files.         |
| `lookup_suggestion`       | string \| null                                   | no       | `null`     | Free-form hint shown to the smart-lookup matcher.                                                                                                                     |
| `lookup_reasoning_effort` | `minimal` \| `low` \| `medium` \| `high` \| null | no       | `null`     | Reasoning effort for the smart-lookup matcher. `null` is the model default. Higher effort improves match reliability on noisy join keys at higher latency and cost.   |
| `lookup_file_uploads`     | [LookupFileUpload](#lookupfileupload)\[]         | no       | `[]`       | Inline reference-file content. Each entry is uploaded and its URI is appended to `lookup_files`. Create-input only: the API does not store it on the node.            |
| `use_images`              | boolean                                          | no       | `false`    | Standard and lite modes: send each PDF page's rendered image alongside its parsed text, so the model can read layout the text missed. Adds vision cost on every page. |

```json theme={null}
{
  "id": "extract_1",
  "type": "extract",
  "extraction_schema": {
    "fields": [
      { "name": "invoice_number", "description": "The unique invoice identifier", "data_type": "string" },
      { "name": "total_amount",   "description": "Total invoice amount",          "data_type": "float" }
    ]
  }
}
```

Echoed by `GET`. Every field carries the server-assigned `persistent_id` and its `source`; echo both back on `PATCH`:

```json theme={null}
{
  "id": "extract_1",
  "type": "extract",
  "mode": "standard",
  "extraction_schema": {
    "fields": [
      { "persistent_id": "0686bb97-8c30-70f0-8000-97669e00aaaa", "name": "invoice_number", "description": "The unique invoice identifier", "source": "extraction", "data_type": "string" },
      { "persistent_id": "0686bb97-8c30-70f0-8000-97669e00aaab", "name": "total_amount",   "description": "Total invoice amount",          "source": "extraction", "data_type": "float" }
    ]
  },
  "lookup_files": [],
  "lookup_suggestion": null,
  "lookup_reasoning_effort": null,
  "lookup_file_uploads": [],
  "use_images": false
}
```

## classify

Labels the document as one of your categories and routes it down that category's branch. Guide: [Classify](/guides/nodes/classify).

| Field         | Type                                     | Required | Default | Description                                                                               |
| ------------- | ---------------------------------------- | -------- | ------- | ----------------------------------------------------------------------------------------- |
| `id`          | string                                   | yes      |         | Stable identifier for this node within the graph.                                         |
| `type`        | `"classify"`                             | yes      |         |                                                                                           |
| `user_prompt` | string \| null                           | no       | `null`  | Extra instructions inserted between the classifier's system prompt and the document text. |
| `categories`  | [ClassifyCategory](#classifycategory)\[] | yes      |         | At least one. Category ids and names must each be unique within the node.                 |

```json theme={null}
{
  "id": "classify_1",
  "type": "classify",
  "categories": [
    { "id": "INVOICE", "name": "Invoice", "description": "A vendor invoice." },
    { "id": "RECEIPT", "name": "Receipt", "description": "A point-of-sale receipt." }
  ]
}
```

## splitter

Breaks one file that holds several documents into pieces, one rule per piece, and routes each rule down its own branch. The wire `type` is `splitter`; the app calls it **Split**. Guide: [Split](/guides/nodes/split).

| Field   | Type                             | Required | Default | Description                                                           |
| ------- | -------------------------------- | -------- | ------- | --------------------------------------------------------------------- |
| `id`    | string                           | yes      |         | Stable identifier for this node within the graph.                     |
| `type`  | `"splitter"`                     | yes      |         |                                                                       |
| `rules` | [SplitterRule](#splitterrule)\[] | yes      |         | At least one. Rule ids and names must each be unique within the node. |

```json theme={null}
{
  "id": "split_1",
  "type": "splitter",
  "rules": [
    { "id": "STATEMENT", "name": "Statement", "description": "A bank account statement.", "partition_key": "account_number" },
    { "id": "CHECK",     "name": "Check",     "description": "A scanned check." }
  ]
}
```

## validate

Checks the values an Extract node produced against your rules and records a verdict per rule. Guide: [Validate](/guides/nodes/validate).

| Field   | Type                                 | Required | Default | Description                                       |
| ------- | ------------------------------------ | -------- | ------- | ------------------------------------------------- |
| `id`    | string                               | yes      |         | Stable identifier for this node within the graph. |
| `type`  | `"validate"`                         | yes      |         |                                                   |
| `rules` | [ValidationRule](#validationrule)\[] | yes      |         | At least one.                                     |

```json theme={null}
{
  "id": "validate_1",
  "type": "validate",
  "rules": [
    { "id": "vendor-legit", "description": "The vendor is a real, named company." },
    {
      "id": "totals-add-up",
      "kind": "deterministic",
      "check": { "type": "arithmetic", "operands": ["<subtotal persistent_id>", "<tax persistent_id>"], "equals": "<total persistent_id>", "tolerance": 0.01 }
    }
  ]
}
```

## if\_else

Evaluates one check against the upstream extraction and routes the run down a `true` or a `false` branch. Guide: [If/Else](/guides/nodes/if-else).

| Field       | Type            | Required | Default | Description                                                                                     |
| ----------- | --------------- | -------- | ------- | ----------------------------------------------------------------------------------------------- |
| `id`        | string          | yes      |         | Stable identifier for this node within the graph.                                               |
| `type`      | `"if_else"`     | yes      |         |                                                                                                 |
| `condition` | [Check](#check) | yes      |         | The check to evaluate. A `validation` check routes on the outcome of an upstream Validate rule. |

The outgoing edges carry `"branch": "true"` and `"branch": "false"`. The `true` edge is required; the `false` edge may be left unwired.

```json theme={null}
{
  "id": "if_else_1",
  "type": "if_else",
  "condition": { "type": "validation", "rule_id": "totals-add-up", "status": "fail" }
}
```

## slack\_alert

Posts a rendered message to a Slack channel at the end of a branch. It emits nothing downstream. Guide: [Slack alert](/guides/nodes/slack-alert).

| Field              | Type                              | Required | Default | Description                                                                                                                                                                                                                                               |
| ------------------ | --------------------------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`               | string                            | yes      |         | Stable identifier for this node within the graph.                                                                                                                                                                                                         |
| `type`             | `"slack_alert"`                   | yes      |         |                                                                                                                                                                                                                                                           |
| `channel_id`       | string                            | yes      |         | Canonical Slack channel id, for example `C0123ABC`. Stable across renames.                                                                                                                                                                                |
| `channel_name`     | string                            | yes      |         | The channel's display name at save time, for example `#finance`. Shown in logs and Studio; not re-resolved on every send.                                                                                                                                 |
| `message_template` | string                            | yes      |         | Message body. `${field.<persistent_id>}` inserts an extracted value. With an upstream Validate node, `${validation.<rule_id>.status}`, `.detail` and `.severity` insert a rule's outcome. A missing field or unknown rule id renders as `<missing: ...>`. |
| `severity`         | `info` \| `warning` \| `critical` | no       | `info`  | Colour of the message's attachment bar. Presentational only.                                                                                                                                                                                              |

```json theme={null}
{
  "id": "slack_alert_1",
  "type": "slack_alert",
  "channel_id": "C0123ABC",
  "channel_name": "#finance",
  "message_template": "Totals do not reconcile on ${field.0686bb97-8c30-70f0-8000-97669e00aaaa}: ${validation.totals-add-up.detail}",
  "severity": "warning"
}
```

## edit

Detects the fillable fields on the parsed PDF, fills them, and returns the filled PDF. Terminal: nothing accepts an edge from it. Guide: [Edit](/guides/nodes/edit).

| Field                    | Type                        | Required | Default     | Description                                                                                                                                                                                   |
| ------------------------ | --------------------------- | -------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                     | string                      | yes      |             | Stable identifier for this node within the graph.                                                                                                                                             |
| `type`                   | `"edit"`                    | yes      |             |                                                                                                                                                                                               |
| `instructions`           | string \| null              | no       | `null`      | Free-text values to write into the detected fields, for example `Name: ACME SL; Date: 2026-01-01; I accept the terms: yes`. Wins over a reference document where both address the same field. |
| `reference_document_ids` | string\[]                   | no       | `[]`        | Ids of reference documents uploaded to this workflow. Every id must belong to this workflow and be fully resolved, or the run is refused.                                                     |
| `font`                   | `sans` \| `serif` \| `mono` | no       | `sans`      | Typeface the filled values are written in.                                                                                                                                                    |
| `output_mode`            | `flattened` \| `editable`   | no       | `flattened` | `flattened` paints the values on and the PDF is final. `editable` leaves each value in a live form field.                                                                                     |

```json theme={null}
{
  "id": "edit_1",
  "type": "edit",
  "instructions": "Name: ACME SL; Date: 2026-01-01; I accept the terms: yes",
  "reference_document_ids": ["069dcc2c-e14c-7606-8000-2ee4fb17b4e2"]
}
```

## knowledge

Indexes every document the workflow parses into a per-workflow knowledge base. Its presence is the switch; it has no options. At most one per workflow. Guide: [Knowledge](/guides/nodes/knowledge).

| Field  | Type          | Required | Default | Description                                       |
| ------ | ------------- | -------- | ------- | ------------------------------------------------- |
| `id`   | string        | yes      |         | Stable identifier for this node within the graph. |
| `type` | `"knowledge"` | yes      |         |                                                   |

```json theme={null}
{ "id": "knowledge_1", "type": "knowledge" }
```

## Shared types

### ExtractionSchema and fields

`extraction_schema` is `{ "fields": [ ... ] }` with at least one field. Every field carries these keys, plus the ones its `data_type` adds. See [Field types](/concepts/field-types) for what each type extracts.

| Field           | Type                                                                                                          | Required                    | Default      | Description                                                                                                                                                          |
| --------------- | ------------------------------------------------------------------------------------------------------------- | --------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data_type`     | `string` \| `integer` \| `float` \| `boolean` \| `date` \| `datetime` \| `enum` \| `multi_select` \| `object` | yes                         |              | The discriminator.                                                                                                                                                   |
| `name`          | string                                                                                                        | yes                         |              | Field name. Used as the key in the extraction results.                                                                                                               |
| `description`   | string                                                                                                        | no                          | `""`         | Free-form description shown to the extraction model. An empty description is accepted but degrades quality.                                                          |
| `source`        | `extraction` \| `smart_lookup` \| `lookup_if_missing`                                                         | no                          | `extraction` | Where the value comes from: the document, the lookup file (always overwrites), or the document first and the lookup file where extraction left no value.             |
| `persistent_id` | string (UUID) \| null                                                                                         | no                          | `null`       | Server-assigned stable identity, returned by `GET`. Echo it on updates, including across renames. Omit for new fields. Sending one on create is rejected with `400`. |
| `enum_options`  | [EnumOption](#enumoption)\[]                                                                                  | `enum`, `multi_select` only |              | At least one option.                                                                                                                                                 |
| `nested_fields` | field\[]                                                                                                      | `object` only               |              | At least one child field. Only one level of nesting: a child cannot be an `object`.                                                                                  |

### EnumOption

| Field         | Type   | Required | Default | Description                               |
| ------------- | ------ | -------- | ------- | ----------------------------------------- |
| `name`        | string | yes      |         | The option value.                         |
| `description` | string | yes      |         | Free-form description shown to the model. |

### ClassifyCategory

| Field         | Type   | Required | Default | Description                                                                                         |
| ------------- | ------ | -------- | ------- | --------------------------------------------------------------------------------------------------- |
| `id`          | string | yes      |         | Stable category id. The `branch` value on the edge that leaves the Classify node for this category. |
| `name`        | string | yes      |         | Display name shown to the model. Must be unique across the node's categories.                       |
| `description` | string | yes      |         | Free-form description shown to the model.                                                           |

### SplitterRule

| Field           | Type   | Required | Default | Description                                                                                                                                                                                        |
| --------------- | ------ | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`            | string | yes      |         | Stable rule id. The `branch` value on the edge that leaves the Split node for this rule.                                                                                                           |
| `name`          | string | yes      |         | Display name shown to the model. Must be unique across the node's rules.                                                                                                                           |
| `description`   | string | yes      |         | Free-form description shown to the model.                                                                                                                                                          |
| `partition_key` | string | no       | `""`    | Field that further partitions this rule into separate sub-documents, for example `invoice_number`. Each distinct value becomes its own split. Empty means the whole rule flows on as one document. |

### ValidationRule

A rule is either AI-evaluated (`kind: "ai"`, a natural-language `description`) or deterministic (`kind: "deterministic"`, a structured `check`). An `ai` rule must not carry a `check`; a `deterministic` rule must carry one.

| Field           | Type                    | Required | Default | Description                                                                                                    |
| --------------- | ----------------------- | -------- | ------- | -------------------------------------------------------------------------------------------------------------- |
| `id`            | string                  | yes      |         | Stable rule id. Round-trips through the validation results, and is what `if_else` and `slack_alert` reference. |
| `name`          | string \| null          | no       | `null`  | Human-readable rule name.                                                                                      |
| `kind`          | `ai` \| `deterministic` | no       | `ai`    | How the rule is evaluated.                                                                                     |
| `description`   | string \| null          | no       | `null`  | Natural-language rule sent to the model. Required when `kind` is `ai`.                                         |
| `check`         | [Check](#check) \| null | no       | `null`  | Structured check run in code. Required when `kind` is `deterministic`. Must not contain a `validation` check.  |
| `severity`      | `error` \| `warning`    | no       | `error` | How a failed rule is labelled in results. Never blocks the run.                                                |
| `source_fields` | string\[]               | no       | `[]`    | Persistent ids of the fields this rule references.                                                             |

### Check

A check is a JSON object discriminated on `type`. Field operands are `persistent_id`s, except in `expression`, which addresses fields by name. Combinators nest.

| `type`       | Fields                                                                                                                                                                       | Description                                                                                                                                                                   |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `range`      | `field` (string, required), `min` (number \| null), `max` (number \| null)                                                                                                   | The numeric field lies within the inclusive bounds.                                                                                                                           |
| `date`       | `field` (string, required), `earliest` (string \| null), `latest` (string \| null)                                                                                           | The date field lies within the inclusive bounds. Each bound is an ISO date `YYYY-MM-DD` or the literal `today`.                                                               |
| `arithmetic` | `operands` (string\[], at least one, required), `operator` (`sum` \| `subtract` \| `product`, default `sum`), `equals` (string, required), `tolerance` (number, default `0`) | Combining the operands with the operator equals the `equals` field, within `tolerance`.                                                                                       |
| `comparison` | `left` (string, required), `op` (`==` \| `!=` \| `>` \| `>=` \| `<` \| `<=`, required), `right` (required)                                                                   | Compares the `left` field against `right`, which is `{ "source": "field", "field": "<persistent_id>" }` or `{ "source": "literal", "value": <number \| string \| boolean> }`. |
| `one_of`     | `field` (string, required), `allowed` (string\[], at least one, required), `case_sensitive` (boolean, default `false`)                                                       | The value is one of `allowed`.                                                                                                                                                |
| `regex`      | `field` (string, required), `pattern` (string, required)                                                                                                                     | The value matches the pattern. Evaluated with RE2 (linear time).                                                                                                              |
| `required`   | `field` (string, required)                                                                                                                                                   | The field has a value.                                                                                                                                                        |
| `confidence` | `field` (string, required), `op` (`<` \| `<=` \| `>` \| `>=` \| `==` \| `!=`, required), `threshold` (integer 0 to 100, required)                                            | Compares the field's extraction confidence against `threshold`. `field` is a `persistent_id` or the field's name.                                                             |
| `expression` | `expression` (string, 1 to 1000 characters, required)                                                                                                                        | A CEL expression over the root variable `data`, for example `data.total == data.subtotal + data.tax`. Fields are addressed by name.                                           |
| `validation` | `rule_id` (string, required), `status` (`fail` \| `pass` \| `inconclusive`, default `fail`)                                                                                  | True when the named upstream Validate rule ended with `status`. Only valid on an `if_else` condition.                                                                         |
| `all_of`     | `checks` (Check\[], 1 to 32, required)                                                                                                                                       | Passes when every child passes.                                                                                                                                               |
| `any_of`     | `checks` (Check\[], 1 to 32, required)                                                                                                                                       | Passes when any child passes.                                                                                                                                                 |

### LookupFileUpload

| Field      | Type   | Required | Default | Description                                   |
| ---------- | ------ | -------- | ------- | --------------------------------------------- |
| `filename` | string | yes      |         | The file's name, for example `suppliers.csv`. |
| `content`  | string | yes      |         | The file's bytes, base64-encoded.             |

## Edges

`edges` is a list of directed connections between node ids. A workflow with only a Parse node needs no edges.

| Field    | Type           | Required | Default | Description                                                                                                                    |
| -------- | -------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `source` | string         | yes      |         | The `id` of the node the edge leaves.                                                                                          |
| `target` | string         | yes      |         | The `id` of the node the edge enters.                                                                                          |
| `branch` | string \| null | no       | `null`  | The source port. Required on every edge that leaves a `classify`, `splitter` or `if_else` node; forbidden on every other edge. |

`branch` must equal a category `id` on a Classify node, a rule `id` on a Split node, or `"true"` / `"false"` on an If/Else node. It is the id, not the name. A `GET` echoes an edge you wrote as `{ "source", "target" }` as `{ "source": "parse_1", "target": "extract_1", "branch": null }`.

```json theme={null}
"edges": [
  { "source": "parse_1",    "target": "classify_1" },
  { "source": "classify_1", "target": "extract_invoice", "branch": "INVOICE" },
  { "source": "classify_1", "target": "extract_receipt", "branch": "RECEIPT" }
]
```

## Topology rules

The API validates the whole graph on create and on update. A graph that breaks a rule is rejected with `400` and `error_code: "TOPOLOGY_INVALID"`; `detail.violations[]` names every rule broken and the node ids involved, so one round trip shows every problem.

Which node may feed which:

| Node          | Accepts an edge from                       |
| ------------- | ------------------------------------------ |
| `parse`       | nothing. Parse is always first.            |
| `classify`    | `parse`                                    |
| `splitter`    | `parse`, `classify`                        |
| `extract`     | `parse`, `classify`, `splitter`            |
| `validate`    | `extract`                                  |
| `if_else`     | `extract`, `validate`                      |
| `slack_alert` | `extract`, `validate`, `if_else`           |
| `knowledge`   | `parse`, `classify`, `extract`, `validate` |
| `edit`        | `parse`                                    |

The graph-level rules:

* Exactly one `parse` node.
* Node ids are unique. Every edge names existing nodes. Every node except `parse` has an inbound edge. No cycles.
* Only `classify`, `splitter` and `if_else` fan out. Every other node has at most one outgoing edge.
* `validate`, `if_else` and `slack_alert` read one upstream extraction, so each accepts exactly one incoming edge. `extract` may receive several branches of one Classify node.
* Each Split rule routes to its own target node. Two rules cannot share a target.
* Within one node, category or rule `id`s are unique and `name`s are unique. Two edges leaving the same node cannot carry the same `branch`.
* Every `if_else` has a `true` outgoing edge.
* An `extract` cannot sit downstream of an `if_else` today. That fan-out shape is not yet supported.
* Every `extract` has at least one field.
* At most one `knowledge` node.
* Every `rule_id` in an `if_else` condition, and every `${validation.<rule_id>...}` token in a `slack_alert` template, names a rule on a Validate node reachable upstream of that node.
* `edit` is terminal: no node accepts an edge from it.
