Creates a purchase. The purchase status comes from the **Default status for new purchases** setting in the system settings, and the status determines whether materials and products are added to stock immediately (see [Changing the Purchase Status](https://developers.controlata.com/hc/api-docs/articles/api-purchases-update-status)).

```
v1/purchases/add.php
```

## Parameters

| Field | Type | Required | Description |
|---|---|---|---|
| supplier_id | int | supplier_id or supplier_name | ID of an existing supplier |
| supplier_name | string | supplier_id or supplier_name | Supplier name. If there is none, a supplier is created from the supplier_ prefixed fields |
| materials | array | materials or products | Material lines |
| products | array | materials or products | Product lines |
| date_placed | string | No | Purchase order date. Default today |
| date_received | string | No | Received date |
| materials_storage_id | int | No | Materials storage location. Default from settings |
| products_storage_id | int | No | Products storage location. Default from settings |
| delivery_price | float | No | Delivery cost. Default 0 |
| discount | float | No | Discount. Default 0 |
| notes | string | No | Notes |

## Purchase lines

The purchase's lines are passed as two arrays: materials for materials and products for products. At least one line in one of the arrays is required.

| Field | Type | Required | Description |
|---|---|---|---|
| id | int | id or sku | Material or product ID |
| sku | string | id or sku | SKU. A product is also matched by its alternative SKUs |
| amount | float | Yes | Quantity |
| total | float | Yes | Line total (not the price per unit) |

Unlike sales, an unknown item is not skipped: the request is rejected with an error and the purchase is not created.

## Supplier

A supplier is specified in one of two ways:

* **supplier_id**: the ID of an existing supplier, e.g. from [v1/suppliers/add](https://developers.controlata.com/hc/api-docs/articles/api-suppliers-add);
* **supplier_name**: the supplier's name. If there is no supplier with that name, Controlata creates one together with the purchase from the fields below.

| Field | Type | Description |
|---|---|---|
| supplier_name | string | Name |
| supplier_email | string | Email |
| supplier_phone | string | Phone |
| supplier_address | string | Address |
| supplier_agreement | string | Agreement |
| supplier_manager_name | string | Manager |
| supplier_manager_post | string | Manager's position |
| supplier_notes | string | Supplier notes |

If both are passed, supplier_id is used and supplier_* are ignored. If a supplier with that name already exists, the purchase is linked to it and supplier_* are not updated.

## Example request

```
{
    "supplier_id": 45,
    "date_placed": "2026-10-06",
    "materials_storage_id": 3998,
    "materials": [
        {
            "sku": "M010",
            "amount": 0.2,
            "total": 26000
        },
        {
            "id": 2052,
            "amount": 10,
            "total": 4000
        }
    ],
    "delivery_price": 1500,
    "notes": "Invoice No. 118"
}
```

## Example response

```
{
    "success": true,
    "purchase_id": 29645,
    "status": 1
}
```

status holds the code of the status the purchase was created in.

## Errors

| Error | Reason |
|---|---|
| No supplier_name or supplier_id in input | No supplier was passed |
| Supplier not found or access denied | The supplier_id supplier was not found or is deleted |
| Supplier name must not be empty | Empty supplier_name |
| Supplier name must be a string | supplier_name is not a string |
| Storage ID not found | Storage location not found or deleted |
| Materials is not an array | materials is not an array (same for products) |
| No products or materials in input | No line was passed |
| Line 0 of materials must be an object | The line is not an object (instead of 0, the line number) |
| SKU or id not set for material 0 | The line has neither id nor sku |
| Material with SKU "M010" not found | The item was not found or is deleted |
| Amount must be greater than 0 for material with SKU "M010" | Quantity is missing, not a number or not above 0 after rounding |
| Total must be 0 or greater for material with SKU "M010" | The line total is missing, not a number or below 0 |
| Date is not a valid date in format YYYY-MM-DD | Invalid order or received date |
| Invalid delivery_price value. Must be a number, 0 or greater | Delivery cost is not a number or below 0 |
| Invalid discount value. Must be a number, 0 or greater | Discount is not a number or below 0 |

In line error texts, material becomes product for product lines, and instead of SKU "M010" there is id 2052 if the line was passed by id.
