# Create an invoice for a sales order

```http
POST https://secure.yoprint.com/v2/api/store/{team_slug}/v2/sales_order/{unique_key}/invoice
```

- API version: v2
- Operation ID: `v2_public_sales_order_invoice_create`
- Section: Invoices and payments › [Invoices](https://developer.yoprint.com/reference/invoices.md)
- Web page: https://developer.yoprint.com/reference/invoices/v2-sales-order-invoice-create/
- Authentication: `Authorization: Bearer YOUR_API_KEY`

Creates an invoice for an order, optionally with its lines in the same call.

The invoice starts with the order's billing and shipping address and issue date unless you send `billing_address_id`, `shipping_address_id` or `issue_date`. It gets the team's next invoice number.

Send `{}` for an empty invoice. Lines go in `item_actions`, in the same shape as the invoice line bulk operation: each entry has your `bid`, an `action` (`create`, `update` or `delete`), `cart_item_id` for updates and deletes, and a `payload`. Without `item_actions` the invoice is created empty. `tax_selection` (with both `expected_tax` and `desired_tax`) sets the invoice's tax as Set the tax of an invoice does; `resort_items: true` re-sorts the lines.

Send the header `X-Yoprint-Decimal-Format: exact-v1`. Without it, a save that would return an amount the old number format can't carry is refused with 422 `amount_out_of_range`.

The answer is the invoice (with its lines when you sent `item_actions`, `tax_selection` or `resort_items`). `{unique_key}` is the order's `id` (for example `V4keXwwxMQyXw7`) or its `unique_key` (`ord_…`).

## Permission

Any member of the team.

## Business rules

- Refused for Shopify orders: "Cannot create invoice for a Shopify-synced order."
- Refused when the order keeps its invoice up to date automatically: "You cannot create an invoice for an auto managed order." Turn that off with the `configure_order_auto_invoice_managed` action first.
- When `tax_selection` is sent it must contain both `expected_tax` and `desired_tax` (either may be null).

## Side effects

- Creates the invoice and its lines and refreshes the order for people who have it open in the web app; on a published sales order, Customer Portal pages the customer has open refresh too. Nothing is emailed.
- With `item_actions`, recalculates the order's invoiced totals and spreads its payments across the invoices again (money of a payment frozen by an open dispute or review stays where it is).

## Path parameters

- `team_slug` (string, required): Your team slug, shown as Team ID next to your API key in Profile. Example `your-team`.
- `unique_key` (string, required): The sales order's or quote's `id` (a hash id), or its `unique_key` (`ord_…`). The order number (`scoped_id`) does not work. Example `V4keXwwxMQyXw7`.

## Request body

Content type `application/json`, required.

Fields:

- `billing_address_id` (string | null, optional): The billing address id. Default: the order's.
- `issue_date` (string (date), optional): The invoice date (ISO 8601). Default: the order's issue date.
- `item_actions` (array of objects, optional): Invoice line operations, each with your own `bid`: `create` with `payload`; `update` with `cart_item_id` and `payload`; `delete` with `cart_item_id`.
  - Each item:
    - `action` (string, required): One of: `"update"`, `"create"`, `"delete"`.
    - `bid` (string, required)
    - `cart_item_id` (string, optional): For `update` and `delete`: the invoice line's id.
    - `payload` (object, optional): Required for `create` and `update`: one invoice line, the same body as Add a line to an invoice. Three common shapes. A sales order line: `{ "product": { "type": "order_item", "properties": { "id": "<order line id>" } }, "quantity": { "type": "unit", "total": { "value": 10, "dp": 0 } }, "tax": { "type": "taxable" }, "position": 100 }`. A shipping fee: `{ "product": { "type": "shipment_fee", "properties": { "name": "Shipping" } }, "pricing": { "type": "fee", "product": { "total": { "value": 1500, "dp": 2 } } }, "position": 200 }`. A percent discount: `{ "product": { "type": "invoice_discount", "properties": { "type": "percent", "rate": { "value": 10, "dp": 0 }, "applies_to_shipping": false } }, "position": 300 }`. Tax lines go in `tax_selection`, not here.
- `resort_items` (boolean, optional): true to re-sort the invoice's lines.
- `shipping_address_id` (string | null, optional): The shipping address id. Default: the order's.
- `tax_selection` (object, optional): The invoice's tax: send both `expected_tax` (the tax line you expect now, or null) and `desired_tax` (the tax you want, or null).
  - `desired_tax` (object | null, optional)
    - `properties` (object, optional): The tax's settings: for `user_tax`, `{ "id": tax preset id }`.
    - `type` (string, optional): One of: `"auto_tax"`, `"user_tax"`, `"override_tax"`.
  - `expected_tax` (object | null, optional)
    - `id` (string, optional): The current tax line's id.
    - `lock_version` (integer, optional): Minimum 0.

### Example request

```bash
curl -X POST 'https://secure.yoprint.com/v2/api/store/your-team/v2/sales_order/V4keXwwxMQyXw7/invoice' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'X-Yoprint-Decimal-Format: exact-v1' \
  -H 'Content-Type: application/json' \
  -d '{
    "issue_date": "2026-11-03",
    "item_actions": [
      {
        "action": "create",
        "bid": "i1",
        "payload": {
          "product": {
            "type": "order_item",
            "properties": {
              "id": "Owr88lhAk28aqL"
            }
          },
          "quantity": {
            "type": "unit",
            "total": {
              "value": 10,
              "dp": 0
            }
          },
          "tax": {
            "type": "taxable"
          },
          "position": 100
        }
      },
      {
        "action": "create",
        "bid": "i2",
        "payload": {
          "product": {
            "type": "order_item",
            "properties": {
              "id": "HF0BYICEJ09rG2"
            }
          },
          "quantity": {
            "type": "unit",
            "total": {
              "value": 12,
              "dp": 0
            }
          },
          "tax": {
            "type": "taxable"
          },
          "position": 200
        }
      }
    ],
    "tax_selection": {
      "desired_tax": {
        "properties": {
          "id": "9jbPSC9ybBqXYJ"
        },
        "type": "user_tax"
      },
      "expected_tax": null
    }
  }'
```

## Response

`200` Successful response.

Fields:

- `data` (object, required): An invoice.
  - `id` (string, required)
  - `order_id` (string, required)
  - `total` (decimal pair, required): Invoice total. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 125000, "dp": 4 }` is 12.50). Kept with 4 decimal places; an amount never set can come as `{ "value": 0, "dp": 0 }`. Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
  - `auto_invoice_managed` (boolean, optional)
  - `billing_address_id` (string | null, optional)
  - `calc_deposit_total` (integer | number | string | null, optional): The order's deposit amount. Thousandths, an older encoding: `12500` is 12.50. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (`1234.5` is 1.2345), or as a string (`"1234.5"`) when you send `X-Yoprint-Decimal-Format: exact-v1`. See [Money and quantities](/concepts/money-and-quantities/).
  - `cart_item_subtotal` (decimal pair, optional): Lines subtotal. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 125000, "dp": 4 }` is 12.50). Kept with 4 decimal places; an amount never set can come as `{ "value": 0, "dp": 0 }`. Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
  - `created_at` (string (date-time) | null, optional): Created. ISO 8601 with offset, or null.
  - `customer` (object | null, optional): The customer record, with the same fields as Get a customer (`GET /v1/api/store/{team_slug}/customer/{hash_id}`); the main ones are listed here.
    - `default_payment_term_id` (string | null, optional)
    - `default_pricing_group_id` (string | null, optional)
    - `id` (string, optional)
    - `is_external` (boolean, optional): true for a customer synced from an integration.
    - `name` (string, optional)
    - `tax_exempt` (boolean, optional)
  - `discount_subtotal` (decimal pair, optional): Discount. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 125000, "dp": 4 }` is 12.50). Kept with 4 decimal places; an amount never set can come as `{ "value": 0, "dp": 0 }`. Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
  - `is_paid` (boolean, optional)
  - `is_valid` (boolean, optional)
  - `issue_date` (string (date-time) | null, optional): Issue date. ISO 8601 with offset, or null.
  - `items` (array of objects, optional): The invoice lines.
  - `order_scoped_id` (string, optional)
  - `paid_amount` (integer | number | string | null, optional): Paid on this invoice. Thousandths, an older encoding: `12500` is 12.50. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (`1234.5` is 1.2345), or as a string (`"1234.5"`) when you send `X-Yoprint-Decimal-Format: exact-v1`. See [Money and quantities](/concepts/money-and-quantities/).
  - `pdf_url` (array of objects, optional): The invoice PDF links, despite the name: a list, first the standard invoice, then one per invoice PDF template the team has turned on.
    - Each item:
      - `description` (string, optional)
      - `label` (string, optional)
      - `object_type` (string, optional)
      - `url` (string, optional): Link to the PDF.
  - `scoped_id` (string | integer, optional): The invoice number, from the team's invoice counter. A new invoice created without lines answers it as a number (`6`); with lines, and in every later read, it is a string (`"6"`). Compare it as text.
  - `shipment_subtotal` (decimal pair, optional): Shipping. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 125000, "dp": 4 }` is 12.50). Kept with 4 decimal places; an amount never set can come as `{ "value": 0, "dp": 0 }`. Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
  - `shipment_tax_subtotal` (decimal pair, optional): Tax on shipping. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 125000, "dp": 4 }` is 12.50). Kept with 4 decimal places; an amount never set can come as `{ "value": 0, "dp": 0 }`. Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
  - `shipping_address_id` (string | null, optional)
  - `tax_subtotal` (decimal pair, optional): Tax. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 125000, "dp": 4 }` is 12.50). Kept with 4 decimal places; an amount never set can come as `{ "value": 0, "dp": 0 }`. Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
- `status` (integer, required)
- `success` (boolean, required)

Example response:

```json
{
  "data": {
    "id": "QRbvVd2ZKcvSTB",
    "is_paid": false,
    "issue_date": "2026-11-03T00:00:00+00:00",
    "items": [
      {
        "id": "Lbzkl5xay8Ep8O",
        "invoice_id": "QRbvVd2ZKcvSTB",
        "type": "order_item"
      },
      {
        "id": "lHgM83jCmkQAnD",
        "invoice_id": "QRbvVd2ZKcvSTB",
        "type": "order_item"
      },
      {
        "id": "aYaSY9SejbWJK9",
        "invoice_id": "QRbvVd2ZKcvSTB",
        "type": "user_tax"
      }
    ],
    "order_id": "V4keXwwxMQyXw7",
    "order_scoped_id": "SO1001",
    "paid_amount": 0,
    "scoped_id": "1001",
    "total": {
      "dp": 4,
      "value": 1350000
    }
  },
  "status": 200,
  "success": true
}
```

## Errors

- `401` The API key is missing or wrong (`unauthenticated`).
- `403` You are not allowed to do this. `api_access_not_in_plan`: your team's plan doesn't include API access (Pro and above, with an active subscription or trial), so every request answers this until the team upgrades. When your role lacks the permission the operation needs, the body is only `{"message": "Insufficient Permission"}`. Other refusals use the error envelope, with `unauthorized` or `user_is_not_authorized`.
- `404` Not found. `page_not_found`: the team slug is unknown, you are not a member of the team, or an id in the path is not one of this team's. `code: null`, with a message that starts "No query results for": there is no record with that id.
- `422` The request was refused and changed nothing. `validation_failed` lists what to fix in `error.fields`; other codes name a business rule, explained in `error.message`. A few checks answer with only `{"message": "..."}`. An operation that calls another service (a payment provider, a carrier, an accounting app) can answer `connect_timeout` ("Please retry again") when that service does not answer in time: the change may or may not have happened there, so read the record back before you retry.
  - When: A Shopify or auto-invoiced order, or an invalid line or tax selection. Fix: Read `error.message`; fix the named line by its `bid`.

## Related

- [Add, change and remove invoice lines at once](https://developer.yoprint.com/reference/invoice-line-items/v2-invoice-item-bulk.md): `POST /v2/api/store/{team_slug}/v2/invoice/{unique_key}/item/bulk`
- [Change the tax on an invoice](https://developer.yoprint.com/reference/invoices/v2-invoice-tax-selection-update.md): `PUT /v2/api/store/{team_slug}/v2/invoice/{unique_key}/tax-selection`
- [Get an invoice](https://developer.yoprint.com/reference/invoices/v2-invoice-show.md): `GET /v2/api/store/{team_slug}/v2/invoice/{unique_key}`
- [Run an action on a sales order or quote](https://developer.yoprint.com/reference/sales-orders/v2-order-single-action.md): `PUT /v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}/action/{action_name}`
