Purchase orders
Read a company's purchase orders, report progress back on them, and attach documents. This is the pair of surfaces an order-fulfilling system needs: GET to learn what was ordered, and POST set-status/ to say what happened to it.
Scopes
The two reads need transactional:read; set-status/ and the document upload need transactional:write. A key holding only the read scope still reaches both GETs and is refused on the writes.
Reporting a status
Identify the order in the body, not the path. set-status/ sits on the collection because which identifier a caller holds depends on the integration: send purchaseOrder (our UUID) or externalId, and at least one is required. Sending both narrows to an order matching both. Nothing matches gives 404; several match gives 400 asking for the UUID.
Only five statuses are writable from outside: O ordered, D delivered, I invoiced, CO completed, RE returned. The rest of the order lifecycle (pending, cancelled, on hold and so on) belongs to flows inside Hivebuy and is rejected here.
Booleans must be real JSON booleans. true, not "true". A string is a 400 naming the field. This catches integration platforms that stringify mapped values.
Retries are safe. Re-sending a status the order already has creates no status row and fires no side effects, but syncStatus, syncText and orderNumber from that same request are still saved, so a retried call is not a wasted one.
Every field is validated at once, so one request tells you everything that is wrong rather than one problem per round trip.
Order level vs item level
Without position the update applies to the whole order. With position (the item's 1-based position within the order) it applies to that one item, and then status is required and the order-level flags finalDelivered / finalInvoiced are refused.
Sending the order on: processOrder
processOrder: true releases an order that is on hold and dispatches it to the supplier. Two things worth knowing, because they are what make a single call work:
- It acts on the status the order had when the request arrived, and it only fires for an on-hold order. On any other status the rest of the update still applies and nothing is dispatched.
externalOrderNumberin the same request is saved before dispatch, and the dispatch runs after the transaction commits, so the order sent to the supplier carries the number you just set. Setting the number and processing the order in one call is the supported way to do it.
Note that for integrated punchout suppliers that return their own order number, that number overwrites externalOrderNumber after the order is sent.
Reading orders
The payload is the same one the legacy /api/company/order/ endpoint returns, field for field, so migrating is a change of URL and auth and nothing else. Two differences to plan for:
totalDbRecordsis gone from the list envelope.totalRecordsis the same number; the old field cost a second full count per page.- Pages are smaller here than elsewhere in this API:
pageSizedefaults to 10 and caps at 30, not 25 and 100. A purchase order is by far the heaviest payload in this tree, and these are the limits the legacy endpoint already applied.
Incremental sync has a caveat. updatedAtFrom compares against the purchase-order row only. Adding a status, editing an item or attaching a document does not bump it, so polling updatedAtFrom alone will miss orders whose only change was nested. Run a periodic full reconcile alongside it.
Documents
Attach one file per call, as base64 JSON or as multipart. The effective size ceiling is about 1.9 MB, which is smaller than most people expect: the request body limit is 2.5 MB and base64 costs roughly a third on top. Accepted extensions are pdf, xls, xlsx, doc, docx, ppt, pptx, odt, png, jpeg, jpg, gif, tif, tiff and eml. The response is the order's whole document list, not just the new file.
Status codes
status on the order, on each item and on each row of statuses is one of these two-letter codes. The five marked writable are the ones set-status/ accepts; the rest are set by Hivebuy.
OOrdered (writable)RRejectedPPendingIInvoiced (writable)SSentRTResentPAPartialRSResolvedDDelivered (writable)COCompleted (writable)CACanceledIDIn deliveryOHOn holdREReturned (writable)
Operations
- GET
/api/v2/{companyId}/purchase-orders/: paginated, newest order number first. Filter bystatus,order,createdBy,updatedAtFrom/updatedAtTo. - GET
/api/v2/{companyId}/purchase-orders/{id}/: one order by its UUID. Unlike the legacy endpoint, which answered 405, this works. - POST
/api/v2/{companyId}/purchase-orders/set-status/: report progress, set the external order number, release an on-hold order. - POST
/api/v2/{companyId}/purchase-orders/{orderId}/documents/: attach one document.
Endpoints
/api/v2/{companyId}/purchase-orders/?page=1&pageSize=10&status=O&order=&createdBy=&updated...
/api/v2/{companyId}/purchase-orders/set-status/
/api/v2/{companyId}/purchase-orders/{id}/
/api/v2/{companyId}/purchase-orders/{orderId}/documents/