Chapter 04 · Section V · 11 min read
4.5 Extraction from messy text: receipts, invoices, forms
Pulling structured fields out of unstructured or semi-structured text. Schemas, missing-field handling, and the specific traps of receipts and invoices.
Extraction is the pattern of pulling a known set of fields out of unpredictable text. Receipts from ten different Kathmandu shops all say roughly the same things — vendor, date, items, total — but the shape of each receipt is different. Extraction turns that mess into JSON your accounting sheet can eat.
The pattern
Extract the following fields from the receipt text and return valid JSON.
Schema:
{
"vendor": string, // shop name
"date": string, // ISO 8601: YYYY-MM-DD
"total_npr": number, // final total in NPR, no currency symbol
"items": [
{
"name": string,
"quantity": number, // 1 if not specified
"unit_price_npr": number // per-unit, in NPR
}
],
"payment_method": string | null // e.g. "cash", "esewa", "khalti", "card"
}
Rules:
- Return only the JSON. No preamble, no code fences.
- If a field is missing from the source, use null. Do not invent values.
- For dates in Bikram Sambat, convert to Gregorian ISO 8601. Note the
conversion in a "date_source" field: "BS" or "AD".
- Ignore anything that isn't a real receipt line (headers, footers, "thank you", VAT breakdowns, etc.).
<receipt>
Bhat Bhateni Supermarket - Maharajgunj
2081-03-14
Basmati Rice 5kg 650
Sunflower Oil 1L 340
Detergent 1kg 280
Subtotal: 1270
VAT (13%): 165
TOTAL: 1435
Paid: eSewa
Thank you for shopping!
</receipt>
Every element of that schema is there because a real receipt somewhere breaks it.
The rules that stop the specific failures
“Return only the JSON. No preamble, no code fences.” Stops “Here’s the extracted data:” and ```json ``` wrappers.
“If a field is missing, use null. Do not invent values.” The single most important rule for extraction. Without it, missing fields get plausible fake data — a made-up phone number here, a guessed date there.
“Ignore anything that isn’t a real receipt line.” Otherwise the model happily extracts “Thank you for shopping!” as an item.
“For dates in Bikram Sambat, convert to Gregorian ISO 8601.” Nepal-specific but a good illustration: name the ambiguity and say which resolution you want. Without this, some receipts come out as "2081-03-14" (BS) and some as "2024-06-28" (AD) and you can’t tell them apart.
Explicit units in the field names. total_npr beats total — the model can’t accidentally return USD.
Extraction from tables
If the source has clear rows and columns, name them:
Extract each row from the table below. Each row has:
- item_name (column 1)
- quantity (column 2, integer)
- unit_price_npr (column 3, number)
Return a JSON array. Skip the header row and any total/subtotal rows.
<table>
Item Qty Price
--------------------------
Rice 5kg 1 650
Oil 1L 2 340
Detergent 1 280
--------------------------
TOTAL 1610
</table>
Naming the columns positionally protects against extra columns you didn’t expect.
Extraction from prose
The other extreme — pulling structured fields from free text:
Extract the following fields from the email below.
Schema:
{
"sender_name": string | null,
"sender_organisation": string | null,
"meeting_date": string | null, // YYYY-MM-DD
"meeting_time": string | null, // HH:MM in 24-hour, local time
"location": string | null, // physical or virtual
"action_items": [string] // things the sender is asking of the recipient
}
Rules:
- Return only the JSON. No preamble.
- If the email is a reply, extract from the newest message only, not the
quoted history below.
- Do not invent action items — only extract explicit asks.
<email>
Hi Suraj,
Just confirming our meeting on Thursday, Ashad 15 at 3 PM at the Baneshwor
office. Please bring the Q1 financials and be ready to present the mobile
launch timeline.
Best,
Anita — Head of Product, Sagoon
</email>
The rules on quoted history and explicit action items are worth stealing — they cover two of the most common email-extraction bugs.
The confidence variant
For extraction that will drive decisions (auto-approving expenses, filing tax data), add confidence:
For each extracted field, also return a "confidence" score:
"high" = the value appears exactly and unambiguously in the source.
"medium" = the value was inferred but is plausible.
"low" = the value is uncertain; a human should review.
Route “low” confidence extractions to a human. This turns a 100% automation dream into a 90% automation reality — which is much more valuable than a 100% automation with a 10% error rate.
Check your understanding
Quick check
—