{}
StrictJSON
Extraction

Invoice Line Items

Every accounts-payable workflow starts with the same tedious step: turning a supplier invoice into structured rows your ERP or bookkeeping tool can ingest. This prompt extracts only the line-item table — one object per printed row, with description, SKU, quantity, unit price, line total and tax rate — plus the invoice currency and the sum of the lines. It deliberately ignores the invoice header: no vendor name, invoice number, dates or grand total, so the output stays focused on what your import needs. That separation matters: most extraction failures come from mixing header totals with line totals. Watch out for multi-page invoices where the table continues after a page break, and for tax rates printed once in the header instead of per line — this schema expects a rate on every line, so use 0 when a line is untaxed rather than guessing. Pair it with Receipt Parser when you need the whole document.

System Prompt

You are a precise invoice line-item extraction engine. Read the provided invoice text and return ONLY a valid JSON object matching the schema below. Do not add explanations, markdown, or extra fields. Extract ONLY the line-item table of the invoice: one object per printed row, in printed order. Ignore the invoice header entirely: do not extract the vendor name, invoice number, issue date, or grand total. Every field is required. For each line, extract description, quantity, unit_price, line_total and tax_rate from the values printed on that row, and never invent a value that is not printed. Set sku to null when no SKU or product code is printed for that line. currency is the 3-letter uppercase ISO code of the invoice, for example USD, EUR or CAD. lines_total is the sum of the printed line_total values. Amounts are numbers with two decimals and no currency symbols. tax_rate is the decimal rate for the line, for example 0.2 for 20 percent; use 0 when a line is genuinely untaxed. If a line is missing a required value that cannot be found anywhere on the invoice, do not produce any output.

Raw JSON Schema

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "InvoiceLineItems",
  "type": "object",
  "additionalProperties": false,
  "required": ["currency", "line_items", "lines_total"],
  "properties": {
    "currency": {
      "type": "string",
      "minLength": 1,
      "pattern": "^[A-Z]{3}$"
    },
    "line_items": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": ["description", "sku", "quantity", "unit_price", "line_total", "tax_rate"],
        "properties": {
          "description": { "type": "string", "minLength": 1 },
          "sku": { "type": ["string", "null"], "minLength": 1 },
          "quantity": { "type": "number", "exclusiveMinimum": 0 },
          "unit_price": { "type": "number", "minimum": 0 },
          "line_total": { "type": "number", "minimum": 0 },
          "tax_rate": { "type": "number", "minimum": 0 }
        }
      }
    },
    "lines_total": { "type": "number", "minimum": 0 }
  }
}

Zod (TypeScript)

import { z } from "zod";

const LineItemSchema = z.object({
  description: z.string().min(1),
  sku: z.string().min(1).nullable(),
  quantity: z.number().positive(),
  unit_price: z.number().nonnegative(),
  line_total: z.number().nonnegative(),
  tax_rate: z.number().nonnegative(),
}).strict();

export const InvoiceLineItemsSchema = z.object({
  currency: z.string().min(1).regex(/^[A-Z]{3}$/),
  line_items: z.array(LineItemSchema).min(1),
  lines_total: z.number().nonnegative(),
}).strict();

Pydantic v2 (Python)

from pydantic import BaseModel, ConfigDict, Field, field_validator


class LineItem(BaseModel):
    model_config = ConfigDict(extra="forbid")

    description: str = Field(..., min_length=1)
    sku: str | None = Field(..., min_length=1)
    quantity: float = Field(..., gt=0)
    unit_price: float = Field(..., ge=0)
    line_total: float = Field(..., ge=0)
    tax_rate: float = Field(..., ge=0)

    @field_validator("description", "sku", mode="before")
    @classmethod
    def _strip_text(cls, v):
        if not isinstance(v, str):
            return v
        return v.strip()


class InvoiceLineItems(BaseModel):
    model_config = ConfigDict(extra="forbid")

    currency: str = Field(..., min_length=1, pattern=r"^[A-Z]{3}$")
    line_items: list[LineItem] = Field(..., min_length=1)
    lines_total: float = Field(..., ge=0)

    @field_validator("currency", mode="before")
    @classmethod
    def _strip_currency(cls, v):
        if not isinstance(v, str):
            return v
        return v.strip()

Try it in the StrictJSON tool →