Skip to main content

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, no Bearer prefix). 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 dedicated associationType 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.
For a vendor invoice, swap both types inside input to VENDOR_INVOICE. Read uploadUrl and documentId from generateUploadPresignedUrl β€” not from the response root.
See Integration patterns for the presigned-upload header gotcha (pass an ArrayBuffer, not a typed Blob, from Node fetch).

Step 2 β€” Upload the PDF

Uploaded bytes are verified server-side. Non-PDF content parks the document with status 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

An empty list usually means parsing is still in progress β€” unless GET /api/rest/documents/{documentId} shows status: "UPLOAD_CONTENT_REJECTED" (invalid PDF; terminal for that sourceSystem + sourceSystemId).
Bound your poll. If the list is still empty after a few minutes and the document is not UPLOAD_CONTENT_REJECTED, stop and treat the upload as a failed parse. A valid PDF that is not recognized as a purchase order / vendor invoice can finish without creating a supporting record and without setting a rejection status β€” there is no separate mismatch flag on the document today. Re-upload with the correct document (or a new sourceSystemId) rather than polling forever.

Find by your source ids

If you stored sourceSystem + sourceSystemId at presign time, you can look up the parsed record without the document id:
Use 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.
Money is integer cents. $1,250.00 β†’ 125000. See Integration patterns.