Guide
Extract invoice line items from PDF to CSV with TypeScript
To turn an invoice PDF into CSV, extract an array of line items, validate the returned rows, then serialize the columns your importer expects. Keep the original extraction envelope alongside the CSV. A flat file is useful for import, but it cannot by itself explain a missing quantity or show which source row supplied an amount.
This guide includes a TypeScript exporter and a synthetic response you can run without an API key. The same script can call Velrim with your own PDF. It exports review material; it does not approve an invoice or post anything to an accounting system.
Choose the row schema before extracting the table
One element of line_items represents one billed row, with description, quantity, unit price and amount. Invoice number and currency stay at document level and repeat on each exported CSV row. Row indexes are zero-based and link back to pointers such as /line_items/0/amount in the evidence file.
The schema uses strings for quantities and amounts so the application can inspect decimal precision and separators before conversion. This requests textual values; it does not guarantee a verbatim transcription. Decide whether 1,250 means a decimal or a thousands-grouped number from the document context. Do not strip punctuation globally or substitute zero when extraction fails.
The sample requires all four row values. For invoices that legitimately omit quantities or unit prices, change the schema and exporter together to retain explicit null or missing states. Keep discounts, tax lines and shipping separate unless your importer has a documented mapping for them.
Run the downloadable example offline first
Use Node 22 or later in a project with npm install @velrim/sdk zod and npm install --save-dev tsx. Download line-items.ts and Download line-items-response.json. Save both in the same directory, then run npx tsx line-items.ts json line-items-response.json demo. No extraction request is made in JSON mode.
The command creates demo.csv and demo.evidence.json, refusing to overwrite existing files. The fixture has two rows, including a quoted description with a comma, and one cited amount whose value is unconfirmed. Expect one evidence check to be flagged. These are constructed examples, not extraction accuracy measurements.
For your own document, set VELRIM_API_KEY and run npx tsx line-items.ts pdf invoice.pdf invoice-output. This mode sends your PDF to Velrim and page billing applies. Check document limits first; use the upload flow if inline input is too large. Start with a document you are allowed to process.
Keep the CSV and evidence together
The script writes the raw envelope before enforcing the application model. If a required amount is missing, Zod rejects the export and the evidence file remains available for review. A successful export still requires business checks: the metadata checks flag absent evidence, conflicts and unconfirmed source values, but do not establish row completeness or field correctness.
Every CSV cell is quoted, internal quotes are doubled and records end with CRLF, following the format described in RFC 4180. Spreadsheet software can interpret certain text prefixes as formulas even inside quoted CSV cells. This sample rejects formula-like prefixes rather than modifying the extracted text; a negative amount represented as a string also triggers this conservative guard. Inspect such cases in the JSON file and choose an importer with explicit text-column handling. OWASP documents CSV injection and the limits of escaping.
CSV does not carry column types. Import identifiers as text when leading zeros matter, and set delimiter and locale options deliberately. Keep evidence files under the same access controls as the invoice: they contain values and source snippets. The command prints only request metadata and a count of flagged checks.
Test the invoice cases that change row meaning
Compare the extracted rows with labeled invoices from each supplier layout. Count missing and extra rows as well as wrong cells. A schema-valid array with one omitted row still passes type validation. Repeated descriptions are not duplicate evidence by themselves, and an array index is not a durable line-item ID across extraction runs.
| Case | What to inspect |
|---|---|
| Table continues on another page | Repeated headers, dropped continuation rows and amounts assigned to the preceding row. |
| Wrapped descriptions | A visual second line becoming a second item, or two items being merged. |
| Discounts and credit notes | Signs, whether an adjustment is a row or document total, and the CSV text guard. |
| Tax, shipping and rounding | Which printed totals include each adjustment; apply an explicit decimal arithmetic policy. |
| Empty or partial arrays | Whether the document has no items or extraction missed them; do not infer completeness from an empty list. |
Connect review before downstream import
Keep the zero-based row index with your review record and open the associated source highlight. Reconcile totals with the document’s tax and discount rules, then map approved data to the importer’s actual columns. Do not infer an acceptance policy from the synthetic fixture’s confidence values.
For a typed integration without CSV, use the Zod and Pydantic guide. For queue-based processing, use asynchronous extraction. The confidence-threshold guide explains how to measure a review policy on your own labeled workload.