Invoice statuses
Invoices carry a lowercasestatus:
The buyer-controlled path is
pending → approved → processing → paid. Each move has a dedicated endpoint; there is no generic status setter.
Reading invoices
supplierFacilityId plus exactly one of workOrderId or projectId (the other is null, along with the hydrated workOrder / project). Work-order invoices always carry locationId and buyerFacilityId; project invoices carry them only when the client supplied them, so both can be null. The invoice carries the money breakdown in sections (labor, material, travel, freight, misc), each with line items, a taxRate, and a totalBeforeTax, rolling up to invoiceTotalBeforeTax, invoiceTax, and invoiceTotalAfterTax. Monetary values serialize as strings. A short AI-derived summary of the scope may appear in title. The rendered PDF is in invoicePDFs; supporting files are in attachments (see Files and attachments).
Filtering by entity type
AddinvoiceEntityType=work_order or invoiceEntityType=project to GET /invoices, /invoices/count_by, and /invoices/download to narrow to one tab; omit it to get both. projectId and projectIdSeq filter to specific projects the same way workOrderId / workOrderIdSeq filter to specific work orders.
Flattened export
GET /v1/buyer/invoice/invoices/download returns the same data as flat rows (one row per invoice with the totals denormalized), built for spreadsheet export and AP-system imports. Same filters as the list endpoint. On project invoice rows, workOrderId, workOrderTitle, problemTypeId, problemTypeName, locationId, and locationName are null; projectId is set.
Moving an invoice through approval
Each transition endpoint takes just the invoice id:status/pending, status/approved, status/processing, and status/paid. Shared behavior:
- Each transition clears the invoice’s dispute flag, then propagates a matching status change to the associated work order.
- If the work-order status mapping fails, the invoice is voided and the call returns
400. Treat a400here as “re-fetch and inspect”, not “retry”. - A save-level validation failure returns
406.
Marking paid by work order id
When your AP system knows the work order but not the OpenWrench invoice id, close the loop with:workOrderId first and falls back to externalWorkOrderId, always within your company. Already-paid invoices are returned unchanged (safe to retry); approved or processing invoices are marked paid; an invoice in any other status returns 400 with “Invoice not found”.
Project invoices
Project invoices are anchored to a project (projectId) instead of a work order (workOrderId), and skip the work-order side of the pipeline. For an invoice with no workOrderId, OpenWrench skips:
- The NTE check on create and update.
- GL-code derivation from the work order.
- Budget spend and asset spend mirroring.
- The work-order status sync that normally runs on every status transition (a status transition on a project invoice never voids and returns
400for a broken WO mapping). - The work-order detail page append on the invoice PDF utility.
- WO-anchored approval hierarchies and their approval / reminder / escalation notifications.
- The per-WO “one invoice per supplier” duplicate check.
- The currency-scoped validation on
taxLineItems(amounts are still validated as numeric).
locationId or buyerFacilityId is excluded from those reports.
The POST status/paid/by_work_order_id shortcut only matches work-order invoices; for a project invoice, mark paid by id via POST status/paid.
Utilities
Append the work order detail page to the PDF.PATCH /v1/buyer/invoice/file/invoice_pdf/add_work_order_detail_page/{invoiceId} regenerates the invoice PDF with the work order detail page appended and returns the new PDF link (envelope type UpdatedInvoicePdf). No request body; not rate limited.
Bulk update with filters. PATCH /v1/buyer/invoice/bulk_update_with_filters applies a column update to every invoice matching a filter, always constrained to your company. Both maps are free-form column→value:
PATCH /v1/buyer/invoice/publish_draft_invoices_if_wo_complete_and_auto_publish_enabled publishes supplier draft invoices whose work order is complete, for suppliers that enabled auto-publish. It requires a buyer super-admin API key and returns 403 for a normal key. Intended for scheduled housekeeping jobs.
AP sync pattern
A robust accounts-payable sync:- Poll
GET /invoices?status=pending(orpublishedAtwindows) on a schedule. - Pull each invoice. For work-order invoices, match totals against the approved proposal and the work order’s
nte. For project invoices, match against your project budget instead. The NTE check does not run server-side for project invoices. POST status/approved, export to your AP system, thenPOST status/processing.- On settlement,
POST status/paidby id, orstatus/paid/by_work_order_idkeyed on the work order reference your AP system carries. - Log the envelope
traceIdon any400/406so support can trace the exact request.