Purchase Lifecycle

Walk a purchase order through every status from DRAFT to DELIVERED, with audit log.

This recipe follows a purchase order through its full lifecycle and shows how each transition is recorded.

See the Purchase Orders concept page for the lifecycle diagram and rules.

1. Create the order as DRAFT

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"
}'

Capture the returned id as PO_ID.

2. Confirm with the supplier

Shell
curl -X POST "{{BASE_URL}}/api/v1/purchases/$PO_ID/transition" \
-H "Content-Type: application/json" \
-d '{
"toStatus": "CONFIRMED",
"reason": "Supplier confirmed by email"
}'

3. Goods shipped — move to IN_TRANSIT

Shell
curl -X POST "{{BASE_URL}}/api/v1/purchases/$PO_ID/transition" \
-H "Content-Type: application/json" \
-d '{
"toStatus": "IN_TRANSIT",
"reason": "Tracking number 1Z999AA..."
}'

4. Delivery arrives — mark as DELIVERED

Pass actualDelivery when transitioning to DELIVERED:

Shell
curl -X POST "{{BASE_URL}}/api/v1/purchases/$PO_ID/transition" \
-H "Content-Type: application/json" \
-d '{
"toStatus": "DELIVERED",
"reason": "Received at warehouse",
"actualDelivery": "2026-04-20T14:30:00.000Z"
}'

5. Inspect the audit log

Shell
curl "{{BASE_URL}}/api/v1/purchases/$PO_ID/logs"

Returns one entry per transition, in chronological order:

JSON
[
{ "fromStatus": null, "toStatus": "DRAFT", "changedAt": "2026-04-21T09:00:00.000Z" },
{ "fromStatus": "DRAFT", "toStatus": "CONFIRMED", "changedAt": "2026-04-21T11:00:00.000Z", "reason": "Supplier confirmed by email" },
{ "fromStatus": "CONFIRMED", "toStatus": "IN_TRANSIT", "changedAt": "2026-04-22T08:00:00.000Z", "reason": "Tracking number 1Z999AA..." },
{ "fromStatus": "IN_TRANSIT","toStatus": "DELIVERED", "changedAt": "2026-04-20T14:30:00.000Z", "reason": "Received at warehouse" }
]

6. Cancel an order that never shipped

Cancelling is a valid transition from any non-terminal state:

Shell
curl -X POST "{{BASE_URL}}/api/v1/purchases/$PO_ID/transition" \
-H "Content-Type: application/json" \
-d '{
"toStatus": "CANCELLED",
"reason": "Supplier discontinued the SKU"
}'

Do's and don'ts

  • ✅ Use the transition endpoint to change status — never PATCH the status field directly.
  • ✅ Always provide a reason — audit logs become much easier to read.
  • ✅ Use PATCH for non-status updates (notes, expected delivery, total amount).
  • ❌ Don't try to go backwards in the lifecycle — invalid transitions are rejected.
  • ❌ Don't DELETE an order that has progressed past DRAFT — cancel it instead to preserve history.