The situation
Invoices arrive by email as PDFs and scans, in every supplier’s own layout. Someone reads them and types the header and the lines into a finance system, checks them against the purchase order by eye, and routes them for approval. Duplicates slip through because a duplicate is only obvious if you are looking at both at once.
These are illustrative scenarios showing how the products are used, not customer case studies.
Build-versus-buy lens
What this helps you decide
Use the example to find where product configuration ends and custom code begins — and whether the boundary leaves your team with less infrastructure to own.
- Model fit
- Can the record shape evolve without coordinating a migration, API and UI release?
- Workflow fit
- Can approvals, exceptions and deadlines move out of application code without losing control?
- Integration fit
- Can your existing APIs become reusable steps with retries, throttling and visible run history?
- Control fit
- Can your team reconstruct model changes, workflow runs and human decisions later?
What gets modeled
The structure as it would actually be defined — sections that repeat are marked.
- Invoicerecord type
- Supplierchoice
Options read live from Suppliers
- Invoice numbertext
Unique per supplier
- Issueddate
- Linesrepeating section
- Descriptiontext
- Quantitydecimal
- Net amountdecimal
- Tax codechoice
- Source documentPDF upload
The original, kept
- Suppliersrecord type
How it gets built
In the order somebody would actually do it. Each step names the capability it rests on, so it can be checked against the product pages.
Model the invoice, including its lines
An invoice is a header and a variable number of lines, so lines repeat. Invoice number is marked unique per supplier and the amounts are typed, so the model rejects what a spreadsheet would have accepted.
What it uses: Repeating sections, with per-field rules including unique and typed values
Read the document instead of retyping it
Upload the PDF or photograph and have its values extracted against the published invoice schema. The extraction is a draft: it is reviewed, regenerated or discarded before anything is created, so a bad read is caught by a person rather than paid.
What it uses: Values extracted from a completed document against a published schema
Keep the original attached
The source document uploads straight to storage through a short-lived signed link and attaches to the record, with signed links for download and PDF viewing. Large files never travel through the application, and the evidence stays with the record it produced.
What it uses: Direct-to-storage upload with short-lived signed links
Define what a duplicate actually is
Duplicate rules are defined on the schema — supplier and invoice number exactly, with a similarity threshold for the near misses that differ by a space or a leading zero. Those rules are stored with the schema version, so they are versioned alongside it rather than living in whichever import script ran.
What it uses: Duplicate detection defined on the schema, with a similarity threshold
Match it against the order before anyone approves
A step reads the purchase order record mid-run and compares totals and lines against it. The invoice may have arrived days earlier; the comparison uses the order as it stands today. A mismatch leaves by a different link, and anything the conditions do not cover falls through to the link marked as the default.
What it uses: A step that reads the record mid-run, and conditional links
Route the exceptions to a person
A mismatch becomes a human decision step with your own reason codes, so why an invoice was held is reportable data. A sentence typed into a comment box is not. The task keeps the options and the form it was created with, so editing the workflow leaves open tasks alone.
What it uses: Human decision steps with reason codes, fixed at task creation
Post it, and tell the systems that care
The approved invoice posts to the finance system as a mapped step. Downstream systems subscribe to the record events instead of polling for them: deliveries are signed, they retry on failure, and every attempt is in the log.
What it uses: Mapped step inputs, and signed event delivery with retries
Numbers used above, and where they come from
Every figure on this site is counted from the product itself. Nothing here is a performance or customer claim, because there is no measurement to cite for one.
- field types
- 9field typesCounted from egav/backend/app/api/v1/egav/schemas/entity_type_attribute.py — DataType
- destinations for an event
- 4destinations for an eventCounted from egav/backend/app/api/v1/egav/lib/webhook/transports — http, sqs, sns, kafka
What changes
The typing stops, the original document stays attached to the record it produced, and a second copy of an invoice is caught by a rule that is versioned with the model rather than by whoever happened to notice.
Written for Teams with data that keeps changing shape.