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

# Invoice processing

> Extract the header and the line items of an invoice with a Parse to Extract workflow

This example reads an invoice and returns its header fields plus every line item as a structured row. One `object` field captures the line-item table, so each row carries its own values, confidence and evidence.

<div className="af-payoff">
  <div className="af-payoff-pane">
    <div className="af-payoff-cap">The document</div>
    <div className="af-payoff-line"><b>INVOICE</b></div>

    <div className="af-payoff-line">
      <span className="af-mark">
        {'Invoice #: INV-2026-9001'}
      </span>
    </div>

    <div className="af-payoff-line">
      {'Issue date: 2026-02-20'}
    </div>

    <div className="af-payoff-line">
      {'Cloud hosting, March · 1 · $1,200.00'}
    </div>

    <div className="af-payoff-line">
      {'Support retainer · 1 · $2,400.00'}
    </div>

    <div className="af-payoff-line">
      <span className="af-mark">
        {'TOTAL: $4,594.62'}
      </span>
    </div>
  </div>

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

  <div className="af-payoff-pane">
    <div className="af-payoff-cap">What comes back</div>
    <div className="af-payoff-line"><span className="af-payoff-key">invoice\_number</span><b>{'INV-2026-9001'}</b>{'  '}<span className="af-cite">98%</span></div>
    <div className="af-payoff-line"><span className="af-payoff-key">issue\_date</span><b>{'2026-02-20'}</b>{'  '}<span className="af-cite">98%</span></div>
    <div className="af-payoff-line"><span className="af-payoff-key">total\_amount</span><b>{'4594.62'}</b>{'  '}<span className="af-cite">98%</span></div>
    <div className="af-payoff-line"><span className="af-payoff-key">currency</span><b>USD</b></div>
    <div className="af-payoff-line"><span className="af-payoff-key">line\_items</span><b>{'2 rows'}</b></div>

    <div className="af-payoff-line">
      <span className="af-cite">
        {'↳ each value cites its source phrase'}
      </span>
    </div>
  </div>
</div>

<div className="af-cost">
  <span><b>Nodes</b><strong>Parse → Extract</strong></span>
  <span><b>Credits</b><strong>60 per page</strong>{' (Parse 25 + Extract 35, default tiers)'}</span>
  <span><b>You get</b>{' 8 header fields + one row per line item'}</span>
</div>

## The workflow

<CodeGroup>
  ```json API theme={null}
  {
    "name": "Invoice processing",
    "description": "Extract the invoice header and its line items",
    "nodes": [
      { "id": "parse_1", "type": "parse" },
      {
        "id": "extract_1",
        "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": "issue_date",     "description": "Date the invoice was issued", "data_type": "date" },
            { "name": "due_date",       "description": "Date by which payment is due", "data_type": "date" },
            { "name": "subtotal",       "description": "Amount before tax", "data_type": "float" },
            { "name": "tax_amount",     "description": "Total tax amount", "data_type": "float" },
            { "name": "total_amount",   "description": "Final total amount due including tax", "data_type": "float" },
            {
              "name": "currency",
              "description": "Currency of the invoice amounts",
              "data_type": "enum",
              "enum_options": [
                { "name": "USD", "description": "US Dollar" },
                { "name": "EUR", "description": "Euro" },
                { "name": "GBP", "description": "British Pound" }
              ]
            },
            {
              "name": "line_items",
              "description": "Individual line items listed on the invoice, one row per item",
              "data_type": "object",
              "nested_fields": [
                { "name": "description", "description": "Description of the item or service", "data_type": "string" },
                { "name": "quantity",    "description": "Number of units", "data_type": "integer" },
                { "name": "unit_price",  "description": "Price per unit", "data_type": "float" },
                { "name": "amount",      "description": "Total amount for this line item", "data_type": "float" }
              ]
            }
          ]
        }
      }
    ],
    "edges": [{ "source": "parse_1", "target": "extract_1" }]
  }
  ```

  ```python Python theme={null}
  import os
  from anyformat.sdk import Client
  from anyformat.workflow import Schema

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

  workflow = (
      client.workflow("Invoice processing", "Extract the invoice header and its line items")
      .parse()
      .extract([
          Schema.string("invoice_number", "The unique invoice identifier"),
          Schema.string("vendor_name",    "Name of the company that issued the invoice"),
          Schema.date("issue_date",       "Date the invoice was issued"),
          Schema.date("due_date",         "Date by which payment is due"),
          Schema.float("subtotal",        "Amount before tax"),
          Schema.float("tax_amount",      "Total tax amount"),
          Schema.float("total_amount",    "Final total amount due including tax"),
          Schema.enum("currency", "Currency of the invoice amounts", options=[
              Schema.option("USD", "US Dollar"),
              Schema.option("EUR", "Euro"),
              Schema.option("GBP", "British Pound"),
          ]),
          Schema.object("line_items", "Individual line items listed on the invoice, one row per item", fields=[
              Schema.string("description", "Description of the item or service"),
              Schema.integer("quantity",   "Number of units"),
              Schema.float("unit_price",   "Price per unit"),
              Schema.float("amount",       "Total amount for this line item"),
          ]),
      ])
      .create()
  )
  ```

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

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

  const workflow = await af
    .workflow("Invoice processing", "Extract the invoice header and its line items")
    .parse()
    .extract([
      Schema.string("invoice_number", "The unique invoice identifier"),
      Schema.string("vendor_name",    "Name of the company that issued the invoice"),
      Schema.date("issue_date",       "Date the invoice was issued"),
      Schema.date("due_date",         "Date by which payment is due"),
      Schema.float("subtotal",        "Amount before tax"),
      Schema.float("tax_amount",      "Total tax amount"),
      Schema.float("total_amount",    "Final total amount due including tax"),
      Schema.enum("currency", "Currency of the invoice amounts", [
        Schema.option("USD", "US Dollar"),
        Schema.option("EUR", "Euro"),
        Schema.option("GBP", "British Pound"),
      ]),
      Schema.object("line_items", "Individual line items listed on the invoice, one row per item", [
        Schema.string("description", "Description of the item or service"),
        Schema.integer("quantity",   "Number of units"),
        Schema.float("unit_price",   "Price per unit"),
        Schema.float("amount",       "Total amount for this line item"),
      ]),
    ])
    .create();
  ```
</CodeGroup>

## Run it and read the result

Upload the invoice, then poll the run until its `status` is `processed`. The results arrive inline on that same read.

<CodeGroup>
  ```bash curl theme={null}
  # 1. Create the workflow with the JSON above
  curl -X POST 'https://api.anyformat.ai/v3/workflows/' \
    -H "Authorization: Bearer $ANYFORMAT_API_KEY" \
    -H 'Content-Type: application/json' \
    -d @invoice-workflow.json
  # → 201 { "id": "<workflow_id>", ... }

  # 2. Upload the invoice 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=@invoice.pdf'
  # → 202 { "run_id": "<run_id>", "document_packet_id": "...", "status": "queued" }

  # 3. Poll until "status" is "processed"
  curl "https://api.anyformat.ai/v3/runs/$RUN_ID/" \
    -H "Authorization: Bearer $ANYFORMAT_API_KEY"
  ```

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

  print(result.fields["invoice_number"].value)
  print(result.fields["total_amount"].value)

  # An object field is a list of rows; each cell is an ExtractedField.
  for row in result.fields["line_items"]:
      print(row["description"].value, row["quantity"].value, row["amount"].value)
  ```

  ```typescript TypeScript theme={null}
  import { readFileSync } from "node:fs";
  import type { ExtractedField, ExtractedRows } from "@anyformat/sdk";

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

  console.log((result.field("invoice_number") as ExtractedField<string> | undefined)?.value);
  console.log((result.field("total_amount") as ExtractedField<string> | undefined)?.value);

  // An object field comes back as rows; each cell is an ExtractedField.
  for (const row of result.field("line_items") as ExtractedRows) {
    console.log(row.description?.value, row.quantity?.value, row.amount?.value);
  }
  ```
</CodeGroup>

## Response

The `results` envelope of a processed run, trimmed to the interesting fields. Every scalar field carries `value`, `confidence`, `evidence`, `verification_status` and `value_override`; the `line_items` rows repeat that shape per cell.

```json theme={null}
{
  "id": "06a8d8f5-1f88-7513-8000-637f6625c2c6",
  "status": "processed",
  "results": {
    "document_packet_id": "06a8d8f5-1dbc-7497-8000-d61ff7a5fecf",
    "parse": { "markdown": "<a id=\"p1_b0\"></a>\n\n# INVOICE\n\n...", "parse_confidence": 98.0, "blocks": [] },
    "classifications": [],
    "splits": [],
    "extractions": [
      {
        "split_name": null,
        "partition": null,
        "fields": {
          "invoice_number": {
            "value": "INV-2026-9001",
            "confidence": 98.0,
            "evidence": [{ "text": "Invoice #: INV-2026-9001", "page_number": 1 }],
            "verification_status": "not_verified",
            "value_override": null
          },
          "total_amount": {
            "value": "4594.62",
            "confidence": 98.0,
            "evidence": [{ "text": "**TOTAL: $4,594.62**", "page_number": 1 }],
            "verification_status": "not_verified",
            "value_override": null
          },
          "line_items": [
            {
              "description": { "value": "Cloud hosting, March", "confidence": 97.0, "evidence": [], "verification_status": "not_verified", "value_override": null },
              "quantity":    { "value": "1",                    "confidence": 97.0, "evidence": [], "verification_status": "not_verified", "value_override": null },
              "amount":      { "value": "1200.00",              "confidence": 97.0, "evidence": [], "verification_status": "not_verified", "value_override": null }
            }
          ]
        }
      }
    ],
    "edits": []
  }
}
```

## When it goes wrong

**A currency comes back empty.** An `enum` constrains the output to the names you declared, so an invoice in a currency you did not list has nothing to match. List every currency you expect in `enum_options`, or use a `string` field when the set is open.

**Your totals are off by a factor.** Numbers arrive as JSON strings (`"4594.62"`). Cast each one with the type you declared before doing arithmetic on it.

**Rows are missing from a dense line-item table.** Standard extraction is a single pass. Move the Extract node to the [agentic tier](/guides/nodes/extract) (150 credits per page), which maps every table to the schema and reasons across sections.

**A value is wrong and you cannot see why.** Read its `evidence`: every value carries the phrase and the `page_number` it was read from. When the evidence points at the wrong block, the field description is the ambiguous part. Name the label as it appears on the page. "Final total amount due including tax" extracts better than "total".

**The numbers do not add up.** Extraction reports what it read, not what is consistent. To check that `subtotal + tax_amount = total_amount`, add a [Validate](/guides/nodes/validate) node with an arithmetic rule. [Contract analysis](/examples/contract-analysis) shows a graph with validation.

## Next steps

<CardGroup cols={2}>
  <Card title="Field types" icon="diagram-project" href="/concepts/field-types">
    Object, enum and the other field shapes
  </Card>

  <Card title="Runs and results" icon="list" href="/concepts/runs-and-results">
    Every section of the results envelope
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.