Extract invoice data from emails, automatically
Invoices arrive by email and die in inboxes. Build a pipeline that catches them, extracts vendor, amount and due date, and sends clean JSON to your tools.
The monthly scavenger hunt
If you run a business (or just an expense report), you know the ritual. End of month, accounting needs the invoices, and they're scattered across an inbox: some as PDF attachments, some as HTML receipts with no attachment at all, some behind a "view your invoice" link, all buried between newsletters from the same vendors. You search for "invoice", miss the ones titled "Your receipt", and forward things one by one to a bookkeeping address.
This is one of the most automatable chores in existence, because invoice emails are machine-generated, repetitive and information-dense. The trick is treating them not as mail to read but as data to extract. Here's the pipeline, stage by stage.
Stage 0: give billing its own address
The highest-leverage move happens before any parsing: stop letting invoices land in your personal inbox at all. Create a dedicated address used only for billing (for example billing@yournick.hidemy.world) and update your vendor accounts to use it. Now the stream you're processing is 90% invoices before you've written a single rule, and each vendor can even get its own variant (aws-billing@…, stripe@…) so the sender is identifiable from the address alone.
This also future-proofs the pipeline. When a vendor's "we've updated our terms" mail arrives at the billing address, it's noise you can filter, instead of noise camouflaged inside everything else you receive.
Stage 1: catch invoices, drop everything else
Even a dedicated address gets non-invoices: marketing from vendors, dunning reminders, terms updates. Some of those you still want (a payment-failure notice matters more than the invoice itself), so "drop everything that isn't an invoice" is usually the wrong spec. Filtering is a layered job. Deterministic rules handle the predictable part: known billing senders, subjects containing "invoice", "receipt" or "payment", messages carrying a PDF attachment. An AI rule then covers the long tail with one sentence: "An invoice, receipt or payment notification, not marketing or product announcements."
The combination matters. Pure keyword rules miss the receipt titled "Thanks for your order"; pure AI evaluation is wasted on the hundredth identical AWS invoice. Cheap rules first, judgement second.
Stage 2: know what you're extracting
Before extracting anything, decide on your target schema. For most bookkeeping purposes six fields cover it:
- vendor: who is charging you
- invoice_number: their reference, for deduplication and disputes
- amount and currency: as separate fields; never parse "€1.234,56" casually
- issue_date and due_date: ISO 8601 or regret
- attachment: the original PDF, preserved for the accountant and the auditor
Stage 3: pull the fields out
Where the data lives varies by vendor. Many receipts put everything in the HTML body: amounts and dates sit in template-generated table cells that CSS selectors can target reliably. Others put a bare "your invoice is attached" in the body and all the substance in the PDF. Subject lines often carry the invoice number. A robust extractor checks body first, falls back to the attachment, and records which source it used.
One awkward pattern deserves a mention: the "view your invoice" email whose body contains nothing but a login link. No parser fixes that: the data simply isn't in the message. Flag these for human handling rather than pretending, and where the vendor offers a setting to attach PDFs to billing emails, switch it on at the source.
Numbers deserve paranoia. Currency symbols, thousands separators and decimal commas differ by locale, and an amount extracted as "1.234" could be one-and-a-bit or one thousand two hundred. Extract the raw string, normalise it explicitly, and keep the original for verification. Dates get the same treatment: "03/04/2026" is ambiguous in exactly the way that ruins quarterly reports.
On HideMy.world this stage is a transformer: a step in the pipeline that reshapes the parsed email into your target JSON (picking fields, running extraction, discarding the rest), so what leaves the pipeline is already in your schema, not a blob of email to post-process.
Stage 4: deliver somewhere useful
Clean JSON is only valuable where it's consumed. Common destinations: a webhook into your accounting system or a small internal service that files the PDF and creates the ledger entry; a Telegram channel where each invoice posts a one-line summary the moment it arrives ("Hetzner - €47.20 - due 28 May"); or a plain forward to your bookkeeper's inbox for the human-in-the-loop version. These aren't mutually exclusive: a summary to Telegram and structured delivery to the webhook is a popular pairing.
Whatever the destination, deduplicate on vendor + invoice_number. Vendors resend invoices, delivery systems retry, and paying an invoice twice is the one failure mode this pipeline must never have.
Retention deserves a thought too. Tax authorities in most countries expect invoices kept for years, so the pipeline's JSON is a convenience layer, not the system of record: make sure the original PDF or full email lands somewhere durable, whether that's your accounting platform's document store or a bucket your webhook handler writes to on receipt.
What you end up with
Assembled, the pipeline reads like a sentence: a dedicated billing address receives everything; rules and one AI rule let only genuine invoices through; a transformer extracts vendor, number, amount and dates into fixed JSON; delivery posts it to the systems that care. No monthly scavenger hunt, no missed receipts, no manual forwarding, and when a new vendor appears, onboarding them is typing an email address into their billing settings.
The broader lesson applies beyond invoices: any recurring, machine-generated email is a data feed wearing a disguise. Give it its own address, filter it, shape it, deliver it, and it stops being mail.