iotoms Docs

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:

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
  }'
  • vendorCode is your own code for the vendor. It must be unique. A duplicate returns 400 with VENDOR_CODE_ALREADY_EXISTS.
  • paymentTermsDays defaults 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, orderedQty and unitOfMeasure.
  • uomId is 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 no uomId, quantities count as base units.
  • unitCost is 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 with UNIT_COST_REQUIRED_FOR_ITEM_<itemId>.
  • receivingLocationId is 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.poNumber is a readable number such as PO-20261018-001.
  • data.status: DRAFT.
  • data.totalAmount: the sum of unitCost × 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"
      }
    ]
  }'
  • purchaseOrderItemId is 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.
  • receivedQty is what arrived and acceptedQty is what you keep. Both are in the order line's unit. The difference is recorded as rejectedQty.
  • If unitCost is left out, the line takes the cost from the linked order line.
  • purchaseOrderId is 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:

  1. Converts the accepted quantity to the item's base unit. With the order line in cases of 24, 9 accepted cases become 216.
  2. 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.
  3. 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

What's next