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

**Nodes used:** [Parse](/guides/nodes/parse), [Extract](/guides/nodes/extract)

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.

## 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": []
  }
}
```

## Tips

* Write specific field descriptions. "Final total amount due including tax" extracts better than "total".
* Numbers arrive as strings in the JSON body (`"4594.62"`). Cast them with the type you declared.
* List every currency you expect in `enum_options`. An `enum` constrains the output to those names.
* Multi-page invoices need no configuration. The evidence `page_number` tells you where each value came from.
* To check that `subtotal + tax_amount = total_amount`, add a [Validate](/guides/nodes/validate) node with an arithmetic rule. See [Contract analysis](/examples/contract-analysis) for 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>
