A procurement agent paired with NetSuite and Coupa watches for the moment a purchase order, a goods receipt, and a vendor bill are all present for the same line. When the three agree on quantity, unit price, and total within tolerance, the agent drafts a three-way match in Dock and routes it to an accounting reviewer. When they disagree, the agent writes the variance, the suspected cause, and the proposed correction, then waits. The PO and the bill stay inside the ERP. The interpretation lives in Dock.
The architecture
Oracle NetSuite and Coupa stay the system of record for the raw procurement data: requisitions, purchase orders, item receipts, vendor bills, and the general ledger postings that follow. Dock is the system of record for what the agent interprets from that data. Each Dock row carries a pointer back to the source record (netsuite_po_id, coupa_invoice_id), the agent identity that drafted the match, the variance category, the reviewer, and the timestamp of every state change. The agent re-fetches PO totals, receipt quantities, and bill amounts via fresh NetSuite and Coupa API reads before each decision.
The Dock surface
A single table, po_match_queue, holds one row per PO line under review:
| netsuite_po_id | coupa_invoice_id | vendor | line_total | match_status | variance_note | reviewer | state |
|---|---|---|---|---|---|---|---|
| PO-48211 | INV-9931 | Tegus Labs | $14,200.00 | clean | none | dana@ | approved |
| PO-48244 | INV-9942 | Mistral Optics | $7,318.40 | qty_short | receipt shows 18 of 20 units, vendor confirmed partial ship | dana@ | held |
| PO-48259 | INV-9971 | Crater Metals | $42,900.00 | price_over | bill unit price $1.50 above PO; freight surcharge | rohan@ | escalated |
The row is the unit of audit. Anyone reading it later can see what the agent concluded, what the human did with that conclusion, and which NetSuite and Coupa records it points back to.
The workflow
- The agent polls NetSuite for newly billed POs and pulls the matching Coupa invoice and item receipt.
- It compares the three and writes a
po_match_queuerow withmatch_statusand a one-sentencevariance_note. - An accounting reviewer opens the row, checks the reasoning against the linked records, and approves the bill or holds it for vendor follow-up.
- On approval, the agent posts the bill in NetSuite. The reviewer's name and timestamp are written into the row before the API call fires.
Why this matters
Three-way match is the control auditors look at first. PCAOB AS 2201 directs auditors to focus on accounts with a reasonable possibility of material misstatement and test the controls that prevent it. When matching happens inside an agent's head, that control is invisible. In a Dock row with reasoning, reviewer, and source pointers attached, the control is legible.
NetSuite remains the ledger of record, consistent with Oracle's ERP documentation (NetSuite Help Center). Coupa remains the spend layer, handling sourcing, requisitioning, and invoice capture (Coupa overview). Dock sits between them and holds the agent's interpretation so it can be reviewed, reversed, and audited.
The pattern generalises to accounting close, legal review of vendor contracts, and the broader agent audit trail.
See the full pattern
Start with the procurement pillar, then read how the same pointers feed the month-end packet.
FAQ
Does Dock replace NetSuite or Coupa? No. NetSuite holds the POs, receipts, bills, and ledger postings. Coupa holds requisitions, sourcing, and invoice capture. Dock holds the agent's match, the variance reasoning, and the reviewer approval that gates posting.
What happens when the agent's match is wrong? The reviewer rejects the row. The agent re-fetches the source records, writes a new variance note, and resubmits. The original row stays with its full state history.
Can the agent post the vendor bill on its own?
Only after a named reviewer approves the row. The Dock state machine refuses the NetSuite post-bill API call until state equals approved and a reviewer identity is present.
How does this satisfy an auditor's three-way-match test?
Each row carries netsuite_po_id, coupa_invoice_id, the receipt reference, agent and reviewer identities, and timestamps. The auditor samples rows, follows the pointers, and re-performs the match.