Purchase execution

Review current open purchase lines, understand receiving progress, inspect the events behind each quantity, and keep supplier documents with the purchase order.

Endpoints

EndpointScopeUse it for
GET /purchase-order-executionpurchase-order-execution:readReview current open purchase lines with receipt state, comparable quantities, and evidence quality.
GET /purchase-order-lines/{id}/receipt-eventspurchase-order-execution:readRead every returned receipt event for one visible purchase-order line.
GET /purchase-orders/{id}/documentspurchase-order-execution:readRead documents attached to one visible purchase order.
POST /purchase-orders/{id}/documentspurchase-order-documents:writePreview or attach one uploaded file or HTTPS document link.

These scopes work with the acting user's current purchase-order permissions. GET /me reports effectiveCapabilities.purchaseOrders.execution, purchase-order document access, and document write readiness.

Review current purchase lines

The default view returns lines from confirmed purchase orders. Add includeDraft=true when your workflow also needs draft purchasing work.

curl "$SHELFCYCLE_API_BASE_URL/purchase-order-execution?supplierId=supplier-id&dueFrom=2026-08-01&dueTo=2026-08-31&limit=25" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "X-ShelfCycle-Client: Purchase follow-up"

Optional first-page filters include an exact supplierId, productId, or productFamilyId; paired dueFrom and dueTo business dates; includeDraft; and limit from 1 to 100.

Each row keeps the purchase order, supplier, product, packaging, receipt progress, operational change time, and direct receipt and document links together:

{
  "data": [
    {
      "type": "purchase_order_execution_line",
      "id": "purchase-order-line-id",
      "purchaseOrder": {
        "id": "purchase-order-id",
        "orderNo": "PO-4812",
        "externalId": "supplier-po-9021",
        "status": "CONFIRMED",
        "type": "STOCK_ORDER",
        "dueAt": "2026-08-18T16:00:00.000Z",
        "url": "https://app.shelfcycle.com/org-northstar/purchase-orders/purchase-order-id"
      },
      "supplier": {
        "id": "supplier-id",
        "displayName": "Northstar Materials",
        "externalId": "SUP-104",
        "url": "https://app.shelfcycle.com/org-northstar/suppliers/supplier-id"
      },
      "product": {
        "id": "product-id",
        "code": "ACETONE-PAIL",
        "productFamily": { "id": "family-id", "name": "Acetone" },
        "url": "https://app.shelfcycle.com/org-northstar/products/product-id"
      },
      "packaging": {
        "id": "packaging-id",
        "name": "55 gallon drum",
        "type": "FIXED",
        "uom": "DRUM",
        "capacityUOM": "55"
      },
      "receiptState": "partially_received",
      "evidenceQuality": "authoritative",
      "expectedQuantity": {
        "value": "10",
        "basis": "packages",
        "uom": "DRUM",
        "productPackagingId": "packaging-id",
        "capacityUOM": null
      },
      "receivedQuantity": {
        "value": "6",
        "basis": "packages",
        "uom": "DRUM",
        "productPackagingId": "packaging-id",
        "capacityUOM": null
      },
      "remainingQuantity": {
        "value": "4",
        "basis": "packages",
        "uom": "DRUM",
        "productPackagingId": "packaging-id",
        "capacityUOM": null
      },
      "overReceivedQuantity": {
        "value": "0",
        "basis": "packages",
        "uom": "DRUM",
        "productPackagingId": "packaging-id",
        "capacityUOM": null
      },
      "receiptEventCount": 2,
      "firstReceivedAt": "2026-08-12T14:00:00.000Z",
      "lastReceivedAt": "2026-08-14T15:30:00.000Z",
      "updatedAt": "2026-08-14T15:30:00.000Z",
      "executionChangedAt": "2026-08-14T15:30:00.000Z",
      "receiptEventsUrl": "/api/v1/purchase-order-lines/purchase-order-line-id/receipt-events",
      "documentsUrl": "/api/v1/purchase-orders/purchase-order-id/documents"
    }
  ],
  "meta": {
    "returnedRows": 1,
    "hasMore": false,
    "nextCursor": null,
    "ordering": "due_at_asc_purchase_order_id_asc_line_id_asc",
    "observedAt": "2026-08-14T16:00:00.000Z",
    "currentState": true,
    "snapshotIsolatedAcrossRequests": false,
    "requestId": "request-id"
  }
}

Quantities are decimal strings. Read value, basis, uom, productPackagingId, and capacityUOM together so packages and base units stay distinct.

Understand receipt state

StateMeaning
no_receipt_evidenceNo receipt rows are present, so the response does not turn absence into a received quantity.
planned_not_receivedReceipt rows are planned and none has been marked received.
quantity_basis_unknownReceipt activity exists, but the expected and received quantities do not have enough compatible evidence for a reliable comparison.
partially_receivedThe comparable received quantity is below the expected quantity.
received_to_expectedThe comparable received quantity matches the expected quantity.
over_receivedThe comparable received quantity is above the expected quantity.

When the quantity basis is unknown, the comparable aggregate fields are null. Use the receipt-event collection for the recorded activity and keep its evidence labels with each value.

Continue and refresh

When meta.hasMore is true, continue with meta.nextCursor by itself:

curl "$SHELFCYCLE_API_BASE_URL/purchase-order-execution?cursor=$NEXT_CURSOR" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY"

The list is a current view of purchase execution. executionChangedAt reports the newest recorded update across the purchase order, line, and included receipt activity. Use line id plus executionChangedAt to identify rows that deserve a fresh comparison, and periodically restart the original filtered request to reconcile the complete current view.

Inspect receipt events

Use the row's receiptEventsUrl when you need the individual activity behind its receipt state or quantity.

curl "$SHELFCYCLE_API_BASE_URL/purchase-order-lines/purchase-order-line-id/receipt-events?limit=100" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY"

Each event includes its recorded quantity, received date, lot assignment, packaging references, basis evidence, and evidence quality. Planned events remain visible with receivedAt: null. When more events are available, continue with the returned cursor alone.

Keep documents with the purchase order

Follow the row's documentsUrl, or read the documents already attached to a selected purchase order directly:

curl "$SHELFCYCLE_API_BASE_URL/purchase-orders/purchase-order-id/documents" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY"

To add a supplier confirmation, bill of lading, or other source document, prepare the private upload through POST /document-uploads, send the returned bytes, and preview the association:

curl "$SHELFCYCLE_API_BASE_URL/purchase-orders/purchase-order-id/documents?dryRun=true" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: purchase-order-id:document-source-id" \
  -d "$(jq -n --arg fileName "$DOCUMENT_FILE_NAME" --arg url "$DOCUMENT_URL" '{fileName:$fileName,url:$url}')"

Execute the same request without dryRun=true after the readiness response matches your policy, then verify it through the purchase order's document list. The same route also accepts an existing HTTPS document link. See Attached documents for the complete upload flow.

Guardrail

Keep receipt state, quantity basis, and evidence quality together when deciding what has arrived. Document association records the evidence on the purchase order without changing its status or receiving inventory.