> ## 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.

# Create Workflow

> Create a workflow from a typed graph of parse / classify / splitter / extract nodes in a single atomic transaction

<Tip>
  We recommend creating workflows in the [anyformat platform](https://app.anyformat.ai) where you can visually configure fields, test with sample documents, and iterate faster. Once your workflow is ready, copy its ID and use it with the API to run workflows programmatically.
</Tip>

`POST /v2/workflows/` creates a workflow from a strongly-typed graph. Use it to:

* **Configure parse-node settings** (standard or agentic mode, prompt hints, figure enhancement)
* **Build parse-only workflows** (no extract node — markdown only)
* **Route documents through classifiers or splitters** to multiple extract nodes
* **Run simple linear `parse → extract` workflows**

## Request Body

| Field         | Type    | Required | Description                                           |
| ------------- | ------- | -------- | ----------------------------------------------------- |
| `name`        | string  | Yes      | Workflow name                                         |
| `description` | string  | No       | Optional description                                  |
| `nodes`       | Node\[] | Yes      | At least one node; exactly one must be `type="parse"` |
| `edges`       | Edge\[] | No       | Directed edges; empty for parse-only workflows        |

### Parse Node

The entry-point node. Exactly one per workflow.

| Field                | Type                      | Default      | Description                                                                            |
| -------------------- | ------------------------- | ------------ | -------------------------------------------------------------------------------------- |
| `id`                 | string                    | —            | Stable identifier (e.g. `"parse_1"`)                                                   |
| `type`               | `"parse"`                 | —            | Discriminator                                                                          |
| `mode`               | `"standard" \| "agentic"` | `"standard"` | Use `"agentic"` for per-block LLM routing through typed strategies (text/table/figure) |
| `prompt_hint`        | string                    | `null`       | Domain hint shown to the parser (e.g. "medical lab report, preserve numerics exactly") |
| `figure_enhancement` | boolean                   | `false`      | Extract structured descriptions of charts and images (extra LLM cost)                  |

### Extract Node

Pulls structured fields from upstream parsed content.

| Field                 | Type        | Required             | Description                                                                                                    |
| --------------------- | ----------- | -------------------- | -------------------------------------------------------------------------------------------------------------- |
| `id`                  | string      | Yes                  | Stable identifier                                                                                              |
| `type`                | `"extract"` | Yes                  | Discriminator                                                                                                  |
| `extraction_schema`   | object      | Yes                  | `{ "fields": [...] }` — at least one field                                                                     |
| `use_images`          | boolean     | No (default `false`) | Pass rendered page images to the extraction model                                                              |
| `lookup_file_uploads` | object\[]   | No                   | Inline reference files for [smart lookup](#smart-lookup), each `{ "filename": string, "content": "<base64>" }` |
| `lookup_files`        | string\[]   | No                   | Reference files already stored as S3 URIs (alternative to `lookup_file_uploads`)                               |
| `lookup_suggestion`   | string      | No                   | Free-form hint telling the matcher how to join the document against the reference file                         |

Field shape — see [Field Types](/concepts/field-types). Any field may set `"source": "smart_lookup"` to make it a [smart-lookup field](#smart-lookup) (resolved from a reference file rather than read from the document), or `"source": "lookup_if_missing"` to extract it too and let the lookup only fill empty slots; the default is `"extraction"`.

### Classify Node

Routes the document to one of `categories[]` based on an LLM verdict. Outgoing edges must set `branch` to a category `id`.

| Field         | Type         | Required | Description                                    |
| ------------- | ------------ | -------- | ---------------------------------------------- |
| `id`          | string       | Yes      | Stable identifier                              |
| `type`        | `"classify"` | Yes      | Discriminator                                  |
| `categories`  | Category\[]  | Yes      | At least one category                          |
| `user_prompt` | string       | No       | Optional prompt prefix shown to the classifier |

Each `Category`:

| Field         | Type   | Required | Description                                                           |
| ------------- | ------ | -------- | --------------------------------------------------------------------- |
| `id`          | string | Yes      | Stable category id; matched against an outgoing edge's `branch` value |
| `name`        | string | Yes      | Display name shown to the LLM                                         |
| `description` | string | Yes      | Free-form description shown to the LLM                                |

### Splitter Node

Partitions a multi-document file into per-rule sub-documents. Each rule fires an outgoing edge whose `branch` matches the rule `id`; the downstream `extract` node runs once per resulting partition.

| Field   | Type         | Required | Description       |
| ------- | ------------ | -------- | ----------------- |
| `id`    | string       | Yes      | Stable identifier |
| `type`  | `"splitter"` | Yes      | Discriminator     |
| `rules` | Rule\[]      | Yes      | At least one rule |

Each `Rule`:

| Field           | Type   | Required          | Description                                                               |
| --------------- | ------ | ----------------- | ------------------------------------------------------------------------- |
| `id`            | string | Yes               | Stable rule id; matched against an outgoing edge's `branch` value         |
| `name`          | string | Yes               | Display name                                                              |
| `description`   | string | Yes               | Free-form description shown to the splitter model                         |
| `partition_key` | string | No (default `""`) | Optional key used to label resulting partitions; empty means routing-only |

### Validate Node

Runs after an `extract` node and emits per-rule validation results. Rules are either AI-evaluated (`kind="ai"` with a natural-language `description`) or deterministic (`kind="deterministic"` with a structured `check` evaluated in pure Python, no model call).

| Field   | Type         | Required | Description       |
| ------- | ------------ | -------- | ----------------- |
| `id`    | string       | Yes      | Stable identifier |
| `type`  | `"validate"` | Yes      | Discriminator     |
| `rules` | Rule\[]      | Yes      | At least one rule |

Each `Rule`:

| Field           | Type                      | Required               | Description                                                                                      |
| --------------- | ------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------ |
| `id`            | string                    | Yes                    | Stable rule id; round-trips through `ValidationResult.rule_id`                                   |
| `kind`          | `"ai" \| "deterministic"` | No (default `"ai"`)    | Evaluation strategy                                                                              |
| `name`          | string                    | No                     | Optional display name for the rule card                                                          |
| `description`   | string                    | Conditional            | Natural-language prompt; required when `kind="ai"`, forbidden when `kind="deterministic"`        |
| `check`         | Check                     | Conditional            | Structured deterministic check; required when `kind="deterministic"`, forbidden when `kind="ai"` |
| `severity`      | `"error" \| "warning"`    | No (default `"error"`) | Severity of a violation                                                                          |
| `source_fields` | string\[]                 | No                     | `persistent_id`s of fields the rule references                                                   |

Deterministic `check` shapes (discriminated on `type`). Every shape below except `expression` names its field operands by `persistent_id`; `expression` addresses fields by extracted name instead.

| `type`       | Required fields                            | Optional fields                                                                               | Meaning                                                                                                                                                                                                                                                                                                                                    |
| ------------ | ------------------------------------------ | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `range`      | `field`                                    | `min`, `max` (at least one)                                                                   | Numeric `field` is within `[min, max]`                                                                                                                                                                                                                                                                                                     |
| `date`       | `field`                                    | `earliest`, `latest` (ISO `YYYY-MM-DD` or the literal `"today"`)                              | Date `field` is within the inclusive bounds                                                                                                                                                                                                                                                                                                |
| `arithmetic` | `operands[]` (at least one item), `equals` | `operator` (`"sum" \| "subtract" \| "product"`, default `"sum"`), `tolerance` (default `0.0`) | `operator(operands)` equals the value of the `equals` field, within `tolerance`                                                                                                                                                                                                                                                            |
| `comparison` | `left`, `op`, `right`                      | —                                                                                             | `left <op> right` holds. `op`: `"==" \| "!=" \| ">" \| ">=" \| "<" \| "<="`. `right` is `{"source": "field", "field": "<pid>"}` or `{"source": "literal", "value": <number\|string\|bool>}`                                                                                                                                                |
| `one_of`     | `field`, `allowed[]` (at least one item)   | `case_sensitive` (default `false`)                                                            | `field`'s value is one of `allowed`                                                                                                                                                                                                                                                                                                        |
| `regex`      | `field`, `pattern`                         | —                                                                                             | `field` matches `pattern`. Evaluated with google-re2 (linear time); stdlib `re` syntax that isn't re2-safe is rejected                                                                                                                                                                                                                     |
| `required`   | `field`                                    | —                                                                                             | `field` is present (non-null, non-empty)                                                                                                                                                                                                                                                                                                   |
| `expression` | `expression` (1–1000 chars)                | —                                                                                             | A [CEL](https://github.com/google/cel-spec) expression over the whole extraction, bound to one root variable `data`. Fields are addressed by extracted name (`data.total`, `data.lineas.map(l, l.importe)`). Helpers on top of CEL: `num(v)`, `sum(list)`, `abs(x)`. Anything the evaluator cannot answer with a boolean is `inconclusive` |

Example — arithmetic check (`subtotal + tax == total`, ±1¢):

```json theme={null}
{
  "id": "totals_balance",
  "kind": "deterministic",
  "check": {
    "type": "arithmetic",
    "operator": "sum",
    "operands": ["pid_subtotal", "pid_tax"],
    "equals": "pid_total",
    "tolerance": 0.01
  },
  "source_fields": ["pid_subtotal", "pid_tax", "pid_total"]
}
```

### Edges

| Field    | Type   | Required    | Description                                                                                                                                                                                  |
| -------- | ------ | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `source` | string | Yes         | Source node id                                                                                                                                                                               |
| `target` | string | Yes         | Target node id                                                                                                                                                                               |
| `branch` | string | Conditional | Required when leaving a `classify` or `splitter` node; forbidden otherwise. **Must equal a category/rule `.id`** on the source node — not its `.name`. Using `.name` is rejected with `400`. |

## Examples

### Linear parse → extract

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST 'https://api.anyformat.ai/v2/workflows/' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -d '{
      "name": "Invoice extractor",
      "nodes": [
        {"id": "parse_1", "type": "parse"},
        {
          "id": "extract_1",
          "type": "extract",
          "extraction_schema": {
            "fields": [
              {"name": "invoice_number", "description": "Invoice ID", "data_type": "string"},
              {"name": "total", "description": "Grand total", "data_type": "float"}
            ]
          }
        }
      ],
      "edges": [{"source": "parse_1", "target": "extract_1"}]
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.anyformat.ai/v2/workflows/",
      headers={"Authorization": "Bearer YOUR_API_KEY"},
      json={
          "name": "Invoice extractor",
          "nodes": [
              {"id": "parse_1", "type": "parse"},
              {
                  "id": "extract_1",
                  "type": "extract",
                  "extraction_schema": {
                      "fields": [
                          {"name": "invoice_number", "description": "Invoice ID", "data_type": "string"},
                          {"name": "total", "description": "Grand total", "data_type": "float"},
                      ]
                  },
              },
          ],
          "edges": [{"source": "parse_1", "target": "extract_1"}],
      },
  )
  workflow_id = response.json()["id"]
  ```
</CodeGroup>

### Parse-only with agentic mode

A workflow with just one `parse` node and no edges. Produces markdown only — no extraction.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST 'https://api.anyformat.ai/v2/workflows/' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -d '{
      "name": "Agentic parse-only",
      "nodes": [
        {
          "id": "parse_1",
          "type": "parse",
          "mode": "agentic"
        }
      ],
      "edges": []
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.anyformat.ai/v2/workflows/",
      headers={"Authorization": "Bearer YOUR_API_KEY"},
      json={
          "name": "Agentic parse-only",
          "nodes": [
              {
                  "id": "parse_1",
                  "type": "parse",
                  "mode": "agentic",
              }
          ],
          "edges": [],
      },
  )
  workflow_id = response.json()["id"]
  ```
</CodeGroup>

<Note>
  **Agentic mode** routes each block through a typed strategy (text / table / figure) using Gemini for routing. The mode is binary — set `mode: "agentic"` and you get the full agentic pipeline. See the [Agentic Parse to Markdown](/guides/recipes/agentic-parse-to-markdown) recipe for an end-to-end walkthrough.
</Note>

### Classify-then-extract (branched)

Each outgoing edge from a `classify` (or `splitter`) sets `branch` to a category/rule **`.id`** on the source node. `.name` is the display label shown to the LLM and is **not** accepted as a branch value — using it returns `400`. In the example below, `cat_invoice` is the id used for routing; `Invoice` is the label the classifier sees.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST 'https://api.anyformat.ai/v2/workflows/' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -d '{
      "name": "Multi-doc",
      "nodes": [
        {"id": "parse_1", "type": "parse"},
        {
          "id": "classify_1",
          "type": "classify",
          "categories": [
            {"id": "cat_invoice", "name": "Invoice", "description": "Vendor invoice."},
            {"id": "cat_receipt", "name": "Receipt", "description": "POS receipt."}
          ]
        },
        {
          "id": "extract_invoice",
          "type": "extract",
          "extraction_schema": {"fields": [{"name": "vendor", "description": "Vendor name.", "data_type": "string"}]}
        },
        {
          "id": "extract_receipt",
          "type": "extract",
          "extraction_schema": {"fields": [{"name": "merchant", "description": "Merchant name.", "data_type": "string"}]}
        }
      ],
      "edges": [
        {"source": "parse_1", "target": "classify_1"},
        {"source": "classify_1", "target": "extract_invoice", "branch": "cat_invoice"},
        {"source": "classify_1", "target": "extract_receipt", "branch": "cat_receipt"}
      ]
    }'
  ```
</CodeGroup>

### Smart lookup

Enrich extracted values by matching them against a **reference file** instead of reading them off the page. Flag the looked-up field with `"source": "smart_lookup"`, attach the reference file (inline as base64 via `lookup_file_uploads`, or by S3 URI via `lookup_files`), and optionally describe the join with `lookup_suggestion`. See [Smart Lookup](/concepts/smart-lookup) for how matching works and the Studio UI equivalent of these fields. Here the model reads `vendor_name` from the document, then resolves the canonical `vendor_id` from a vendor catalog:

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST 'https://api.anyformat.ai/v2/workflows/' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -d '{
      "name": "Invoice + vendor lookup",
      "nodes": [
        {"id": "parse_1", "type": "parse"},
        {
          "id": "extract_1",
          "type": "extract",
          "lookup_file_uploads": [
            {"filename": "vendor_catalog.csv", "content": "dmVuZG9yX25hbWUsdmVuZG9yX2lkCkFjbWUgQ29ycCxWLTAwMDEK"}
          ],
          "lookup_suggestion": "Match the extracted vendor_name against the vendor_name column and return the matching vendor_id.",
          "extraction_schema": {
            "fields": [
              {"name": "vendor_name", "description": "Vendor as printed on the document", "data_type": "string"},
              {"name": "total", "description": "Grand total", "data_type": "float"},
              {"name": "vendor_id", "description": "Canonical vendor code from the catalog, joined on vendor_name", "data_type": "string", "source": "smart_lookup"}
            ]
          }
        }
      ],
      "edges": [{"source": "parse_1", "target": "extract_1"}]
    }'
  ```
</CodeGroup>

<Note>
  `lookup_file_uploads[].content` is the **base64-encoded** bytes of the reference file (CSV/text). The lookup field (`vendor_id`) is returned alongside the extraction fields in the results payload — there is no separate response section for it. An extract node that has a `source: smart_lookup`/`lookup_if_missing` field but no reference file is rejected with `400`.
</Note>

## Topology Rules

The endpoint enforces graph correctness — if any rule is violated, the request returns `400` and nothing is persisted.

| Rule                           | Message                                                                                                                                                                                          |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Exactly one parse node         | `workflow must contain exactly one \`parse\` node\`                                                                                                                                              |
| Unique node ids                | `duplicate node ids: [...]`                                                                                                                                                                      |
| Edges reference existing nodes | `edge source X not in nodes` / `edge target Y not in nodes`                                                                                                                                      |
| Edge predecessor compatibility | `extract does not accept extract as predecessor`                                                                                                                                                 |
| Branch routing                 | `edge from classify X requires \`branch\` to be set\`                                                                                                                                            |
| Branch matches source `.id`    | `edge from classify 'classify_1' sets branch='Invoice', but no category on 'classify_1' has that id (valid ids: ['cat_invoice']). \`branch\` must match a category \`.id\`, not its \`.name\`.\` |
| No fan-out from non-routers    | `node Y (extract) has 3 outgoing edges; only classify and splitter may fan out`                                                                                                                  |

## Response

`201 Created` returns the workflow resource:

```json theme={null}
{
  "id": "0686bb97-8c30-70f0-8000-97669e000eb8",
  "name": "Invoice extractor",
  "description": "",
  "created_at": "2026-05-11T10:00:00Z",
  "updated_at": "2026-05-11T10:00:00Z"
}
```

Save the `id` — you'll reuse it when uploading documents and fetching results.

## Next Steps

<CardGroup cols={2}>
  <Card title="Agentic Parse to Markdown" icon="wand-magic-sparkles" href="/guides/recipes/agentic-parse-to-markdown">
    End-to-end recipe: create → upload → results, with the new agentic parse mode
  </Card>

  <Card title="Parse-Only Workflow" icon="file-lines" href="/guides/recipes/parse-only-workflow">
    Convert documents to markdown without extraction
  </Card>

  <Card title="Field Types" icon="list" href="/concepts/field-types">
    All supported `data_type` values for extraction fields
  </Card>

  <Card title="Response Formats" icon="reply" href="/api-reference/response-formats">
    The shape of `parse`, `extractions`, `splits`, and `classifications` on the results endpoint
  </Card>
</CardGroup>


## OpenAPI

````yaml POST /v2/workflows/
openapi: 3.1.0
info:
  title: anyformat API
  description: >
    Document extraction and workflow automation API.


    AnyFormat lets you define extraction workflows that pull structured data
    from any document — PDFs, images, scanned files, or plain text. Upload a
    file, run it through a workflow, and get back structured fields with
    confidence scores and source evidence.


    ## Quick start


    See the [Quick start
    guide](https://docs.anyformat.ai/api-reference/introduction) for a
    walkthrough.


    ## Authentication


    All endpoints (except `/health/`) require a Bearer token in the
    `Authorization` header:


    ```

    Authorization: Bearer <your-api-key>

    ```


    Get your API key from
    [app.anyformat.ai/api-key](https://app.anyformat.ai/api-key).


    ## Versioning


    Endpoints are versioned by path prefix (`/v2/`, `/v3/`). Every response
    under

    a versioned prefix includes `X-API-Version`.
  version: 2.0.0
servers:
  - url: https://api.anyformat.ai
    description: API server
security: []
tags:
  - name: workflows
    description: >-
      Workflows define extraction templates — what fields to extract from
      documents. Create workflows, upload files, run extractions, and fetch
      results.
  - name: files
    description: >-
      File collections group uploaded documents and track their extraction
      progress. Upload files, check status, and retrieve extraction results.
  - name: suggestions
    description: >-
      AI-powered schema and workflow suggestion endpoints for the AnyFormat
      webapp. Authenticated with a Cognito JWT rather than an API key.
  - name: document-packets
    description: >-
      Document packets group uploaded documents for a workflow (v3). Address a
      packet directly to (re-)run its workflow.
  - name: runs
    description: >-
      Extraction runs — the v3 first-class result entity. Each run is a single
      execution of a workflow against a document packet; fetch its status and
      results flat by run id.
  - name: health
    description: Health check endpoints to verify API availability.
paths:
  /v2/workflows/:
    post:
      tags:
        - workflows
      summary: Create workflow
      description: |-
        Create a workflow from a strongly-typed graph (atomic).

        Provide an explicit list of typed `nodes` (parse / classify / splitter /
        extract) and `edges` between them. The full workflow — fields, nodes,
        routing — is created in a single transaction.
      operationId: v2_create_workflow
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowCreateRequest'
      responses:
        '201':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowResponse'
          headers:
            X-RateLimit-Limit:
              description: Requests allowed per window (60s).
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            X-RateLimit-Reset:
              description: Seconds until the current window resets.
              schema:
                type: integer
        '401':
          description: Authentication failed — missing, invalid, or expired API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                AUTH_FAILED:
                  value:
                    error: Authentication failed.
                    detail: Authentication failed.
                    error_code: AUTH_FAILED
                    retryable: false
                    request_id: req_abc123
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
        '429':
          description: >-
            Rate limited — retry after the `Retry-After` header. Default
            windows: 60 requests/minute for extraction submission, 600
            requests/minute for general endpoints.
          headers:
            Retry-After:
              description: Seconds until the next request is allowed.
              schema:
                type: integer
            X-RateLimit-Limit:
              description: Requests allowed per window (60s).
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            X-RateLimit-Reset:
              description: Seconds until the current window resets.
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                RATE_LIMITED:
                  value:
                    error: Rate limit exceeded — retry after backoff.
                    detail: Rate limit exceeded — retry after backoff.
                    error_code: RATE_LIMITED
                    retryable: true
                    request_id: req_abc123
      security:
        - ApiKeyAuth: []
components:
  schemas:
    WorkflowCreateRequest:
      properties:
        name:
          type: string
          minLength: 1
          title: Name
          examples:
            - Invoice or receipt
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          default: ''
        nodes:
          items:
            oneOf:
              - $ref: '#/components/schemas/PublicParseNode'
              - $ref: '#/components/schemas/ClassifyNode'
              - $ref: '#/components/schemas/SplitterNode'
              - $ref: '#/components/schemas/PublicExtractNode'
              - $ref: '#/components/schemas/PublicValidateNode'
              - $ref: '#/components/schemas/PublicIfElseNode'
              - $ref: '#/components/schemas/PublicSlackAlertNode'
            discriminator:
              propertyName: type
              mapping:
                classify:
                  $ref: '#/components/schemas/ClassifyNode'
                extract:
                  $ref: '#/components/schemas/PublicExtractNode'
                if_else:
                  $ref: '#/components/schemas/PublicIfElseNode'
                parse:
                  $ref: '#/components/schemas/PublicParseNode'
                slack_alert:
                  $ref: '#/components/schemas/PublicSlackAlertNode'
                splitter:
                  $ref: '#/components/schemas/SplitterNode'
                validate:
                  $ref: '#/components/schemas/PublicValidateNode'
          type: array
          minItems: 1
          title: Nodes
        edges:
          items:
            $ref: '#/components/schemas/Edge'
          type: array
          title: Edges
      additionalProperties: false
      type: object
      required:
        - name
        - nodes
      title: WorkflowCreateRequest
      description: >-
        Public-surface workflow create body — typed graph of parse / classify /
        splitter / extract / validate nodes.


        A ``validate`` node carries ``rules``; each rule is either AI-evaluated

        (``kind="ai"`` + a natural-language ``description``) or deterministic

        (``kind="deterministic"`` + a structured ``check``, evaluated in pure

        Python with no model call).


        Uses ``PublicNode`` (public request models with only the fields callers

        may set); the domain ``AnyNode`` also carries ``filter`` (worker-only)

        and executor-injected / staff-only fields, so the public bodies pin a

        stricter node schema. Call :meth:`to_domain` before validating topology

        or forwarding to the backend.
      examples:
        - description: Pull the invoice number and total from each document.
          edges:
            - source: parse
              target: extract
          name: Invoice totals
          nodes:
            - id: parse
              mode: standard
              type: parse
            - extraction_schema:
                fields:
                  - data_type: string
                    description: Invoice number as printed on the document.
                    name: invoice_number
                  - data_type: float
                    description: Invoice total in the document's currency.
                    name: total_amount
              id: extract
              mode: standard
              type: extract
    WorkflowResponse:
      properties:
        id:
          type: string
          title: Id
          description: Unique identifier of the workflow (UUID).
          examples:
            - 0686bb97-8c30-70f0-8000-97669e000eb8
        name:
          type: string
          title: Name
          description: Human-readable name of the workflow.
          examples:
            - Invoice Processing
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: Optional description of what this workflow extracts.
          examples:
            - A workflow for processing invoices and retrieving invoice details.
        created_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Created At
          description: Timestamp when the workflow was created (ISO 8601).
        updated_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Updated At
          description: Timestamp when the workflow was last modified (ISO 8601).
        fields:
          anyOf:
            - items:
                additionalProperties: true
                type: object
              type: array
            - type: 'null'
          title: Fields
          description: >-
            List of extraction field definitions configured for this workflow.
            `null` if not yet configured.
      type: object
      required:
        - id
        - name
      title: WorkflowResponse
      description: >-
        A workflow defines the extraction template — what fields to extract from
        documents, their types, and validation rules.
    ErrorEnvelope:
      type: object
      title: ErrorEnvelope
      description: >-
        Uniform error response. `retryable=true` means the same request may
        succeed on retry (with backoff); `retryable=false` means the caller must
        change the request before retrying.
      required:
        - error
        - detail
        - error_code
        - retryable
        - request_id
      properties:
        error:
          type: string
          description: Short human-readable summary.
        detail:
          description: >-
            Machine-readable detail. String for most errors; list of field
            errors for validation failures.
          oneOf:
            - type: string
            - type: array
              items:
                type: object
        error_code:
          type: string
          description: Stable identifier for programmatic handling.
          examples:
            - VALIDATION_ERROR
            - AUTH_FAILED
            - NOT_FOUND
            - PRECONDITION_FAILED
            - RATE_LIMITED
            - INTERNAL_ERROR
            - GATEWAY_TIMEOUT
        retryable:
          type: boolean
          description: True when the caller should retry (with backoff for 429/412/5xx).
        request_id:
          type: string
          description: Correlates this response with server logs.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    PublicParseNode:
      properties:
        id:
          type: string
          minLength: 1
          title: Id
          description: Stable identifier for this node within the graph.
        type:
          type: string
          const: parse
          title: Type
        mode:
          type: string
          enum:
            - standard
            - agentic
            - lite
          title: Mode
          description: >-
            Parse tier. 'standard' (default): page-by-page parsing with
            reading-order correction — the best quality/speed balance for most
            documents and tables. 'agentic': adaptive parsing that works hardest
            on dense tables (50+ rows, many columns) — highest fidelity on
            complex documents; extra cost per page. 'lite' (shown as 'Fast' in
            the app): OCR-first tier, fastest and cheapest — accurate OCR by
            default; set skip_routing_review=False to add a correction pass when
            reading order, figures, or text formatting matter.
          default: standard
        prompt_hint:
          anyOf:
            - type: string
            - type: 'null'
          title: Prompt Hint
          description: >-
            Free-form hint that biases the parse output. In lite mode it guides
            the correction pass, so it requires skip_routing_review=False.
        figure_enhancement:
          type: boolean
          title: Figure Enhancement
          description: >-
            Standard mode: extract structured data from charts and images —
            extra processing per figure. Ignored in agentic and in lite (use
            `figures` instead).
          default: false
        cache:
          type: boolean
          title: Cache
          description: >-
            Reuse parsed output when the same file was already parsed with the
            same mode and settings — a hit skips the whole parse. Disable to
            force a fresh parse.
          default: true
        effort:
          type: string
          enum:
            - low
            - mid
            - accurate
          title: Effort
          description: >-
            Agentic mode: quality/cost preset. 'low' = several times cheaper but
            more errors on dense or low-contrast tables, 'mid' (default) =
            balanced, 'accurate' = highest fidelity. (Distinct from lite
            `ocr_effort`.)
          default: mid
        ocr_effort:
          type: string
          enum:
            - medium
            - high
          title: Ocr Effort
          description: >-
            Lite mode: 'high' (default) = the strongest OCR — best accuracy on
            complex layouts, tables, and handwriting. 'medium' = faster and
            cheaper, for clean digital documents. (Distinct from agentic
            `effort`.)
          default: high
        skip_routing_review:
          type: boolean
          title: Skip Routing Review
          description: >-
            Lite mode: emit the OCR output as-is (the default — fastest and
            cheapest). Set False to add a correction pass that fixes reading
            order and OCR errors.
          default: true
        figures:
          type: boolean
          title: Figures
          description: >-
            Lite mode: the correction pass reads data points off charts/figures
            and reconstructs them as HTML tables (needs
            skip_routing_review=False).
          default: false
        text_formatting:
          type: boolean
          title: Text Formatting
          description: >-
            Lite mode: the correction pass restores inline styling
            (bold/italic/superscript), code, and LaTeX math (needs
            skip_routing_review=False).
          default: false
        routing_effort:
          anyOf:
            - type: string
              enum:
                - minimal
                - low
                - medium
                - high
            - type: 'null'
          title: Routing Effort
          description: >-
            Lite mode: effort of the correction pass's reading-order analysis.
            Lower it to cut latency on simple layouts.
      additionalProperties: false
      type: object
      required:
        - id
        - type
      title: PublicParseNode
    ClassifyNode:
      properties:
        id:
          type: string
          minLength: 1
          title: Id
          description: Stable identifier for this node within the graph.
        type:
          type: string
          const: classify
          title: Type
        user_prompt:
          anyOf:
            - type: string
            - type: 'null'
          title: User Prompt
          description: >-
            Optional extra instructions inserted between the classifier's system
            prompt and the document text.
        categories:
          items:
            $ref: '#/components/schemas/ClassifyCategory'
          type: array
          minItems: 1
          title: Categories
      additionalProperties: false
      type: object
      required:
        - id
        - type
        - categories
      title: ClassifyNode
    SplitterNode:
      properties:
        id:
          type: string
          minLength: 1
          title: Id
          description: Stable identifier for this node within the graph.
        type:
          type: string
          const: splitter
          title: Type
        rules:
          items:
            $ref: '#/components/schemas/SplitterRule'
          type: array
          minItems: 1
          title: Rules
      additionalProperties: false
      type: object
      required:
        - id
        - type
        - rules
      title: SplitterNode
    PublicExtractNode:
      properties:
        id:
          type: string
          minLength: 1
          title: Id
          description: Stable identifier for this node within the graph.
        type:
          type: string
          const: extract
          title: Type
        mode:
          type: string
          enum:
            - standard
            - agentic
            - lite
          title: Mode
          description: >-
            Extraction tier. 'standard' (default): single-pass extraction — the
            best balance for simple documents and layouts. 'agentic': multi-step
            extraction that maps every table to the schema and reasons across
            sections — for dense or cross-page tables; extra cost per page.
            'lite' (shown as 'Fast' in the app): standard extraction, faster and
            cheaper; quality may vary on complex layouts.
          default: standard
        extraction_schema:
          $ref: '#/components/schemas/ExtractionSchema'
          description: Schema for the fields this node extracts. Required.
        lookup_files:
          items:
            type: string
          type: array
          title: Lookup Files
          description: Smart-lookup reference document URIs persisted on the extract node.
        lookup_suggestion:
          anyOf:
            - type: string
            - type: 'null'
          title: Lookup Suggestion
          description: Free-form hint shown to the smart-lookup matcher.
        lookup_reasoning_effort:
          anyOf:
            - type: string
              enum:
                - minimal
                - low
                - medium
                - high
            - type: 'null'
          title: Lookup Reasoning Effort
          description: >-
            Reasoning effort for the smart-lookup matcher (gpt-5 family):
            minimal | low | medium | high. None = model default. Higher effort
            improves match reliability on noisy join keys at higher
            latency/cost.
        lookup_file_uploads:
          items:
            $ref: '#/components/schemas/LookupFileUpload'
          type: array
          title: Lookup File Uploads
          description: >-
            Inline lookup-file content for the typed create call. Each entry is
            uploaded and the resulting URI is appended to ``lookup_files``; this
            field is create-input only.
        use_images:
          type: boolean
          title: Use Images
          description: >-
            Standard/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.
          default: false
      additionalProperties: false
      type: object
      required:
        - id
        - type
        - extraction_schema
      title: PublicExtractNode
    PublicValidateNode:
      properties:
        id:
          type: string
          minLength: 1
          title: Id
          description: Stable identifier for this node within the graph.
        type:
          type: string
          const: validate
          title: Type
        rules:
          items:
            $ref: '#/components/schemas/ValidationRule'
          type: array
          minItems: 1
          title: Rules
      additionalProperties: false
      type: object
      required:
        - id
        - type
        - rules
      title: PublicValidateNode
    PublicIfElseNode:
      properties:
        id:
          type: string
          minLength: 1
          title: Id
          description: Stable identifier for this node within the graph.
        type:
          type: string
          const: if_else
          title: Type
        condition:
          oneOf:
            - $ref: '#/components/schemas/RangeCheck'
            - $ref: '#/components/schemas/DateCheck'
            - $ref: '#/components/schemas/ArithmeticCheck'
            - $ref: '#/components/schemas/ComparisonCheck'
            - $ref: '#/components/schemas/OneOfCheck'
            - $ref: '#/components/schemas/RegexCheck'
            - $ref: '#/components/schemas/RequiredCheck'
            - $ref: '#/components/schemas/ConfidenceCheck'
            - $ref: '#/components/schemas/ExpressionCheck'
            - $ref: '#/components/schemas/ValidationCheck'
            - $ref: '#/components/schemas/AllOfCheck'
            - $ref: '#/components/schemas/AnyOfCheck'
          title: Condition
          discriminator:
            propertyName: type
            mapping:
              all_of:
                $ref: '#/components/schemas/AllOfCheck'
              any_of:
                $ref: '#/components/schemas/AnyOfCheck'
              arithmetic:
                $ref: '#/components/schemas/ArithmeticCheck'
              comparison:
                $ref: '#/components/schemas/ComparisonCheck'
              confidence:
                $ref: '#/components/schemas/ConfidenceCheck'
              date:
                $ref: '#/components/schemas/DateCheck'
              expression:
                $ref: '#/components/schemas/ExpressionCheck'
              one_of:
                $ref: '#/components/schemas/OneOfCheck'
              range:
                $ref: '#/components/schemas/RangeCheck'
              regex:
                $ref: '#/components/schemas/RegexCheck'
              required:
                $ref: '#/components/schemas/RequiredCheck'
              validation:
                $ref: '#/components/schemas/ValidationCheck'
      additionalProperties: false
      type: object
      required:
        - id
        - type
        - condition
      title: PublicIfElseNode
    PublicSlackAlertNode:
      properties:
        id:
          type: string
          minLength: 1
          title: Id
          description: Stable identifier for this node within the graph.
        type:
          type: string
          const: slack_alert
          title: Type
        channel_id:
          type: string
          minLength: 1
          title: Channel Id
        channel_name:
          type: string
          minLength: 1
          title: Channel Name
        message_template:
          type: string
          minLength: 1
          title: Message Template
        severity:
          type: string
          enum:
            - info
            - warning
            - critical
          title: Severity
          default: info
      additionalProperties: false
      type: object
      required:
        - id
        - type
        - channel_id
        - channel_name
        - message_template
      title: PublicSlackAlertNode
      description: >-
        Terminal alert-sink. ``field_index`` is executor-injected — omitted
        here.
    Edge:
      properties:
        source:
          type: string
          minLength: 1
          title: Source
        target:
          type: string
          minLength: 1
          title: Target
        branch:
          anyOf:
            - type: string
            - type: 'null'
          title: Branch
          description: >-
            Source-port label for branch routing. Required when leaving a
            classify or splitter node by category/rule.
      additionalProperties: false
      type: object
      required:
        - source
        - target
      title: Edge
      description: >-
        A directed edge between two nodes. ``branch`` carries the source-port
        label

        used for routing out of ``classify`` (category id) or ``splitter`` (rule
        id) nodes.
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
    ClassifyCategory:
      properties:
        id:
          type: string
          minLength: 1
          title: Id
          description: Stable category id used as the edge `branch` value when routing.
        name:
          type: string
          minLength: 1
          title: Name
          description: >-
            Display name shown to the LLM; classification and routing key off
            it, so it must be unique across categories.
        description:
          type: string
          minLength: 1
          title: Description
          description: Free-form description shown to the LLM.
      additionalProperties: false
      type: object
      required:
        - id
        - name
        - description
      title: ClassifyCategory
    SplitterRule:
      properties:
        id:
          type: string
          minLength: 1
          title: Id
        name:
          type: string
          minLength: 1
          title: Name
        description:
          type: string
          minLength: 1
          title: Description
        partition_key:
          type: string
          title: Partition Key
          description: >-
            Field that further partitions this category into separate
            sub-documents (e.g. invoice_number): each distinct value becomes its
            own split, extracted independently. Empty = routing-only, the whole
            category flows on as one document.
          default: ''
      additionalProperties: false
      type: object
      required:
        - id
        - name
        - description
      title: SplitterRule
    ExtractionSchema:
      properties:
        fields:
          items:
            oneOf:
              - $ref: '#/components/schemas/StringField'
              - $ref: '#/components/schemas/IntegerField'
              - $ref: '#/components/schemas/FloatField'
              - $ref: '#/components/schemas/BooleanField'
              - $ref: '#/components/schemas/DateField'
              - $ref: '#/components/schemas/DatetimeField'
              - $ref: '#/components/schemas/EnumField'
              - $ref: '#/components/schemas/MultiSelectField'
              - $ref: '#/components/schemas/ObjectField'
            discriminator:
              propertyName: data_type
              mapping:
                boolean:
                  $ref: '#/components/schemas/BooleanField'
                date:
                  $ref: '#/components/schemas/DateField'
                datetime:
                  $ref: '#/components/schemas/DatetimeField'
                enum:
                  $ref: '#/components/schemas/EnumField'
                float:
                  $ref: '#/components/schemas/FloatField'
                integer:
                  $ref: '#/components/schemas/IntegerField'
                multi_select:
                  $ref: '#/components/schemas/MultiSelectField'
                object:
                  $ref: '#/components/schemas/ObjectField'
                string:
                  $ref: '#/components/schemas/StringField'
          type: array
          minItems: 1
          title: Fields
          description: Field definitions making up this extract's output.
      additionalProperties: false
      type: object
      required:
        - fields
      title: ExtractionSchema
      description: |-
        Schema describing what an extract node produces.

        Modeled as a typed object (not a bare ``list[AnyField]``) so metadata
        can be added later (versioning, derived flags, output-shape hints)
        without another wire-format break. At least one field is required.
    LookupFileUpload:
      properties:
        filename:
          type: string
          minLength: 1
          title: Filename
        content:
          type: string
          minLength: 1
          title: Content
          description: Base64-encoded file bytes.
      additionalProperties: false
      type: object
      required:
        - filename
        - content
      title: LookupFileUpload
      description: |-
        Inline lookup-file content carried on the typed create call.

        The backend reads ``filename`` + ``content`` (base64-encoded bytes),
        uploads the file, and stores the resulting URI in
        ``ExtractNode.lookup_files``. Create-input only — not persisted on
        the node after upload.
    ValidationRule:
      properties:
        id:
          type: string
          minLength: 1
          title: Id
          description: Stable rule id; round-trips through validation results.
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: >-
            Optional human-readable rule name. Round-trips through the config
            endpoint so renames are preserved.
        kind:
          type: string
          enum:
            - ai
            - deterministic
          title: Kind
          description: >-
            'deterministic' = a structured check run in code — instant, free,
            reproducible; 'ai' = a model judges the rule description against the
            extraction. Prefer deterministic whenever the condition is exact and
            testable.
          default: ai
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: >-
            Natural-language description sent to the LLM. Required when kind ==
            'ai'.
        check:
          anyOf:
            - oneOf:
                - $ref: '#/components/schemas/RangeCheck'
                - $ref: '#/components/schemas/DateCheck'
                - $ref: '#/components/schemas/ArithmeticCheck'
                - $ref: '#/components/schemas/ComparisonCheck'
                - $ref: '#/components/schemas/OneOfCheck'
                - $ref: '#/components/schemas/RegexCheck'
                - $ref: '#/components/schemas/RequiredCheck'
                - $ref: '#/components/schemas/ConfidenceCheck'
                - $ref: '#/components/schemas/ExpressionCheck'
                - $ref: '#/components/schemas/ValidationCheck'
                - $ref: '#/components/schemas/AllOfCheck'
                - $ref: '#/components/schemas/AnyOfCheck'
              discriminator:
                propertyName: type
                mapping:
                  all_of:
                    $ref: '#/components/schemas/AllOfCheck'
                  any_of:
                    $ref: '#/components/schemas/AnyOfCheck'
                  arithmetic:
                    $ref: '#/components/schemas/ArithmeticCheck'
                  comparison:
                    $ref: '#/components/schemas/ComparisonCheck'
                  confidence:
                    $ref: '#/components/schemas/ConfidenceCheck'
                  date:
                    $ref: '#/components/schemas/DateCheck'
                  expression:
                    $ref: '#/components/schemas/ExpressionCheck'
                  one_of:
                    $ref: '#/components/schemas/OneOfCheck'
                  range:
                    $ref: '#/components/schemas/RangeCheck'
                  regex:
                    $ref: '#/components/schemas/RegexCheck'
                  required:
                    $ref: '#/components/schemas/RequiredCheck'
                  validation:
                    $ref: '#/components/schemas/ValidationCheck'
            - type: 'null'
          title: Check
          description: >-
            Structured deterministic check. Required when kind ==
            'deterministic'.
        severity:
          type: string
          enum:
            - error
            - warning
          title: Severity
          description: >-
            How a failed rule is labelled in results. Display-level only — it
            never blocks the run.
          default: error
        source_fields:
          items:
            type: string
          type: array
          title: Source Fields
          description: Persistent ids of fields this rule references.
      additionalProperties: false
      type: object
      required:
        - id
      title: ValidationRule
    RangeCheck:
      properties:
        type:
          type: string
          const: range
          title: Type
        field:
          type: string
          minLength: 1
          title: Field
          description: persistent_id of the bounded numeric field.
        min:
          anyOf:
            - type: number
            - type: 'null'
          title: Min
        max:
          anyOf:
            - type: number
            - type: 'null'
          title: Max
      additionalProperties: false
      type: object
      required:
        - type
        - field
      title: RangeCheck
    DateCheck:
      properties:
        type:
          type: string
          const: date
          title: Type
        field:
          type: string
          minLength: 1
          title: Field
          description: persistent_id of the date field.
        earliest:
          anyOf:
            - type: string
            - type: 'null'
          title: Earliest
        latest:
          anyOf:
            - type: string
            - type: 'null'
          title: Latest
      additionalProperties: false
      type: object
      required:
        - type
        - field
      title: DateCheck
    ArithmeticCheck:
      properties:
        type:
          type: string
          const: arithmetic
          title: Type
        operands:
          items:
            type: string
          type: array
          minItems: 1
          title: Operands
          description: persistent_ids combined by `operator`.
        operator:
          type: string
          enum:
            - sum
            - subtract
            - product
          title: Operator
          default: sum
        equals:
          type: string
          minLength: 1
          title: Equals
          description: persistent_id whose value the result must equal.
        tolerance:
          type: number
          minimum: 0
          title: Tolerance
          description: Absolute slack allowed around `equals`.
          default: 0
      additionalProperties: false
      type: object
      required:
        - type
        - operands
        - equals
      title: ArithmeticCheck
    ComparisonCheck:
      properties:
        type:
          type: string
          const: comparison
          title: Type
        left:
          type: string
          minLength: 1
          title: Left
          description: persistent_id of the left-hand field.
        op:
          type: string
          enum:
            - '=='
            - '!='
            - '>'
            - '>='
            - <
            - <=
          title: Op
        right:
          oneOf:
            - $ref: '#/components/schemas/_FieldOperand'
            - $ref: '#/components/schemas/_LiteralOperand'
          title: Right
          discriminator:
            propertyName: source
            mapping:
              field:
                $ref: '#/components/schemas/_FieldOperand'
              literal:
                $ref: '#/components/schemas/_LiteralOperand'
      additionalProperties: false
      type: object
      required:
        - type
        - left
        - op
        - right
      title: ComparisonCheck
    OneOfCheck:
      properties:
        type:
          type: string
          const: one_of
          title: Type
        field:
          type: string
          minLength: 1
          title: Field
        allowed:
          items:
            type: string
          type: array
          minItems: 1
          title: Allowed
        case_sensitive:
          type: boolean
          title: Case Sensitive
          default: false
      additionalProperties: false
      type: object
      required:
        - type
        - field
        - allowed
      title: OneOfCheck
    RegexCheck:
      properties:
        type:
          type: string
          const: regex
          title: Type
        field:
          type: string
          minLength: 1
          title: Field
        pattern:
          type: string
          minLength: 1
          title: Pattern
          description: Evaluated with google-re2 (linear time); never stdlib re.
      additionalProperties: false
      type: object
      required:
        - type
        - field
        - pattern
      title: RegexCheck
    RequiredCheck:
      properties:
        type:
          type: string
          const: required
          title: Type
        field:
          type: string
          minLength: 1
          title: Field
      additionalProperties: false
      type: object
      required:
        - type
        - field
      title: RequiredCheck
    ConfidenceCheck:
      properties:
        type:
          type: string
          const: confidence
          title: Type
        field:
          type: string
          minLength: 1
          title: Field
          description: >-
            Field whose extraction confidence to compare. Either the
            ``persistent_id`` (Studio field-picker path) or the
            ``sanitized_name`` (SDK/API/assistant path) — the executor resolves
            both against the workflow's field tree, same as ``ValidateNode``
            deterministic checks.
        op:
          type: string
          enum:
            - <
            - <=
            - '>'
            - '>='
            - '=='
            - '!='
          title: Op
        threshold:
          type: integer
          maximum: 100
          minimum: 0
          title: Threshold
          description: Confidence percentile 0..100.
      additionalProperties: false
      type: object
      required:
        - type
        - field
        - op
        - threshold
      title: ConfidenceCheck
    ExpressionCheck:
      properties:
        type:
          type: string
          const: expression
          title: Type
        expression:
          type: string
          maxLength: 1000
          minLength: 1
          title: Expression
      additionalProperties: false
      type: object
      required:
        - type
        - expression
      title: ExpressionCheck
      description: >-
        A CEL expression over the whole extraction, evaluated in a sandbox.


        The expression reads one root variable ``data`` and addresses fields by

        extracted name (``data.total``, ``data.lineas.map(l, l.importe)``),
        never by

        ``persistent_id`` — so ``field_ids`` returns ``[]`` for it, and a field

        rename silently breaks the rule. An expression the evaluator cannot
        parse,

        cannot answer with a boolean, or prices above its evaluation budget
        reports

        ``inconclusive``.


        ADR platform/0036 records why the CEL parser stays out of this package,
        so

        ``expression`` is unvalidated here.
    ValidationCheck:
      properties:
        type:
          type: string
          const: validation
          title: Type
        rule_id:
          type: string
          minLength: 1
          title: Rule Id
          description: Stable id of the upstream ValidationRule to route on.
        status:
          type: string
          enum:
            - fail
            - pass
            - inconclusive
          title: Status
          description: >-
            Outcome to match. ``fail`` (default) is the common case for routing
            to an alert branch.
          default: fail
      additionalProperties: false
      type: object
      required:
        - type
        - rule_id
      title: ValidationCheck
      description: |-
        Routes on the outcome of a named validation rule already run upstream.

        Reads from ``state.validation_results`` (populated by an upstream
        ``ValidateNode``), filtered to the current partition (``split_id``) so
        per-partition IfElse routing matches per-partition rule outcomes. The
        ``status`` values mirror the worker enum
        (``anyformat_core.operations.validate_rules`` — ``pass`` / ``fail`` /
        ``inconclusive``) verbatim: no translation layer, so template tokens
        like ``${validation.<rule_id>.status}`` render the same three strings
        the enum uses.

        Unlike every other ``Check`` variant this consumes no extracted fields
        (``field_ids`` returns ``[]``); the rule identity is the entire input.
        A ``rule_id`` with no matching entry evaluates to ``inconclusive`` — the
        rule didn't run for this partition, so the outcome is unknown.
    AllOfCheck:
      properties:
        type:
          type: string
          const: all_of
          title: Type
        checks:
          items:
            oneOf:
              - $ref: '#/components/schemas/RangeCheck'
              - $ref: '#/components/schemas/DateCheck'
              - $ref: '#/components/schemas/ArithmeticCheck'
              - $ref: '#/components/schemas/ComparisonCheck'
              - $ref: '#/components/schemas/OneOfCheck'
              - $ref: '#/components/schemas/RegexCheck'
              - $ref: '#/components/schemas/RequiredCheck'
              - $ref: '#/components/schemas/ConfidenceCheck'
              - $ref: '#/components/schemas/ExpressionCheck'
              - $ref: '#/components/schemas/ValidationCheck'
              - $ref: '#/components/schemas/AllOfCheck'
              - $ref: '#/components/schemas/AnyOfCheck'
            discriminator:
              propertyName: type
              mapping:
                all_of:
                  $ref: '#/components/schemas/AllOfCheck'
                any_of:
                  $ref: '#/components/schemas/AnyOfCheck'
                arithmetic:
                  $ref: '#/components/schemas/ArithmeticCheck'
                comparison:
                  $ref: '#/components/schemas/ComparisonCheck'
                confidence:
                  $ref: '#/components/schemas/ConfidenceCheck'
                date:
                  $ref: '#/components/schemas/DateCheck'
                expression:
                  $ref: '#/components/schemas/ExpressionCheck'
                one_of:
                  $ref: '#/components/schemas/OneOfCheck'
                range:
                  $ref: '#/components/schemas/RangeCheck'
                regex:
                  $ref: '#/components/schemas/RegexCheck'
                required:
                  $ref: '#/components/schemas/RequiredCheck'
                validation:
                  $ref: '#/components/schemas/ValidationCheck'
          type: array
          maxItems: 32
          minItems: 1
          title: Checks
      additionalProperties: false
      type: object
      required:
        - type
        - checks
      title: AllOfCheck
      description: >-
        AND combinator — passes iff every sub-check passes.


        Tri-state: FAIL if any child fails; else INCONCLUSIVE if any child is

        inconclusive; else PASS. Children are the same closed ``Check`` union

        (mutual recursion via forward ref) so combinators nest by construction —

        the CheckBuilder UI restricts nesting to a single level for MVP.


        ``max_length=32`` bounds runaway fan-out per combinator. Practical rules

        stay well below the cap; a legitimate use case wanting more should
        motivate

        the raise rather than land silently. Nesting-depth is bounded by
        pydantic's

        own recursion during parse.
    AnyOfCheck:
      properties:
        type:
          type: string
          const: any_of
          title: Type
        checks:
          items:
            oneOf:
              - $ref: '#/components/schemas/RangeCheck'
              - $ref: '#/components/schemas/DateCheck'
              - $ref: '#/components/schemas/ArithmeticCheck'
              - $ref: '#/components/schemas/ComparisonCheck'
              - $ref: '#/components/schemas/OneOfCheck'
              - $ref: '#/components/schemas/RegexCheck'
              - $ref: '#/components/schemas/RequiredCheck'
              - $ref: '#/components/schemas/ConfidenceCheck'
              - $ref: '#/components/schemas/ExpressionCheck'
              - $ref: '#/components/schemas/ValidationCheck'
              - $ref: '#/components/schemas/AllOfCheck'
              - $ref: '#/components/schemas/AnyOfCheck'
            discriminator:
              propertyName: type
              mapping:
                all_of:
                  $ref: '#/components/schemas/AllOfCheck'
                any_of:
                  $ref: '#/components/schemas/AnyOfCheck'
                arithmetic:
                  $ref: '#/components/schemas/ArithmeticCheck'
                comparison:
                  $ref: '#/components/schemas/ComparisonCheck'
                confidence:
                  $ref: '#/components/schemas/ConfidenceCheck'
                date:
                  $ref: '#/components/schemas/DateCheck'
                expression:
                  $ref: '#/components/schemas/ExpressionCheck'
                one_of:
                  $ref: '#/components/schemas/OneOfCheck'
                range:
                  $ref: '#/components/schemas/RangeCheck'
                regex:
                  $ref: '#/components/schemas/RegexCheck'
                required:
                  $ref: '#/components/schemas/RequiredCheck'
                validation:
                  $ref: '#/components/schemas/ValidationCheck'
          type: array
          maxItems: 32
          minItems: 1
          title: Checks
      additionalProperties: false
      type: object
      required:
        - type
        - checks
      title: AnyOfCheck
      description: |-
        OR combinator — passes iff any sub-check passes.

        Tri-state: PASS if any child passes; else INCONCLUSIVE if any child is
        inconclusive; else FAIL.

        ``max_length=32`` — see :class:`AllOfCheck` for the rationale.
    StringField:
      properties:
        persistent_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Persistent Id
          description: >-
            Server-assigned stable field identity (UUID), emitted by GET
            /definition/. Echo it back on updates — including across renames —
            to keep quality metrics, ground-truth links and analytics attached
            to the field. Matching is scoped to the field's nesting level in the
            immediately-prior version; an id that matches nothing at its level,
            appears on more than one field, or is sent at workflow creation is
            rejected with 400. Omit for new fields; when omitted on an update,
            the server matches the field to the prior version by name.
        name:
          type: string
          minLength: 1
          title: Name
          description: Field name. Used as the key in the extraction response.
        description:
          type: string
          title: Description
          description: Free-form description shown to the extraction model.
          default: ''
        source:
          type: string
          enum:
            - extraction
            - smart_lookup
            - lookup_if_missing
          title: Source
          description: >-
            How the field gets its value: extraction (from the document),
            smart_lookup (from the lookup file, always overwrites) or
            lookup_if_missing (extracted too; the lookup only fills it where
            extraction left no value).
          default: extraction
        data_type:
          type: string
          const: string
          title: Data Type
      additionalProperties: false
      type: object
      required:
        - name
        - data_type
      title: StringField
    IntegerField:
      properties:
        persistent_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Persistent Id
          description: >-
            Server-assigned stable field identity (UUID), emitted by GET
            /definition/. Echo it back on updates — including across renames —
            to keep quality metrics, ground-truth links and analytics attached
            to the field. Matching is scoped to the field's nesting level in the
            immediately-prior version; an id that matches nothing at its level,
            appears on more than one field, or is sent at workflow creation is
            rejected with 400. Omit for new fields; when omitted on an update,
            the server matches the field to the prior version by name.
        name:
          type: string
          minLength: 1
          title: Name
          description: Field name. Used as the key in the extraction response.
        description:
          type: string
          title: Description
          description: Free-form description shown to the extraction model.
          default: ''
        source:
          type: string
          enum:
            - extraction
            - smart_lookup
            - lookup_if_missing
          title: Source
          description: >-
            How the field gets its value: extraction (from the document),
            smart_lookup (from the lookup file, always overwrites) or
            lookup_if_missing (extracted too; the lookup only fills it where
            extraction left no value).
          default: extraction
        data_type:
          type: string
          const: integer
          title: Data Type
      additionalProperties: false
      type: object
      required:
        - name
        - data_type
      title: IntegerField
    FloatField:
      properties:
        persistent_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Persistent Id
          description: >-
            Server-assigned stable field identity (UUID), emitted by GET
            /definition/. Echo it back on updates — including across renames —
            to keep quality metrics, ground-truth links and analytics attached
            to the field. Matching is scoped to the field's nesting level in the
            immediately-prior version; an id that matches nothing at its level,
            appears on more than one field, or is sent at workflow creation is
            rejected with 400. Omit for new fields; when omitted on an update,
            the server matches the field to the prior version by name.
        name:
          type: string
          minLength: 1
          title: Name
          description: Field name. Used as the key in the extraction response.
        description:
          type: string
          title: Description
          description: Free-form description shown to the extraction model.
          default: ''
        source:
          type: string
          enum:
            - extraction
            - smart_lookup
            - lookup_if_missing
          title: Source
          description: >-
            How the field gets its value: extraction (from the document),
            smart_lookup (from the lookup file, always overwrites) or
            lookup_if_missing (extracted too; the lookup only fills it where
            extraction left no value).
          default: extraction
        data_type:
          type: string
          const: float
          title: Data Type
      additionalProperties: false
      type: object
      required:
        - name
        - data_type
      title: FloatField
    BooleanField:
      properties:
        persistent_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Persistent Id
          description: >-
            Server-assigned stable field identity (UUID), emitted by GET
            /definition/. Echo it back on updates — including across renames —
            to keep quality metrics, ground-truth links and analytics attached
            to the field. Matching is scoped to the field's nesting level in the
            immediately-prior version; an id that matches nothing at its level,
            appears on more than one field, or is sent at workflow creation is
            rejected with 400. Omit for new fields; when omitted on an update,
            the server matches the field to the prior version by name.
        name:
          type: string
          minLength: 1
          title: Name
          description: Field name. Used as the key in the extraction response.
        description:
          type: string
          title: Description
          description: Free-form description shown to the extraction model.
          default: ''
        source:
          type: string
          enum:
            - extraction
            - smart_lookup
            - lookup_if_missing
          title: Source
          description: >-
            How the field gets its value: extraction (from the document),
            smart_lookup (from the lookup file, always overwrites) or
            lookup_if_missing (extracted too; the lookup only fills it where
            extraction left no value).
          default: extraction
        data_type:
          type: string
          const: boolean
          title: Data Type
      additionalProperties: false
      type: object
      required:
        - name
        - data_type
      title: BooleanField
    DateField:
      properties:
        persistent_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Persistent Id
          description: >-
            Server-assigned stable field identity (UUID), emitted by GET
            /definition/. Echo it back on updates — including across renames —
            to keep quality metrics, ground-truth links and analytics attached
            to the field. Matching is scoped to the field's nesting level in the
            immediately-prior version; an id that matches nothing at its level,
            appears on more than one field, or is sent at workflow creation is
            rejected with 400. Omit for new fields; when omitted on an update,
            the server matches the field to the prior version by name.
        name:
          type: string
          minLength: 1
          title: Name
          description: Field name. Used as the key in the extraction response.
        description:
          type: string
          title: Description
          description: Free-form description shown to the extraction model.
          default: ''
        source:
          type: string
          enum:
            - extraction
            - smart_lookup
            - lookup_if_missing
          title: Source
          description: >-
            How the field gets its value: extraction (from the document),
            smart_lookup (from the lookup file, always overwrites) or
            lookup_if_missing (extracted too; the lookup only fills it where
            extraction left no value).
          default: extraction
        data_type:
          type: string
          const: date
          title: Data Type
      additionalProperties: false
      type: object
      required:
        - name
        - data_type
      title: DateField
    DatetimeField:
      properties:
        persistent_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Persistent Id
          description: >-
            Server-assigned stable field identity (UUID), emitted by GET
            /definition/. Echo it back on updates — including across renames —
            to keep quality metrics, ground-truth links and analytics attached
            to the field. Matching is scoped to the field's nesting level in the
            immediately-prior version; an id that matches nothing at its level,
            appears on more than one field, or is sent at workflow creation is
            rejected with 400. Omit for new fields; when omitted on an update,
            the server matches the field to the prior version by name.
        name:
          type: string
          minLength: 1
          title: Name
          description: Field name. Used as the key in the extraction response.
        description:
          type: string
          title: Description
          description: Free-form description shown to the extraction model.
          default: ''
        source:
          type: string
          enum:
            - extraction
            - smart_lookup
            - lookup_if_missing
          title: Source
          description: >-
            How the field gets its value: extraction (from the document),
            smart_lookup (from the lookup file, always overwrites) or
            lookup_if_missing (extracted too; the lookup only fills it where
            extraction left no value).
          default: extraction
        data_type:
          type: string
          const: datetime
          title: Data Type
      additionalProperties: false
      type: object
      required:
        - name
        - data_type
      title: DatetimeField
    EnumField:
      properties:
        persistent_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Persistent Id
          description: >-
            Server-assigned stable field identity (UUID), emitted by GET
            /definition/. Echo it back on updates — including across renames —
            to keep quality metrics, ground-truth links and analytics attached
            to the field. Matching is scoped to the field's nesting level in the
            immediately-prior version; an id that matches nothing at its level,
            appears on more than one field, or is sent at workflow creation is
            rejected with 400. Omit for new fields; when omitted on an update,
            the server matches the field to the prior version by name.
        name:
          type: string
          minLength: 1
          title: Name
          description: Field name. Used as the key in the extraction response.
        description:
          type: string
          title: Description
          description: Free-form description shown to the extraction model.
          default: ''
        source:
          type: string
          enum:
            - extraction
            - smart_lookup
            - lookup_if_missing
          title: Source
          description: >-
            How the field gets its value: extraction (from the document),
            smart_lookup (from the lookup file, always overwrites) or
            lookup_if_missing (extracted too; the lookup only fills it where
            extraction left no value).
          default: extraction
        data_type:
          type: string
          const: enum
          title: Data Type
        enum_options:
          items:
            $ref: '#/components/schemas/EnumOption'
          type: array
          minItems: 1
          title: Enum Options
      additionalProperties: false
      type: object
      required:
        - name
        - data_type
        - enum_options
      title: EnumField
    MultiSelectField:
      properties:
        persistent_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Persistent Id
          description: >-
            Server-assigned stable field identity (UUID), emitted by GET
            /definition/. Echo it back on updates — including across renames —
            to keep quality metrics, ground-truth links and analytics attached
            to the field. Matching is scoped to the field's nesting level in the
            immediately-prior version; an id that matches nothing at its level,
            appears on more than one field, or is sent at workflow creation is
            rejected with 400. Omit for new fields; when omitted on an update,
            the server matches the field to the prior version by name.
        name:
          type: string
          minLength: 1
          title: Name
          description: Field name. Used as the key in the extraction response.
        description:
          type: string
          title: Description
          description: Free-form description shown to the extraction model.
          default: ''
        source:
          type: string
          enum:
            - extraction
            - smart_lookup
            - lookup_if_missing
          title: Source
          description: >-
            How the field gets its value: extraction (from the document),
            smart_lookup (from the lookup file, always overwrites) or
            lookup_if_missing (extracted too; the lookup only fills it where
            extraction left no value).
          default: extraction
        data_type:
          type: string
          const: multi_select
          title: Data Type
        enum_options:
          items:
            $ref: '#/components/schemas/EnumOption'
          type: array
          minItems: 1
          title: Enum Options
      additionalProperties: false
      type: object
      required:
        - name
        - data_type
        - enum_options
      title: MultiSelectField
    ObjectField:
      properties:
        persistent_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Persistent Id
          description: >-
            Server-assigned stable field identity (UUID), emitted by GET
            /definition/. Echo it back on updates — including across renames —
            to keep quality metrics, ground-truth links and analytics attached
            to the field. Matching is scoped to the field's nesting level in the
            immediately-prior version; an id that matches nothing at its level,
            appears on more than one field, or is sent at workflow creation is
            rejected with 400. Omit for new fields; when omitted on an update,
            the server matches the field to the prior version by name.
        name:
          type: string
          minLength: 1
          title: Name
          description: Field name. Used as the key in the extraction response.
        description:
          type: string
          title: Description
          description: Free-form description shown to the extraction model.
          default: ''
        source:
          type: string
          enum:
            - extraction
            - smart_lookup
            - lookup_if_missing
          title: Source
          description: >-
            How the field gets its value: extraction (from the document),
            smart_lookup (from the lookup file, always overwrites) or
            lookup_if_missing (extracted too; the lookup only fills it where
            extraction left no value).
          default: extraction
        data_type:
          type: string
          const: object
          title: Data Type
        nested_fields:
          items:
            oneOf:
              - $ref: '#/components/schemas/StringField'
              - $ref: '#/components/schemas/IntegerField'
              - $ref: '#/components/schemas/FloatField'
              - $ref: '#/components/schemas/BooleanField'
              - $ref: '#/components/schemas/DateField'
              - $ref: '#/components/schemas/DatetimeField'
              - $ref: '#/components/schemas/EnumField'
              - $ref: '#/components/schemas/MultiSelectField'
            discriminator:
              propertyName: data_type
              mapping:
                boolean:
                  $ref: '#/components/schemas/BooleanField'
                date:
                  $ref: '#/components/schemas/DateField'
                datetime:
                  $ref: '#/components/schemas/DatetimeField'
                enum:
                  $ref: '#/components/schemas/EnumField'
                float:
                  $ref: '#/components/schemas/FloatField'
                integer:
                  $ref: '#/components/schemas/IntegerField'
                multi_select:
                  $ref: '#/components/schemas/MultiSelectField'
                string:
                  $ref: '#/components/schemas/StringField'
          type: array
          minItems: 1
          title: Nested Fields
          description: >-
            Child fields of this object. Only one level of nesting is supported
            — an object field's children must be non-object types.
      additionalProperties: false
      type: object
      required:
        - name
        - data_type
        - nested_fields
      title: ObjectField
    _FieldOperand:
      properties:
        source:
          type: string
          const: field
          title: Source
        field:
          type: string
          minLength: 1
          title: Field
          description: persistent_id of the compared field.
      additionalProperties: false
      type: object
      required:
        - source
        - field
      title: _FieldOperand
    _LiteralOperand:
      properties:
        source:
          type: string
          const: literal
          title: Source
        value:
          anyOf:
            - type: number
            - type: string
            - type: boolean
          title: Value
      additionalProperties: false
      type: object
      required:
        - source
        - value
      title: _LiteralOperand
    EnumOption:
      properties:
        name:
          type: string
          minLength: 1
          title: Name
        description:
          type: string
          minLength: 1
          title: Description
          description: Free-form description shown to the model.
      additionalProperties: false
      type: object
      required:
        - name
        - description
      title: EnumOption
  securitySchemes:
    ApiKeyAuth:
      type: http
      description: >-
        API key issued from app.anyformat.ai/api-key. Send as `Authorization:
        Bearer <key>`.
      scheme: bearer

````