Difficulty: Intermediate
Your team receives invoice PDFs by email. Before approving them, someone needs to find the purchase order, compare the amount, and make sure the invoice has not already been registered. The next invoice starts the same sequence again. If your company already uses Power Automate and SharePoint, how can you use them to automate these checks and the handoff to a reviewer?
In this step-by-step tutorial, you will build a Power Automate workflow that reads invoice attachments with AI Builder, checks them against purchase-order data in SharePoint, and sends invoices that pass validation to a person for approval. It records approved and rejected decisions, and flags validation exceptions for investigation. You will see the completed flow and its results first, then build it one step at a time.
What is AI Builder? AI Builder is a Microsoft Power Platform capability that lets you use prebuilt or custom AI models in Power Automate and Power Apps. Here, you will use its prebuilt invoice-processing model to extract fields such as the invoice number, vendor name, purchase-order number, and invoice total from a PDF. You do not need to train a model for this tutorial. Power Automate uses those extracted values to run the checks and route the invoice.
The example uses Northwind Components, a fictional business, and synthetic invoices. SharePoint stores the extracted values, source identifiers, and processing status. The original PDF remains in the shared mailbox.
AI extracts candidate values. Rules decide whether an invoice is ready for review. A person makes the approval decision. Approval in this tutorial records a review outcome; it does not create an accounting entry or release a payment.
See the finished workflow first
Here is the completed flow you will build. Follow the center of the diagram from the email trigger to invoice extraction, the two SharePoint lookups, and the validation condition. The left branch creates a pending invoice, waits for a human decision, and updates the record. The right branch records an exception without requesting approval.
The workflow you’ll build: Extract invoice data from incoming emails, check purchase orders and duplicates, route valid invoices for human approval, and record approval outcomes or validation exceptions in SharePoint.
In the diagram, Condition 1 checks the review outcome. Update item records Approved, while Update item 1 records Rejected on the same pending invoice record.
The result is a list a person can act on. In the lab, one invoice was approved, another was rejected, and three submissions were held for a total mismatch, a missing purchase order, or a duplicate invoice.
Five observed outcomes in InvoiceRegister. This view excludes setup and earlier test rows; those rows remain in the list.
You will build this in four stages: prepare the lists, test extraction on one PDF, add the validation branches, and connect the human review. Each stage ends with a checkpoint so you can find mistakes before adding the next part.
What you will build
The accounts-payable team at Northwind receives PDF invoices by email. For this exercise, one invoice refers to one purchase order, and the expected total is recorded in a small SharePoint register. The team needs a visible answer to two questions: ‘Can I review this invoice now?’ and ‘If not, what needs to be fixed?’
The intended outcomes are:
| Situation | Intended result |
|---|---|
| Required fields are present and the purchase order and total match | Request a human review. |
| A required field is missing or the purchase order cannot be matched | Keep the invoice in an exception state with a reason. |
| The extracted total differs from the expected total | Hold the invoice for investigation. |
| An invoice has already been registered | Prevent a second approval request for the same registered invoice. |
| The reviewer approves or rejects | Record that decision without posting or paying the invoice. |
| Processing fails or a review expires | Requires additional failure and timeout handling, described under production considerations. |
The first five outcomes were observed in the lab. Failure recovery and approval expiry remain extensions to implement before production use.
For a short example of the broader business problem, see Microsoft’s Komatsu Australia customer story. Northwind, its sample documents, and the implementation in this tutorial are independent teaching examples.
Who this is for
This walkthrough is for Power Automate makers who can create a cloud flow and want to move beyond extracting text from a PDF. It focuses on the point where an extracted value becomes a business decision: validating it, recording an exception, and giving a reviewer enough context to decide.
In this tutorial
- Prepare the invoice documents and purchase-order register.
- Extract the invoice fields and inspect the actual model output.
- Validate the invoice and record its processing state.
- Add human review and test the normal and exception paths.
Architecture and resources
| Service | Purpose |
|---|---|
| Outlook shared mailbox | Receive the PDF invoice for the lab. |
| Power Automate | Orchestrate the intake, validation, and review steps. |
| AI Builder invoice processing | Extract candidate invoice values from the document. |
| SharePoint | Store the sample purchase orders, extracted invoice values, and review status. |
| Approvals | Collect a human decision after validation. |
The purchase-order register is the reference for this exercise. The invoice model does not decide whether a supplier should be paid or whether a purchase order is legitimate. The AI service used here is AI Builder invoice processing. You do not need a Microsoft Foundry project or a Copilot Studio agent to complete this lab. A suggested Copilot Studio extension appears after the tested walkthrough.
Scope and boundaries
The supplied PDFs are synthetic. They contain no real supplier, customer, bank, or payment details. Use them in a development environment.
This first version covers a single-currency, one-invoice-per-PDF exercise. It compares an invoice total with a registered expected total. It does not perform line-item reconciliation, goods-receipt matching, tax validation, supplier onboarding, ERP posting, or payment execution. Matching a total alone is not sufficient evidence that a real invoice should be paid.
Prerequisites and cost
Prepare a development Power Platform environment with Dataverse, permission to create flows, a SharePoint site where you can create lists, and access to a shared Outlook mailbox. Use an approver account that you control for the lab.
Check licensing before building. An AI Builder action makes this a premium Power Automate flow. Flow licensing and AI processing capacity are separate requirements. AI Builder runs consume available AI Builder credits or Copilot Credits; a successful sign-in does not establish that capacity is available. AI Builder trials have been discontinued. Check your environment’s allocation with your administrator and review the current AI Builder licensing overview before running the sample.
For this exercise, use the supplied single-page PDFs. Microsoft’s invoice model requirements specify supported file formats and a 20 MB file-size limit. Keeping one invoice in each PDF also avoids mixing values from separate invoices. If processing a page range, it must cover a single invoice. See invoice processing in Power Automate.
Sample files
Download the five PDF attachments at the end of this article. Create the two CSV files from the copyable blocks in Steps 1 and 3 before importing the lists.
| File | Purpose |
|---|---|
01-valid.pdf |
A USD 2,400 invoice referencing the open sample purchase order. |
02-total-mismatch.pdf |
A USD 2,550 invoice referencing the same order. |
03-missing-po.pdf |
An invoice with no usable purchase-order reference. |
04-second-valid.pdf |
A separate valid invoice for the rejected review path. |
05-third-valid.pdf |
A separate valid invoice for the approved review path. |
purchase-orders.csv |
An open order and a closed order for the reference register. |
invoice-register.csv |
The setup schema for tracking invoice results. |
Step 1: Prepare the invoice documents and reference data
Services and tools used in this step
| Service or tool | Purpose |
|---|---|
| SharePoint | Prepare the purchase-order register and invoice tracking data. |
| Sample PDFs and CSV | Provide repeatable inputs with known expected values. |
Create the purchase-order register
Save the following UTF-8 text as purchase-orders.csv:
Title,VendorName,Currency,ExpectedTotal,POStatus,VendorId
PO-2026-1001,CONTOSO COMPONENTS,USD,2400.00,Open,VENDOR-001
PO-2026-1002,CONTOSO COMPONENTS,USD,1800.00,Closed,VENDOR-001
- Open your lab SharePoint site and select Site contents.
- Select New. The menu contains several resource types; choose List.
- In How would you like to start?, find Import from and select CSV. The blank List tile would start without the supplied columns and rows.
- On From CSV, select Upload file beside Upload from this device. In the file picker, choose the
purchase-orders.csvfile you saved above and select Open.
- Wait for Customize. Check that the first row became column headers and that the preview contains the two purchase orders. The controls above the headers define the SharePoint column types.
- Open the type selector above ExpectedTotal and select Number. A formatted amount stored as text is unsuitable for the numeric total comparison later in the flow. Keep
Currencyas text containingUSD.
- Check the remaining types against this table. Use the horizontal scrollbar below the preview to reach VendorId on the right.
| Column | Type | Meaning |
|---|---|---|
Title |
Title | Purchase-order reference used for the lookup. |
VendorName |
Single line of text | Expected supplier name for this exercise. |
Currency |
Single line of text | Currency recorded in the reference data. |
ExpectedTotal |
Number | Expected invoice total, not a formatted currency string. |
POStatus |
Single line of text | Open or Closed. |
VendorId |
Single line of text | Stable supplier identifier from the reference register. |
- Select Next. In the list-name field, enter InvoicePurchaseOrders. Keep Show list in site navigation selected so you can reopen the register from the site navigation.
- Select Create and wait for the list to open. If you are repeating the lab and this list already exists, open that existing list and verify its schema instead of creating a second reference register. The creation-dialog screenshots above replay the import settings; the following screenshot is the existing register used in the verified runs.
- Check the two imported rows:
| Title | VendorName | Currency |
ExpectedTotal |
POStatus | VendorId |
|---|---|---|---|---|---|
| PO-2026-1001 | CONTOSO COMPONENTS | USD |
2400.00 |
Open | VENDOR-001 |
| PO-2026-1002 | CONTOSO COMPONENTS | USD |
1800.00 |
Closed | VENDOR-001 |
Checkpoint: the first order is open, the second is closed, and ExpectedTotal is numeric. Both belong to the same supplier. A supplier identifier is more stable than using the AI-extracted name as the identity of a business record.
The purchase-order register provides the expected supplier, currency, total, and order status.
Step 2: Extract the invoice fields
Services and tools used in this step
| Service or tool | Purpose |
|---|---|
| Power Automate | Run a small extraction check before building the full flow. |
| AI Builder | Process a sample PDF and expose its extracted values. |
In the current lab designer, the invoice action is labeled Process invoices. The extraction check starts with a manual trigger containing a file input named Invoice PDF. This allows the PDF and model output to be checked before email intake and business rules are added.
The RW – prefix identifies the example flows in this tutorial. You can use your own flow names. Keep the action names inside each flow as shown, because the expressions reference them.
- Open Power Automate and confirm the development environment in the header. Select Create in the left navigation.
- Under Start from blank, select Instant cloud flow. An instant flow lets you supply one PDF manually before you connect email intake.
- In Flow name, enter RW – Invoice Extraction Check, or a name of your choice. Select Manually trigger a flow in the trigger list.
The additional ‘Walkthrough’ suffix identifies the separate flow used to replay the creation screens. The successful extraction results below come from the original verified lab flow.
- Select Create. When the designer opens, select the Manually trigger a flow card. Its Parameters pane contains Add an input.
- Select Add an input. The designer displays six input types. Select File, the document icon.
- Replace the default input label with Invoice PDF. Leave the adjacent Please select file or image box unchanged. You will supply the PDF when you test the flow, not while defining the trigger.
- Collapse the trigger pane and select the + below Manually trigger a flow. If the card is outside the visible canvas, use Zoom view to fit first. The Add an action pane opens.
- Enter Process invoices in Search for an action or connector. Under AI Builder, select Process invoices. Check the connector heading because other connectors also appear in the search results.
- Wait for the action’s Parameters pane. If prompted, sign in to the Microsoft Dataverse connection with your lab account. The bottom of the pane should show a connected connection name. Invoice file is the required input.
- Click inside Invoice file. Press / and select Insert dynamic content. In the dynamic-content search box, enter Invoice PDF.
- Select Invoice PDF contentBytes. This token contains the uploaded document. Invoice PDF name contains only its filename and cannot provide the document for extraction. The required-field warning shown while the input is empty should clear after the token is inserted.
- Select Save in the designer toolbar. Wait for Your flow is ready to go. We recommend you test it. Check that the canvas contains the manual trigger followed by Process invoices, and that Invoice file contains the blue dynamic-content token.
- Select Test in the toolbar. In Test Flow, select Manually, then select Test at the bottom of the pane. Automatically is unavailable until the flow has a previous run to reuse.
- In Run flow, select Import beside Invoice PDF. Choose
01-valid.pdffrom the sample files and confirm that its filename appears in the field.
- Select Run flow and open the completed run. This step invokes AI Builder and consumes the environment’s available AI capacity. The walkthrough screenshots above replay setup through file selection; the results below are from the original completed lab run.
- Expand Process invoices and inspect its output. The values are under
responsev2 > predictionOutput > result > fieldsin this run.
The single-page sample completed successfully in 14 seconds. These are the values observed in the lab, not values copied from the source PDF and assumed to be model output:
| Output | Observed value |
Confidence |
|---|---|---|
| Invoice ID | INV-2026-1001 |
0.985 |
| Invoice date | 2026-10-31 |
0.93 |
| Due date | 2026-11-30 |
0.93 |
| Purchase order | PO-2026-1001 |
0.982 |
| Vendor name | CONTOSO COMPONENTS |
0.79 |
| Customer name | Northwind Components |
0.94 |
| Invoice total | 2400 |
0.984 |
| Currency code | USD |
0.984 |
Checkpoint: operationStatus is Success, pageCount is 1, and the invoice total contains both the numeric value and currencyCode. In this environment, this run reported 8 Copilot Credits and 0 AI Builder credits. That is an observed result for this one run, not a pricing estimate. Capacity and metering can differ by environment and licensing configuration.
The confidence value is the model’s confidence in an extracted field. It is not measured accuracy and it is not approval evidence. Notice that the vendor name was extracted correctly with a confidence of 0.79. If you use confidence in a rule, make the threshold a documented workflow setting and send lower-confidence records to review rather than discarding them.
Check the successful action output before using its fields in downstream expressions.
Step 3: Validate the invoice and record its state
Services and tools used in this step
| Service or tool | Purpose |
|---|---|
| Outlook shared mailbox | Start the workflow when an invoice email arrives. |
| AI Builder | Extract candidate values from each PDF attachment. |
| SharePoint | Look up the purchase order and keep an auditable invoice state. |
| Power Automate expressions | Apply explicit validation and duplicate rules. |
Create the invoice register
Save the following UTF-8 text as invoice-register.csv. Create a second SharePoint list from it and name the list InvoiceRegister. The CSV contains one setup row so SharePoint can infer every column. Exclude that setup row from your result view; it is not a valid invoice or duplicate seed.
Title,SourceKey,BusinessKey,VendorId,VendorName,PurchaseOrder,Currency,InvoiceTotal,Status,ExceptionReason,ApprovalId,DecisionComments,ReceivedAt,ProcessedAt
SETUP-REMOVE,SETUP-REMOVE,SETUP-REMOVE,VENDOR-SETUP,SETUP VENDOR,PO-SETUP,USD,0,Setup,Remove this setup row after importing the list,,,2026-10-31T00:00:00Z,2026-10-31T00:00:00Z
- Return to your lab site’s Site contents and start another New > List > CSV import.
- Upload
invoice-register.csv. Confirm that the first row is used as column headers. - In Customize, open the type selector above SourceKey, the second column. Select Multiple lines of text. This is where the combined Outlook message and attachment identifiers will be stored; they can exceed a single-line text field’s limit.
- Move the preview’s horizontal scrollbar to the right until ExceptionReason is visible. Open its selector and choose Multiple lines of text.
- Continue right to DecisionComments and choose Multiple lines of text there too. Leave ApprovalId as Single line of text. The screenshot shows the resulting types after these selections.
- Check InvoiceTotal and keep its type set to Number. At the right end of the preview, check ReceivedAt and ProcessedAt and select Date and time for each. In the observed import, these three types were inferred correctly; the long-text fields required a change.
- Review every remaining type against the table below, then select Next.
- Enter InvoiceRegister in the name field and select Create. If the list already exists from an earlier lab run, cancel this replay and open the existing list to check its column settings.
- Keep the setup row identifiable as
SETUP-REMOVEand exclude it from the view you use to inspect invoice outcomes. An imported setup row is not evidence that invoice processing has run.
Use these column types:
| Column | Type | Purpose |
|---|---|---|
Title |
Title | Display key for the invoice. This lab uses VendorId|InvoiceId, without spaces around the separator. |
SourceKey |
Multiple lines of text | Email message ID and attachment ID, useful for tracing the source. Outlook source identifiers can exceed SharePoint’s 255-character single-line limit. |
BusinessKey |
Single line of text | Stable duplicate key. This lab also uses VendorId|InvoiceId. |
VendorId |
Single line of text | Supplier identity from the purchase-order register, not from AI. |
VendorName |
Single line of text | AI-extracted supplier name for comparison and display. |
PurchaseOrder |
Single line of text | AI-extracted purchase-order reference. |
Currency |
Single line of text | AI-extracted currency code. |
InvoiceTotal |
Number | AI-extracted numeric total. |
Status |
Single line of text | Pending, Approved, Rejected, or Needs Review. |
ExceptionReason |
Multiple lines of text | The first failed validation rule. |
ApprovalId |
Single line of text | Approval record identifier. |
DecisionComments |
Multiple lines of text | The approval response summary. |
ReceivedAt |
Date and time | Email receipt time. |
ProcessedAt |
Date and time | Approval completion time. |
The lab writes the same composite key to Title and BusinessKey for validated invoices and queries Title for duplicates. Exception rows use the extracted invoice ID as their title. A supplier name can vary in punctuation or spelling, so the lab resolves VendorId from the purchase order before building the key. Keep the key format identical when writing and querying it.
Lab-verified schema note: The first live valid-invoice run produced a 333-character source value. A single-line
SourceKeyfailed before SharePoint created the pending row because that type accepts at most 255 characters. Microsoft documents the 255-character single-line limit and the supported conversion to multiple-line text. Set this column to Multiple lines of text before you test the flow.
Create the email-triggered flow
Use the action names shown below before pasting expressions. Power Automate references them using internal names such as Process_invoices and Get_PO_record. If you use different names, update the references in every expression. A display label copied into a text field is not the same as selecting dynamic content or inserting an expression.
- Open Power Automate in the same development environment used for the extraction check. Select Create in the left navigation.
- Under Start from blank, select Automated cloud flow. In Build an automated cloud flow, enter RW – Invoice Email Intake and Review, or a name of your choice, in Flow name.
- In Choose your flow’s trigger, search for
shared mailbox. Select When a new email arrives in a shared mailbox (V2) under Office 365 Outlook. A blue check appears beside the selected result and Create becomes available.
- Select Create to open the designer, then select the trigger card to open Parameters. If you already have the completed lab flow, cancel the creation dialog and open that flow instead. This screenshot replays trigger selection; the following configuration screenshots show the original verified flow.
- Enter your own lab mailbox in Original Mailbox Address. Select the matching mailbox result so the address appears as a token.
- Open the arrow in Advanced parameters. Select the checkbox beside Folder, then Importance, Only with Attachments, Include Attachments, and Subject Filter. Reopen the menu if it closes after a selection. Fields already displayed in the pane do not appear again in this list.
This menu screenshot replays the field-selection state before configuration. On a new trigger, select the required fields; do not clear an already configured trigger to follow the tutorial.
Use these target values:
| Setting | Lab value |
|---|---|
| Original Mailbox Address | Your lab shared mailbox address |
| Folder | Inbox |
| Importance | Any |
| Only with Attachments | Yes |
| Include Attachments | Yes |
| Subject Filter | Invoice |
- Keep Importance set to Any. Open the arrow at the right of Only with Attachments. The menu offers No, Yes, and a custom value.
- Select Yes in Only with Attachments. This is a filter on incoming messages.
- Open the arrow at the right of Include Attachments.
- Select Yes in Include Attachments. This supplies attachment content for extraction; it serves a different purpose from the preceding message filter.
- Enter
Invoicein Subject Filter. - Set Folder to Inbox, then compare all six settings with the completed pane below.
- Select + below the trigger and search for Apply to each. Choose the Control action. The saved lab canvas labels this container For each.
- Click Select an output from previous steps in the loop’s Parameters pane.
- Select the lightning icon at the right edge of that input to open dynamic content. Under When a new email arrives in a shared mailbox (V2), locate Attachments, described as ‘The file attachments for the message.’
- Select Attachments. Confirm that the loop input contains one Attachments token. If you are inspecting an existing flow that already has the token, close the picker without inserting another copy.
- Select + inside the loop and search for Process invoices, as in Step 2. Choose the AI Builder action.
- Click Invoice file, then select its lightning icon to open dynamic content.
- Enter
contentin the picker’s search box. Locate Attachments Content, described as ‘Attachment content.’ Attachments Content-Type is MIME metadata, and Attachments Content Id is an identifier; neither supplies the PDF bytes.
- Select Attachments Content. Keep Process invoices inside the existing attachment loop. The saved lab token is displayed as
contentBytesand resolves toitem()?[‘contentBytes’], the current attachment’s content. Do not add a second token when inspecting the saved configuration.
- Leave the manual extraction-check flow separate. Its Invoice PDF contentBytes input is useful for testing one file, but it is not the input to this email flow.
Checkpoint: the canvas now has the email trigger, one attachment loop, and Process invoices inside that loop. For the supplied tests, attach one PDF to each email. The trigger requires an attachment but does not itself guarantee that every attachment is a valid PDF invoice.
Look up the purchase order
- Inside the same loop, select + after Process invoices. In Add an action, search for
Get items. Under SharePoint, select Get items (plural). This action supports a filter query; Get item requires an item ID that this lookup does not yet have.
- Click the editable action title at the top of its pane and name it Get PO record. The field is labeled Card title for accessibility. Use this exact name before inserting expressions that reference
Get_PO_record.
- Open Site Address and select your lab site. The example uses RW Lab.
- Open List Name. Type
Invoiceto narrow the available list names.
- Select InvoicePurchaseOrders from the results. Do not select Use ‘Invoice’ as a custom value; the search text is not the list name. Confirm that the full selected name appears in the field.
- Open the Advanced parameters arrow. Select the checkbox beside Filter Query. Reopen the menu if needed and select Top Count. Click outside the menu to return to the action fields, then enter
1in Top Count.
The next screenshot shows the completed lookup configuration:
- Click Filter Query, then select the fx icon at its right edge to open the expression editor. Insert the following expression there. Do not paste it as a literal filter string:
concat(‘Title eq ”’, body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘purchaseOrder’]?[‘valueText’], ””)
Select Add when inserting a new expression, or Update when editing an existing expression. Back in Filter Query, confirm that the value is an expression token. The screenshot shows the saved expression open for inspection.
For 01-valid.pdf, the expression produces Title eq ‘PO-2026-1001’. The outer concat creates the OData filter; the quotes around the PO reference are part of that filter. This lab uses the supplied PO references. A production implementation must handle apostrophes and other unexpected input safely when constructing OData filters.
Look for an existing invoice
- Add another SharePoint Get items immediately after Get PO record, still inside the attachment loop.
- Rename it Get duplicate invoices.
- Select the same lab site and InvoiceRegister as the list.
- Expose Filter Query and Top Count. Set Top Count to
1because this lookup only needs to establish whether a matching invoice exists.
- Click Filter Query, then select its fx icon. If the field already contains a purple expression token, click that token to reopen its editor.
- Insert the following expression into the expression editor:
concat(
‘Title eq ”’,
coalesce(
first(union(body(‘Get_PO_record’)?[‘value’], createArray(json(‘{}’))))?[‘VendorId’],
‘NO-PO’
),
‘|’,
body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘invoiceId’]?[‘valueText’],
””
)
- Select Add for a new expression or Update when replacing an existing token. Confirm that Filter Query contains the purple
concat(…)token and Top Count remains1. The screenshot shows the saved expression reopened for inspection; the code block preserves the same expression with line breaks for copying.
union(array, createArray(json(‘{}’))) ensures that first() still receives one object when the PO query returns no rows. Without that guard, a missing purchase order can fail the flow before it reaches the exception branch.
For the approved sample invoice, the resulting filter is Title eq ‘VENDOR-001|INV-2026-1005’. The NO-PO fallback lets the duplicate lookup complete when the PO is missing; it does not make that invoice valid. The next condition rejects the unmatched PO.
Add the validation gate
- After Get duplicate invoices, select + inside the attachment loop. Search for
Condition, select the Built-in filter, then choose Condition under Control.
- Rename it Invoice is valid and unique.
- Click the left Choose a value field and select the fx icon. Insert the entire
and(…)expression below into the expression editor. The formatted code block is the copyable source; the screenshot that follows shows the beginning of the saved expression in the editor.
and(
equals(length(body(‘Get_PO_record’)?[‘value’]), 1),
equals(length(body(‘Get_duplicate_invoices’)?[‘value’]), 0),
equals(coalesce(first(union(body(‘Get_PO_record’)?[‘value’], createArray(json(‘{}’))))?[‘field_4’], ”), ‘Open’),
equals(
toUpper(trim(coalesce(first(union(body(‘Get_PO_record’)?[‘value’], createArray(json(‘{}’))))?[‘field_1’], ”))),
toUpper(trim(coalesce(body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘vendorName’]?[‘valueText’], ”)))
),
equals(
toUpper(trim(coalesce(first(union(body(‘Get_PO_record’)?[‘value’], createArray(json(‘{}’))))?[‘field_2’], ”))),
toUpper(trim(coalesce(body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘invoiceTotal’]?[‘currencyCode’], ”)))
),
equals(
float(coalesce(first(union(body(‘Get_PO_record’)?[‘value’], createArray(json(‘{}’))))?[‘field_3’], 0)),
float(coalesce(body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘invoiceTotal’]?[‘valueNumber’], -1))
),
not(empty(body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘invoiceId’]?[‘valueText’])),
greaterOrEquals(
float(coalesce(body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘vendorName’]?[‘confidence’], 0)),
0.75
)
)
- Select Add for a new expression, or Update if editing an existing token. The left field should now contain a purple
and(…)token. - Keep the middle operator set to is equal to.
- Click the right Choose a value field and open its fx expression editor.
- Enter
truewithout quotation marks. The lab compares the left expression’s Boolean result with Booleantrue, rather than the text string‘true’.
- Select Add or Update to insert the right-side expression. Confirm that both fields display purple expression tokens.
The completed comparison is the row containing the two expression tokens. The additional empty row visible in this designer view is not another validation rule to populate.
Important:
field_1throughfield_4are the internal SharePoint names observed in this lab for vendor, currency, expected total, and PO status. Your internal names can differ, especially when columns are renamed after creation. Use dynamic content or inspect a successful Get items output and replace these names with your own. Do not copy thefield_*names blindly.
This gate requires one returned PO, no registered duplicate, an open PO, matching normalized vendor and currency, an exact numeric total, an invoice ID, and vendor confidence of at least 0.75. The threshold is a lab choice, not a Microsoft default or a guarantee of accuracy. Because the PO query has Top Count = 1, it cannot detect duplicate PO references in the source list; keep the sample references unique and enforce uniqueness in a production register.
Create the pending record
- In the validation condition’s True branch, add SharePoint Create item.
- Rename it Create pending invoice.
- Select the lab site and InvoiceRegister.
- Open the Advanced parameters arrow. Select Title, VendorName, PurchaseOrder, Currency, InvoiceTotal, Status, SourceKey, BusinessKey, VendorId, and ReceivedAt, one checkbox at a time. Reopen the menu when needed, then click outside it. Use the mapping below to fill the exposed fields: AI Builder supplies extracted values, while the matched PO supplies
VendorId.
The same InvoiceRegister column names are available to the exception and update actions later. For the pending record, leave ExceptionReason, ApprovalId, DecisionComments, and ProcessedAt unused.
- Enter
Pendingas the Status text. Leave the approval result fields empty until the reviewer responds.
| Field | Value |
|---|---|
Title and BusinessKey |
PO VendorId, |, and extracted invoice ID, with no separator spaces |
SourceKey |
Email message ID, |, and attachment ID |
VendorId |
VendorId from the matched PO |
VendorName |
vendorName.valueText |
PurchaseOrder |
purchaseOrder.valueText |
Currency |
invoiceTotal.currencyCode |
InvoiceTotal |
invoiceTotal.valueNumber |
Status |
Pending |
ReceivedAt |
Trigger receivedDateTime |
- Click Title and select its fx icon. In the Function editor, paste the following expression. If the field already contains a purple expression token, click that token to inspect it.
concat(
first(body(‘Get_PO_record’)?[‘value’])?[‘VendorId’],
‘|’,
body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘invoiceId’]?[‘valueText’]
)
- Select Add, or Update when editing an existing expression. Confirm that Title contains a purple expression token.
- Scroll to BusinessKey, open its fx editor, paste the same expression, and select Add. The two fields must use the same separator without spaces around it.
Here first() is inside the validated branch: the condition has already confirmed that a PO was returned. Keep the empty-array guard in the earlier duplicate lookup and validation expression.
- Click SourceKey, open its fx editor, and paste the following expression. It joins the email message ID and the current attachment ID:
concat(triggerOutputs()?[‘body/id’], ‘|’, item()?[‘id’])
- Select Add or Update to keep the expression in SourceKey. This is a source-tracing value; the duplicate lookup uses the business key instead.
- For VendorId, open the expression editor and insert
first(body(‘Get_PO_record’)?[‘value’])?[‘VendorId’]. - Click ReceivedAt, open dynamic content with its lightning icon, and select Received Time under the email trigger. This corresponds to
triggerOutputs()?[‘body/receivedDateTime’]in the lab.
Scroll down in the same Parameters pane to verify the supplier identity and receipt time. These fields belong to Create pending invoice, not a second Create item action.
When a named AI Builder token is difficult to distinguish, use its verified output path. For example, VendorName uses:
body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘vendorName’]?[‘valueText’]
Use the same prefix through [‘fields’] for the purchaseOrder, invoiceTotal.currencyCode, and invoiceTotal.valueNumber paths in the table. Preserve the exact casing shown by your extraction run.
Create the exception record
- In the validation condition’s False branch, add another SharePoint Create item.
- Rename it Create exception record and select the lab site and InvoiceRegister.
- Set Title to the extracted invoice ID and Status to the literal text
Needs Review. - Map VendorName, PurchaseOrder, Currency, InvoiceTotal, and ReceivedAt from the same sources used above.
- Expose ExceptionReason and insert the expression below. This branch ends after the exception is recorded; do not add an approval action to it.
The lab records one of: Duplicate business key, Missing or unmatched purchase order, Purchase order is not open, Vendor name mismatch, Currency mismatch, Invoice total mismatch, Missing invoice ID, Vendor confidence below 0.75, or Validation failed.
Set its Title to the extracted invoice ID and ReceivedAt to the trigger’s received time. Map vendor, PO, currency, and total as above. The lab leaves source key, business key, supplier ID, approval metadata, and processed time empty on exception rows. For production traceability, extend this branch to retain the source identifiers too.
- Click ExceptionReason and open its fx editor. Paste this expression, replacing the internal PO column names if needed:
if(greater(length(body(‘Get_duplicate_invoices’)?[‘value’]),0),’Duplicate business key’,
if(not(equals(length(body(‘Get_PO_record’)?[‘value’]),1)),’Missing or unmatched purchase order’,
if(not(equals(coalesce(first(union(body(‘Get_PO_record’)?[‘value’],createArray(json(‘{}’))))?[‘field_4′],”),’Open’)),’Purchase order is not open’,
if(not(equals(toUpper(trim(coalesce(first(union(body(‘Get_PO_record’)?[‘value’],createArray(json(‘{}’))))?[‘field_1’],”))),toUpper(trim(coalesce(body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘vendorName’]?[‘valueText’],”))))),’Vendor name mismatch’,
if(not(equals(toUpper(trim(coalesce(first(union(body(‘Get_PO_record’)?[‘value’],createArray(json(‘{}’))))?[‘field_2’],”))),toUpper(trim(coalesce(body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘invoiceTotal’]?[‘currencyCode’],”))))),’Currency mismatch’,
if(not(equals(float(coalesce(first(union(body(‘Get_PO_record’)?[‘value’],createArray(json(‘{}’))))?[‘field_3’],0)),float(coalesce(body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘invoiceTotal’]?[‘valueNumber’],-1)))),’Invoice total mismatch’,
if(empty(body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘invoiceId’]?[‘valueText’]),’Missing invoice ID’,
if(less(float(coalesce(body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘vendorName’]?[‘confidence’],0)),0.75),’Vendor confidence below 0.75′,’Validation failed’))))))))
- Scroll inside the editor to check the complete expression, then select Add or Update. The screenshot shows its beginning; the code block above contains the full expression.
- Confirm that Status is
Needs Review, ExceptionReason contains the expression token, and ReceivedAt contains the email receipt time. Keep this branch separate from Review invoice.
The flow configuration was saved successfully and Power Automate reported Your flow is ready to go. That message verifies the designer configuration, not the business outcome. The test matrix in Step 4 supplies the outcome evidence.
Checkpoint: the saved flow contains the shared-mailbox trigger, one attachment loop, AI Builder extraction, guarded PO and duplicate lookups, a validation condition, a Pending approval branch, and a reason-specific Needs Review branch. Power Automate must report that the flow is ready before you send a test message. Do not treat that designer message as proof that any invoice outcome passed.
Compare your branch layout with the completed flow shown at the start of the article. At this point the validation and record-creation actions are in place; Step 4 adds the approval and its two final updates to the validated branch.
Step 4: Add human review and test the workflow
Services and tools used in this step
| Service or tool | Purpose |
|---|---|
| Approvals | Collect a controlled human decision. |
| SharePoint | Update the same pending record with the final review state. |
| Synthetic invoice set | Exercise normal, duplicate, and exception paths. |
Configure the approval
- In the validation condition’s True branch, select + after Create pending invoice. In Add an action, select the All filter if Built-in is still selected. Search for
Start and wait for an approval, then choose that action under Standard approvals. Rename the card Review invoice.
- Open the Approval type dropdown. Locate Approve/Reject – First to respond.
- Select Approve/Reject – First to respond. Confirm that this exact choice remains in the field after the menu closes.
- Click the people-search box under Assigned to. Enter at least two characters of the name or email address of an account you control. Wait for the directory result.
- Select an approver account that you control from the results. Confirm that it appears as a selected account token. Email addresses are hidden in the screenshots for privacy. The screenshot inspects an existing selection, so do not add the same reviewer twice.
- Click Title, then select fx to open the Function editor. Build a title that includes the invoice ID, vendor, currency, and amount:
Review invoice <invoice ID> from <vendor> for <currency> <amount>
The angle-bracket values above are placeholders, not literal text to leave in the approval. To reproduce the saved lab title, insert this expression into Title:
concat(
‘Review invoice ‘,
body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘invoiceId’]?[‘valueText’],
‘ from ‘,
body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘vendorName’]?[‘valueText’],
‘ for ‘,
body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘invoiceTotal’]?[‘currencyCode’],
‘ ‘,
string(body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘invoiceTotal’]?[‘valueNumber’])
)
- Select Add, or Update when editing an existing title. Confirm that Title contains the purple expression token. The screenshot shows the beginning of the saved expression; use the complete code block above.
- Click Details, then select fx. Include the invoice ID, vendor, purchase order, amount, the checks that passed, and this boundary statement:
Approval records a review decision only. It does not post or pay the invoice.
Use the same extracted fields as the title. The saved lab separates the sections with decodeUriComponent(‘%0A%0A’), which inserts a blank line. Here is its Details expression, formatted for copying:
concat(
‘**Invoice ID:** ‘,
body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘invoiceId’]?[‘valueText’],
decodeUriComponent(‘%0A%0A’),
‘**Vendor:** ‘,
body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘vendorName’]?[‘valueText’],
decodeUriComponent(‘%0A%0A’),
‘**Purchase order:** ‘,
body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘purchaseOrder’]?[‘valueText’],
decodeUriComponent(‘%0A%0A’),
‘**Invoice total:** ‘,
body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘invoiceTotal’]?[‘currencyCode’],
‘ ‘,
string(body(‘Process_invoices’)?[‘responsev2’]?[‘predictionOutput’]?[‘result’]?[‘fields’]?[‘invoiceTotal’]?[‘valueNumber’]),
decodeUriComponent(‘%0A%0A’),
‘**Validation:** Open PO matched; vendor, currency, and total matched; no registered duplicate.’,
decodeUriComponent(‘%0A%0A’),
‘Approval records a review decision only. It does not post or pay the invoice.’
)
- Select Add or Update to insert the Details expression. Scroll within the editor when checking it; the full expression is longer than the visible editor area.
- Leave Item link and Item link description empty for this lab. Keep Enable notifications and Enable reassignment set to Yes, matching the inspected configuration.
The lab includes invoice context in the approval but does not attach the source PDF or provide a source-document link. A production reviewer needs an authorized route to the original invoice as well.
Create the pending record before requesting a review, then update that same record after the decision.
Save the review decision to the same record
- Under Review invoice, add a Condition. The saved lab names it Condition 1.
- Select Outcome from Review invoice as the left value.
- Choose is equal to and enter
Approveon the right. The approval output isApprove; the SharePoint status you will store isApproved.
- In this condition’s True branch, add SharePoint Update item. Select your lab site and InvoiceRegister.
- Click Id, then select the lightning icon to open dynamic content. Locate the Create pending invoice section. The picker also offers IDs from the two earlier lookups, so check the action heading before choosing a value.
- Select ID under Create pending invoice. Confirm that Id contains one token. If inspecting the saved flow, close the picker without inserting a duplicate token.
- Open Advanced parameters and expose Status, ApprovalId, DecisionComments, and ProcessedAt.
- Enter
Approvedas literal text in Status. - Click ApprovalId and select the lightning icon. Under Review invoice, locate Approval ID.
- Select Approval ID for ApprovalId.
- Click DecisionComments, open its dynamic-content picker, and select Response summary under Review invoice.
- Click ProcessedAt, open its dynamic-content picker, and select Completion date under Review invoice. Compare all five fields with this table:
| Update item field | Value or dynamic content | Verified expression |
|---|---|---|
Id |
ID from Create pending invoice | outputs(‘Create_pending_invoice’)?[‘body/ID’] |
Status |
Literal text Approved |
No expression needed. |
ApprovalId |
Approval ID from Review invoice | outputs(‘Review_invoice’)?[‘body/name’] |
DecisionComments |
Response summary from Review invoice | outputs(‘Review_invoice’)?[‘body/responseSummary’] |
ProcessedAt |
Completion date from Review invoice | outputs(‘Review_invoice’)?[‘body/completionDate’] |
- In the outcome condition’s False branch, add another Update item. The saved lab names this card Update item 1.
- Use the same site, list, pending item ID, and approval metadata. Set Status to
Rejectedinstead ofApproved.
- Save the flow and check for designer errors before sending a test invoice. Keep both update actions after the approval, inside the validated branch of the outer condition.
Updating the ID returned by Create pending invoice matters. A second lookup can find the wrong row when retries or duplicate submissions are being investigated.
The verified Approved branch updates the existing pending invoice with the review outcome and approval metadata.
The pending approval request created by the verified email-triggered run.
Configuration checkpoint: the approval uses Approve/Reject – First to respond, is assigned to a controlled lab account, includes the invoice and validation context, and is followed by separate Approved and Rejected updates that target the ID returned by Create pending invoice.
Run the test matrix
Test one path at a time so the email, flow run, approval, and list row can be matched without guessing. Send each synthetic PDF to your shared mailbox with Invoice in the subject. Use one attachment per message for the clearest run history.
- Send
05-third-valid.pdfwith a subject such asInvoice lab – approved – INV-2026-1005. - Open the flow’s run history and select the run for that email. Check that extraction and both SharePoint lookups complete, and that Invoice is valid and unique selects the True branch.
- Open InvoiceRegister while the approval is waiting. Find
VENDOR-001|INV-2026-1005and confirm that its status isPending. - Open the approval as the assigned reviewer. Compare the supplier, PO, currency, and amount with the sample. Select Approve and complete the response.
- Return to the run and the same SharePoint row. Confirm that Update item completes, the row becomes
Approved, and the approval ID, response summary, and completion time are recorded. - Send that same PDF again in a new email. Confirm that the duplicate lookup finds the existing composite key, the validation condition selects False, and Review invoice is skipped. The new exception row should say
Needs ReviewwithDuplicate business key. - Send
04-second-valid.pdfin a separate email. Reject its approval and verify that Update item 1 recordsRejectedon its pending row. - Send
02-total-mismatch.pdf. ConfirmNeeds ReviewandInvoice total mismatch, with no approval request. - Send
03-missing-po.pdf. ConfirmNeeds ReviewandMissing or unmatched purchase order, with no approval request. - For each test, compare the business result with the table below as well as the run status. A flow can succeed while taking an unintended branch; a green run alone is not the acceptance criterion.
These are the recorded results from the lab:
| Test | Input | Expected record | Evidence status |
|---|---|---|---|
| Valid and approved | 05-third-valid.pdf, then Approve |
Pending to Approved |
Verified 2026-11-01 11:50 KST; run 08584081058602629821308106869CU15 succeeded and saved the final state and approval metadata |
| Duplicate replay | Send the approved 05-third-valid.pdf again |
Needs Review, Duplicate business key |
Verified 2026-11-01 11:58 KST; run 08584081049678939975499423226CU05 succeeded, and the approval action was skipped |
| Total mismatch | 02-total-mismatch.pdf |
Needs Review, Invoice total mismatch |
Observed 2026-11-01 09:41 KST, run 08584081131932657744975275828CU19, succeeded and created the expected reason-specific row |
| Missing PO | 03-missing-po.pdf |
Needs Review, Missing or unmatched purchase order |
Observed 2026-11-01 09:41 KST, run 08584081131932657743975275828CU27, succeeded and created the expected reason-specific row |
| Valid and rejected | 04-second-valid.pdf, then Reject |
Pending to Rejected |
Verified 2026-11-01 11:51 KST; run 08584081056621189571001129958CU31 succeeded and saved the final state and approval metadata |
| Review expires | Valid invoice with a short test timeout and no response | Visible unresolved or timed-out state owned by a follow-up process | Not implemented in this lab version |
| Extraction fails | Unsupported or unreadable input | Visible exception owned by a failure-handling branch | Not implemented in this lab version |
Do not treat the final two rows as passing tests. They expose production controls that still need to be added. A production flow should place the main actions in a Try scope, use a Catch scope configured with Run after, and update the register to a visible failure state before terminating. Approval expiry needs its own timeout and follow-up branch.
Checkpoint: all five business outcomes were observed in successful email-triggered runs and in their final SharePoint records. The corrected duplicate run evaluated the validation condition to false, skipped Create pending invoice and Review invoice, and created the exception record.
Compare your five outcomes with the InvoiceRegister screenshot at the start of the article. Filter your view to the records from these tests. Keep earlier setup and failed-test rows separate so that an old record does not look like the result of the run you are checking.
Where Copilot Studio fits
Optional extension, not implemented or tested in this lab. The email flow already starts automatically. Copilot Studio becomes useful when a person wants to ask questions about its work: ‘Which invoices need review?’ or ‘Why was INV-2026-1002 held?’
My suggested first extension is an invoice review assistant with a small, read-only tool that queries InvoiceRegister. Copilot Studio can call an agent flow as a tool to retrieve data. For this design, return the matching row’s ID, status, exception reason, amount, and a link to the record. Have the agent explain those returned facts, identify missing data, and link the user to the record. It should not invent a reason or treat a conversational reply as an approval.
Keep two information sources distinct:
| User question | Suggested source | Agent’s role |
|---|---|---|
| ‘What is the status of this invoice?’ | A query of InvoiceRegister through a tool | Report the current stored status and link to the matching record. |
| ‘Why does this invoice need review?’ | The row’s ExceptionReason and recorded values | Explain the recorded failure; ask the user to inspect the invoice or PO when the evidence is incomplete. |
| ‘What is our process for resolving a mismatch?’ | An approved procedure document in SharePoint | Explain the procedure with source references, separately from the invoice’s status. |
SharePoint documents can provide knowledge for generative answers. That is useful for procedures; an explicit list query is the design proposed here for current transaction status. Configure and test access separately for knowledge and tools. A flow that uses a maker’s connection must not become a route to invoices the caller is not allowed to see.
For the lookup tool, use a separate flow with When an agent calls the flow and Respond to the agent, in a solution in the same environment as the agent. Microsoft requires a synchronous response within the 100-second action limit. Keep the human approval wait in the existing email flow; a lookup should return the current status promptly. See the agent-flow requirements and creating an agent flow tool.
Before extending this to updates or reprocessing, define a separate authorized action, confirmation behavior, and audit record. The existing validation rules, duplicate checks, and human approval remain responsible for changing an invoice’s processing state. This extension is a possible next lab, not a capability demonstrated by the screenshots above.
Troubleshooting
The Dataverse or AI Builder connection times out during sign-in
Wait for the Microsoft account picker to finish loading and complete the sign-in in the same browser profile. If the designer still shows a failed connection, reopen the action’s connection picker and create or select the connection again. Do not rebuild the flow until the action actually shows a connected account.
The invoice action receives the filename instead of the file
Choose the attachment’s file content, not its name. In the manual probe, the correct value was Invoice PDF contentBytes. In the email flow, use the current attachment content inside Apply to each.
A resubmitted email run fails before extraction
Replay is not always equivalent to a fresh file trigger. In this lab, both Resubmit and Automatically > With a recently used trigger contained PDF base64 input but failed at Process invoices with InvalidPredictionInput: Unable to identify the mimetype input. Fresh emails containing the same PDFs passed extraction. The cause of the replay failure was not established. A fresh synthetic email can help distinguish a replay-specific failure from a document extraction problem.
A missing PO causes first() to fail
Do not call first(body(‘Get_PO_record’)?[‘value’]) on an array that may be empty. The expressions in this tutorial union the result with createArray(json(‘{}’)) before calling first().
Currency or total is blank
In the observed AI Builder output, currency and amount are both children of invoiceTotal. Use invoiceTotal.currencyCode and invoiceTotal.valueNumber. Inspect your run output instead of assuming a schema from an older screenshot.
The vendor and PO fields in this run use the case-sensitive paths vendorName.valueText and purchaseOrder.valueText. Incorrect casing or a valueString property produced blank SharePoint values during testing.
A duplicate invoice creates another approval request
Compare the exact stored key with the lookup expression. In the lab, VENDOR-001|INV-2026-1005 was stored without spaces, while an earlier lookup inserted spaces around the separator. After using | consistently, resending the same PDF created Needs Review / Duplicate business key and skipped approval. Existing test rows were preserved; the unintended duplicate request was rejected.
The condition never matches even though the list looks correct
Check SharePoint’s internal column names in the Get items output. Display names and internal names are not always the same. Also confirm that ExpectedTotal is a Number column and that the PO status is exactly Open after trimming.
The approval succeeds but the wrong row is updated
Use the item ID returned by Create pending invoice. Do not search by vendor name or invoice number after the approval completes.
Create item rejects SourceKey as longer than 255 characters
Change the SourceKey column from Single line of text to Multiple lines of text. In this lab, the concatenated Outlook message and attachment identifiers were 333 characters, so the SharePoint connector rejected the single-line field before creating the pending record. After changing the column type, refresh the SharePoint action’s list schema in the designer before retesting.
Production considerations
Before using this pattern with real invoices, replace the sample register with an authoritative source, define supplier identity and duplicate rules, and decide who owns unresolved invoices. Add a unique constraint or another concurrency-safe idempotency control; the lab’s lookup-then-create pattern can race when two copies arrive at the same time.
Add explicit retention, access, data-loss prevention, retry, timeout, and monitoring policies. Decide how corrections are audited and how a reviewer opens the source document from the approval. Validate taxes, line items, goods receipt, and supplier bank-detail changes through the systems and controls that own those facts.
Approval and payment remain separate business controls. The lab’s extracted values and confidence scores do not replace those controls, and this flow must not be connected directly to payment execution without the organization’s accounting and segregation-of-duties design.
Clean up
When you have finished the lab:
- Turn off both flows you created in this tutorial. If you used the example names, these are RW – Invoice Email Intake and Review and RW – Invoice Extraction Check.
- Remove the synthetic messages from the lab mailbox if your test-data policy requires it.
- Delete or archive the InvoiceRegister test rows and sample approvals according to your lab policy.
- Delete the InvoicePurchaseOrders and InvoiceRegister lists only if they were created solely for this exercise.
- Remove the lab connections only when no other flow uses them.
These steps remove test resources. They do not reverse any real accounting action because this tutorial never posts or pays an invoice.


