Vendor purchase order lifecycle
A vendor purchase order (PO) is a retailer's request to buy stock from a brand. It is different from a customer order: the retailer requests units, the vendor decides how many it can accept, and the retailer later records how many it received. Use this guide to build a PO work queue, submit complete acknowledgement decisions, and reconcile the result. The operation reference supplies every parameter and response field.
The lifecycle at a glance
| Stage | What it means | Your next action |
|---|---|---|
Unconfirmed | A PO has been captured, but no acknowledgement has been recorded. | Read its full acknowledgement group and decide the open units on every line. |
Confirmed | The acknowledgement has been sent and accepted by the connected retailer, or a confirmed PO has been imported. | Track its delivery window and wait for receipt data. |
Closed | The connected retailer or a complete CSV import has recorded the delivered/received outcome. | Compare accepted and received units; investigate shortages. |
status is the stage. outcome is the more specific operational interpretation: for example acknowledgmentOverdue, deliveryOverdue, deliveredWithShortage, rejected, or successfulDelivery. An Unconfirmed PO can become overdue without changing its status. A Closed PO may have a shortage, rejection, cancellation, or no received units. Read both fields instead of inferring the outcome from status alone.
The usual path is Unconfirmed → Confirmed → Closed. A full CSV import or channel synchronization can also bring in a PO already at a later stage. Imports can reopen a Closed PO as Unconfirmed; they cannot move Confirmed directly back to Unconfirmed. Acknowledgement itself only moves an Unconfirmed PO to Confirmed.
1. Find the POs that need action
Connected retail accounts supply PO updates to Nasam. A partner can also import a complete PO CSV. First list purchase orders with the brand and channel filters appropriate to the caller's access:
curl --get 'https://backend.nasam.co/v1/vendor/purchase-orders' \
--data-urlencode 'brandIds=20' \
--data-urlencode 'statuses=Unconfirmed' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=20' \
-H 'accept: application/json' \
-H 'key: YOUR_API_KEY'
The response contains purchaseOrders and pagination. Keep paging until the requested queue is complete. Each summary carries id, poNumber, brand, saleChannel, status, outcome, the order and acknowledgement dates, fulfillment center, and requested totals. Use the returned id for detail and acknowledgement requests; the retailer's poNumber is a business identifier, not the API path ID.
For a deadline queue, filter statuses=Unconfirmed with isDueToday=true or isOverdue=true. For delivery work, use statuses=Confirmed with the same due filters. isDueThisWeek=true covers dates through the next seven days. hasShortage=true selects closed POs with a line whose received units are below accepted units; hasCancellations=true selects POs with cancelled units. search matches a PO number or line SKU. The startDate and endDate filters apply to the order date, not acknowledgement or delivery dates, and compare calendar days in Riyadh.
Repeat brandIds, saleChannelIds, statuses, or fulfillmentCenters to select more than one value. Omitting brandIds uses the caller's accessible brands. Read one PO before acting: its detail includes dates, currency, line items, requested/cancelled/accepted/received units, and values.
2. Prepare one complete acknowledgement
Call GET /v1/vendor/purchase-orders/{id}/acknowledgement immediately before building a decision. Its brands array gives each brand's purchaseOrderId and line items, including the current lineItemId (id in each line). Its group totals include openUnits.
A retailer account shared by several brands can produce separate Nasam PO rows with the same poNumber. In that case isShared is true, and the acknowledgement group includes the sibling brands. Submit one decision for every line across all brands in the group. A single brand's PO detail is not enough to build the complete submission. For a non-shared account, the group contains that one PO.
For each line, calculate:
open units = requestedUnits - (cancelledUnits or 0)
rejected units = open units - acceptedUnits
acceptedUnits must be a nonnegative integer no greater than open units. Provide a decision for every line, including a fully cancelled line with acceptedUnits: 0. If any open units are rejected, include that line's rejectionReason: TemporarilyUnavailable, InvalidProductIdentifier, or ObsoleteProduct. A decision without a reason is valid only when all open units are accepted. Duplicate line IDs, missing lines, a line from another group, or an acceptance above open units cause the request to fail before it is sent to the retailer.
For example, if a line requests 12 units and 2 were cancelled, it has 10 open units. Accepting 7 means rejecting 3, so the decision needs a rejection reason. The JSON structure is:
{
"items": [
{
"lineItemId": 501,
"acceptedUnits": 7,
"rejectionReason": "TemporarilyUnavailable"
},
{
"lineItemId": 502,
"acceptedUnits": 5
}
]
}
Use actual line IDs and quantities from the acknowledgement group. The second line above illustrates a full acceptance; a real submission must include every group line. After an import changes a PO's line set, fetch the group again because line IDs may change.
3. Submit and verify the acknowledgement
Send the complete items array to POST /v1/vendor/purchase-orders/{id}/acknowledge:
curl -X POST 'https://backend.nasam.co/v1/vendor/purchase-orders/123/acknowledge' \
-H 'accept: application/json' \
-H 'content-type: application/json' \
-H 'key: YOUR_API_KEY' \
--data '{"items":[{"lineItemId":501,"acceptedUnits":7,"rejectionReason":"TemporarilyUnavailable"},{"lineItemId":502,"acceptedUnits":5}]}'
The connected retailer account must be active and support PO acknowledgement. Nasam checks the group and submits its complete decision to the retailer before recording the confirmed state locally. For a shared PO, its sibling rows are confirmed together. A successful response returns the selected PO's updated detail; read the group or list again when you need the other brands' rows.
The retailer's PO can change between your read and submission. If the current quantities or SKUs no longer match, refresh the PO and acknowledgement group, then recalculate all decisions. A concurrent acknowledgement can return a conflict; refresh before retrying. Do not blindly replay the same POST after an uncertain network result: read the PO status first. Once it is no longer Unconfirmed, another acknowledgement is rejected.
4. Reconcile delivery and shortages
Acknowledgement records accepted units. Receipt data arrives later from retail account synchronization or a complete CSV import. Use PO detail to compare totals.requestedUnits, cancelledUnits, acceptedUnits, and receivedUnits, then inspect the lines that explain any gap. deliveryStart and deliveryEnd describe the delivery window when supplied by the source. A Confirmed PO past deliveryEnd has outcome: deliveryOverdue; a Closed PO with fewer received than accepted units can have outcome: deliveredWithShortage.
Treat an unknown amount differently from zero. Before acknowledgement, accepted totals in detail are null; before closure, received totals and shortage are null. In list summaries, accepted or received totals may be absent, including for older imported data that never recorded that stage. A value of 0 means units were recorded as zero; a missing or null value means the stage has not supplied a usable figure.
For work queues, vendor overview groups pending acknowledgement and delivery actions alongside acceptance, fulfillment, and on-time measures. Vendor top performers ranks products in the vendor PO book rather than customer-order sales. For a file at PO or SKU-line grain, request the respective exports.
Import or reconcile a PO from CSV
POST /v1/vendor/purchase-orders/import accepts multipart file, brandId, and saleChannelId for an active retail account. Each CSV row is one SKU line; rows with the same poNumber form one PO. The importer reads the PO's header fields from its first row.
The principal CSV columns are poNumber, status, orderDate, fulfillmentCenter, sku, requestedUnits, and unitCost. Optional columns include acknowledgementDueAt, acknowledgedAt, deliveredAt, deliveryStart, deliveryEnd, externalId, currency, cancelledUnits, acceptedUnits, and receivedUnits. The parser also recognizes display labels such as PO Number, Order Date, and Requested Units. Dates must be parseable, and status accepts Unconfirmed, Confirmed, or Closed (also New, Open, or Closed respectively). If acknowledgementDueAt is absent, Nasam sets it to 72 hours after the order date.
poNumber,status,orderDate,fulfillmentCenter,sku,requestedUnits,unitCost,deliveryEnd
PO-1042,Unconfirmed,2026-09-28T09:00:00Z,RUH1,SKU-TEA-01,12,19.50,2026-10-05T18:00:00Z
PO-1042,Unconfirmed,2026-09-28T09:00:00Z,RUH1,SKU-COFFEE-02,5,32.00,2026-10-05T18:00:00Z
curl -X POST 'https://backend.nasam.co/v1/vendor/purchase-orders/import' \
-H 'accept: application/json' \
-H 'key: YOUR_API_KEY' \
-F 'brandId=20' \
-F 'saleChannelId=8' \
-F 'file=@purchase-orders.csv;type=text/csv'
Send the entire current line set for every PO you re-import. An import replaces that PO's lines; omitted SKUs are removed. Check removedLineItems before treating an update as complete. Re-uploading an unchanged file succeeds without creating a second PO. A CSV parse error returns success: false and writes nothing from the file. Later PO-level validation can skip an invalid PO while writing other valid POs, so inspect success, errors, imported, removedLineItems, and purchaseOrderIds even when the HTTP request succeeds. On a shared retailer account, Nasam attributes lines by SKU ownership across brands; an unknown or ambiguous SKU can prevent the whole affected PO from being imported.
Confirmed imports require accepted units on each line. Closed imports require accepted units unless all units were cancelled, and received units unless all units were cancelled or rejected; Nasam can fill zero received units for those fully cancelled or rejected lines. These values represent a PO's recorded state, not an acknowledgement request to the retailer. Use the acknowledgement operation for a live retailer decision.
Access and related operations
PO list, detail, and acknowledgement-group reads require vendor.read; import, acknowledgement, and deletion require vendor.write. The overview and top-performer reads require salesPerformance.read. The API key also needs access to the PO's brand; see authentication and scope. Delete PO permanently removes the selected Nasam record, so treat it as an administrative correction rather than a lifecycle transition.
For channel capture problems, use Sync Health. For issued financial documents, use invoicing. PO received value is a stock receipt measure; it is not a settled payout. Use profitability when the question concerns cleared money, fees, or statements.