---
search:
  tags:
    - Sales
    - POST
seo:
  description: >-
    Admin or cashier. Prices the cart, freezes the branch rate, and writes the
    sale and… Reference for the POST /api/v1/sales endpoint in the PharmaFlow
    API.
sidebar:
  label: Create a sale
  badge: POST
title: Create a sale
type: openapi-operation
---
Admin or cashier. Prices the cart, freezes the branch rate, and writes the sale and stock. It is idempotent via the X-Idempotency-Key, so replaying a key returns the original sale.

`POST /api/v1/sales`

**Request body** (`application/json`, required)

- `items` (object[], required)
- `branch_id` (string)
- `payment_method` (string)
- `payment_lines` (object[] | null)
- `client_mutation_id` (string)
- `device_id` (string | null)
- `notes` (string)
- `customer_id` (string | null)
- `customer_name` (string)
- `coupon_code` (string | null)
- `global_discount_pct` (number | null)
- `fx_rate_to_base` (number | string | null)
- `tax_policy` (object)
- `patient_national_id` (string)
- `doctor_license` (string)

Request body example:

```json
{
  "items": [
    {
      "medicine_id": "string",
      "quantity": -9007199254740991,
      "unit_price": 0
    }
  ],
  "branch_id": "string",
  "payment_method": "string",
  "payment_lines": [
    {
      "method": "string",
      "amount": 0
    }
  ],
  "client_mutation_id": "string",
  "device_id": "string",
  "notes": "string",
  "customer_id": "string",
  "customer_name": "string",
  "coupon_code": "string",
  "global_discount_pct": 0,
  "fx_rate_to_base": 0,
  "tax_policy": {
    "enabled": true,
    "standard_rate": 0,
    "medicine_categories": {
      "property1": "string",
      "property2": "string"
    }
  },
  "patient_national_id": "string",
  "doctor_license": "string"
}
```

**Responses**

- `201` — Sale created, or the original sale for a replayed idempotency key
- `400` — The cart is empty, a payment method is unsupported, the discount is outside 0 to 100, a queued tax policy is stale, or the stock cannot cover the cart
- `401` — Not authenticated
- `403` — Access forbidden for this role
- `404` — Customer not found, coupon not found, or medicine not found
- `409` — A sale with this idempotency key is still processing, so the same key has to be retried later

Response example, 201:

```json
{
  "id": "string",
  "invoice_number": "string",
  "uuid": "string",
  "icv": -9007199254740991,
  "client_mutation_id": "string",
  "branch_id": "string",
  "user_id": "string",
  "subtotal_amount": "string",
  "vat_amount": "string",
  "coupon_discount": "string",
  "global_discount": "string",
  "total_amount": "string",
  "currency_code": "string",
  "fx_rate_to_base": "string",
  "base_currency_code": "string",
  "base_total_amount": "string",
  "coupon_id": "string",
  "customer_name": "string",
  "vat_breakdown": [
    {
      "rate": 0,
      "taxable_amount": 0,
      "vat_amount": 0
    }
  ],
  "payment_method": "string",
  "payment_lines": [
    {
      "method": "string",
      "amount": 0
    }
  ],
  "notes": "string",
  "sold_at": "string",
  "items": [
    {
      "id": "string",
      "medicine_id": "string",
      "batch_id": "string",
      "quantity": -9007199254740991,
      "unit_price": "string",
      "vat_rate": "string",
      "vat_amount": "string",
      "cost_at_sale": "string"
    }
  ]
}
```
