Home Integration API reference
REST API for integrations and external apps
Purchases
This anchor groups the purchase-requisition endpoints.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/purchase-requisitions |
| Permission | purchase_requisition.view_all or purchase_requisition.view_own. |
| Feature requirement | Settings -> Purchases -> Enable Purchase Requisition must be enabled for the business. |
| Visibility | Rows are limited to permitted locations. When the token user lacks purchase_requisition.view_all but has purchase_requisition.view_own, only requisitions created by that user are returned. |
| CSV behavior | format=csv streams all matching rows with a UTF-8 BOM and ignores page and per_page. Nested location and created_by objects are JSON-encoded in CSV cells. |
| Success response | 200 with paginated PurchaseRequisitionRow rows. |
| Parameter | Type | Required | Description |
|---|---|---|---|
per_page, page | integer | No | Pagination controls. per_page accepts 1 to 100 and defaults to 20. |
location_id | integer | No | Filters by requisition location id. |
status | string | No | ordered, partial, or completed. |
start_date, end_date | string | No | Optional Y-m-d bounds on transaction_date. The filter is only applied when both values are present. |
required_by_start, required_by_end | string | No | Optional Y-m-d bounds on delivery_date. The filter is only applied when both values are present. |
q | string | No | Minimum 2 characters when sent. Matches ref_no and numeric requisition id. |
format | string | No | json (default) or csv. |
PurchaseRequisitionRow object| Field | Type | Description |
|---|---|---|
id | integer | Purchase-requisition id. |
ref_no | string | null | Requisition reference number. |
status | string | null | Current requisition workflow status. |
transaction_date, delivery_date | string | null | ISO-8601 requisition timestamps. |
location | object | null | Business-location summary with id and name. |
created_by | object | null | User summary with id and combined display name. |
| Field | Type | Description |
|---|---|---|
data | array<PurchaseRequisitionRow> | The current result page. |
meta.current_page, meta.last_page, meta.per_page, meta.total | integer | Laravel paginator metadata. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The requisition list was returned successfully. | { "data": [...], "meta": { ... } } or CSV download. |
403 | The token user lacks view permission or purchase requisitions are disabled. | { "message": string } |
422 | The query string failed validation or q was shorter than 2 characters. | Laravel validation JSON or { "message": string }. |
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/purchase-requisitions/{id} |
| Permission | Same permission and visibility rules as List purchase requisitions. |
| CSV behavior | format=csv streams one UTF-8 BOM row whose data_json column matches the JSON data payload. |
| Success response | 200 with one PurchaseRequisitionDetail object. |
PurchaseRequisitionLine object| Field | Type | Description |
|---|---|---|
id, product_id, variation_id | integer | Identifiers for the requisition line and linked catalog records. |
quantity, secondary_unit_quantity, po_quantity_purchased | number | null | Requested quantity, optional secondary-unit quantity, and quantity already purchased on linked purchase orders. |
product_name, sku, variation_name, variation_label, sub_sku, unit | string | null | Resolved product and unit display fields. |
PurchaseRequisitionDetail object| Field | Type | Description |
|---|---|---|
id, ref_no | integer | string | Requisition identifiers. |
status | string | null | Workflow status. |
transaction_date, delivery_date | string | null | ISO-8601 requisition timestamps. |
location | object | null | Business-location summary with id and name. |
created_by | object | null | User summary with id, name, and email. |
lines | array<PurchaseRequisitionLine> | Requisition lines ordered by line id. |
| Field | Type | Description |
|---|---|---|
data | PurchaseRequisitionDetail | The requested requisition payload. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The requisition was returned successfully. | { "data": { ... } } or CSV download. |
403 | The token user lacks view permission or purchase requisitions are disabled. | { "message": string } |
404 | The requisition id does not exist in the visible query. | { "message": "Not found" } |
422 | The query string failed validation. | Laravel validation JSON. |
| Property | Value |
|---|---|
| Method | POST |
| Path | /api/v1/integration/purchase-requisitions |
| Permission | purchase_requisition.create. |
| Feature requirement | Purchase requisitions must be enabled for the business. |
| Location rule | location_id must belong to the business. The controller does not separately enforce permitted-location membership on create. |
| Transaction defaults | The controller creates type = purchase_requisition, status = ordered, and uses the current timestamp for transaction_date. |
| Line rule | At least one line must have a positive quantity or secondary_unit_quantity. |
| Success response | 201 with the created id and reference number. |
| Field | Type | Required | Description |
|---|---|---|---|
location_id | integer | Yes | Business location id. |
delivery_date | string | null | No | Optional required-by date in Y-m-d format. |
purchases | array<PurchaseRequisitionCreateLine> | Yes | At least one requisition line. |
PurchaseRequisitionCreateLine object| Field | Type | Required | Description |
|---|---|---|---|
product_id | integer | Yes | Product id. |
variation_id | integer | Yes | Variation id. |
quantity | number | null | No | Requested quantity for the line. |
secondary_unit_quantity | number | null | No | Optional secondary-unit quantity. |
| Field | Type | Description |
|---|---|---|
message | string | Localized create-success message. |
data.id | integer | The created requisition id. |
data.ref_no | string | null | The saved requisition reference number. |
| Status | When it happens | Response shape |
|---|---|---|
201 | The requisition was created successfully. | { "message": string, "data": { "id": integer, "ref_no": string | null } } |
403 | The token user lacks create permission or purchase requisitions are disabled. | { "message": string } |
422 | The request body failed validation or all lines had zero quantity. | Laravel validation JSON or { "message": string }. |
500 | The requisition create transaction failed unexpectedly. | { "message": "something_went_wrong" } |
| Property | Value |
|---|---|
| Method | PUT or PATCH |
| Path | /api/v1/integration/purchase-requisitions/{id}/status |
| Permission | purchase_requisition.create or purchase_requisition.delete. |
| Feature requirement | Purchase requisitions must be enabled for the business. |
| Visibility | The target requisition is loaded through the same permitted-location and own/all visibility query used by the list endpoint. |
| Allowed statuses | ordered, partial, or completed. |
| Success response | 200 with the updated id and status. |
| Field | Type | Required | Description |
|---|---|---|---|
status | string | Yes | New requisition workflow status. |
| Field | Type | Description |
|---|---|---|
message | string | Localized update-success message. |
data.id | integer | The requisition id. |
data.status | string | The saved requisition status. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The requisition status was updated successfully. | { "message": string, "data": { "id": integer, "status": string } } |
403 | The token user lacks the required permission or purchase requisitions are disabled. | { "message": string } |
404 | The requisition id does not exist in the visible query. | { "message": "Not found" } |
422 | The request body failed validation. | Laravel validation JSON. |
| Property | Value |
|---|---|
| Method | DELETE |
| Path | /api/v1/integration/purchase-requisitions/{id} |
| Permission | purchase_requisition.delete. |
| Feature requirement | Purchase requisitions must be enabled for the business. |
| Delete scope | The action looks up the row by business and type = purchase_requisition. Unlike list and show, it does not reapply the list visibility filters. |
| Delete behavior | Clears purchase_requisition_line_id on linked purchase lines before deleting the requisition. |
| Success response | 200 with the deleted id. |
| Field | Type | Description |
|---|---|---|
message | string | Localized delete-success message. |
data.id | integer | The deleted requisition id. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The requisition was deleted successfully. | { "message": string, "data": { "id": integer } } |
403 | The token user lacks delete permission or purchase requisitions are disabled. | { "message": string } |
404 | No purchase requisition with that id exists for the business. | { "message": "Not found" } |
500 | The requisition delete failed unexpectedly. | { "message": string } |
This anchor groups the purchase-order endpoints.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/purchase-orders |
| Permission | purchase_order.view_all or purchase_order.view_own. |
| Feature requirement | Settings -> Purchases -> Enable purchase order must be enabled for the business. |
| Visibility | Rows are limited to permitted locations. When the token user lacks purchase_order.view_all but has purchase_order.view_own, only orders created by that user are returned. |
| CSV behavior | format=csv streams all matching rows with a UTF-8 BOM and ignores page and per_page. |
| Success response | 200 with paginated PurchaseOrderRow rows. |
| Parameter | Type | Required | Description |
|---|---|---|---|
per_page, page | integer | No | Pagination controls. per_page accepts 1 to 100 and defaults to 20. |
location_id | integer | No | Filters by purchase-order location id. |
contact_id | integer | No | Filters by contact id. |
status | string | No | ordered, partial, or completed. |
shipping_status | string | No | Exact match against the stored shipping status. |
start_date, end_date | string | No | Optional Y-m-d bounds on transaction_date. The filter is only applied when both are present. |
q | string | No | Minimum 2 characters when sent. Matches reference number, document, numeric id, and linked contact fields. |
format | string | No | json (default) or csv. |
PurchaseOrderRow object| Field | Type | Description |
|---|---|---|
id | integer | Purchase-order id. |
ref_no, document | string | null | Stored reference and document fields. |
transaction_date | string | null | ISO-8601 transaction timestamp. |
status, shipping_status | string | null | Order workflow and shipping status. |
final_total, total_before_tax, tax_amount, discount_amount | number | null | Order totals from the transaction header. |
po_qty_remaining | number | null | Remaining quantity not yet purchased against the order. |
contact | object | null | Contact summary with id, name, mobile, contact_id, and supplier_business_name. |
location | object | null | Business-location summary with id and name. |
| Field | Type | Description |
|---|---|---|
data | array<PurchaseOrderRow> | The current result page. |
meta.current_page, meta.last_page, meta.per_page, meta.total | integer | Laravel paginator metadata. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The purchase-order list was returned successfully. | { "data": [...], "meta": { ... } } or CSV download. |
403 | The token user lacks view permission or purchase orders are disabled. | { "message": string } |
422 | The query string failed validation or q was shorter than 2 characters. | Laravel validation JSON or { "message": string }. |
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/purchase-orders/{id} |
| Permission | Same permission and visibility rules as List purchase orders. |
| CSV behavior | format=csv streams one UTF-8 BOM row whose data_json column matches the JSON data payload. |
| Success response | 200 with one PurchaseOrderDetail object. |
PurchaseOrderLine object| Field | Type | Description |
|---|---|---|
id, product_id, variation_id | integer | Identifiers for the purchase-order line and linked catalog records. |
quantity, po_quantity_purchased, quantity_remaining | number | Ordered quantity, already purchased quantity, and remaining quantity. |
pp_without_discount, discount_percent, purchase_price, purchase_price_inc_tax, item_tax | number | null | Line pricing and tax fields. |
line_tax | object | null | Optional tax summary with id, name, and amount. |
purchase_requisition_line_id | integer | null | Optional linked purchase-requisition line id. |
product_name, sub_sku | string | null | Resolved product display fields. |
PurchaseOrderDetail object| Field | Type | Description |
|---|---|---|
id, ref_no | integer | string | Purchase-order identifiers. |
document | string | null | Stored document filename or path. |
transaction_date, delivery_date | string | null | ISO-8601 order timestamps. |
status, shipping_status | string | null | Order workflow and shipping status. |
exchange_rate, final_total, total_before_tax, tax_amount, discount_amount, shipping_charges | number | null | Order totals and exchange-rate values. |
discount_type, shipping_details, additional_notes, pay_term_type, shipping_address, delivered_to | string | null | Saved order metadata fields. |
pay_term_number | number | null | Payment-term number. |
purchase_requisition_ids | array<integer> | Linked requisition ids. |
order_tax | object | null | Order-level tax summary with id, name, amount, and is_tax_group. |
contact | object | null | Contact summary with id, name, mobile, email, contact_id, and supplier_business_name. |
location | object | null | Business-location summary with id and name. |
lines | array<PurchaseOrderLine> | Order lines sorted by line id. |
| Field | Type | Description |
|---|---|---|
data | PurchaseOrderDetail | The requested purchase-order payload. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The purchase order was returned successfully. | { "data": { ... } } or CSV download. |
403 | The token user lacks view permission or purchase orders are disabled. | { "message": string } |
404 | The purchase-order id does not exist in the visible query. | { "message": "Not found" } |
422 | The query string failed validation. | Laravel validation JSON. |
| Property | Value |
|---|---|
| Method | POST |
| Path | /api/v1/integration/purchase-orders |
| Permission | purchase_order.create. |
| Feature and subscription | Purchase orders must be enabled and the business must have an active subscription. |
| Location rule | location_id must belong to the business. The controller does not separately enforce permitted-location membership on create. |
| Multipart behavior | The endpoint accepts optional document upload and also passes shipping_documents through media upload when sent. |
| Success response | 201 with the created id and reference number. |
| Field | Type | Required | Description |
|---|---|---|---|
contact_id | integer | Yes | Business contact id used on the order. |
location_id | integer | Yes | Business location id. |
transaction_date | string | Yes | Order date accepted by Laravel date validation. |
total_before_tax, final_total | number | Yes | Order totals before and after tax. |
exchange_rate | number | null | No | Optional exchange rate. Defaults to 1. |
ref_no | string | null | No | Optional order reference number. |
discount_type | string | null | No | fixed or percentage. |
discount_amount, tax_amount, shipping_charges | number | null | No | Optional order-level financial adjustments. |
tax_id | integer | null | No | Optional tax id. |
shipping_details, additional_notes, shipping_address, shipping_status, delivered_to | string | null | No | Optional shipping and notes fields. |
delivery_date | string | null | No | Optional delivery date. |
pay_term_number | number | null | No | Optional payment-term number. |
pay_term_type | string | null | No | days or months. |
purchase_requisition_ids | array<integer> | null | No | Optional linked purchase-requisition ids whose status should be refreshed. |
purchases | array<PurchaseOrderLineInput> | Yes | At least one purchase-order line. |
PurchaseOrderLineInput object| Field | Type | Required | Description |
|---|---|---|---|
product_id, variation_id | integer | Yes | Product and variation ids. |
quantity, pp_without_discount, purchase_price, purchase_price_inc_tax, item_tax | number | Yes | Required quantity and pricing fields. |
discount_percent | number | null | No | Optional line discount percentage. |
purchase_line_tax_id, sub_unit_id, product_unit_id, purchase_requisition_line_id | integer | null | No | Optional tax, unit, and requisition-link ids. |
secondary_unit_quantity | number | null | No | Optional secondary-unit quantity. |
lot_number | string | null | No | Optional lot number. |
mfg_date, exp_date | string | null | No | Optional manufacturing and expiry dates. |
| Field | Type | Description |
|---|---|---|
message | string | Localized create-success message. |
data.id | integer | The created purchase-order id. |
data.ref_no | string | null | The saved reference number. |
| Status | When it happens | Response shape |
|---|---|---|
201 | The purchase order was created successfully. | { "message": string, "data": { "id": integer, "ref_no": string | null } } |
402 | The business subscription is inactive. | { "message": string } |
403 | The token user lacks create permission or purchase orders are disabled. | { "message": string } |
422 | The request body failed validation. | Laravel validation JSON. |
500 | The purchase-order create transaction failed unexpectedly. | { "message": "something_went_wrong" } |
| Property | Value |
|---|---|
| Method | PUT or PATCH |
| Path | /api/v1/integration/purchase-orders/{id} |
| Permission | purchase_order.update. |
| Feature and subscription | Purchase orders must be enabled and the business must have an active subscription. |
| Lookup scope | The action loads the order by business and type = purchase_order. It does not reapply the list visibility rules on update. |
| Request body | Same schema as Create purchase order, plus optional purchases.*.purchase_line_id for existing rows. location_id is not accepted on update. |
| Success response | 200 with the updated id and reference number. |
| Field | Type | Required | Description |
|---|---|---|---|
purchases.*.purchase_line_id | integer | No | Existing purchase-order line id to update in place. |
| Field | Type | Description |
|---|---|---|
message | string | Localized update-success message. |
data.id | integer | The updated purchase-order id. |
data.ref_no | string | null | The saved reference number. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The purchase order was updated successfully. | { "message": string, "data": { "id": integer, "ref_no": string | null } } |
402 | The business subscription is inactive. | { "message": string } |
403 | The token user lacks update permission or purchase orders are disabled. | { "message": string } |
404 | No purchase order with that id exists for the business. | { "message": "Not found" } |
422 | The request body failed validation. | Laravel validation JSON. |
500 | The purchase-order update failed unexpectedly. | { "message": string } |
| Property | Value |
|---|---|
| Method | DELETE |
| Path | /api/v1/integration/purchase-orders/{id} |
| Permission | purchase_order.delete. |
| Feature requirement | Purchase orders must be enabled for the business. |
| Lookup scope | The action loads the order by business and type = purchase_order. It does not reapply the list visibility rules on delete. |
| Delete behavior | Clears purchase_order_line_id on linked purchase lines, writes an activity log entry, then deletes the order. |
| Success response | 200 with the deleted id. |
| Field | Type | Description |
|---|---|---|
message | string | Localized delete-success message. |
data.id | integer | The deleted purchase-order id. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The purchase order was deleted successfully. | { "message": string, "data": { "id": integer } } |
403 | The token user lacks delete permission or purchase orders are disabled. | { "message": string } |
404 | No purchase order with that id exists for the business. | { "message": "Not found" } |
500 | The purchase-order delete failed unexpectedly. | { "message": string } |
| Property | Value |
|---|---|
| Method | PUT or PATCH |
| Path | /api/v1/integration/purchase-orders/{id}/status |
| Permission | Business admin only. |
| Feature requirement | Purchase orders must be enabled for the business. |
| Lookup scope | The action loads the order by business and type = purchase_order. It does not reapply the list visibility rules on status updates. |
| Allowed statuses | ordered, partial, or completed. |
| Success response | 200 with the updated id and status. |
| Field | Type | Required | Description |
|---|---|---|---|
status | string | Yes | New order status: ordered, partial, or completed. |
| Field | Type | Description |
|---|---|---|
message | string | Localized success message. |
data.id | integer | The purchase-order id. |
data.status | string | The saved order status. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The purchase-order status was updated successfully. | { "message": string, "data": { "id": integer, "status": string } } |
403 | The token user is not a business admin or purchase orders are disabled. | { "message": string } |
404 | No purchase order with that id exists for the business. | { "message": "Not found" } |
422 | The request body failed validation. | Laravel validation JSON. |
Lists purchase transactions that are visible to the token user.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/purchases |
| Permission | At least one of purchase.view, purchase.create, or view_own_purchase. |
| Visibility | Rows are limited to permitted locations. When the token user lacks purchase.view but has view_own_purchase, only purchases created by that user are returned. |
| Overdue filter | payment_status=overdue narrows to due/partial purchases whose pay-term deadline is before the current date. |
| CSV behavior | format=csv streams all matching rows with a UTF-8 BOM and ignores page and per_page. Nested contact and location objects are JSON-encoded in CSV cells. |
| Success response | 200 with paginated PurchaseRow rows. |
| Parameter | Type | Required | Description |
|---|---|---|---|
per_page, page | integer | No | Pagination controls. per_page accepts 1 to 100 and defaults to 20. |
location_id, contact_id | integer | No | Optional filters for location and supplier/contact id. |
start_date, end_date | string | No | Optional Y-m-d bounds on transaction_date. The filter is only applied when both values are present. |
payment_status | string | No | paid, due, partial, or overdue. |
status | string | No | received, pending, or ordered. |
q | string | No | Minimum 2 characters when sent. Matches reference number, invoice number, document, numeric id, and linked supplier fields. |
format | string | No | json (default) or csv. |
PurchaseRow object| Field | Type | Description |
|---|---|---|
id | integer | Purchase transaction id. |
ref_no, document | string | null | Stored reference and document fields. |
transaction_date | string | null | ISO-8601 purchase timestamp. |
status, payment_status | string | null | Purchase workflow and payment state. |
final_total, total_before_tax, tax_amount, discount_amount, total_paid | number | null | Purchase totals from the transaction header and payment subquery. |
contact | object | null | Supplier summary with id, name, mobile, contact_id, and supplier_business_name. |
location | object | null | Business-location summary with id and name. |
| Field | Type | Description |
|---|---|---|
data | array<PurchaseRow> | The current result page. |
meta.current_page, meta.last_page, meta.per_page, meta.total | integer | Laravel paginator metadata. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The purchase list was returned successfully. | { "data": [...], "meta": { ... } } or CSV download. |
403 | The token user lacks purchase-list access. | { "message": string } |
422 | The query string failed validation or q was shorter than 2 characters. | Laravel validation JSON or { "message": string }. |
Checks whether a supplier reference number is available for this business.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/purchases/check-ref-number |
| Permission | purchase.create or purchase.update. |
| Behavior | If ref_no is blank or contact_id is missing, the endpoint returns available = true without running the duplicate query. |
| Success response | 200 with an availability payload. |
| Parameter | Type | Required | Description |
|---|---|---|---|
ref_no | string | No | Supplier reference number to check. |
contact_id | integer | No | Supplier/contact id for the uniqueness check. |
exclude_transaction_id | integer | No | Optional purchase id to ignore when editing an existing row. |
| Field | Type | Description |
|---|---|---|
data.available | boolean | Whether the reference is available for the given supplier/contact. |
data.ref_no | string | null | Echoes the trimmed reference value. |
data.contact_id | integer | null | Echoes the contact id used for the lookup. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The availability check completed successfully. | { "data": { "available": boolean, "ref_no": string | null, "contact_id": integer | null } } |
403 | The token user lacks create/update purchase permission. | { "message": string } |
422 | The query string failed validation. | Laravel validation JSON. |
Returns one visible purchase with line and payment details.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/purchases/{id} |
| Permission | Same permission and visibility rules as List purchases. |
| CSV behavior | format=csv streams one UTF-8 BOM row whose data_json column matches the JSON data payload. |
| Success response | 200 with one PurchaseDetail object. |
PurchaseLine object| Field | Type | Description |
|---|---|---|
id, product_id, variation_id | integer | Identifiers for the purchase line and linked catalog records. |
product_label, sub_sku | string | null | Resolved product display label and variation sub-SKU. |
quantity | number | Purchased quantity. |
purchase_price, purchase_price_inc_tax, pp_without_discount, discount_percent, item_tax | number | null | Line pricing and tax fields. |
line_tax | object | null | Optional tax summary with id, name, and amount. |
PurchasePaymentSummary object| Field | Type | Description |
|---|---|---|
id | integer | Payment-line id. |
amount | number | null | Stored payment amount. |
method | string | null | Payment method key. |
paid_on | string | null | ISO-8601 payment timestamp. |
payment_ref_no | string | null | Generated payment reference number. |
note | string | null | Optional payment note. |
is_return | boolean | Whether the payment is a return/negative payment row. |
PurchaseDetail object| Field | Type | Description |
|---|---|---|
id, ref_no | integer | string | Purchase identifiers. |
document | string | null | Stored document filename or path. |
transaction_date | string | null | ISO-8601 purchase timestamp. |
status, payment_status | string | null | Purchase workflow and payment state. |
final_total, total_before_tax, tax_amount, discount_amount, shipping_charges, total_paid | number | null | Purchase totals from the header and payment lines. |
additional_notes, staff_note | string | null | Saved notes. |
purchase_order_ids | array<integer> | Linked purchase-order ids. |
contact | object | null | Supplier summary with id, name, mobile, email, contact_id, and supplier_business_name. |
location | object | null | Business-location summary with id and name. |
order_tax | object | null | Order-level tax summary with id, name, amount, and is_tax_group. |
lines | array<PurchaseLine> | Purchase lines sorted by line id. |
payments | array<PurchasePaymentSummary> | Payment lines linked to the purchase. |
| Field | Type | Description |
|---|---|---|
data | PurchaseDetail | The requested purchase payload. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The purchase was returned successfully. | { "data": { ... } } or CSV download. |
403 | The token user lacks purchase-list access. | { "message": string } |
404 | The purchase id does not exist in the visible query. | { "message": "Not found" } |
422 | The query string failed validation. | Laravel validation JSON. |
Creates a new purchase and can optionally post initial payment lines in the same request.
| Property | Value |
|---|---|
| Method | POST |
| Path | /api/v1/integration/purchases |
| Permission | purchase.create. |
| Subscription | The business must have an active subscription. |
| Location rule | location_id must belong to the business. The controller does not separately enforce permitted-location membership on create. |
| Create behavior | The controller creates type = purchase, initializes payment_status = due, updates the business purchase exchange rate, creates purchase lines, optionally creates payment lines, refreshes linked purchase-order status, and adjusts stock over-selling. |
| Upload rule | The request may include multipart document; its size limit follows the configured document size limit. |
| Success response | 201 with the created id and reference number. |
| Field | Type | Required | Description |
|---|---|---|---|
contact_id | integer | Yes | Business contact id used on the purchase. |
location_id | integer | Yes | Business location id. |
status | string | Yes | received, pending, or ordered. |
transaction_date | string | Yes | Purchase date accepted by Laravel date validation. |
total_before_tax, final_total | number | Yes | Purchase totals before and after tax. |
exchange_rate | number | null | No | Optional exchange rate. Defaults to 1. |
ref_no | string | null | No | Optional supplier reference number. |
discount_type | string | null | No | fixed or percentage. |
discount_amount, tax_amount, shipping_charges | number | null | No | Optional financial adjustments. |
tax_id | integer | null | No | Optional business tax-rate id. |
shipping_details, additional_notes | string | null | No | Optional notes and shipping text. |
pay_term_number | number | null | No | Optional pay-term number. |
pay_term_type | string | null | No | days or months. |
purchase_order_ids | array<integer> | null | No | Optional linked purchase-order ids. |
custom_field_1 ... custom_field_4 | string | null | No | Optional custom purchase fields. |
shipping_custom_field_1 ... shipping_custom_field_5 | string | null | No | Optional shipping custom fields. |
additional_expense_key_1 ... additional_expense_key_4 | string | null | No | Optional additional-expense labels. |
additional_expense_value_1 ... additional_expense_value_4 | number | null | No | Optional additional-expense amounts. |
payment | array<PurchaseCreatePaymentInput> | null | No | Optional initial payment rows created with the purchase. |
document | file | null | No | Optional uploaded document. |
purchases | array<PurchaseLineInput> | Yes | At least one purchase line. |
PurchaseLineInput object| Field | Type | Required | Description |
|---|---|---|---|
product_id, variation_id | integer | Yes | Product and variation ids. |
quantity, pp_without_discount, purchase_price, purchase_price_inc_tax, item_tax | number | Yes | Required quantity and pricing fields. |
discount_percent | number | null | No | Optional line discount percentage. |
purchase_line_tax_id, sub_unit_id, product_unit_id, purchase_order_line_id | integer | null | No | Optional line tax, unit, and purchase-order-link ids. |
secondary_unit_quantity | number | null | No | Optional secondary-unit quantity. |
lot_number | string | null | No | Optional lot number. |
mfg_date, exp_date | string | null | No | Optional manufacturing and expiry dates. |
PurchaseCreatePaymentInput object| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Payment amount. |
method | string | Yes | Payment method key. |
paid_on | string | null | No | Optional payment date. |
account_id | integer | null | No | Optional account id. |
note | string | null | No | Optional payment note. |
is_return | boolean | null | No | Optional return-payment flag. |
| Field | Type | Description |
|---|---|---|
message | string | Localized create-success message. |
data.id | integer | The created purchase id. |
data.ref_no | string | null | The saved supplier reference number. |
| Status | When it happens | Response shape |
|---|---|---|
201 | The purchase was created successfully. | { "message": string, "data": { "id": integer, "ref_no": string | null } } |
402 | The business subscription is inactive. | { "message": string } |
403 | The token user lacks create permission. | { "message": string } |
422 | The request body failed validation. | Laravel validation JSON. |
500 | The purchase create transaction failed unexpectedly. | { "message": "something_went_wrong" } |
Updates an existing visible purchase and rewrites its purchase-line set.
| Property | Value |
|---|---|
| Method | PUT or PATCH |
| Path | /api/v1/integration/purchases/{id} |
| Permission | purchase.update. |
| Subscription | The business must have an active subscription. |
| Visibility | The target purchase is loaded through the same permitted-location and own/all visibility query used by list and show. |
| Edit blockers | The purchase cannot be updated when a linked return exists or when it falls outside the business edit window defined by transaction_edit_days. |
| Request body | Reuses the create schema except location_id and the optional initial payment array are not accepted on update. Existing lines may send purchases.*.purchase_line_id. |
| Success response | 200 with the updated id and reference number. |
| Field | Type | Required | Description |
|---|---|---|---|
purchases.*.purchase_line_id | integer | No | Existing purchase-line id to update in place. |
| Field | Type | Description |
|---|---|---|
message | string | Localized update-success message. |
data.id | integer | The updated purchase id. |
data.ref_no | string | null | The saved supplier reference number. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The purchase was updated successfully. | { "message": string, "data": { "id": integer, "ref_no": string | null } } |
402 | The business subscription is inactive. | { "message": string } |
403 | The token user lacks update permission. | { "message": string } |
404 | The purchase id does not exist in the visible query. | { "message": "Not found" } |
422 | A linked return exists, the purchase is outside the allowed edit window, or the request body failed validation. | Laravel validation JSON or { "message": string }. |
500 | The purchase update transaction failed unexpectedly. | { "message": string } |
Records a payment against a visible purchase transaction.
| Property | Value |
|---|---|
| Method | POST |
| Path | /api/v1/integration/purchases/{id}/payments |
| Permission | purchase.payments plus the same purchase visibility required by List purchases. |
| Demo and subscription | Returns 403 in demo environments and 402 when the business subscription is inactive. |
| Payment-method rule | method must be one of the payment methods currently allowed for the purchase location. |
| Advance rule | When method = advance, the payment amount cannot exceed the linked contact balance. |
| Related endpoints | Created payment rows can later be managed through the generic /api/v1/integration/transaction-payments/{id} endpoints. |
| Success response | 201 with the created payment id and refreshed purchase payment status. |
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Payment amount. Minimum 0.01. |
method | string | Yes | Payment method key. |
paid_on | string | null | No | Optional payment date. Defaults to the current timestamp when omitted. |
note | string | null | No | Optional payment note up to 1000 characters. |
account_id | integer | null | No | Optional account id. Ignored when method = advance. |
card_number, card_holder_name, card_transaction_number, card_type, card_month, card_year, card_security | string | null | No | Optional card-payment metadata. |
cheque_number | string | null | No | Optional cheque number. |
bank_account_number | string | null | No | Optional bank-account reference. |
transaction_no_1, transaction_no_2, transaction_no_3 | string | null | No | Optional transaction references used by custom_pay_1, custom_pay_2, and custom_pay_3. |
| Field | Type | Description |
|---|---|---|
message | string | Localized payment-added message. |
data.transaction_id, data.payment_id | integer | Purchase id and created payment id. |
data.payment_ref_no | string | null | Generated payment reference number. |
data.payment_status | string | null | Refreshed purchase payment status after the insert. |
data.amount | number | Stored payment amount. |
data.method | string | null | Stored payment method. |
data.paid_on | string | null | ISO-8601 payment timestamp. |
| Status | When it happens | Response shape |
|---|---|---|
201 | The payment was recorded successfully. | { "message": string, "data": { ... } } |
402 | The business subscription is inactive. | { "message": string } |
403 | Demo mode is active or the token user lacks purchase.payments. | { "message": string } |
404 | The purchase id does not exist in the visible query. | { "message": "Not found" } |
422 | The purchase is already fully paid, the request body failed validation, the payment method is invalid for the location, or an advance payment exceeds the contact balance. | Laravel validation JSON or { "message": string }. |
500 | The purchase payment transaction failed unexpectedly. | { "message": "something_went_wrong" } |
Deletes a visible purchase and reverses stock-side effects when required.
| Property | Value |
|---|---|
| Method | DELETE |
| Path | /api/v1/integration/purchases/{id} |
| Permission | purchase.delete. |
| Visibility | The target purchase is loaded through the same permitted-location and own/all visibility query used by list and show. |
| Delete blockers | The purchase cannot be deleted when a linked return exists. If lot tracking is enabled, deletion is also blocked when the lot has already been used in sales. |
| Stock behavior | For received purchases the controller reverses stock quantities, deletes purchase lines, updates purchase-sell mappings, removes account transactions, and dispatches the purchase modified event. |
| Success response | 200 with the deleted id. |
| Field | Type | Description |
|---|---|---|
message | string | Localized delete-success message. |
data.id | integer | The deleted purchase id. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The purchase was deleted successfully. | { "message": string, "data": { "id": integer } } |
403 | The token user lacks delete permission. | { "message": string } |
404 | The purchase id does not exist in the visible query. | { "message": "Not found" } |
422 | A linked return exists or a lot-number constraint blocks deletion. | { "message": string } |
500 | The purchase delete transaction failed unexpectedly. | { "message": string } |
Lists purchase-return transactions for the current business.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/purchase-returns |
| Permission | purchase.view or purchase.create. |
| Visibility | Rows are limited to permitted locations. Unlike purchase list visibility, this endpoint does not apply an own-purchase filter. |
| Search behavior | The search also matches the parent purchase reference number when the return is linked to a purchase. |
| CSV behavior | format=csv streams all matching rows with a UTF-8 BOM and ignores page and per_page. |
| Success response | 200 with paginated PurchaseReturnRow rows. |
| Parameter | Type | Required | Description |
|---|---|---|---|
per_page, page | integer | No | Pagination controls. per_page accepts 1 to 100 and defaults to 20. |
location_id, contact_id | integer | No | Optional location and supplier/contact filters. |
start_date, end_date | string | No | Optional Y-m-d bounds on transaction_date. The filter is only applied when both values are present. |
q | string | No | Minimum 2 characters when sent. Matches ref, document, numeric id, supplier fields, and linked parent purchase ref. |
format | string | No | json (default) or csv. |
PurchaseReturnRow object| Field | Type | Description |
|---|---|---|
id | integer | Purchase-return transaction id. |
ref_no, document | string | null | Stored reference and document fields. |
transaction_date | string | null | ISO-8601 return timestamp. |
status, payment_status | string | null | Return workflow and payment state. |
final_total, total_before_tax, tax_amount, total_paid | number | null | Return totals from the header and payment subquery. |
return_parent_id | integer | null | Linked parent purchase id when this return was created against a purchase. |
parent_purchase | object | null | Parent purchase summary with id and ref_no. |
contact | object | null | Supplier summary with id, name, mobile, contact_id, and supplier_business_name. |
location | object | null | Business-location summary with id and name. |
| Field | Type | Description |
|---|---|---|
data | array<PurchaseReturnRow> | The current result page. |
meta.current_page, meta.last_page, meta.per_page, meta.total | integer | Laravel paginator metadata. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The purchase-return list was returned successfully. | { "data": [...], "meta": { ... } } or CSV download. |
403 | The token user lacks purchase-return list access. | { "message": string } |
422 | The query string failed validation or q was shorter than 2 characters. | Laravel validation JSON or { "message": string }. |
Returns one purchase return by id, including parent-linked or standalone return lines.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/purchase-returns/{id} |
| Permission | Same permission and visibility rules as List purchase returns. |
| Line source | Linked returns read lines from the parent purchase where quantity_returned > 0. Standalone combined returns read lines from the return itself. |
| CSV behavior | format=csv streams one UTF-8 BOM row whose data_json column matches the JSON data payload. |
| Success response | 200 with one PurchaseReturnDetail object. |
PurchaseReturnLine object| Field | Type | Description |
|---|---|---|
purchase_line_id, product_id, variation_id | integer | Identifiers for the source purchase line and linked catalog records. |
product_label, sub_sku | string | null | Resolved product display label and variation sub-SKU. |
quantity_returned | number | Returned quantity for the line. |
purchase_price_inc_tax, line_total | number | null | Unit price including tax and computed line total. |
source | string | parent_purchase for linked returns or purchase_return for standalone combined returns. |
PurchaseReturnDetail object| Field | Type | Description |
|---|---|---|
id, ref_no | integer | string | Purchase-return identifiers. |
document | string | null | Stored document filename or path. |
transaction_date | string | null | ISO-8601 return timestamp. |
status, payment_status | string | null | Return workflow and payment state. |
final_total, total_before_tax, tax_amount, total_paid | number | null | Return totals from the header and payment lines. |
return_parent_id | integer | null | Linked parent purchase id when present. |
parent_purchase | object | null | Parent purchase summary with id and ref_no. |
contact | object | null | Supplier summary with id, name, mobile, email, contact_id, and supplier_business_name. |
location | object | null | Business-location summary with id and name. |
order_tax | object | null | Return tax summary with id, name, amount, and is_tax_group. |
lines | array<PurchaseReturnLine> | Returned lines currently included in the payload. |
payments | array<PurchasePaymentSummary> | Payment lines linked to the return. |
| Field | Type | Description |
|---|---|---|
data | PurchaseReturnDetail | The requested purchase-return payload. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The purchase return was returned successfully. | { "data": { ... } } or CSV download. |
403 | The token user lacks purchase-return list access. | { "message": string } |
404 | The purchase-return id does not exist in the visible query. | { "message": "Not found" } |
422 | The query string failed validation. | Laravel validation JSON. |
This anchor documents both return-creation flows: returns against an existing purchase and standalone combined returns.
| Property | Value |
|---|---|
| Method | POST |
| Path | /api/v1/integration/purchase-returns |
| Permission | purchase.update. |
| Demo and subscription | Returns 403 in demo environments and 402 when the business subscription is inactive. |
| Parent purchase access | The target purchase must belong to the business, be type purchase, be in a permitted location, and when the token user only has view_own_purchase, the purchase must have been created by that user. |
| Behavior | Creates or updates the single purchase_return row linked to that purchase, updates quantity_returned on parent purchase lines, adjusts stock, and recalculates the return totals. |
| Success response | 201 with the return id, ref number, and parent purchase id. |
| Field | Type | Required | Description |
|---|---|---|---|
transaction_id | integer | Yes | Parent purchase transaction id. |
returns | array<PurchaseReturnInputLine> | Yes | Return quantities keyed by purchase line id. |
tax_amount | number | null | No | Optional return tax amount. |
ref_no | string | null | No | Optional custom return reference number. |
PurchaseReturnInputLine object| Field | Type | Required | Description |
|---|---|---|---|
purchase_line_id | integer | Yes | Purchase-line id that belongs to the given parent purchase. |
quantity | number | Yes | Returned quantity for that line. The controller applies sub-unit multipliers where configured. |
| Field | Type | Description |
|---|---|---|
message | string | Localized return-added message. |
data.id | integer | The purchase-return id. |
data.ref_no | string | null | The saved return reference number. |
data.parent_purchase_id | integer | The parent purchase id. |
| Status | When it happens | Response shape |
|---|---|---|
201 | The purchase return was created or updated successfully. | { "message": string, "data": { "id": integer, "ref_no": string | null, "parent_purchase_id": integer } } |
402 | The business subscription is inactive. | { "message": string } |
403 | Demo mode is active or the token user lacks purchase.update. | { "message": string } |
404 | The parent purchase id does not exist or is not accessible to the token user. | { "message": "Not found" } |
422 | The request body failed validation or a returns[].purchase_line_id does not belong to the given purchase. | Laravel validation JSON or { "message": string }. |
500 | The purchase-return create transaction failed unexpectedly. | { "message": "something_went_wrong" } |
| Property | Value |
|---|---|
| Method | POST |
| Path | /api/v1/integration/purchase-returns/combined |
| Permission | purchase.update. |
| Demo and subscription | Returns 403 in demo environments and 402 when the business subscription is inactive. |
| Location rule | location_id must belong to the business and be included in the token user's permitted locations. |
| Validation rule | Every products[].variation_id must belong to the stated product_id and to the current business. |
| Behavior | Creates a standalone purchase_return with no parent purchase, creates return lines on the return itself, and decreases stock immediately. |
| Success response | 201 with the return id and return_parent_id = null. |
| Field | Type | Required | Description |
|---|---|---|---|
location_id | integer | Yes | Business location id. |
transaction_date | string | Yes | Return date. |
final_total | number | Yes | Total return amount including tax. |
tax_amount | number | null | No | Optional return tax amount. |
tax_id | integer | null | No | Optional business tax-rate id. |
contact_id | integer | null | No | Optional contact id for the standalone return. |
ref_no | string | null | No | Optional custom return reference number. |
document | file | null | No | Optional uploaded document, up to 5 MB. |
products | array<CombinedPurchaseReturnLineInput> | Yes | At least one return line. |
CombinedPurchaseReturnLineInput object| Field | Type | Required | Description |
|---|---|---|---|
product_id, variation_id | integer | Yes | Product and variation ids. |
unit_price | number | Yes | Return unit price. |
quantity | number | Yes | Returned quantity. Minimum 0.0001. |
lot_number | string | null | No | Optional lot number. |
exp_date | string | null | No | Optional expiry date in Y-m-d format. |
| Field | Type | Description |
|---|---|---|
message | string | Localized return-added message. |
data.id | integer | The standalone purchase-return id. |
data.ref_no | string | null | The saved return reference number. |
data.return_parent_id | null | Always null for this standalone flow. |
| Status | When it happens | Response shape |
|---|---|---|
201 | The standalone purchase return was created successfully. | { "message": string, "data": { "id": integer, "ref_no": string | null, "return_parent_id": null } } |
402 | The business subscription is inactive. | { "message": string } |
403 | Demo mode is active or the token user lacks purchase.update. | { "message": string } |
404 | The chosen location is not allowed for the token user. | { "message": "Not found" } |
422 | The request body failed validation or a variation does not belong to the given product and business. | Laravel validation JSON or { "message": string }. |
500 | The standalone purchase-return create transaction failed unexpectedly. | { "message": "something_went_wrong" } |
Records a payment against a visible purchase return.
| Property | Value |
|---|---|
| Method | POST |
| Path | /api/v1/integration/purchase-returns/{id}/payments |
| Permission | purchase.payments plus the same return visibility required by List purchase returns. |
| Demo and subscription | Returns 403 in demo environments and 402 when the business subscription is inactive. |
| Payment-method rule | method must be one of the payment methods currently allowed for the return location. |
| Advance rule | When method = advance, the payment amount cannot exceed the linked contact balance. |
| Success response | 201 with the created payment id and refreshed return payment status. |
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Payment amount. Minimum 0.01. |
method | string | Yes | Payment method key. |
paid_on | string | null | No | Optional payment date. Defaults to the current timestamp when omitted. |
note | string | null | No | Optional payment note up to 1000 characters. |
account_id | integer | null | No | Optional account id. Ignored when method = advance. |
card_number, card_holder_name, card_transaction_number, card_type, card_month, card_year, card_security | string | null | No | Optional card-payment metadata. |
cheque_number | string | null | No | Optional cheque number. |
bank_account_number | string | null | No | Optional bank-account reference. |
transaction_no_1, transaction_no_2, transaction_no_3 | string | null | No | Optional transaction references used by custom_pay_1, custom_pay_2, and custom_pay_3. |
| Field | Type | Description |
|---|---|---|
message | string | Localized payment-added message. |
data.transaction_id, data.payment_id | integer | Return id and created payment id. |
data.payment_ref_no | string | null | Generated payment reference number. |
data.payment_status | string | null | Refreshed return payment status after the insert. |
data.amount | number | Stored payment amount. |
data.method | string | null | Stored payment method. |
data.paid_on | string | null | ISO-8601 payment timestamp. |
| Status | When it happens | Response shape |
|---|---|---|
201 | The payment was recorded successfully. | { "message": string, "data": { ... } } |
402 | The business subscription is inactive. | { "message": string } |
403 | Demo mode is active or the token user lacks purchase.payments. | { "message": string } |
404 | The purchase-return id does not exist in the visible query. | { "message": "Not found" } |
422 | The return is already fully paid, the request body failed validation, the payment method is invalid for the location, or an advance payment exceeds the contact balance. | Laravel validation JSON or { "message": string }. |
500 | The purchase-return payment transaction failed unexpectedly. | { "message": "something_went_wrong" } |
Deletes a visible purchase return and restores stock or parent purchase quantities as needed.
| Property | Value |
|---|---|
| Method | DELETE |
| Path | /api/v1/integration/purchase-returns/{id} |
| Permission | purchase.delete. |
| Demo mode | Returns 403 in demo environments. |
| Visibility | The target return is loaded through the same permitted-location query used by list and show. |
| Delete behavior | Standalone combined returns restore stock from the return's own lines. Parent-linked returns restore parent purchase quantities by resetting quantity_returned on the parent purchase lines. |
| Success response | 200 with the deleted id. |
| Field | Type | Description |
|---|---|---|
message | string | Localized delete-success message. |
data.id | integer | The deleted purchase-return id. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The purchase return was deleted successfully. | { "message": string, "data": { "id": integer } } |
403 | Demo mode is active or the token user lacks purchase.delete. | { "message": string } |
404 | The purchase-return id does not exist in the visible query. | { "message": "Not found" } |
500 | The purchase-return delete transaction failed unexpectedly. | { "message": "something_went_wrong" } |