# Read a PDF with AI and have the sender confirm it

Let an AI model read an invoice, an order or an application in n8n, prefill a Formstep request with what it read, and have the person who sent the document confirm or correct each value.

The pattern fits any document someone sends you: an order, a claim, an application, a contract to check. Checked on self-hosted n8n 2.44 with version 0.13.0 of the node.

![The finished execution: Invoice, Download the invoice, Extract the text, Read the invoice with AI with its Claude model, Keep the file, Ask the supplier to confirm, Wait for the supplier, Confirmed? and What the supplier corrected, all green](https://docs.formstep.io/guides/n8n/n8n-invoice-workflow.png)

Import it from

[formstep-invoice-ai-confirm.json](https://github.com/formstep/n8n-nodes-formstep/blob/main/examples/formstep-invoice-ai-confirm.json)

with **Import from File**. The example pins a sample result on the AI node, so it runs before you add a model; unpin it once your model is connected.

## Before you start

- The Formstep node on an n8n with a public HTTPS address, and a [Formstep credential](https://docs.formstep.io/guides/n8n/connect/).

- A credential for a chat model in n8n: Anthropic, OpenAI, Google Gemini or any other the AI nodes take.

- A form for the confirmation. In Formstep, open **Templates**, pick **Confirm Invoice Details**, click **Use template** and publish it. Its field keys are `supplier_name`, `invoice_number`, `invoice_date`, `due_date`, `total_amount`, `confirmed` and `notes`, and it has a Documents block for the invoice.

## 1. Get the PDF

The example downloads a [sample invoice](https://docs.formstep.io/guides/n8n/samples/invoice.pdf) with an **HTTP Request** node, **Response Format** **File**, and takes the supplier’s address from an **Edit Fields** node named _Invoice_. In your workflow it is usually a Gmail or Outlook trigger on the invoices inbox, with the attachment in a binary field and the sender as the recipient, or a new file in a Google Drive folder.

## 2. Extract the text

Add an **Extract from File** node with **Operation** **Extract From PDF**. It reads the PDF’s text into `{{ $json.text }}`.

> **Scanned documents**
>
> Extract From PDF reads the text a PDF carries. A scan or a photo has none. For those, use a model node that reads images or PDFs itself instead of these two steps; the rest of the guide stays the same.

## 3. Read it with AI

Add an **Information Extractor** node, connect your chat model to it, and set **Text** to `{{ $json.text }}`. Under **Attributes**, add one attribute per value you want, named after the field key it fills:

| Name | Type | Description |
| - | - | - |
| supplier\_name | String | The company that issued the invoice |
| invoice\_number | String | The invoice number, as printed |
| invoice\_date | Date | The invoice date, as YYYY-MM-DD |
| due\_date | Date | The payment due date, as YYYY-MM-DD |
| total\_amount | Number | The total to pay, including VAT, as a number without currency |

![The Information Extractor node: Text {{ $json.text }}, Schema Type From Attribute Descriptions, and the first two attributes supplier\_name and invoice\_number open with their type, description and Required on](https://docs.formstep.io/guides/n8n/n8n-invoice-extractor.png)

The node returns what it read under `{{ $json.output }}`, as `{{ $json.output.total_amount }}`. Naming the attributes after the field keys keeps the mapping in the next step one to one.

## 4. Put the file back

The Information Extractor returns what it read, not the PDF. To attach the invoice to the request, add a **Merge** node with **Mode** **Combine** and **Combine By** **Position**. Connect the HTTP Request node to input 1 and the Information Extractor to input 2. Each item then has the file in `data` and the reading in `output`.

## 5. Ask the supplier to confirm

Add a Formstep **Create request** node and pick the Confirm Invoice Details form. Set **Recipient Email** to the supplier’s address, map each field under **Values to Send** to its value, like `{{ $json.output.invoice_number }}`, and add a document from the binary field `data`, named `Invoice {{ $json.output.invoice_number }}`. Turn on **Wait for the Outcome** and add a **Wait** node after it, **Resume** **On Webhook Call**.

Dates and the amount arrive in their own inputs: the date fields take `2026-10-20`, the amount a number. The supplier sees every value filled in, with the invoice above them, and fixes what is wrong:

![The request page: Check Your Invoice Details, the attached Invoice INV-2026-0187 PDF, Supplier Fjord Office Supplies AS, Invoice Number INV-2026-0187, Invoice Date October 20, 2026, Due Date November 3, 2026, Total Amount 27,235, and the confirmation switch](https://docs.formstep.io/guides/n8n/n8n-invoice-recipient.png)

## 6. See what was corrected

Add an **If** node that checks `{{ $json.body.type }}` is `request.completed`, and on its true output a **Code** node that compares the AI’s reading with the confirmed answers:

```javascript
// Compare what the AI read with what the supplier confirmed.
const read = $('Read the invoice with AI').item.json.output
const confirmed = $json.body.data.answers
const corrected = Object.keys(read).filter((key) => String(read[key]) !== String(confirmed[key]))
return { json: { invoice: confirmed, corrected, notes: confirmed.notes ?? null } }
```

In the test run the model read the total as 27 235. The supplier corrected it to 27 325 and left a note, so `corrected` lists `total_amount`:

![The output of What the supplier corrected: invoice with confirmed true, the dates, the invoice number, the supplier's note, supplier\_name and total\_amount 27325, and corrected with one entry, total\_amount](https://docs.formstep.io/guides/n8n/n8n-invoice-corrected.png)

From here, book the invoice with `invoice` in your accounting tool, and send the ones where `corrected` is not empty to a person for a second look. The false output of the If node catches a request that expired: chase the supplier there.

## Variations

- **Check it yourself instead.** Send the request to someone on your team rather than the sender, for documents nobody outside should see again.

- **Lock what you are sure of.** Values that come from your own system, like the order number you issued, go under **Read-Only Field Names or IDs**, so only the AI’s reading is open to correction.

- **No form yet.** Set **Form Source** to **New Form** and build the form from the attributes; see [Collect the documents each case needs](https://docs.formstep.io/guides/n8n/collect-documents/).

## Next

[Collect the documents each case needs in n8n](https://docs.formstep.io/guides/n8n/collect-documents/) — Build the form from a checklist and file every upload.

[Value shapes](https://docs.formstep.io/requests/creating-requests/#value-shapes) — What each field type takes as a prefilled value.
