Purchase orders and vendor invoices via API
Purchase orders and vendor invoices are standalone supporting documents β inbound POs from your customers and generic (non-freight) vendor AP invoices. You upload a PDF through the same presigned-URL flow as other documents; Upwell parses it asynchronously and creates the structured record you can then list, search, and patch. This page covers the end-to-end workflow. For the always-current request and response schemas, see the API Reference entries under Purchase Orders and Vendor Invoices.These are not freight carrier invoices (
carrier_invoices) and not customer AR invoices (invoices). For those flows see Submitting carrier invoices via API and Managing customer invoices (AR).Prerequisites
- An API key on every request (
Authorization: YOUR_API_KEY, noBearerprefix). See Authentication. - A PDF of the purchase order or vendor invoice (standalone supporting-doc uploads are PDF-only, one document per upload).
The flow
1
Request a presigned upload URL
POST /api/rest/generate-upload-presigned-url with associationType of PURCHASE_ORDER or VENDOR_INVOICE. Omit associationId β the parsed record is created from the upload, not attached to an existing entity.2
PUT the PDF bytes
Upload the raw file to
uploadUrl. Content-Type must match the mimeType you declared (application/pdf).3
Poll until the parsed record appears
GET /api/rest/documents/{documentId}/purchase-orders or .../vendor-invoices returns an empty list until parsing finishes (typically within a couple of minutes). Bound the poll β see Step 3.4
Read, search, or update
Use the list / get / search / patch endpoints below once the record exists.
Step 1 β Request a presigned URL
Standalone supporting-doc ingestion uses a dedicatedassociationType and no associationId:
Pass
sourceSystem + sourceSystemId so retries are safe: re-requesting an already-uploaded document returns 400 DOCUMENT_ALREADY_UPLOADED; re-requesting one whose upload never completed returns a fresh uploadUrl for the same document.
input to VENDOR_INVOICE. Read uploadUrl and documentId from generateUploadPresignedUrl β not from the response root.
Step 2 β Upload the PDF
UPLOAD_CONTENT_REJECTED and is never parsed β check GET /api/rest/documents/{documentId}. Rejection is terminal for that sourceSystem + sourceSystemId; re-upload with a new sourceSystemId.
Step 3 β Poll for the parsed record
GET /api/rest/documents/{documentId} shows status: "UPLOAD_CONTENT_REJECTED" (invalid PDF; terminal for that sourceSystem + sourceSystemId).
Find by your source ids
If you storedsourceSystem + sourceSystemId at presign time, you can look up the parsed record without the document id:
POST /api/rest/vendor-invoices/search for vendor invoices β same body shape.
Step 4 β List, get, and update
List endpoints accept an optional
limit query parameter. Get-one responses include line items and party addresses; vendor invoices also include the matched purchase order when one exists.
Patch body shape
Updates use{ "input": { ...fields to set... } } with the recordβs camelCase field names. Amounts are integer cents. System-managed columns (ids, tenant/document linkage, party references, match state) are not updatable and are rejected.
Related
- Integration patterns β presigned upload, integer cents,
sourceSystempairs - How documents flow through Upwell
- API Reference: Purchase Orders, Vendor Invoices

