Buy and receive stock
Create a vendor, raise and approve a purchase order, record the delivery as a goods receipt and confirm it so the stock lands in a stockroom.
This guide follows one delivery from order to shelf. You create a vendor, raise a purchase order and move it through approval, record what arrived as a goods receipt, then confirm the receipt. Confirming is the step that adds the stock.
At the end you will have the ordered items in stock in a stockroom of your choice, and a purchase order that shows how much has been received.
Before you start you need:
- a token and the merchant's tenant code. See Authentication.
- the items in the catalog, with their
itemId. See Add products to the catalog. - the ID of the stockroom to receive into. See Set up a location, Step 4.
How a purchase order moves
DRAFT → SUBMITTED → APPROVED → SENT, then PARTIAL or RECEIVED as goods are
confirmed. A call made in the wrong order returns 400 with INVALID_STATUS_TRANSITION.
You can cancel an order at any point before it is fully received.
Step 1 — Create the vendor
Every purchase order and receipt names the vendor it is with.
curl -X POST 'https://beta.iotoms.com/api/v1/procurement/vendors' \
-H 'X-TenantID: <tenant-code>' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"vendorCode": "V-0042",
"vendorName": "Blue Valley Distributors",
"contactName": "Asha Menon",
"contactEmail": "orders@bluevalley.example",
"contactPhone": "+971 4 555 0142",
"paymentTermsDays": 30
}'vendorCodeis your own code for the vendor. It must be unique. A duplicate returns 400 withVENDOR_CODE_ALREADY_EXISTS.paymentTermsDaysdefaults to 30.
The response (201) gives you data.id, the vendor ID (a UUID). To reuse a vendor that
already exists, find it with List vendors.
Endpoint: Create a vendor
Step 2 — Create the purchase order
The purchase order records what you are asking the vendor for and at what cost.
curl -X POST 'https://beta.iotoms.com/api/v1/procurement/purchase-orders' \
-H 'X-TenantID: <tenant-code>' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"vendorId": "3d07c219-0a88-45be-9cf6-2b51e8a4d6c0",
"orderDate": "2026-10-18",
"expectedDeliveryDate": "2026-10-21",
"notes": "Weekly restock",
"items": [
{
"itemId": "SKU-10042",
"orderedQty": 10,
"unitOfMeasure": "CASE",
"uomId": "e4c81b27-0f5a-4d39-b6e2-81a9c0d3f57e",
"unitCost": 15.60,
"receivingLocationId": "5b0f6c1e-2d8a-4f3b-9e71-0c4d2a6b8f13"
}
]
}'- Each line needs
itemId,orderedQtyandunitOfMeasure. uomIdis the unit the line is ordered in. When it is set, received quantities are converted to the item's base unit using that unit's factor. With nouomId, quantities count as base units.unitCostis the price per ordered unit. If you leave it out, iotoms fills it in. It tries an agreed price with this vendor first, then what was last paid, then the vendor's price list, and finally the item's cost price. If none of these exist, the call returns 400 withUNIT_COST_REQUIRED_FOR_ITEM_<itemId>.receivingLocationIdis the stockroom the line should be received into.- An unknown vendor or item returns 404.
The response (201) gives you:
data.id: the purchase order ID (UUID).data.poNumberis a readable number such asPO-20261018-001.data.status:DRAFT.data.totalAmount: the sum ofunitCost × orderedQty.data.items[].id: the purchase order line ID of each line. You need it in Step 5.
Endpoint: Create a purchase order
Step 3 — Submit and approve it
A draft must be submitted for approval and then approved before it goes to the vendor.
curl -X PATCH 'https://beta.iotoms.com/api/v1/procurement/purchase-orders/8f2a6c4e-1b3d-4a7f-9c05-6e2d1f8b3a71/submit' \
-H 'X-TenantID: <tenant-code>' \
-H 'Authorization: Bearer <token>'data.status is now SUBMITTED. Then approve it. approvedBy is a required query parameter
naming who approved:
curl -X PATCH 'https://beta.iotoms.com/api/v1/procurement/purchase-orders/8f2a6c4e-1b3d-4a7f-9c05-6e2d1f8b3a71/approve?approvedBy=Asha%20Menon' \
-H 'X-TenantID: <tenant-code>' \
-H 'Authorization: Bearer <token>'data.status is now APPROVED.
Endpoints: Submit a purchase order · Approve a purchase order
Step 4 — Mark it as sent to the vendor
This records that the order has gone to the vendor.
curl -X PATCH 'https://beta.iotoms.com/api/v1/procurement/purchase-orders/8f2a6c4e-1b3d-4a7f-9c05-6e2d1f8b3a71/send' \
-H 'X-TenantID: <tenant-code>' \
-H 'Authorization: Bearer <token>'data.status is now SENT. Only an approved order can be sent.
Endpoint: Send a purchase order to the vendor
Step 5 — Record the delivery as a goods receipt
When the goods arrive, a goods receipt records what came in and what was accepted. It starts as a draft and does not change stock yet.
curl -X POST 'https://beta.iotoms.com/api/v1/procurement/receipts' \
-H 'X-TenantID: <tenant-code>' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"purchaseOrderId": "8f2a6c4e-1b3d-4a7f-9c05-6e2d1f8b3a71",
"vendorId": "3d07c219-0a88-45be-9cf6-2b51e8a4d6c0",
"receivedDate": "2026-10-21",
"deliveryNoteNumber": "DN-55871",
"receivedBy": "Ravi K",
"items": [
{
"purchaseOrderItemId": 512,
"itemId": "SKU-10042",
"receivedQty": 10,
"acceptedQty": 9,
"condition": "GOOD",
"receivingLocationId": "5b0f6c1e-2d8a-4f3b-9e71-0c4d2a6b8f13",
"notes": "1 case crushed"
}
]
}'purchaseOrderItemIdis the line ID from Step 2. It links the receipt line to the order line. The receipt line is then counted in the order line's unit, and confirming it adds to the order line's received quantity.receivedQtyis what arrived andacceptedQtyis what you keep. Both are in the order line's unit. The difference is recorded asrejectedQty.- If
unitCostis left out, the line takes the cost from the linked order line. purchaseOrderIdis optional, so you can also receive goods that were never ordered.
The response (201) gives you data.id, the goods receipt ID. It also gives
data.grnNumber, a readable number such as GRN-20261021-001, and data.status, which is
DRAFT.
Endpoint: Create a goods receipt
Step 6 — Confirm the receipt
Confirming posts the accepted quantities into stock. Nothing changes in stock until you do.
curl -X PATCH 'https://beta.iotoms.com/api/v1/procurement/receipts/c6b19e3a-7d24-4f80-a1e5-0b9c2d4f6e83/confirm' \
-H 'X-TenantID: <tenant-code>' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"confirmedBy": "Ravi K"
}'For each line, iotoms:
- Converts the accepted quantity to the item's base unit. With the order line in cases of 24, 9 accepted cases become 216.
- Adds that quantity to the stockroom named on the receipt line. If the receipt line names none, it uses the stockroom on the order line, and failing that the merchant's default warehouse.
- Adds the accepted quantity to the order line's received quantity.
The purchase order then moves to RECEIVED if every line is fully received, or to PARTIAL if
not. Send another receipt for the rest. The receipt's data.status becomes CONFIRMED, with
confirmedBy and confirmedAt filled in.
Needs its own permission
Confirming a receipt adds stock, so it needs a separate feature on the user's role. Without
it the call returns 400 with ACCESS_DENIED. Ask the merchant's administrator to grant
it (see Permissions). Only a DRAFT receipt can
be confirmed, and a receipt can be confirmed only once.
Endpoint: Confirm a goods receipt
Step 7 — Check stock on hand
Check the result for the item across the business and in the stockroom you received into.
curl 'https://beta.iotoms.com/api/v1/inventory/items/SKU-10042' \
-H 'X-TenantID: <tenant-code>' \
-H 'Authorization: Bearer <token>'data.quantityOnHand is the item's total in base units. data.reservedQuantity is held by
reservations, and data.availableQuantity is what is free to sell.
For one stockroom, call GET /api/v1/inventory/locations/{id}/items with the stockroom ID. Each
row has itemId, quantityOnHand and availableQuantity for that stockroom.
To check the order, get the purchase order.
Each line shows receivedQty and outstandingQty.
Endpoints: Get stock on hand for an item · List stock at a location