# Purchase execution

Review open purchase lines, receipt progress, receipt events, and purchase-order documents.

Canonical HTML: https://developer.shelfcycle.com/guides/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

| Endpoint | Scope | Use it for |
| --- | --- | --- |
| `GET /purchase-order-execution` | `purchase-order-execution:read` | Review current open purchase lines with receipt state, comparable quantities, and evidence quality. |
| `GET /purchase-order-lines/{id}/receipt-events` | `purchase-order-execution:read` | Read every returned receipt event for one visible purchase-order line. |
| `GET /purchase-orders/{id}/documents` | `purchase-order-execution:read` | Read documents attached to one visible purchase order. |
| `POST /purchase-orders/{id}/documents` | `purchase-order-documents:write` | Preview 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.

```bash
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:

```json
{
  "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

| State | Meaning |
| --- | --- |
| `no_receipt_evidence` | No receipt rows are present, so the response does not turn absence into a received quantity. |
| `planned_not_received` | Receipt rows are planned and none has been marked received. |
| `quantity_basis_unknown` | Receipt activity exists, but the expected and received quantities do not have enough compatible evidence for a reliable comparison. |
| `partially_received` | The comparable received quantity is below the expected quantity. |
| `received_to_expected` | The comparable received quantity matches the expected quantity. |
| `over_received` | The 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:

```bash
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.

```bash
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:

```bash
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:

```bash
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](/guides/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.
