Purchases API

Create and manage supplier purchase orders with full status lifecycle and audit log.

Endpoints

MethodPathDescription
GET/api/v1/purchasesList purchases (filter by status / supplierId)
GET/api/v1/purchases/{id}Get a single purchase
POST/api/v1/purchasesCreate a new purchase order
PATCH/api/v1/purchases/{id}Partially update fields (not status — see transition)
DELETE/api/v1/purchases/{id}Delete a purchase order (only allowed for deletable states)
POST/api/v1/purchases/{id}/transitionTransition status with audit log entry
GET/api/v1/purchases/{id}/logsRetrieve the full status change audit log

See Purchase orders concept for the lifecycle diagram. Use Purchase Documents for files attached to purchase orders.


GET /api/v1/purchases

Query parameterTypeRequiredAllowed values
statusenumDRAFT, CONFIRMED, IN_TRANSIT, DELIVERED, CANCELLED
supplierIduuidFilter by supplier

Combine filters freely. Both parameters can be used together.

Shell
curl "{{BASE_URL}}/api/v1/purchases?status=IN_TRANSIT"
curl "{{BASE_URL}}/api/v1/purchases?supplierId=3f2b9a1c-…"
curl "{{BASE_URL}}/api/v1/purchases?status=DRAFT&supplierId=3f2b9a1c-…"

Response 200 OK — JSON array of SupplierPurchaseRecordDto.


POST /api/v1/purchases

Request body (CreateSupplierPurchaseRecordDto)

FieldTypeRequiredDescription
supplierIduuidSupplier the order is placed with
orderNumberstringYour internal order reference
orderDatedate-timeWhen the order was placed
statusenumInitial status (default DRAFT)
expectedDeliverydate-timeExpected delivery date
totalAmountstring (decimal)Total amount (preserve precision as string)
currencystringISO 4217 currency code
notesstringFree-form notes
Shell
curl -X POST {{BASE_URL}}/api/v1/purchases \
-H "Content-Type: application/json" \
-d '{
"supplierId": "3f2b9a1c-…",
"orderNumber": "PO-2026-0001",
"orderDate": "2026-04-21T09:00:00.000Z",
"expectedDelivery": "2026-05-10T00:00:00.000Z",
"totalAmount": "1249.50",
"currency": "EUR",
"notes": "Seasonal restock"
}'

Response 201 CreatedSupplierPurchaseRecordDto.


GET /api/v1/purchases/{id}


PATCH /api/v1/purchases/{id}

Partially updates fields of a purchase. status transitions must go through the transition endpoint instead.

Allowed fields: orderNumber, orderDate, expectedDelivery, totalAmount, currency, notes.

Shell
curl -X PATCH {{BASE_URL}}/api/v1/purchases/… \
-H "Content-Type: application/json" \
-d '{ "expectedDelivery": "2026-05-15T00:00:00.000Z" }'

DELETE /api/v1/purchases/{id}

Deletes a purchase order. Only deletable from certain states (e.g. DRAFT or CANCELLED); attempting to delete an order in another state returns an error.

  • 204 No Content — deleted.
  • 404 Not Found — purchase does not exist in your tenant.

POST /api/v1/purchases/{id}/transition

Transitions the purchase to a new status and records the change in the audit log.

Request body (TransitionPurchaseStatusDto)

FieldTypeRequiredDescription
toStatusenumTarget status — must be a valid successor of the current status
reasonstringReason for the transition (highly recommended)
actualDeliverydate-timeSet when transitioning to DELIVERED
Shell
curl -X POST {{BASE_URL}}/api/v1/purchases/…/transition \
-H "Content-Type: application/json" \
-d '{
"toStatus": "DELIVERED",
"reason": "Received at warehouse",
"actualDelivery": "2026-04-20T14:30:00.000Z"
}'

Valid transitions follow the lifecycle diagram.


GET /api/v1/purchases/{id}/logs

Retrieves the ordered list of status changes for a purchase.

Shell
curl {{BASE_URL}}/api/v1/purchases/…/logs

Response 200 OK — JSON array of PurchaseStatusChangeLogDto:

JSON
[
{
"id": "l0g_01…",
"purchaseRecordId": "…",
"fromStatus": "CONFIRMED",
"toStatus": "IN_TRANSIT",
"reason": "Courier picked up at supplier",
"changedByUserId": "user_…",
"changedAt": "2026-04-22T08:00:00.000Z"
}
]

Purchase schema

SupplierPurchaseRecordDto:

JSON
{
"id": "po-uuid…",
"supplierId": "3f2b9a1c-…",
"orderNumber": "PO-2026-0001",
"status": "IN_TRANSIT",
"orderDate": "2026-04-21T09:00:00.000Z",
"expectedDelivery": "2026-05-10T00:00:00.000Z",
"actualDelivery": null,
"totalAmount": "1249.50",
"currency": "EUR",
"notes": "Seasonal restock",
"createdAt": "2026-04-21T09:00:00.000Z",
"updatedAt": "2026-04-22T08:00:00.000Z"
}