Automation · Finance
How to build three-way matching
An invoice matches its purchase order, so it gets paid, and nobody notices the goods never arrived. The model behind matching that checks all three documents, the prompts that build it, and what it takes to run it.
Built with Tray Headless
- System Coupa
- Step Match line by line
- Step Hold until received
- Step Clear within tolerance
- System NetSuite
An invoice that arrives before its receipt is held, and released on its own the moment the receipt is recorded.
The short answer
What is three-way matching?
Three-way matching has four parts: a comparison of order, receipt and invoice at the line rather than the total, tolerances on price and quantity that finance sets and can change, holds for invoices that arrive before their receipt that release on their own, and every mismatch sent to a named person with the three documents side by side. The step that fails most is the receipt. If nobody records that goods or services arrived, every invoice either waits forever or gets matched two ways, and two-way matching pays for things that never came.
Stage 6 of 7: Three-way match and payment. Part of Procure-to-pay, end to end : every stage, the systems it runs on and the guide that builds it.
What matters here
- Match at the line, not the total. Two wrong lines can add up to a right total.
- Ask the requester to confirm services were delivered. There is no delivery note for a consultant's week.
- Hold an invoice that arrives before its receipt, and release it the moment the receipt lands. Rejecting it annoys a supplier who did nothing wrong.
- Treat a price change as an exception, never a tolerance. That is a supplier changing terms.
- Measure the first-pass match rate. It tells you whether the tolerances or the receipts are the problem.
Who this is for
You run accounts payable or finance systems. Invoices are matched to the purchase order by eye, receipts are recorded when somebody remembers, and the exceptions queue is a shared inbox.
How it works in practice
What has to happen between a supplier's invoice arriving and it being cleared to pay.
- 1
The invoice is tied to its purchase order
By the order number on the invoice, or by supplier, amount and open orders when the number is missing or wrong.
- 2
Each invoice line is matched to an order line and a receipt
Quantity, unit price and amount compared line by line, with freight and tax handled as their own lines.
- 3
Differences inside tolerance clear on their own
A small rounding difference or a quantity a few percent over, within limits finance sets per category.
- 4
An invoice ahead of its receipt waits, then releases
Held rather than rejected, and cleared automatically when the receipt is recorded, usually a day or two later.
- 5
Everything else goes to a named person
With the order, the receipt and the invoice side by side and the difference highlighted, not a line in a shared queue.
- 6
Cleared invoices go forward for payment
On the supplier's terms, with the match result recorded against the invoice for the audit.
What three-way matching is made of
Four parts, and the second is where most mismatches really start.
Line-level comparison
Each invoice line against its order line and receipt, so a short delivery on one item is caught even when the total looks right.
Receipts for goods and services
Goods received from the warehouse or the requester, services confirmed by the person who ordered them, with a reminder when an invoice is waiting on one.
Tolerances finance controls
Price and quantity limits by category and supplier, held in a table, with a price change always treated as an exception.
Holds and assigned exceptions
Invoices ahead of their receipt wait and release on their own. Real mismatches go to one named person with an age on them.
The Tray Headless prompts
Paste these into Claude Code or Codex with the Tray Headless plugin installed. Each stage runs on its own. The systems named in them are the worked example rather than a requirement, and every prompt says so.
Once per project, run
/tray-workflows:set-workspace
to pick the workspace these build in. Point it at a sandbox first.
- 1
First, read how orders and receipts are held
Matching is only as good as the receipt data under it.
Headless skills
build-workflowUse build-workflow. The systems in play are Coupa, NetSuite, Slack, Okta and Snowflake, or whatever we run in those seats. Before you plan anything, tell me which of them are already authenticated in the workspace. I need the purchase order and its lines, the receipt and its lines, and the supplier invoice and its lines, in whichever system holds each. Then tell me: are services receipted at all today, or only goods. That answer decides how much of this is matching and how much is getting receipts recorded in the first place.
- 2
Match line by line
Two wrong lines can add up to a right total.
Headless skills
build-workflowtray-gotchasUse build-workflow and tray-gotchas. When an invoice is posted as a draft, tie it to its purchase order: By the order number on the invoice, if it is valid for that supplier Otherwise by supplier and open orders with enough remaining value Otherwise stop and route to AP, never guess between two orders Then match each invoice line to an order line and its received quantity: quantity, unit price and line amount. Treat freight, tax and discounts as their own lines, not as differences. Handle units explicitly. An order for 2 boxes of 50 and an invoice for 100 units is a match.
- 3
Set tolerances finance can change
So the small differences clear and the real ones stop.
Hold tolerances in a table, by category and optionally by supplier: price: a percentage and an absolute cap, whichever is smaller quantity: a percentage over, never under amount: a total cap on the invoice difference Inside every tolerance, clear the line. Outside any, stop. Always treat these as exceptions regardless of tolerance: A unit price different from the order on every line. That is a price change, and it needs the buyer. An invoice quantity above the ordered quantity in total. The same invoice number from the same supplier twice.
- 4
Hold invoices that are ahead of the receipt
The goods are usually two days behind the paperwork.
When an invoice line has no receipt yet: For goods, hold the invoice and release it automatically when the receipt is recorded and the line matches. For services, message the requester in Slack: did this arrive, with the order line and the invoice amount, and buttons for yes, partly and no. Record their answer as the receipt. If a hold is still open after a number of days I set, remind the requester, then route to AP with the age shown. For a partial receipt, clear the matched quantity and keep the rest of the invoice on hold. Never clear the whole invoice for part of the goods.
- 5
Send every mismatch to a person, and report the rate
A shared exceptions queue is where early payment discounts expire.
Every exception goes to one named person: the buyer for a price change, the requester for a receipt question, AP for anything else. Show the order line, the receipt and the invoice line side by side with the difference highlighted, in Slack, with the age of the invoice and any discount deadline. Report weekly: first-pass match rate, exceptions by reason and by age, invoices on hold waiting for a receipt, and discounts lost to an open exception. If most exceptions are missing receipts, the fix is receipting, not the tolerances.
- 6
Validate, then hand the tolerances to AP
Because what counts as close enough is finance judgement.
Run the per-step schema checks and the whole-workflow audit before this touches production. Run it in report-only mode for one month end first, so the tolerances are set from real invoices. Then open the same workflow in Tray Build so accounts payable can change the tolerance table and the hold periods in the visual canvas.
What it connects to
The order and receipt live with procurement, the invoice and the payment with finance.
Coupa
Read purchase order lines and receipts where procurement runs there, and record service receipts confirmed by the requester.
Reads and writes
NetSuite
Read the draft vendor bill and its lines, write the match result, and clear matched invoices for payment.
Reads and writes
Slack
Ask the requester to confirm a service was delivered, and send each exception to its owner with the three documents.
Reads and writes
Okta
Find the requester, the buyer and their deputies today, so an exception never goes to somebody who has left.
Reads
Snowflake
Land every match result, so first-pass match rate by supplier and category is a query.
Writes
Same build, other stacks
The design does not change if you run something else in one of these seats. The same prompts build it against SAP S/4HANA, Google BigQuery, Microsoft Teams, Azure Active Directory, Oracle or Databricks.
Connections in this build
Field mapping, templates and common problems for each pairing: Coupa + NetSuite and Okta + Slack.
Named systems are the ones most teams run, not the only ones that work. Each is an authentication in your Tray workspace, referenced by name, so the workflow never holds a credential. Where we have a connector page, the name links to it.
Running it in production
This decides which invoices get paid without anybody looking. It is audited, line by line.
It runs on the platform, not on your laptop
Matching, holds and releases run on the same engine through the month end spike, with every run recorded.
Every cleared line is evidence
The order line, the receipt, the invoice line, the tolerance applied and its version. That is what an auditor samples.
Credentials are managed, never written into the build
A credential that can clear invoices for payment is one of the most sensitive you hold. It lives in your workspace, scoped and separately rotatable.
AP owns the tolerances
The tolerance table and hold periods open in Tray Build, tuned by the team that works the exceptions.
Nothing clears on a guess
An invoice that could belong to two orders stops for a person. A wrong match pays the wrong order and leaves the right one open.
Questions people ask
What is the difference between two-way and three-way matching?
Two-way matching compares the order and the invoice. Three-way adds the receipt, so you only pay for what actually arrived. Two-way can be a sensible policy for low-value services, as long as it is a choice and not a gap.
How do you receipt services?
Ask the person who ordered them. A short message with the order line and the invoice amount, answered with yes, partly or no, is a receipt an auditor will accept.
What should happen when the invoice arrives before the goods?
Hold it and release it automatically when the receipt is recorded. The goods are usually a day or two behind, and rejecting the invoice means the supplier sends it again.
What tolerance should we set?
Start narrow and set it from real data. Run matching in report-only mode for a month end, see where the differences cluster, and set tolerances by category from that.
Can AP change the tolerances without engineering?
Yes. The tolerance table and hold periods open in Tray Build, so a change in policy is a table edit.
Related guides
Finance
How to build a procure-to-pay integration
Requisition to PO to receipt to payment, with the approval matrix as data, three-way matching, and the budget checked before the commitment. The prompts.
Finance
How to build AP invoice processing
Take invoices from wherever they arrive, extract and verify instead of trust, match against the order, and never pay the same one twice. The prompts that build it.
Finance
How to build purchase request approval
Take requests where people already ask, check for an existing contract first, send each one to the reviewers it needs, and chase approvers before the request stalls. The prompts.
Finance
How to build a vendor master sync
Onboard a supplier once, verify bank details out of band, keep one record per legal entity, and make a change to payment details an event. The prompts.
Last reviewed October 2026.