Purchase order data extraction
Purchase order data extraction reads a PO from a PDF or scan and returns the buyer, supplier, delivery terms and order lines as structured data, ready for order entry or invoice matching. docRefinery's purchase order OCR API covers 47 field definitions. Extracted values come with a confidence score and page and box citations, and anything the PO doesn't state comes back null.
Updated
Which purchase orders are worth extracting
Start with who wrote the PO. If your own ERP issued it, you already hold the data, and reading it back from a PDF adds nothing. Extraction earns its keep on purchase orders that come from outside.
- Sales order entry. Your customers each send POs on their own forms, and a coordinator keys them into your order system. The lines, ship-to and requested dates are the job.
- Accounts payable. Suppliers' invoices need checking against POs held only as PDFs or scans, often after an acquisition or from a legacy system.
- Contract and spend reviews. Old POs need reading in bulk for prices, terms and contract references.
The first case suits docRefinery best: a stable customer base means a stable set of layouts, each learned once and remembered. The use cases page covers the AP side.
Purchase order fields docRefinery extracts
There are 47 definitions, counting the line table and its 10 columns. Requisitioner and Approver Name record who raised a PO and who signed it off. Line-level GL Account matters where a PO is coded before it leaves the buyer.
| Category | Fields |
|---|---|
| Order | Purchase Order ID, Purchase Order Date, Revision Number, Requested Delivery Date, Expiry Date, Contract Reference, Special Instructions |
| Buyer | Buyer Name, Buyer Address, Buyer Email, Buyer Phone, Buyer Tax ID, Buyer Contact Name |
| Authorisation | Requisitioner, Approver Name, Approval Date |
| Supplier | Supplier Name, Supplier Address, Supplier Email, Supplier Phone, Supplier Tax ID, Supplier Number |
| Delivery and billing | Ship To Name, Ship To Address, Bill To Name, Bill To Address, Shipping Terms, Shipping Method, Carrier |
| Commercial | Payment Terms, Currency, Net Amount, Total Tax Amount, Freight Amount, Discount Amount, Total Amount |
| Line Item (table) | Line Number, Description, Product Code, Quantity, Unit, Unit Price, Amount, Tax Rate, Line Requested Delivery Date, GL Account |
Example purchase order JSON
A trimmed excerpt with invented values. This one is revision 2 with a line-level date that differs from the header. Extract a document shows the whole response.
{
"document_type": "purchase_order",
"result": {
"fields": [
{ "alias": "Purchase Order ID", "text": "PO-000123", "field_confidence": 0.99,
"location": { "page": 1, "bbox": [0.70, 0.07, 0.88, 0.09] } },
{ "alias": "Revision Number", "text": "2", "field_confidence": 0.91,
"location": { "page": 1, "bbox": [0.70, 0.10, 0.74, 0.12] } },
{ "alias": "Requested Delivery Date", "text": "2026-12-01", "field_confidence": 0.95,
"location": { "page": 1, "bbox": [0.70, 0.13, 0.84, 0.15] } },
{ "alias": "Contract Reference", "text": null, "location": null },
{ "alias": "Line Item", "table": [[
{ "alias": "Product Code", "text": "EX-1001", "field_confidence": 0.97,
"location": { "page": 1, "bbox": [0.08, 0.46, 0.20, 0.48] } },
{ "alias": "Quantity", "text": "40", "field_confidence": 0.98,
"location": { "page": 1, "bbox": [0.56, 0.46, 0.60, 0.48] } },
{ "alias": "Line Requested Delivery Date", "text": "2026-12-15", "field_confidence": 0.93,
"location": { "page": 1, "bbox": [0.78, 0.46, 0.92, 0.48] } }
]] }
],
"extraction_approval": "auto",
"low_confidence_fields": []
}
}What order desks get from docRefinery
| The problem | What docRefinery does |
|---|---|
| Every customer sends its own form | Each form is learned once and remembered, and a new version is re-trained when it first arrives. |
| A new customer needs setting up | A workflow is built from one sample PO. Versions, replay and compare let you check it before it goes live. |
| Your order system has fixed column names | A Return as alias gives each value your name, so Unit Price can arrive as price_each and Ship To Address as deliver_to. |
| POs and invoices need matching | Both return the same output format, so one parser reads each side of the match. |
| Order lines must stay lines | Lines come back as table rows, with quantity, unit and price together, with page and box citations on the cells. |
| Totals that don't add up | A rule such as line amounts adding up to the net amount flags the run when it fails. |
| The same misread keeps coming back | A coordinator's correction can be offered as a fix for that customer's form, after a test on its own POs. |
| Orders are keyed into a web screen | The Chrome extension runs the PO and auto-fills the form. It is in early access and not in the Chrome Web Store yet. |
What makes purchase orders hard to read
- Revisions. A change order can restate the whole PO or list only what moved, and it can look almost identical to the original.
- Dates at two levels. Header dates and per-line delivery dates disagree on purpose when a shipment is split.
- Three addresses. Buyer, bill-to and ship-to can all differ, with a warehouse in one place and shared services in another.
- Whose part number? Lines may carry the buyer's code, the supplier's, or both.
- Units. "10 CS" is ten cases, not ten items, and pack sizes hide in the description.
- Boilerplate. Pages of terms and conditions follow the order, and free-text notes about delivery slots end up between lines.
How low-confidence PO values are handled
A wrong quantity on a PO ships the wrong goods, so most teams set Quantity, Unit Price and Ship To Address strictly and let Buyer Phone go loose. A value under its threshold puts the run in the review queue instead of the order system; the coordinator sees the highlighted line, fixes the cell, adds or removes a row if needed, and approves. Clean runs are marked auto and carry on. A missing Contract Reference is simply null, unless you've made the field required, when it is flagged as missing. Scoring tells you the page was read well, not that the PO itself is right.
Common questions
Can a purchase order OCR API feed our ERP directly?
Close, though not through a native connector yet. The API returns JSON in one format for every document, under the Return as names you choose, so your team or integrator maps it once. CSV and XLSX exports suit import screens. Webhooks and ERP connectors are in design on the roadmap.
Does docRefinery link a revised PO to the original?
It reads Revision Number and Purchase Order ID on each document. Deciding which revision stands, and applying the change, stays in your order system.
Can I test it without using a customer's PO?
Yes. The dashboard has a fictional sample purchase order with a vendor, ship-to and items. It counts as one page against the free Starter allowance of 100 a month.
Related
- Shipping order OCRFree shipping document OCR for shipping orders, bills of lading and packing lists. Extract parties, cargo and container data as structured JSON. No signup.
- InvoiceExtract data from invoice PDFs and scans: 62 fields including line items and VAT, as JSON with confidence scores and page and box citations on values.
- Vendor layout changesWhy a sender changing their form breaks document extraction, how to detect a layout change early, how to handle it, and what to ask your extraction vendor.
- Use casesWhere document extraction fits best: accounts payable, lending, KYC, insurance claims, logistics, healthcare admin and contracts, wherever senders repeat.
- docRefinery vs DocuPipedocRefinery vs DocuPipe: no-template extraction against layouts learned once. Compare confidence, visual review, ISO 27001 and HIPAA, deployment and pricing.
- Extract a documentExtract data from an invoice with the docRefinery API: submit with curl, Python or JavaScript, poll the run, then read fields, tables and low-confidence flags.
Try it on your own documents.
Upload a real file and see each field with its confidence score, ready to check.
100 pages a month free. No credit card, no sales call.