# Extract and validate fields from invoice text

Turn supplied invoice text into a small validated record using ordinary chat output.

## Start with text you can inspect

Pass an invoice's already-extracted text to a chat model and request a small record. This recipe does not read PDFs, perform OCR, find character coordinates, or assign confidence scores. If the original document is an image, text extraction is a separate step that your application must provide and check.

Use the output to assist review. A model's record is not authorization to pay an invoice. Keep the source text available so a person can resolve missing fields, ambiguous taxes, and currency symbols.

## Validate the returned record

Ask for JSON with invoice number, currency, subtotal, tax, and total. Request monetary amounts as decimal strings, then validate structure and arithmetic in Python. This complete local example parses a simulated model response; it makes no API request.

```python
from decimal import Decimal, InvalidOperation
import json
import re

FIELDS = {"invoice_number", "currency", "subtotal", "tax", "total"}

def parse_invoice(text):
    value = json.loads(text)
    if not isinstance(value, dict) or set(value) != FIELDS:
        raise ValueError("Unexpected invoice fields")
    if not all(isinstance(value[k], str) for k in FIELDS):
        raise ValueError("All fields must be strings")
    if not value["invoice_number"].strip():
        raise ValueError("Missing invoice number")
    if not re.fullmatch(r"[A-Z]{3}", value["currency"]):
        raise ValueError("Use an explicit three-letter currency")
    try:
        amounts = [Decimal(value[k]) for k in ("subtotal", "tax", "total")]
    except InvalidOperation as error:
        raise ValueError("Invalid decimal amount") from error
    if any(not amount.is_finite() or amount < 0 for amount in amounts):
        raise ValueError("Expected finite non-negative amounts")
    subtotal, tax, total = amounts
    if subtotal + tax != total:
        raise ValueError("Totals require review")
    return value

sample = '{"invoice_number":"DEMO-1","currency":"USD",' \
         '"subtotal":"100.00","tax":"16.00","total":"116.00"}'
print(parse_invoice(sample))
```

## Add the model request

Call `https://api.mandapi.com/v1` through the OpenAI-compatible client using `MANDAPI_API_KEY` and `MANDAPI_MODEL_ID`. Ask the model to treat invoice text as data and return only the specified fields. Use `null` or a separate review result for missing information; do not fabricate amounts. This parser intentionally rejects incomplete records.

## Check the result

Try wrong totals, ambiguous currency, missing values, and non-numeric amounts. Adjust the schema for discounts, withholding, or tax-inclusive invoices before using such documents. A matching sum checks consistency only; it does not prove that the invoice is genuine or correctly extracted.

## References

- [https://openrouter.ai/docs/cookbook/evaluate-and-optimize/extract-invoice-fields-with-scaledown](https://openrouter.ai/docs/cookbook/evaluate-and-optimize/extract-invoice-fields-with-scaledown)
