The operating thesis.
A weak three-way match workflow makes AP look fast while moving risk into GRNI, supplier disputes, and payment runs. A strong workflow does the opposite: it makes clean invoices almost invisible and makes every exception obvious, owned, timed, and explainable.
The design below treats three-way match as a stateful ERP control. It separates commercial agreement, physical receipt, invoice assertion, accounting posting, exception ownership, and payment readiness. That separation is what makes the workflow useful to CFOs, controllers, AP managers, procurement teams, warehouse teams, and auditors.
For US and UK operators, the practical bar is simple: a matched invoice should explain itself later. The ERP should show the source purchase order, receiving evidence, invoice payload, tolerance policy, journal impact, approval history, exception trail, payment hold status, and every override.
Diagrams that define the workflow.
Diagram 1
Operating workflow
The happy path is intentionally narrow. Anything outside tolerance leaves the auto-match lane and becomes a buyer-owned exception.
Supplier invoice received
Normalize invoice lines
Load PO commitments
Load goods receipt lines
Apply tolerance policy
Post GRNI and AP
Queue payment
Diagram 2
Invoice line state machine
A line, not only an invoice header, needs a lifecycle. Partial receipts, rejected variances, and approved overrides must not collapse into a vague pending status.
| From | To | Guard |
|---|---|---|
| PENDING_MATCH | AUTO_MATCHED | PO, GRN, invoice line inside tolerance |
| AUTO_MATCHED | PAYMENT_QUEUED | GRNI and AP posting committed |
| PENDING_MATCH | EXCEPTION | Price, quantity, tax, supplier, or currency mismatch |
| EXCEPTION | BUYER_REVIEW | Owner assigned and SLA clock started |
| BUYER_REVIEW | RESOLVED | Corrected document or approved variance |
| BUYER_REVIEW | REJECTED | Invalid invoice, duplicate, blocked supplier, or failed policy |
| PENDING_MATCH | OPEN_RECEIPT | Invoice arrived before full receipt |
| OPEN_RECEIPT | PENDING_MATCH | Additional receipt posted |
Diagram 3
Cross-team sequence
The durable workflow shows which system or team owns each decision. AP should not silently resolve commercial differences that belong to procurement.
Diagram 4
Data model boundary
The match record is the join table for commercial agreement, receipt evidence, supplier assertion, exception handling, and ledger impact.
Diagram 5
Accounting impact
The matched line should clear GRNI and create the AP liability in the same commit. A split commit is how reconciliation debt is born.
Controls, exceptions, and evidence.
Control matrix
| Control | Failure it prevents | Evidence to preserve |
|---|---|---|
| Line-level PO, GRN, and invoice join | Header-level approval hiding line variance | Matched line id, PO line id, GRN line id, invoice line id |
| Independent price and quantity tolerance | A quantity overage being disguised as a price variance | Tolerance policy version and computed variance |
| Buyer-owned exception queue | AP approving commercial changes without context | Owner, SLA, buyer decision, reason code |
| Atomic GRNI and AP posting | GRNI cleared without a corresponding liability | Journal id, transaction id, debit line, credit line |
| Idempotent match run | Duplicate match run from retry or integration replay | External id and original match run id |
Exception taxonomy
| Exception | Likely owner | Resolution path |
|---|---|---|
| Invoice before receipt | Warehouse | Hold line in OPEN_RECEIPT until receiving evidence exists |
| Price variance | Buyer | Approve variance, request credit memo, or ask supplier to reissue |
| Quantity variance | Warehouse and buyer | Correct receipt, split invoice, or dispute excess quantity |
| Duplicate invoice | AP | Reject and link to existing invoice or payment |
| Blocked supplier or sanctions hit | Procurement and compliance | Hold payment until supplier status is cleared |
Audit evidence checklist
| Evidence | Why it matters |
|---|---|
| Original invoice file or EDI payload | Shows what the supplier asserted |
| PO approval chain | Shows commercial agreement before the invoice arrived |
| Goods receipt timestamp and receiver | Shows physical receipt or service confirmation |
| Tolerance policy version | Shows which rule allowed or blocked auto-match |
| Exception assignment and comments | Shows ownership and reasoned resolution |
| Journal posting transaction id | Connects operational match to ledger impact |
API, error, and database contracts.
Match creation payload
{
"external_id": "match_run_2026_07_01_supplier_5821_invoice_88401",
"invoice_id": "inv_88401",
"purchase_order_id": "po_43822",
"goods_receipt_id": "grn_99140",
"policy": {
"price_tolerance_basis_points": 200,
"quantity_tolerance_units": 0,
"tax_tolerance_minor": 100
},
"requested_by": "ap_clerk_17"
}Exception response for a price variance
{
"type": "https://rivane.ai/problems/invoice-match.price-variance",
"title": "Invoice line price exceeds tolerance",
"status": 422,
"detail": "Supplier invoice line 3 is 4.8 percent above the purchase order unit price.",
"instance": "/v1/invoice-matches/match_129/exceptions/exc_44",
"code": "invoice_match.price_variance",
"owner_role": "buyer",
"sla_deadline": "2026-07-03T17:00:00Z"
}Relational constraints that prevent silent drift
alter table invoice_match
add constraint invoice_match_status_check
check (status in (
'PENDING_MATCH',
'AUTO_MATCHED',
'PAYMENT_QUEUED',
'EXCEPTION',
'BUYER_REVIEW',
'RESOLVED',
'REJECTED',
'OPEN_RECEIPT'
));
create unique index invoice_match_external_id_unique
on invoice_match(entity_id, external_id)
where external_id is not null;
create unique index invoice_match_line_once
on invoice_match(invoice_line_id)
where status in ('AUTO_MATCHED', 'PAYMENT_QUEUED', 'RESOLVED');Implementation quality bar.
Implementation quality bar
| Area | Minimum acceptable behavior |
|---|---|
| User experience | AP sees one queue with clear reason codes, not generic pending work |
| Performance | Batch match runs are resumable and idempotent across retries |
| Controls | Override permissions are separate from invoice entry permissions |
| Reporting | GRNI aging reconciles to open receipt and open invoice lines |
| Recovery | A failed posting leaves no half-cleared GRNI state |
Do not treat tolerance as a global setting. Supplier risk, item category, jurisdiction, currency, and receiving pattern all change the right tolerance design.
Do not let AP override procurement terms by editing invoice lines after match. Corrections should preserve the original assertion and create an explicit adjustment or supplier reissue path.
Do not collapse partial receipts into exceptions that look like errors. Partial fulfillment is normal. The state model should show that the line is waiting for receipt evidence.
Do not release payment only because an invoice header is approved. Payment readiness should come from line-level match status, supplier status, payment terms, tax handling, and hold checks.
Sources and reference context.
Book a Demo.
If your current three-way match workflow cannot show line-level state, exception ownership, GRNI impact, and payment readiness from one audit trail, the control is probably weaker than it looks.