PO_HEADERS_INTERFACE, PO_INTERFACE_ERRORS: Oracle PO Import

Aug 30, 2026

Convert a purchase order to Excel, CSV, or JSON

PDF, JPG, PNG, BMP, HEIC, TIFF

Submit your purchase orders

Oracle Purchasing does not let you insert a purchase order straight into PO_HEADERS_ALL. Every programmatic load goes through four staging tables, PO_HEADERS_INTERFACE, PO_LINES_INTERFACE, PO_LINE_LOCATIONS_INTERFACE and PO_DISTRIBUTIONS_INTERFACE, and is then processed by the Purchasing Document Open Interface. Rows you insert start with PROCESS_CODE set to PENDING. After the import runs, each row ends up ACCEPTED, REJECTED or NOTIFIED, and any validation failure is written one row per bad column into PO_INTERFACE_ERRORS. This page is the field reference for that process, including the two behaviors that cost people the most time: NOTIFIED rows that never imported and were never errors, and a single header fault that silently discards every line beneath it.

Last updated August 2026. The column and status detail here describes E-Business Suite R12 Purchasing. The Oracle Fusion Cloud equivalent, which uses the same interface table names behind a file based load, is covered near the end.

The four Oracle PO interface tables

The staging structure mirrors the document structure. A purchase order is a header, with lines, with shipment schedules under each line, with accounting distributions under each schedule. Each of those levels gets its own interface table, and each child points at its parent through an interface identifier rather than a real purchase order id, because at load time no purchase order exists yet.

Interface tableHoldsKeyPoints at its parent through
PO_HEADERS_INTERFACESupplier, currency, buyer, operating unit, document type, approval statusINTERFACE_HEADER_IDNothing. This is the top level.
PO_LINES_INTERFACEItem, quantity, unit of measure, unit price, line type, categoryINTERFACE_LINE_IDINTERFACE_HEADER_ID
PO_LINE_LOCATIONS_INTERFACEShip to location, promised and need by dates, quantity per scheduleINTERFACE_LINE_LOCATION_IDINTERFACE_LINE_ID
PO_DISTRIBUTIONS_INTERFACECharge account, deliver to location, requester, quantity ordered per distributionINTERFACE_DISTRIBUTION_IDINTERFACE_LINE_LOCATION_ID

For a straightforward standard purchase order you can often populate only the header and lines table and let Purchasing derive a single default shipment and distribution from your setup. That shortcut works right up until you need two ship to locations on one line or a split across two cost centers, at which point the lower two tables become mandatory and the interface identifier chain has to be right all the way down.

One more table matters and is not part of the chain: PO_INTERFACE_ERRORS, which is where every validation failure lands. It is not a log file, it is a queryable table, and it is the single most useful object in this whole area.

PO_HEADERS_INTERFACE: the columns you must populate

The table has well over a hundred columns and almost all of them are optional. The short list below is what a standard purchase order load actually requires. Everything else either defaults from your supplier and organization setup or is only relevant to blanket agreements and catalog quotations.

ColumnWhat it isNotes
INTERFACE_HEADER_IDYour unique identifier for this header within the batchTake it from PO_HEADERS_INTERFACE_S. Every line you want under this header repeats this value.
BATCH_IDGroups the rows that belong to one import runYou choose the value. It is what you pass to the concurrent program so it picks up your rows and not somebody else's.
PROCESS_CODERow statusInsert as PENDING. The import program only picks up PENDING rows and rewrites this column when it finishes.
ACTIONCreate, update or add to an existing documentSee the section on ACTION values below. This is the column most often set wrongly.
DOCUMENT_TYPE_CODEThe kind of document to createSTANDARD for a standard purchase order. BLANKET and QUOTATION are the other supported paths.
ORG_IDOperating unitMust match the operating unit the concurrent request runs in, or nothing is found.
VENDOR_ID and VENDOR_SITE_IDSupplier and supplier siteResolve these before you insert. Passing a supplier name and hoping Purchasing matches it is the second most common cause of rejection.
CURRENCY_CODEDocument currencyA rate is required as well if it differs from the ledger currency.
AGENT_IDBuyerMust be a valid, active buyer. A terminated employee who is still in your source extract will fail here.
APPROVAL_STATUSWhat state the created document lands inAPPROVED, INCOMPLETE or INITIATE APPROVAL. Set carefully. APPROVED skips your approval hierarchy entirely.
DOCUMENT_NUMThe purchase order numberLeave it null and Purchasing assigns one, provided your numbering is set to automatic. Supply it only when numbering is manual.
INTERFACE_SOURCE_CODEA label for where the data came fromNot validated, but it is the only breadcrumb you will have when auditing a bad load six months later. Populate it.

The APPROVAL_STATUS choice deserves a second look before a production load. Setting APPROVED means the imported orders are live commitments the moment the concurrent program completes, with no approval hierarchy involved. That is correct for a legacy data migration, where the orders were already approved in the old system, and wrong for almost every ongoing integration.

PO_LINES_INTERFACE: the columns you must populate

ColumnWhat it isNotes
INTERFACE_LINE_IDUnique identifier for this lineFrom PO_LINES_INTERFACE_S.
INTERFACE_HEADER_IDThe header this line belongs toMust exactly match a row in PO_HEADERS_INTERFACE in the same batch.
LINE_NUMLine number on the documentSequential within the header. Gaps are tolerated but confuse everyone later.
LINE_TYPE or LINE_TYPE_IDGoods, services, or a custom line typeDrives whether quantity and price or amount is expected. A services line validated as goods fails on the unit of measure.
ITEM or ITEM_IDThe inventory itemOptional for a description only line, mandatory otherwise, and validated against the item master in the correct inventory organization.
ITEM_DESCRIPTIONFree text descriptionRequired when there is no item. This is the route for one off purchases.
UNIT_OF_MEASUREUOMThe full unit of measure name, not the three character code, and it must be a valid conversion for the item.
QUANTITYQuantity orderedMust reconcile with the quantities you put on the shipment and distribution rows, if you supply them.
UNIT_PRICEPrice per unitValidated against price tolerance where a source document or catalog price exists. See NOTIFIED below.
CATEGORY_IDPurchasing categoryDerived from the item when there is one, mandatory when there is not.

The item validation is the step that fails most often on a first load, because the part number in the source system and the part number in the Oracle item master are rarely identical. Teams whose part numbers really live in a separate inventory and reorder system rather than in the ERP hit this on essentially every line until a cross reference is built, so it is worth resolving ITEM_ID up front in your staging query instead of letting the interface discover it.

What PROCESS_CODE means, and why NOTIFIED is not an error

PROCESS_CODE is the single column that tells you what happened to a row, and it has four values that people routinely collapse into two. You insert PENDING. The import program rewrites it. A row that is neither PENDING nor ACCEPTED did not import, but only one of the remaining values is an error.

PROCESS_CODESet byMeansWhat to do
PENDINGYou, on insertWaiting to be picked up. The import program selects only these rows.Nothing. If rows are still PENDING after the program completes, your BATCH_ID or ORG_ID parameter did not match.
IN PROCESSThe import programCurrently being worked. Transient.Nothing, unless it is still there after the request ends, which means the program died mid run.
ACCEPTEDThe import programThe header and all of its successfully validated children were created in Purchasing.Query PO_HEADERS_ALL for the resulting document. This is success.
REJECTEDThe import programValidation failed. Details are in PO_INTERFACE_ERRORS.Read the errors, fix the source data, reset to PENDING and rerun.
NOTIFIEDThe import programApplies to blanket agreements and standard purchase orders when a supplier price catalog update exceeds your price tolerance. The line is flagged for a buyer to look at.Review the price change and accept or reject it. Do not treat it as a defect in your load.

NOTIFIED is the value that wastes days. It is not in PO_INTERFACE_ERRORS, so an error driven reconciliation reports nothing wrong. It is not ACCEPTED, so the document is not in Purchasing. A monitoring query that counts rejections and finds zero will declare a clean run while the rows sit in the interface table indefinitely. If you build one check on this page, make it a count grouped by PROCESS_CODE rather than a count of errors.

This is the same misreading shape that catches people in every other integration Oracle and SAP ship. A status that means "handed off" is read as a status that means "done". The EDI 997 acknowledgment code AK901 equal to A is exactly this, and so is SAP IDoc status 03. In each case the fix is the same: check for the state you actually want rather than the absence of the state you fear.

One bad header rejects every line under it

This behavior surprises people because it is not how they would have written it. If validation fails on a header row, none of its lines are processed at all. Purchasing sets PROCESS_CODE to REJECTED on the header and stops. The child rows are not individually validated, so PO_INTERFACE_ERRORS contains the header fault and nothing else.

The practical consequence is that a load can look like it has one small problem and actually have twenty. You fix the header, rerun, and now the line level validation happens for the first time and produces a fresh crop of errors on rows that were never examined. Budget for at least two rounds on any first load, and do not read the error count from round one as a measure of how much work is left.

The inverse is also worth knowing. A line level failure does not necessarily reject the whole document in every release and configuration, which means a partially loaded order is possible. Always reconcile line counts between your source extract and the created documents rather than assuming a header that reached ACCEPTED brought all of its lines with it.

PO_INTERFACE_ERRORS: how errors are actually recorded

Oracle's own technical reference describes PO_INTERFACE_ERRORS as containing error message information generated by the open interface programs in Purchasing, with one row for each column that has an error on an interface transaction row. That granularity is the useful part and also the part that confuses first time readers: a single bad line can produce five rows here, one per faulty column, so error counts and bad record counts are different numbers.

ColumnWhat it tells you
INTERFACE_TYPEWhich open interface program generated the message. Purchasing has several, so this distinguishes PDOI errors from receiving or requisition import errors.
INTERFACE_TRANSACTION_IDThe interface row the error belongs to. This is what you join back on.
TABLE_NAMEWhich interface table the bad row is in, so you know whether to look at the header, line, shipment or distribution.
COLUMN_NAMEThe specific column that failed validation.
ERROR_MESSAGEThe message text. Frequently a message code that needs looking up rather than a sentence.
BATCH_IDThe batch the failing row belonged to.
PROCESSING_DATEWhen the message was generated, which is how you separate today's run from last week's.

Because it is a table rather than a log, the whole reconciliation is one query. This is the query worth saving, since it answers "what happened to my batch" in a single pass:

-- 1. What state did every row in the batch end up in?
SELECT process_code, COUNT(*)
  FROM po_headers_interface
 WHERE batch_id = :batch_id
 GROUP BY process_code;

-- 2. Every validation failure, with the column that caused it
SELECT e.table_name,
       e.column_name,
       e.error_message,
       h.interface_header_id,
       h.document_num
  FROM po_interface_errors e,
       po_headers_interface h
 WHERE e.batch_id = :batch_id
   AND e.interface_transaction_id = h.interface_header_id
 ORDER BY h.interface_header_id, e.table_name;

Run the first query before the second. If it returns rows sitting at NOTIFIED or still at PENDING, the second query will come back empty and you will spend an hour looking for errors that do not exist. Oracle also ships a seeded Purchasing Interface Errors Report that formats the same content, which is the right tool to hand a functional colleague who does not have SQL access.

ACTION values, and why the documentation disagrees with itself

ACTION tells the interface whether you are creating a new document, replacing one, or adding to an existing one. It is the column where published guidance genuinely conflicts: community references and older implementation notes variously list ORIGINAL, UPDATE, ADD, NEW and REPLACE, and not all of them are valid in every release or for every document type.

The honest advice is to not take the list from any article, including this one. Check the valid values for your own release in Oracle's Electronic Technical Reference Manual for PO_HEADERS_INTERFACE, or by reading the value set the interface validates against in your instance. For a plain new standard purchase order load, ORIGINAL is the value that works across the widest range of R12 instances, and it is what most migration scripts use. If your load fails immediately on every row with an ACTION related message, that is the first place to look rather than the last.

How do you run Import Standard Purchase Orders?

Once the interface tables hold PENDING rows, the seeded concurrent program does the work. It is called Import Standard Purchase Orders and its short name is POXPOPDOI. Submit it from the Purchasing responsibility in the same operating unit whose ORG_ID you populated.

The parameters that matter are the document type, the approval status you want the created documents to have, the batch id, and the operating unit. Batch id is the one people leave blank. Leaving it null makes the program pick up every PENDING row in the table, including rows another integration inserted five minutes ago, which is how a test load ends up creating live purchase orders nobody expected. Always pass your own batch id.

To reprocess rejected rows after fixing the data, set PROCESS_CODE back to PENDING on the affected rows and resubmit with the same batch id. Deleting and reinserting works too, and is cleaner when the original interface identifiers have already been reused elsewhere.

The same job in Oracle Fusion Cloud

Fusion keeps the interface table names but you no longer insert into them with SQL. The load is file based: you fill an FBDI spreadsheet template, generate the CSV zip, upload it through the file import and export page into UCM, and then run a scheduled process. The interface chain is unchanged, with PO_HEADERS_INTERFACE referenced by PO_LINES_INTERFACE, which is referenced by PO_LINE_LOCATIONS_INTERFACE, which is referenced by PO_DISTRIBUTIONS_INTERFACE.

Import Orders parameterWhat it does
Procurement BUThe business unit whose documents are imported. The Fusion equivalent of getting ORG_ID right.
Default BuyerUsed for any row where the buyer was not supplied in the file.
Approval ActionDo Not Approve leaves documents incomplete, Submit for Approval routes them, Bypass Approval opens them directly. The direct analogue of APPROVAL_STATUS in EBS.
Default Requisitioning BUUsed where the requisitioning business unit is absent from the data.
Import SourceMatched against the interface rows to select which set to process.
Batch IDUsed together with Import Source to pick the right records. The same discipline applies: always set it.
Create or Update ItemWhether the process may create items that do not exist. Leave it off unless you mean it.
Communicate OrdersWhether imported documents are sent to the supplier. Leave it off on a migration, or your suppliers receive several thousand historical orders.

Two Fusion specific constraints are worth writing down. File based data import does not support creating or updating purchasing documents with more than 10,000 distributions, and exceeding that causes performance problems and failures rather than a clean rejection, so split large migrations by business unit or date range. And only one instance of the Import Orders process runs at a time, which the application enforces, so scheduling it every fifteen minutes does not make it faster.

Errors in Fusion come back through the process output rather than a table you can query. View the PDF output of the completed request for the list of documents created and the error detail, and the log for anything the output does not explain. That is a genuine step backwards from PO_INTERFACE_ERRORS if you are used to EBS, and it is why Fusion teams tend to validate harder before upload rather than after.

What is the difference between PO_HEADERS_ALL and PO_HEADERS_INTERFACE?

PO_HEADERS_ALL is the real table. It holds live purchase orders and is what you query for reporting. PO_HEADERS_INTERFACE is a staging table that holds candidate rows on their way in, and it is emptied or purged on a schedule. A row existing in PO_HEADERS_INTERFACE proves only that somebody inserted it, not that a purchase order exists. The full read side reference, including the join path down to lines, shipments and distributions, is in the Oracle purchase order tables guide.

Why did the import program complete successfully but create no purchase orders?

Almost always because it found no rows to process. The three usual causes, in order of frequency: the BATCH_ID parameter does not match what you inserted, the concurrent request ran in a different operating unit than the ORG_ID on your rows, or PROCESS_CODE was left null instead of PENDING. A completed request with zero documents created is a selection problem, not a validation problem, and PO_INTERFACE_ERRORS will be empty because nothing was ever validated.

Where the data comes from before it reaches the interface table

Everything above assumes you already have clean rows to insert. In practice that is the harder half of the project, because the purchase orders being loaded usually start life as documents rather than as database records: a customer emails an order as a PDF, a supplier sends a scanned confirmation, a legacy system exports something that is not quite a file format.

PurchaseOrders handles that first mile. It reads purchase orders that arrive as PDFs, scans or email attachments and returns the header plus every line item as clean columns, in Excel, CSV or JSON, with no template to build per supplier. Those columns map directly onto the interface tables above: order number and supplier to PO_HEADERS_INTERFACE, and one row per item with quantity, unit of measure and unit price to PO_LINES_INTERFACE. If your Oracle load is currently fed by somebody retyping documents into a spreadsheet, the purchase order to Oracle page covers the mapping, and bulk purchase order upload covers doing a migration's worth in one pass.

Related reading

Stop retyping purchase orders

Upload a PDF, scan, or photo of any PO and get clean Excel, CSV, or JSON line items in seconds.

Try it free

25 pages free. No credit card required.