Purchase request form that sets up PO matching for AP
A purchase request form that captures vendor, amount, cost code and approver so invoices match a PO automatically and AP skips manual lookups.

A purchase request form is where PO matching starts. If the form captures the vendor, the amount, the cost code and the approver in fixed fields, an invoice matcher can check each incoming ihvoice against the request without anyone searching email. This guide lists the fields, the validation rules and the webhook that connect the form to accounts payable.
Why invoices fail to match
Most unmatched invoices trace back to the request, not the invoice. Someone asked for a purchase in a chat message, the approver said "fine" in a reply, and the PO number was typed into a spreadsheet a week later with a slightly different vendor name. When the invoice arrives, the matcher has nothing reliable to compare it with, so a person opens three tabs to find out who approved it.
In the AI Invoice & Document Processing system we build, the form sits at the very start of the workflow, before the inbox. It creates the PO record. The extraction step later reads the invoice from the shared mailbox, validates it, and looks for the PO record to match against. The ledger push to QuickBooks, Xero or Zoho only happens after the match or after someone clears the exception. A clean request record is what makes the match deterministic.
Use the purchase request template
Fields the form must capture
Every field below exists because the matcher or the approval router reads it. Anything that does not feed one of those two jobs can stay out of the form.
| Field | Block type | What the matcher or router does with it |
|---|---|---|
| Vendor | Dropdown of approved vendors, plus "New vendor" | Compares to the vendor name extracted from the invoice. Using a vendor ID avoids spelling mismatches. |
| New vendor details | Short answers, shown only for "New vendor" | Creates a vendor record that finance reviews before the PO is issued. |
| Description | Long answer | Gives the matcher context for line items on the invoice. |
| Quantity and unit price | Number | Feeds the calculated total. |
| Total amount | Calculated field | The number the invoice total is compared with, within your tolerance. |
| Currency | Dropdown | Stops a 5,000 AED invoice from matching a 5,000 USD request. |
| Cost code | Dropdown | Becomes the GL account or class when the bill is pushed to the ledger. |
| Approver | Dropdown, or set by logic | Decides who gets the approval message. |
| Needed by date | Date | Lets you flag invoices that arrive long after the expected delivery. |
| Quote or attachment | File upload | Evidence for the approver. Not used in matching. |
| Requester | Hidden field | Who to notify when an invoice matches or fails. |
If you also track recurring purchases, add a Checkboxes or Dropdown block for "one time" versus "monthly" and a contract end date that appears only for recurring items. Recurring requests need a different match rule, because several invoices will hit one PO.
Rules that keep the data clean
The form does most of the validation work if you set it up in the editor instead of cleaning data later.
- Use a dropdown for vendor and cost code, never free text. Free text is where "Acme Supplies Ltd" and "ACME Supplies" come from.
- Make the total a calculated field from quantity and unot price. Requesters stop typing a rounded figure that never matches the invoice.
- Add conditional logic that picks the approver from the amount. Under a threshold goes to the department head, above it also goes to finance. Set the thresholds from your own approval policy.
- Pass the requester's email and department in as hidden fields from the link you share, so nobody retypes them.
- Turn on email notifications to the approver and a confirmation email to the requester.
State whether the amount includes tax. A single label such as "Total including tax" prevents the most common near miss, whee the invoice is higher than the request by exactly the tax.
Set it up step by step
- Start from the template. Open the purchase request template and keep only the fields in the table above. Rename the labels to the words your team already uses.
- Load the lists. Paste your approved vendors and cost codes into the two dropdowns. Include the vendor ID or GL code in the option text if your ledger uses one.
- Add the calculation. Insert a calculated field that multiplies quantity by unit price and displays the total before submit.
- Add the logic. Create a rule for "New vendor" that reveals the vendor dteail fields, and a rule that sets the approver based on the total.
- Add hidden fields. Create
requester_emailanddepartment, then share the form link with those values filled in per department or per person. - Connect the webhook. In the form's integrations, add the URL of your receiving endpoint. Our team sets up this endpoint as part of the system: it stores the request, assigns the PO number, and starts the approval.
- Test with a real invoice. Submit a request, approve it, then forward a real vendor invoice to the processing inbox and watch the match result.
- Close the loop. Decide who sees the exceptions queue and how often it is reviewed.
You can build the form in Qwenvo, a free form builder that our team makes. Webhooks, the REST API, conditional logic, calculated fields, hidden fields, file uploads, teammates and a custom domain are all included at $0, so the form can live at something like purchasing.yourbusiness.com without a paid tier. Qwenvo does not yet have one-click connections to QuickBooks or Xero, so the webhook goes to an endpoint you control, which is how we wire it in this system anyway.
What the matcher does with the record
The receiving endpoint turns the form submission into a PO record. The shape below is the record we store, not Qwenvo's raw webhook format, so adapt the mapping to your own endpoint.
{
"po_number": "PO-2026-00417",
"request_id": "sub_8f2c91",
"vendor_id": "V-1032",
"vendor_name": "Northside Office Supply",
"currency": "USD",
"total": 1840.00,
"tax_included": true,
"cost_code": "6200-Facilities",
"approver": "j.rivera@yourbusiness.com",
"requester": "m.chen@yourbusiness.com",
"status": "approved",
"needed_by": "2026-12-01"
}
When an invoice is extracted, the matcher looks for an open PO with the same vendor ID. It then checks the currency, compares the invoice total with total inside a tolerance you choose (a percentage or a flat amount), and confirms the PO is approved and not already fully billed. A pass sends the bill to the ledger with cost_code already applied. A fail goes to the exceptions queue with the reason attached, so the reviewer sees "total is above the PO" instead of an unlabeled bill.
If you need three-way matching, add a second short form that the receiver fills out when goods arrive, with the PO number and quantity received. The matcher then also checks that something was actually delivered.
Failure cases to plan for
- Invoice before PO. Vendors sometimes bill first, or the purchase happened without a request. The matcher cannot find a PO, so it routes the invoice to the exceptions queue with a "no PO" reason, and the approver can raise a request after the fact.
- Partial deliveries. One PO, several invoices. Track a running billed amount against the PO and close it only when the total is reached.
- Price changes after approval. The vendor adds freight. Inside tolerance it passes; outside it, the original approver gets asked again.
- Retired cost codes. A code disappears from the ledger but stays in the dropdown. Update both lists on the same day.
- Departed approvers. An approver who left will leave requests stuck. Keep the approver list in the form current, and give the router a fallback address.
- Vendor name drift. The invoice says "Northside Office Supply LLC" and the request says "Northside Office Supply." Matching on vendor ID, with names as an alias list, handles this.
A form is not a procurement suite. It will not enforce budgets across periods or manage vendor contracts. If you need those, a dedicated procurement tool sits beside this setup, and the form feeds it the same webhook. Also note that Qwenvo is hosted in the United States, which matters if your data policy requires a specific region.
What to measure
Pick a handful of numbers and review them every month:
- The share of invoices that match a PO without a human touching them.
- Exceptions grouped by reason: no PO, amount over tolerance, wrong vendor, closed PO.
- Time from request submitted to PO approved.
- Invoices that arrived with no request at all, by department.
If one department keeps showing up under "no PO," the problem is the process, not the matcher.
Questions teams ask
Do we need a PO number on the form?
No. The requester should not type one. Your endpoint assigns it when the request is approved, and the form's submission ID links back to the original request.
Can the form pick the approver automatically?
Yes. Use conditional logic on the total and, if needed, the department, then send the approval message to the matching address. Qwenvo's own notification emails cover the simple case. Anything more complex, such as a two-step chain, runs through the webhook and your endpoint.
What if the invoice total does not equal the PO total?
Set a tolerance. Inside it, the match passes. Outside it, the invoice goes to the exceptions queue with the difference shown. The tolerance is your call, and many teams use a tighter value for large purchases.
Can the same form handle expense reimbursements?
Keep them separate. Reimbursements have no vendor ID and no PO to match against, so mixing them weakens the validation. The template library has a separate expense reimbursement form.
Next step
Open the purchase request template, keep the eleven fields above, and load your real vendor and cost code lists. Then send the webhook URL to us, and we will map it into your invoice inox in the AI Invoice & Document Processing system, a fixed-scope build at $1,800 setup and $349 per month for care.
AI Invoice & Document Processing
Third-party platform and model usage billed to you directly. No lock-in. Live in 14 days.