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

# The scan from accounting

> One file that holds several documents: cut it into pieces by kind, then extract each kind with its own fields

Somebody fed the whole tray into the scanner. What lands in your inbox is one PDF holding three supplier invoices, a shipping label and, for reasons nobody will explain, a boarding pass. A [Split](/guides/nodes/split) node cuts that file into pieces by kind, and each kind goes to the [Extract](/guides/nodes/extract) node that suits it.

<div className="af-payoff">
  <div className="af-payoff-pane">
    <div className="af-payoff-cap">One file, five pages</div>

    <div className="af-payoff-line">
      {'p1  INVOICE   '}

      <b>
        {'INV-2026-1000'}
      </b>
    </div>

    <div className="af-payoff-line">
      {'p2  INVOICE   '}

      <b>
        {'INV-2026-1001'}
      </b>
    </div>

    <div className="af-payoff-line">
      {'p3  INVOICE   '}

      <b>
        {'INV-2026-1002'}
      </b>
    </div>

    <div className="af-payoff-line">
      {'p4  SHIPPING LABEL'}
    </div>

    <div className="af-payoff-line">
      {'p5  BOARDING PASS'}
    </div>
  </div>

  <div className="af-payoff-arrow">→</div>

  <div className="af-payoff-pane">
    <div className="af-payoff-cap">Five documents</div>
    <div className="af-payoff-line"><span className="af-payoff-key">Invoice</span><b>{'3 partitions'}</b>{'  '}<span className="af-cite">98%</span></div>

    <div className="af-payoff-line">
      <span className="af-payoff-key">
        {'↳ INV-…1000'}
      </span>

      <b>
        {'Acme Industries · 1810.50'}
      </b>
    </div>

    <div className="af-payoff-line">
      <span className="af-payoff-key">
        {'↳ INV-…1001'}
      </span>

      <b>
        {'Globex Corp. · 932.45'}
      </b>
    </div>

    <div className="af-payoff-line">
      <span className="af-payoff-key">
        {'↳ INV-…1002'}
      </span>

      <b>
        {'Initech LLC · 5648.15'}
      </b>
    </div>

    <div className="af-payoff-line"><span className="af-payoff-key">Shipping label</span><b>{'FedEx · RK51FQKH1DNN'}</b></div>
    <div className="af-payoff-line"><span className="af-payoff-key">Boarding pass</span><b>{'LH4433 · seat 3F'}</b></div>
  </div>
</div>

<div className="af-cost">
  <span><b>Nodes</b><strong>{'Parse → Split → Extract ×3'}</strong></span>
  <span><b>Credits</b><strong>85 per page</strong>{' (Parse 25 + Split 25 + Extract 35)'}</span>
  <span><b>You get</b>{' one extraction per document, not per file'}</span>
</div>

## The workflow

Each Split **rule** is one kind of document and one outgoing branch. The invoice rule also carries a `partition_key`: the field that tells one invoice from the next inside the same rule, so three invoices in a row become three documents rather than one three-page one.

<CodeGroup>
  ```json API theme={null}
  {
    "name": "The scan from accounting",
    "description": "One scanned file that holds several documents",
    "nodes": [
      { "id": "parse_1", "type": "parse" },
      {
        "id": "split_1",
        "type": "splitter",
        "rules": [
          {
            "id": "INVOICE",
            "name": "Invoice",
            "description": "A supplier invoice: an invoice number, a bill-to party, line items and a total due.",
            "partition_key": "invoice_number"
          },
          {
            "id": "SHIPPING_LABEL",
            "name": "Shipping label",
            "description": "A carrier shipping label: a tracking number, a sender and a recipient address, a weight."
          },
          {
            "id": "BOARDING_PASS",
            "name": "Boarding pass",
            "description": "An airline boarding pass: a passenger name, a flight number, a seat, an origin and a destination airport."
          }
        ]
      },
      {
        "id": "extract_invoice",
        "type": "extract",
        "extraction_schema": {
          "fields": [
            { "name": "invoice_number", "description": "The unique invoice identifier", "data_type": "string" },
            { "name": "vendor_name",    "description": "Name of the company that issued the invoice", "data_type": "string" },
            { "name": "total_amount",   "description": "Final total amount due including tax", "data_type": "float" }
          ]
        }
      },
      {
        "id": "extract_label",
        "type": "extract",
        "extraction_schema": {
          "fields": [
            { "name": "tracking_number", "description": "The carrier tracking number", "data_type": "string" },
            { "name": "carrier",         "description": "The shipping carrier", "data_type": "string" },
            { "name": "recipient_name",  "description": "Name of the recipient the parcel ships to", "data_type": "string" }
          ]
        }
      },
      {
        "id": "extract_pass",
        "type": "extract",
        "extraction_schema": {
          "fields": [
            { "name": "passenger_name", "description": "Name of the passenger", "data_type": "string" },
            { "name": "flight_number",  "description": "The flight number", "data_type": "string" },
            { "name": "seat",           "description": "The seat assignment", "data_type": "string" }
          ]
        }
      }
    ],
    "edges": [
      { "source": "parse_1", "target": "split_1" },
      { "source": "split_1", "target": "extract_invoice", "branch": "INVOICE" },
      { "source": "split_1", "target": "extract_label",   "branch": "SHIPPING_LABEL" },
      { "source": "split_1", "target": "extract_pass",    "branch": "BOARDING_PASS" }
    ]
  }
  ```

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

  from anyformat.sdk import Client
  from anyformat.workflow import Schema, SplitterRule

  client = Client(api_key=os.environ["ANYFORMAT_API_KEY"])

  invoice = SplitterRule(
      id="INVOICE",
      name="Invoice",
      description="A supplier invoice: an invoice number, a bill-to party, line items and a total due.",
      partition_key="invoice_number",
  )
  label = SplitterRule(
      id="SHIPPING_LABEL",
      name="Shipping label",
      description="A carrier shipping label: a tracking number, a sender and a recipient address, a weight.",
  )
  boarding_pass = SplitterRule(
      id="BOARDING_PASS",
      name="Boarding pass",
      description="An airline boarding pass: a passenger name, a flight number, a seat, an origin and a destination airport.",
  )

  workflow = (
      client.workflow("The scan from accounting", "One scanned file that holds several documents")
      .parse()
      .split(invoice, label, boarding_pass)
      .extract([
          Schema.string("invoice_number", "The unique invoice identifier"),
          Schema.string("vendor_name",    "Name of the company that issued the invoice"),
          Schema.float("total_amount",    "Final total amount due including tax"),
      ], branch=invoice)
      .extract([
          Schema.string("tracking_number", "The carrier tracking number"),
          Schema.string("carrier",         "The shipping carrier"),
          Schema.string("recipient_name",  "Name of the recipient the parcel ships to"),
      ], branch=label)
      .extract([
          Schema.string("passenger_name", "Name of the passenger"),
          Schema.string("flight_number",  "The flight number"),
          Schema.string("seat",           "The seat assignment"),
      ], branch=boarding_pass)
      .create()
  )
  ```

  ```typescript TypeScript theme={null}
  import { Anyformat, Schema } from "@anyformat/sdk";

  const af = new Anyformat({ apiKey: process.env.ANYFORMAT_API_KEY! });

  const invoice = {
    id: "INVOICE",
    name: "Invoice",
    description: "A supplier invoice: an invoice number, a bill-to party, line items and a total due.",
    partition_key: "invoice_number",
  };
  const label = {
    id: "SHIPPING_LABEL",
    name: "Shipping label",
    description: "A carrier shipping label: a tracking number, a sender and a recipient address, a weight.",
  };
  const boardingPass = {
    id: "BOARDING_PASS",
    name: "Boarding pass",
    description: "An airline boarding pass: a passenger name, a flight number, a seat, an origin and a destination airport.",
  };

  const workflow = await af
    .workflow("The scan from accounting", "One scanned file that holds several documents")
    .parse()
    .split([invoice, label, boardingPass])
    .extract([
      Schema.string("invoice_number", "The unique invoice identifier"),
      Schema.string("vendor_name",    "Name of the company that issued the invoice"),
      Schema.float("total_amount",    "Final total amount due including tax"),
    ], { branch: invoice })
    .extract([
      Schema.string("tracking_number", "The carrier tracking number"),
      Schema.string("carrier",         "The shipping carrier"),
      Schema.string("recipient_name",  "Name of the recipient the parcel ships to"),
    ], { branch: label })
    .extract([
      Schema.string("passenger_name", "Name of the passenger"),
      Schema.string("flight_number",  "The flight number"),
      Schema.string("seat",           "The seat assignment"),
    ], { branch: boardingPass })
    .create();
  ```
</CodeGroup>

## Run it and read the result

Upload the file once. Split decides which pages belong to which rule, so you send nothing per document.

<CodeGroup>
  ```bash curl theme={null}
  # Upload the whole scan and start a run
  curl -X POST "https://api.anyformat.ai/v3/workflows/$WORKFLOW_ID/upload/run/" \
    -H "Authorization: Bearer $ANYFORMAT_API_KEY" \
    -F 'files=@scan.pdf'
  # → 202 { "run_id": "<run_id>", "status": "queued" }

  curl "https://api.anyformat.ai/v3/runs/$RUN_ID/" \
    -H "Authorization: Bearer $ANYFORMAT_API_KEY"
  ```

  ```python Python theme={null}
  result = workflow.run("scan.pdf").wait()

  # One extraction per document, tagged with the rule and the partition it came from.
  for extraction in result.extractions:
      print(extraction.split_name, extraction.partition)
  ```

  ```typescript TypeScript theme={null}
  import { readFileSync } from "node:fs";

  const file = new File([readFileSync("scan.pdf")], "scan.pdf", { type: "application/pdf" });
  const result = await (await workflow.run(file)).wait();

  for (const extraction of result.extractions) {
    console.log(extraction.split_name, extraction.partition);
  }
  ```
</CodeGroup>

## Response

`splits` says which pages became which document. `extractions` then carries one entry per document, each tagged with the `split_name` and the `partition` it came from, so you never have to work out which invoice a value belongs to.

```json theme={null}
{
  "status": "processed",
  "results": {
    "splits": [
      {
        "name": "Invoice",
        "files": [{ "file_name": "scan.pdf", "pages": [1, 2, 3] }],
        "confidence": 98,
        "partitions": [
          { "name": "INV-2026-1000", "files": [{ "file_name": "scan.pdf", "pages": [1] }], "confidence": 98 },
          { "name": "INV-2026-1001", "files": [{ "file_name": "scan.pdf", "pages": [2] }], "confidence": 98 },
          { "name": "INV-2026-1002", "files": [{ "file_name": "scan.pdf", "pages": [3] }], "confidence": 98 }
        ]
      },
      { "name": "Shipping label", "files": [{ "file_name": "scan.pdf", "pages": [4] }], "confidence": 98, "partitions": [] },
      { "name": "Boarding pass",  "files": [{ "file_name": "scan.pdf", "pages": [5] }], "confidence": 98, "partitions": [] },
      { "name": "Other", "files": [], "confidence": null, "partitions": [] }
    ],
    "extractions": [
      {
        "split_name": "Invoice",
        "partition": "INV-2026-1000",
        "fields": {
          "invoice_number": {
            "value": "INV-2026-1000",
            "confidence": 97.0,
            "evidence": [{ "text": "Invoice #: INV-2026-1000  \nIssue date: 2026-03-26", "page_number": 1 }],
            "verification_status": "not_verified",
            "value_override": null
          },
          "vendor_name":  { "value": "Acme Industries", "confidence": 97.0, "evidence": [{ "text": "**From: Acme Industries**", "page_number": 1 }] },
          "total_amount": { "value": "1810.5", "confidence": 97.0, "evidence": [{ "text": "**TOTAL: $1,810.50**", "page_number": 1 }] }
        },
        "validations": []
      },
      {
        "split_name": "Shipping label",
        "partition": null,
        "fields": {
          "carrier":         { "value": "FedEx", "confidence": 99.0, "evidence": [{ "text": "Carrier: FedEx", "page_number": 4 }] },
          "tracking_number": { "value": "RK51FQKH1DNN", "confidence": 99.0, "evidence": [{ "text": "TRACKING NUMBER RK51FQKH1DNN", "page_number": 4 }] }
        },
        "validations": []
      }
    ]
  }
}
```

## When it goes wrong

**Everything lands in "Other".** Split always emits an `Other` group for pages that match no rule, and it is empty on a clean run. A full one means your rule descriptions read like labels rather than descriptions. Write what is printed on the page: "a tracking number, a sender and a recipient address, a weight" beats "shipping stuff".

**Three invoices come back as one document.** That is the rule without a `partition_key`. The key names the field that changes between documents of the same kind, usually the identifier printed on each one, and it is what turns one three-page invoice group into three invoices.

**A sparse document extracts with low confidence.** The boarding pass in this run returned its flight number at 80% and its seat at 78%, against 97-99% for the invoices. A card with six words on it gives the model little to cite. Treat a low confidence as "have someone look", not as a failure: see [verification and review](/guides/workflows/verification-review).

**The pages are in the wrong order in the source file.** Split works by page, so a document interleaved with another cannot be recovered. Fix the scan, or split the file before you upload it.

**The workflow is rejected when you save it.** Once a workflow has a Split node, every Extract needs a `branch`, and each `branch` must be a rule **id** (`INVOICE`), not the rule's display name (`Invoice`).

## Next steps

<CardGroup cols={2}>
  <Card title="Split" icon="scissors" href="/guides/nodes/split">
    Every rule option, and Split after a Classify node
  </Card>

  <Card title="Sort invoices from receipts" icon="code-branch" href="/examples/classify-and-route">
    One document per file, routed by kind
  </Card>
</CardGroup>
