The pattern
Two halves, joined by one workflow call:- A FlowX Database holds the approved purchase orders that invoices are matched against.
- An integration workflow does the matching: it extracts the invoice, looks up its PO, and returns
MATCHEDorEXCEPTIONtogether with the list of reasons. It never decides anything a person should decide. - A process owns the business flow: an Email Trigger starts it for each invoice email, it hands the attachment to the workflow, and it routes the result either to automatic approval or to a reviewer.

Step 1: Store the purchase orders
Model a purchase order
Create the FlowX Database
PurchaseOrders, and select PurchaseOrder as its schema. Keep the default partition key _id.Add a lookup operation
findPurchaseOrder with one String parameter, poNumber, and this filter:
Load sample purchase orders
insertPurchaseOrders with an Object parameter orders and the payload ${orders}. Then create a run-once workflow, seedPurchaseOrders: a Script node that builds the orders, followed by a Data Source node that calls insertPurchaseOrders with orders bound to ${orders}.PurchaseOrders: it lists the three orders. In a real deployment, your ERP feeds this collection instead.Step 2: Extract the invoice
Create a workflow namedreconcileInvoice. It takes one input, the path of the invoice file in the Document Plugin, and returns the reconciliation result.

Declare the input
filePath and declare it as the Start node’s input parameter.Add the Document Extraction node
Extract invoice data after Start:- Presets: Metadata Generation, then replace the prefilled instructions with the ones below
- Document Source: Document Plugin
- File Path:
${filePath} - Response Key:
invoice

Branch the failure path
End failure. An unreadable file or a model error then ends the run visibly instead of passing empty data downstream.Step 3: Match the invoice against its purchase order
Parse the extraction result
Parse invoice on the extraction node’s success branch. Document Extraction returns its answer as a JSON string in responseObjects[0], so parse it and copy the fields into a plain object:Look up the purchase order
Find purchase order. Select the PurchaseOrders database and the findPurchaseOrder operation, set the poNumber parameter to ${invoiceData.poNumber}, and set Response Key to purchaseOrder. Connect its failure branch to End failure.
purchaseOrder.data.Compare invoice and PO
Compare to purchase order. It checks the PO’s status, vendor, currency, subtotal, and every invoice line, and collects one readable message per difference:Road freight FTL Bucharest - Vienna, 13.6 m trailer still matches the PO line Road freight FTL Bucharest - Vienna.Declare the output
reconciliation with status, invoiceNumber, poNumber, vendorName, and currency (String), invoiceSubtotal, poSubtotal, and variance (Number), and exceptions (Array of String). Declare it as the output of the success End node.
.map() or JSON.stringify() on their input, and they only put plain objects in output. Clearing invoice, purchaseOrder, and invoiceData with {} once they are consumed keeps the data that travels between nodes small.Step 4: Test the workflow on sample invoices
Create two PDF invoices to test with. Any tool that prints a PDF works; what matters is the content:Step 5: Receive invoices by email
Create the Email Trigger
Invoice mailbox. Enter the IMAP connection of your AP mailbox. Reference the host, user, and password as configuration parameters (${configParam}) instead of typing them in, so each environment uses its own mailbox and the password never sits in the project.Under the filtering criteria, set:- Start Process/Workflow for: Only emails with these attachment file types,
.pdf - Save email attachments on process/workflow: Only these attachment file types,
.pdf

Start the process from the trigger
invoiceIntake with a swimlane named Accounts payable. Its start node is a Message Start Event named Invoice email received. In its node config, set Trigger Type to Email Trigger and select Invoice mailbox.
emailMessage.fileAttachments, one entry per file with its filePath. For the full email schema, see Email Trigger.
Step 6: Hand the invoice to the workflow
Pick the attachment
Pick invoice attachment with a business rule action:Start the workflow
Reconcile invoice with a Start Integration Workflow action. Select reconcileInvoice and map its filePath input to ${invoice.filePath}.
Receive the result
Receive reconciliation. Add a data stream with Source set to Workflow, select reconcileInvoice, and in the Output Mapping of its success End node map each of the nine reconciliation fields to the process attribute of the same name. Click Update, then save the node.
Step 7: Route exceptions to a reviewer
Summarize the exceptions
Summarize exceptions with a business rule that joins the list into one block of text the review form can show:Branch on the result
Matched?. Send the flow to an End node named Approved automatically when input.reconciliation.status == "MATCHED", and to a User Task named Review exception otherwise.Build the review form
Review exception in a navigation area (for example a Page named Invoice review), then open its UI and add a Card titled Review invoice exception that holds a Form with:saveData action, and connect the task to an End node named Review complete.
Step 8: Run it end to end
Test with mock email content
invoiceIntake with mock email content instead of a real email: fill in a subject, a sender, and a body, and upload the Northwind sample invoice. See Testing with mock email content.
Review the exception
Review exception with both differences listed. Choose a decision, add a comment, and click Submit review. The instance finishes at Review complete.
Watch a clean match pass
Approved automatically without creating a task.Go live
Invoice mailbox from Manage Triggers in Runtime Settings. From then on, each PDF invoice that arrives in the mailbox starts an instance.Adapt the matching rules
The Compare script is the one place where AP policy lives. Common changes:- Tolerance:
TOLERANCE = 0.02accepts a 2% difference on the subtotal and on unit prices. Set it to0for exact matching, or split it into separate rate and total tolerances. - Partial deliveries: the quantity check only flags invoiced quantities above the PO. To bill against the remaining PO balance, store the quantity invoiced so far on each PO line and compare against the balance.
- Duplicate invoices: add a FlowX Database of processed invoice numbers, look it up before the comparison, and raise an exception when the vendor has billed the same number before.
- Line matching: prefix matching on descriptions suits carriers that append details. When suppliers print item codes, add a
codeattribute toPurchaseOrderLineand match on it instead.

