MCP tools
Sales and purchase orders
MCP tools for sales orders from customers and purchase orders to suppliers, including goods receipts.
A sales order is what a customer ordered from your organization, and a purchase order is what your organization ordered from a supplier. Deliveries fulfil a sales order, while what arrives from a supplier is recorded with the receipt tool on this page. The orders guide explains the three steps in the app; these tools do the same things from Claude or from an MCP client you build.
id is changed in place, a line without one is added, and a line you leave out is deleted. Changing a line in place is what keeps a sales order line linked to its deliveries and a purchase order line's received quantity.| Tool | What it does |
|---|---|
| sales_orders_list | List sales orders |
| sales_orders_get | Get a sales order |
| sales_orders_create | Create a sales order |
| sales_orders_update | Update a sales order |
| sales_orders_set_status | Set a sales order's status |
| sales_orders_delete | Delete a sales order |
| sales_orders_create_deliveries | Plan a sales order's deliveries |
| sales_orders_mark_delivered | Mark a sales order delivered in full |
| sales_orders_calendar | Delivery calendar |
| purchase_orders_list | List purchase orders |
| purchase_orders_get | Get a purchase order |
| purchase_orders_create | Create a purchase order |
| purchase_orders_update | Update a purchase order |
| purchase_orders_set_status | Set a purchase order's status |
| purchase_orders_delete | Delete a purchase order |
| purchase_orders_receive | Record a goods receipt |
Sales orders
A sales order's status runs new, confirmed, in_progress and fulfilled, or ends in cancelled. Every order gets a sequential number for your organization: SO-000001, then SO-000002 and so on.
sales_orders_list
Lists your organization's sales orders, newest first, each with its tender and quotation. Filter by status, tender or quotation, or search the order number, the customer's name or the customer's own reference.
Read-only · scope orders.read · roles: owner, admin, sales, viewer, viewer (no pricing) · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | No | Only orders in this status: new, confirmed, in_progress, fulfilled or cancelled. |
hide_fulfilled | boolean | No | Send true to leave fulfilled orders out, as the sales orders page does by default. It has no effect when you send status. |
tender_id | integer | No | Only orders won on this tender. |
quotation_id | integer | No | Only orders that accepted this quotation. |
q | string | No | Text to look for in the order number, the customer's name and the customer's reference; it matches part of the text. Up to 200 characters. |
page | integer | No | Page number, starting at 1. |
page_size | integer | No | Results per page: 20 by default, 100 at most. |
Returns: A page of orders with total and total_pages. Each order carries its number, customer, status, dates, currency, total_amount, tender and quotation.
- The viewer (no pricing) role sees the orders without
total_amount. - A
statusthat is not one of the listed values is refused with a 400.
sales_orders_get
Returns one sales order with everything around it: its lines and how much of each has been delivered, the overall fulfilment, where the deliveries differ from the order, the tender and quotation, the linked deliveries, and the purchase orders sourcing its goods.
Read-only · scope orders.read · roles: owner, admin, sales, viewer, viewer (no pricing) · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The sales order's ID, from sales_orders_list. |
Returns: The order in sales_order, its lines in items, then fulfilment, deviations, tender, quotation, deliveries, purchase_orders and documents.
- Each line has
delivered_quantity(on deliveries marked delivered),pending_quantity(on deliveries still draft, scheduled or on the way),remaining_quantityandexcess_quantity(delivered beyond what was ordered). A cancelled delivery counts for nothing. deviationsnames every way the deliveries differ from the order:over_delivered(a line delivered beyond its ordered quantity),extra_lines(lines delivered outside the order),substitutions(a different catalog product shipped),late_deliveries(a delivery delivered or scheduled afterdue_on) andclosed_short(the order marked fulfilled while quantities remain), with acountof them all. They are a warning and block nothing.- The viewer (no pricing) role gets the order without
total_amountand withoutunit_priceon the lines.purchase_orderscomes backnullfor the sales and viewer (no pricing) roles, because purchase orders name suppliers and their costs. documentsare the files kept on the order, first of all the customer's purchase order as a PDF. Each has apath, aname, asizeand aurlvalid for about an hour. The path is what `sales_order_document_delete` takes. Every role that sees sales orders sees them.- An order that is not in your organization answers 404.
sales_orders_create
Records an order that came in from a customer, with its lines. It assigns the next sequential number, starts the order as new, and computes the total from the priced lines only.
Changes data · scope orders.write · roles: owner, admin, sales · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
customer_id | integer | No | The customer profile, from `customers_list`. When you leave customer_name out, the order takes the customer's legal name, or else the display name. Send null for no link. |
customer_name | string | No | Who ordered, 1 to 200 characters. Required unless you send customer_id. |
customer_reference | string | No | The customer's own reference, such as their purchase order or contract number, up to 200 characters. Send null to clear it. |
tender_id | integer | No | The tender the order was won on, from the tender tools. Send null to leave it empty. |
quotation_id | integer | No | The quotation the customer accepted, from `quotations_list`. It must be your organization's quotation. Send null to leave it empty. |
currency | string | No | The three-letter currency code (ISO 4217), such as SAR. Defaults to SAR, and is stored in capitals. |
ordered_on | string | No | The order date, as YYYY-MM-DD. Defaults to today in Riyadh. |
due_on | string | No | The date the customer expects delivery by, as YYYY-MM-DD. Send null to clear it. |
notes | string | No | Notes on the order, up to 2,000 characters. Send null to clear them. |
items | object[] | Yes | The order lines, from 1 to 500. |
items[].id | integer | No | Not used when creating; leave it out. |
items[].product_id | integer | No | The catalog product this line is for, from products_list. It must be your organization's product. Send null to remove the link. |
items[].description | string | Yes | What was ordered, 1 to 1,000 characters. |
items[].quantity | number | Yes | The quantity ordered; it must be above zero. |
items[].unit | string | No | The unit of measure, such as box, up to 50 characters. Send null to clear it. |
items[].unit_price | number | No | The price per unit in the order's currency, zero or more. Leave it out or send null while the line has no price; it then stays out of the order total. |
items[].notes | string | No | Notes on the line, up to 1,000 characters. Send null to clear them. |
items[].tender_item_id | integer | No | The tender item this line answers, from tenders_get. Useful when the order comes from a quotation or a tender. Send null to remove the link. |
items[].line_number | string | No | The line number as the customer prints it on their purchase order, such as 10 or 20, up to 50 characters. Left out on an update it stays as it is; send null to clear it. |
items[].material_code | string | No | The customer's item code, such as the trade number on a NUPCO purchase order, up to 50 characters. Left out on an update it stays as it is; send null to clear it. |
items[].ship_to | string | No | Where this line is delivered, as the purchase order prints it, such as a NUPCO warehouse or a health directorate, up to 300 characters. One order can send its lines to many destinations, and `sales_orders_create_deliveries` turns them into one draft delivery per destination. Left out on an update it stays as it is; send null to clear it. |
items[].due_on | string | No | The delivery deadline of this line as YYYY-MM-DD, the date the purchase order gives for it. sales_orders_calendar places the line on the delivery calendar by it. Left out on an update it stays as it is; send null to clear it. |
{
"customer_name": "Al Noor Hospital",
"customer_reference": "PO-2026-0457",
"tender_id": 48213,
"quotation_id": 1207,
"ordered_on": "2026-10-04",
"due_on": "2026-11-15",
"items": [
{
"description": "Sterile syringe 5 ml, box of 100",
"quantity": 200,
"unit": "box",
"unit_price": 36.5,
"tender_item_id": 902114
},
{
"description": "Nitrile gloves, medium, box of 100",
"quantity": 500,
"unit": "box",
"unit_price": 21
}
]
}Returns: The new order's sales_order_id and order_number.
- A name is required: send
customer_name, orcustomer_idand the order takes the customer's name from the profile. A name you send wins over the profile's. With neither, the tool answers 400. total_amountcounts only the lines that have aunit_price. When no line is priced, the order has no total.- A tender you cannot see, or a quotation, customer or product that is not your organization's, answers 404.
- To fulfil the order, create deliveries linked to it with `deliveries_create`. The order moves to
in_progressonce the first delivery exists, and tofulfilledwhen every line is delivered.
sales_orders_update
Changes an order's customer, dates, links, notes or lines. Only what you send changes, and null clears an optional field. The number and the status cannot be edited here: use `sales_orders_set_status` for the status.
Changes data · scope orders.write · roles: owner, admin, sales · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The sales order's ID. |
customer_id | integer | No | The customer profile linked to the order, from `customers_list`. Send null to unlink. Linking does not change the customer name written on the order, so send customer_name too if you want that. |
customer_name | string | No | Who ordered, 1 to 200 characters. |
customer_reference | string | No | The customer's own reference, such as their purchase order or contract number, up to 200 characters. Send null to clear it. |
tender_id | integer | No | The tender the order was won on, from the tender tools. Send null to leave it empty. |
quotation_id | integer | No | The quotation the customer accepted, from `quotations_list`. It must be your organization's quotation. Send null to leave it empty. |
currency | string | No | The three-letter currency code (ISO 4217), such as SAR. Defaults to SAR, and is stored in capitals. |
ordered_on | string | No | The order date, as YYYY-MM-DD. |
due_on | string | No | The date the customer expects delivery by, as YYYY-MM-DD. Send null to clear it. |
notes | string | No | Notes on the order, up to 2,000 characters. Send null to clear them. |
items | object[] | No | The complete line list, reconciled by ID (see the top of this page), from 1 to 500 lines. Leave it out to keep the lines and the total as they are. |
items[].id | integer | No | The stored line's ID, from sales_orders_get. A line sent with its ID is changed in place; a line without one is added. |
items[].product_id | integer | No | The catalog product this line is for, from products_list. It must be your organization's product. Send null to remove the link. |
items[].description | string | Yes | What was ordered, 1 to 1,000 characters. |
items[].quantity | number | Yes | The quantity ordered; it must be above zero. |
items[].unit | string | No | The unit of measure, such as box, up to 50 characters. Send null to clear it. |
items[].unit_price | number | No | The price per unit in the order's currency, zero or more. Leave it out or send null while the line has no price; it then stays out of the order total. |
items[].notes | string | No | Notes on the line, up to 1,000 characters. Send null to clear them. |
items[].tender_item_id | integer | No | The tender item this line answers, from tenders_get. Useful when the order comes from a quotation or a tender. Send null to remove the link. |
items[].line_number | string | No | The line number as the customer prints it on their purchase order, such as 10 or 20, up to 50 characters. Left out on an update it stays as it is; send null to clear it. |
items[].material_code | string | No | The customer's item code, such as the trade number on a NUPCO purchase order, up to 50 characters. Left out on an update it stays as it is; send null to clear it. |
items[].ship_to | string | No | Where this line is delivered, as the purchase order prints it, such as a NUPCO warehouse or a health directorate, up to 300 characters. One order can send its lines to many destinations, and `sales_orders_create_deliveries` turns them into one draft delivery per destination. Left out on an update it stays as it is; send null to clear it. |
items[].due_on | string | No | The delivery deadline of this line as YYYY-MM-DD, the date the purchase order gives for it. sales_orders_calendar places the line on the delivery calendar by it. Left out on an update it stays as it is; send null to clear it. |
{
"id": 214,
"due_on": "2026-11-30",
"items": [
{
"id": 1881,
"description": "Sterile syringe 5 ml, box of 100",
"quantity": 250,
"unit": "box",
"unit_price": 36.5
},
{
"description": "Examination gloves, large, box of 100",
"quantity": 100,
"unit": "box",
"unit_price": 21
}
]
}Returns: The sales_order_id of the updated order.
- A line ID that is not on this order, or that is sent twice, is refused with 400. A line you leave out of the list is deleted, and any delivery line that pointed at it loses that link.
- When you send
items, the total is recomputed from the new lines. - The tender, quotation and customer are checked as they are when creating, and one that cannot be found answers 404.
sales_orders_set_status
Moves a sales order to a new status. Any status may follow any other, so a mistaken move can be undone. This is the manual override of what deliveries move on their own.
Changes data · scope orders.write · roles: owner, admin, sales · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The sales order's ID. |
status | string | Yes | The new status: new, confirmed, in_progress, fulfilled or cancelled. |
{
"id": 214,
"status": "confirmed"
}Returns: A confirmation with the new status.
- Entering
fulfilledstampsfulfilled_at, and leaving it clears the stamp. - Linked deliveries also move the order on their own, but only forward: to
in_progressonce the first delivery exists and tofulfilledwhen every line is delivered. They never move it back, and never out ofcancelledorfulfilled.
sales_orders_delete
Deletes a sales order and its lines. The deliveries and purchase orders that pointed at it stay and are unlinked from it. This cannot be undone.
Deletes or overwrites data · scope orders.write · roles: owner, admin, sales · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The sales order's ID. |
Returns: A confirmation of the deletion.
sales_orders_create_deliveries
Creates one draft delivery for each destination and day the order's lines name in ship_to and due_on, each carrying the lines that go there. A NUPCO purchase order that sends one product to twelve warehouses becomes twelve drafts in one call.
Changes data · scope orders.write or logistics.write · roles: owner, admin · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The sales order's ID, from sales_orders_list. |
warehouse_id | integer | No | The warehouse every delivery ships from, from warehouses_list. Leave it out to choose later, or send null for none. |
destinations | object[] | No | Extra details for the destinations the lines name: an address, a city or a person to call on delivery. They are matched by the ship_to text, ignoring case and extra spaces. Up to 200. |
destinations[].ship_to | string | Yes | The destination exactly as the lines spell it in ship_to, 1 to 300 characters. |
destinations[].address | string | No | The destination's street address, up to 500 characters. |
destinations[].city | string | No | The city, up to 100 characters. |
destinations[].contact_name | string | No | The person to call on delivery, up to 200 characters. |
destinations[].contact_phone | string | No | Their phone number, up to 50 characters. |
{
"id": 1042,
"warehouse_id": 3,
"destinations": [
{
"ship_to": "NUPCO C.WH in Jeddah for MOH - MOH - Taif",
"contact_name": "Receiving desk",
"contact_phone": "+966500000000"
}
]
}Returns: deliveries: for each draft its delivery_id, delivery_number, destination_name, due_on and the IDs of the order lines on it. Then skipped: the lines that were not planned and why, no_ship_to or fully_planned.
- It plans only what no live delivery carries yet, so calling it again creates nothing new, and a line that has shipped in part gets a draft for the rest. A line with no
ship_tois skipped and listed inskipped. - Each draft is scheduled for noon Riyadh time on its lines' due day, and a line with no date of its own goes on the order's due date, as on the calendar. The order's status does not move until one of the deliveries is worked on.
- It needs the admin or owner role because it creates deliveries, and both the
orders.writeandlogistics.writescopes. A cancelled order answers 409, and so does an order whose deliveries are being planned at that moment from somewhere else: wait a minute, then call again to plan whatever is left. - If a draft cannot be created the call stops and says how many were created before it. Those stay, and calling again plans only what is left.
sales_orders_mark_delivered
Records a sales order as delivered in full, such as an order invoiced in Zoho Books: every open delivery of the order becomes delivered, whatever is left gets a delivery per destination that is delivered at once, and the order then closes as fulfilled.
Changes data · scope orders.write or logistics.write · roles: owner, admin · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The sales order's ID, from sales_orders_list. |
note | string | No | A note written on the deliveries it creates, up to 500 characters. By default it says the order was invoiced in Zoho Books. |
{
"id": 1042
}Returns: delivered: the IDs of the deliveries it set delivered. created: the IDs of the deliveries it made.
- Cancelled deliveries are left as they are and an order already delivered in full does not change, so calling it again is safe. A cancelled order answers 409. To record part of an order, use
deliveries_createanddeliveries_set_statusfor those lines. - It needs the admin or owner role because it creates deliveries and moves their status, and both the
orders.writeandlogistics.writescopes.
sales_orders_calendar
Shows the days customers' orders have to be delivered by: one entry for each order, destination and day, with the lines due and the deliveries that already cover them. Use it to see what is overdue, what is due soon and what has no delivery planned yet.
Read-only · scope orders.read · roles: owner, admin, sales, viewer, viewer (no pricing) · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
from | string | No | The first day of the window, YYYY-MM-DD. It defaults to today. Leave it out with open_only and send to alone to reach everything that is overdue. |
to | string | No | The last day of the window, YYYY-MM-DD. It defaults to 30 days after from, and the window is at most 100 days. |
open_only | boolean | No | Leaves out the entries that are already delivered. With to and no from it returns every open entry up to that day, the overdue ones included. |
{
"from": "2026-11-01",
"to": "2026-11-30"
}Returns: events, ordered by date. Each entry has date, sales_order_id, order_number, order_status, customer_name, customer_reference (the customer's purchase order number), destination, area, state, days_until, line_count, open_line_count, items and deliveries. Then today, from, to, open_only, summary (overdue, soon and unplanned) and truncated.
- An entry is what one planned delivery would carry: the lines of one order going to one destination on one day, ignoring case and extra spaces in the destination's name.
itemslists only the first 20 lines,line_countis the total, and `sales_orders_get` shows them all. - A line is placed on its own
due_on, or on the order's when it has none. A line with no date of its own or its order's does not appear, and neither do cancelled orders. Days are Riyadh days. stateisoverdue(something is still open and the day has passed),upcoming, ordelivered(every line has shipped on deliveries marked delivered, or the order is closed as fulfilled).days_untilis negative when overdue and null once delivered.summary.sooncounts what is due within 3 days.areais the Saudi region the destination is in, read from its words by the first place it names:riyadh,makkah,madinah,eastern,qassim,asir,tabuk,hail,northern_borders,jazan,najran,al_bahahoral_jouf. It isnullwhen the destination names no place the platform knows. The map on the app's delivery calendar is built on it.deliveriesare the non-cancelled deliveries carrying the entry's lines, drafts, scheduled and in-transit ones included. An open entry with none still needs `sales_orders_create_deliveries`, andsummary.unplannedcounts those entries.- At most 500 entries come back. If the window holds more,
truncatedistrue: narrow the window. Entries carry no prices.
Purchase orders
A purchase order's status runs draft, sent, confirmed, partially_received and received, or ends in cancelled. Every order gets a sequential number: PO-000001, then PO-000002.
purchase_orders_list
Lists the purchase orders placed with suppliers, newest first, each with its tender and the sales order it sources. Filter by status, manufacturer, tender or sales order, or search the order number, the supplier's name or the supplier's reference.
Read-only · scope orders.read · roles: owner, admin, purchasing, viewer · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | No | Only orders in this status: draft, sent, confirmed, partially_received, received or cancelled. |
manufacturer_id | integer | No | Only orders placed with this manufacturer from your directory. |
tender_id | integer | No | Only orders sourcing this tender. |
sales_order_id | integer | No | Only orders sourcing this sales order. |
q | string | No | Text to look for in the order number, the supplier's name and the supplier's reference; it matches part of the text. Up to 200 characters. |
page | integer | No | Page number, starting at 1. |
page_size | integer | No | Results per page: 20 by default, 100 at most. |
Returns: A page of orders with total and total_pages. Each order carries its number, supplier, status, dates, currency, total_amount, tender and linked sales order.
- Purchase orders are cost data: the sales, technical and viewer (no pricing) roles do not see them.
purchase_orders_get
Returns one purchase order with its lines, each showing what has been received and what remains, the receipt totals, and the manufacturer, tender and sales order linked to it.
Read-only · scope orders.read · roles: owner, admin, purchasing, viewer · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The purchase order's ID, from purchase_orders_list. |
Returns: The order in purchase_order, its lines in items (each with received_quantity and remaining_quantity), then receipt, manufacturer, tender and sales_order.
receiptholdsordered,received,remainingandcomplete. The line IDs here are the ones `purchase_orders_receive` takes.- An order that is not in your organization answers 404.
purchase_orders_create
Records an order placed with a supplier, with its lines. It assigns the next sequential number and starts the order as draft. The supplier is a manufacturer from your directory or a name you type, and one of the two is required.
Changes data · scope orders.write · roles: owner, admin, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
manufacturer_id | integer | No | The manufacturer you are ordering from, from `manufacturers_list`. The order takes its name when you leave out supplier_name. It must be in your organization's directory. |
supplier_name | string | No | The supplier's name as filed, 1 to 200 characters. Required unless you send manufacturer_id. |
supplier_reference | string | No | The supplier's own reference, such as their confirmation or proforma number, up to 200 characters. Send null to clear it. |
tender_id | integer | No | The tender this purchase sources, from the tender tools. Send null to leave it empty. |
sales_order_id | integer | No | The sales order this purchase sources goods for, from `sales_orders_list`. It must be your organization's order. Send null to leave it empty. |
currency | string | No | The three-letter currency code (ISO 4217), such as SAR. Defaults to SAR, and is stored in capitals. |
ordered_on | string | No | The order date, as YYYY-MM-DD. Defaults to today in Riyadh. |
expected_on | string | No | The date the goods are expected, as YYYY-MM-DD. Send null to clear it. |
notes | string | No | Notes on the order, up to 2,000 characters. Send null to clear them. |
items | object[] | Yes | The order lines, from 1 to 500. |
items[].id | integer | No | Not used when creating; leave it out. |
items[].product_id | integer | No | The catalog product this line is for, from products_list. It must be your organization's product. Send null to remove the link. |
items[].description | string | Yes | What was ordered, 1 to 1,000 characters. |
items[].quantity | number | Yes | The quantity ordered; it must be above zero. |
items[].unit | string | No | The unit of measure, such as box, up to 50 characters. Send null to clear it. |
items[].unit_price | number | No | The cost per unit in the order's currency, zero or more. Leave it out or send null while the line has no price; it then stays out of the order total. |
items[].notes | string | No | Notes on the line, up to 1,000 characters. Send null to clear them. |
{
"manufacturer_id": 87,
"supplier_reference": "PI-2026-0331",
"tender_id": 48213,
"sales_order_id": 214,
"expected_on": "2026-11-01",
"items": [
{
"description": "Sterile syringe 5 ml, box of 100",
"quantity": 200,
"unit": "box",
"unit_price": 24.8
}
]
}Returns: The new order's purchase_order_id and order_number.
- When you send
manufacturer_id, the order copies the manufacturer's name intosupplier_name, so the order still reads after the manufacturer leaves your directory. With neither, the tool answers 400. - A manufacturer, sales order or product that is not your organization's, or a tender you cannot see, answers 404.
- Every line starts with nothing received. Record arrivals with `purchase_orders_receive`.
purchase_orders_update
Changes an order's supplier, dates, links, notes or lines. Only what you send changes, and null clears an optional field. Use `purchase_orders_set_status` for the status and `purchase_orders_receive` for arrivals.
Changes data · scope orders.write · roles: owner, admin, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The purchase order's ID. |
manufacturer_id | integer | No | The manufacturer linked to the order, from `manufacturers_list`. The order takes its name unless you also send supplier_name. Send null to unlink; the supplier name already written stays. |
supplier_name | string | No | The supplier's name as filed, 1 to 200 characters. |
supplier_reference | string | No | The supplier's own reference, such as their confirmation or proforma number, up to 200 characters. Send null to clear it. |
tender_id | integer | No | The tender this purchase sources, from the tender tools. Send null to leave it empty. |
sales_order_id | integer | No | The sales order this purchase sources goods for, from `sales_orders_list`. It must be your organization's order. Send null to leave it empty. |
currency | string | No | The three-letter currency code (ISO 4217), such as SAR. Defaults to SAR, and is stored in capitals. |
ordered_on | string | No | The order date, as YYYY-MM-DD. |
expected_on | string | No | The date the goods are expected, as YYYY-MM-DD. Send null to clear it. |
notes | string | No | Notes on the order, up to 2,000 characters. Send null to clear them. |
items | object[] | No | The complete line list, reconciled by ID (see the top of this page), from 1 to 500 lines. Leave it out to keep the lines and the total as they are. |
items[].id | integer | No | The stored line's ID, from purchase_orders_get. A line sent with its ID is changed in place; a line without one is added. |
items[].product_id | integer | No | The catalog product this line is for, from products_list. It must be your organization's product. Send null to remove the link. |
items[].description | string | Yes | What was ordered, 1 to 1,000 characters. |
items[].quantity | number | Yes | The quantity ordered; it must be above zero. |
items[].unit | string | No | The unit of measure, such as box, up to 50 characters. Send null to clear it. |
items[].unit_price | number | No | The cost per unit in the order's currency, zero or more. Leave it out or send null while the line has no price; it then stays out of the order total. |
items[].notes | string | No | Notes on the line, up to 1,000 characters. Send null to clear them. |
{
"id": 98,
"supplier_reference": "PI-2026-0331-R1",
"expected_on": "2026-11-10"
}Returns: The purchase_order_id of the updated order.
- A line sent with its ID keeps what was received on it. Lowering its quantity below what has already arrived is refused with 400, and so is a line ID that is not on the order or is sent twice.
- A line you leave out of the list is deleted along with what was received on it. When you send
items, the total is recomputed.
purchase_orders_set_status
Moves a purchase order to a new status. Any status may follow any other, so a mistaken move can be undone. It does not touch the received quantities.
Changes data · scope orders.write · roles: owner, admin, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The purchase order's ID. |
status | string | Yes | The new status: draft, sent, confirmed, partially_received, received or cancelled. |
{
"id": 98,
"status": "sent"
}Returns: A confirmation with the new status.
- Entering
receivedstampsreceived_at, and leaving it clears the stamp. The quantities received on the lines stay as they are: moving toreceivedby hand says the goods are in, without changing what was recorded on the lines. - A cancelled order takes no receipts. Move it back to
draftfirst.
purchase_orders_delete
Deletes a purchase order together with its lines and the receipts recorded on them. This cannot be undone.
Deletes or overwrites data · scope orders.write · roles: owner, admin, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The purchase order's ID. |
Returns: A confirmation of the deletion.
Goods receipts
When a supplier's goods arrive, record them against the purchase order's lines and the order's status moves on its own.
purchase_orders_receive
Records goods that arrived against a purchase order. Each line adds its quantity to what was already received on that line, and the order then moves to partially_received, or to received once every line is complete.
Changes data · scope orders.write · roles: owner, admin, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The purchase order's ID. |
lines | object[] | Yes | What arrived now, from 1 to 500 lines. |
lines[].item_id | integer | Yes | The purchase order line's ID, from items in `purchase_orders_get`. |
lines[].quantity | number | Yes | The quantity that arrived now, above zero. It is added to the received total, not set in its place. |
received_at | string | No | When the goods arrived, as ISO 8601 with a time zone, such as 2026-10-12T09:30:00+03:00. Defaults to now. It is recorded only if this receipt completes the order. |
{
"id": 98,
"lines": [
{
"item_id": 4410,
"quantity": 120
},
{
"item_id": 4411,
"quantity": 40
}
],
"received_at": "2026-10-12T09:30:00+03:00"
}Returns: A confirmation with the order's new status, and in items the new received total (received_quantity) of every line the receipt touched.
- The whole receipt is refused, and nothing is saved, if a line names an item that is not on the order or would take a line past what was ordered. A line named twice has its quantities added together.
- A cancelled order answers 409. Move it back to
draftwith `purchase_orders_set_status`, then record the receipt. - The order moves forward from wherever it is,
draftincluded.received_atis recorded only once: an order already marked received keeps its date.