> ## Documentation Index
> Fetch the complete documentation index at: https://docs.horizonpay.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a New Draft Invoice — Invoice AI REST API

> Create a new draft invoice with line items. Drafts have no invoice number and can be freely edited or deleted before you finalize them.

Creates a new invoice in `draft` status. A draft does not yet have an invoice number (`number` is `null`), is not visible to your customer, and can be modified or deleted at any time. To make the invoice official and send it to the customer, call [POST /api/v1/invoices/:id/finalize](/api-reference/invoices/finalize) after creation.

Line items can be ad-hoc (supply `description` and `unit_amount` directly) or catalog-based (supply a `price` ID to inherit description, rate, and tax rate from a saved price).

```http theme={null}
POST /api/v1/invoices
```

**Base URL:** `https://invoice.horizonpay.co`

***

## Authentication

<ParamField header="Authorization" type="string" required>
  Bearer token in the form `Bearer inv_live_…`. The token must carry the `invoices:write` scope.
</ParamField>

<ParamField header="Idempotency-Key" type="string" required>
  A unique key (UUID or any string up to 255 characters) you generate per logical create operation. If the request is retried with the same key, the original response is replayed and no second draft is created. The replayed response includes the header `Idempotent-Replayed: true`.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`.
</ParamField>

***

## Body Parameters

<ParamField body="customer" type="string" required>
  The `cus_…` public ID (or UUID) of an existing customer to attach to this invoice. The customer's address and contact details are snapshotted onto the invoice when it is finalized.
</ParamField>

<ParamField body="currency" type="string">
  Three-letter ISO 4217 currency code (e.g. `"USD"`, `"EUR"`, `"INR"`). Defaults to your account's configured currency. All `unit_amount` values in `items` must be expressed in the minor units of this currency.
</ParamField>

<ParamField body="collection_method" type="string">
  How you intend to collect payment. One of `"send_invoice"` (default) or `"charge_automatically"`.
</ParamField>

<ParamField body="issue_date" type="string">
  ISO 8601 date the invoice is issued (`YYYY-MM-DD`). Defaults to today if omitted.
</ParamField>

<ParamField body="due_date" type="string">
  ISO 8601 date payment is due (`YYYY-MM-DD`). Pass an empty string `""` to clear a previously set date.
</ParamField>

<ParamField body="days_until_due" type="integer">
  Convenience alternative to `due_date`. Number of days from `issue_date` until payment is due (0–365). Ignored if `due_date` is also supplied.
</ParamField>

<ParamField body="description" type="string">
  Notes displayed on the invoice body. Pass `null` or omit to leave blank.
</ParamField>

<ParamField body="footer" type="string">
  Footer text shown at the bottom of the invoice, typically used for payment terms. Pass `null` or omit to leave blank.
</ParamField>

<ParamField body="items" type="array">
  One or more line items to add to the invoice. At least one line item is required. Each element is an object with the fields below.

  <Expandable title="Line item fields">
    <ParamField body="items[].price" type="string">
      The `price_…` public ID of a catalog price. When supplied, `description`, `unit_amount`, and `tax_rate` are inherited from the price and its parent product — you do not need to repeat them. The price's currency must match the invoice `currency`.
    </ParamField>

    <ParamField body="items[].description" type="string" required>
      What you are billing for. Required for ad-hoc lines (those without a `price`). Optional when `price` is provided — the product name is used as the fallback.
    </ParamField>

    <ParamField body="items[].quantity" type="number">
      Number of units billed. Must be greater than zero. Defaults to `1`.
    </ParamField>

    <ParamField body="items[].unit" type="string">
      Unit of measure. One of: `NOS`, `PCS`, `KGS`, `GMS`, `LTR`, `MTR`, `SQF`, `SQM`, `HRS`, `DAY`, `MON`, `BOX`, `SET`, `OTH`. Defaults to `NOS`.
    </ParamField>

    <ParamField body="items[].unit_amount" type="integer" required>
      Price per unit in integer minor units (e.g. `5000` = \$50.00 USD). Required for ad-hoc lines. Omit when `price` is supplied — the price's `unit_amount` is used instead.
    </ParamField>

    <ParamField body="items[].discount_percent" type="number">
      Percentage discount applied to this line (0–100). Defaults to `0`.
    </ParamField>

    <ParamField body="items[].tax_rate" type="number">
      Tax rate as a percentage (0–100). Defaults to `0` for ad-hoc lines. Inherited from the catalog price when `price` is supplied.
    </ParamField>
  </Expandable>
</ParamField>

***

## Request Example

```bash theme={null}
curl -X POST https://invoice.horizonpay.co/api/v1/invoices \
  -H "Authorization: Bearer inv_live_sk_1234abcd5678efgh" \
  -H "Idempotency-Key: 018e4c70-3f2a-7b8d-9c12-a1b2c3d4e5f6" \
  -H "Content-Type: application/json" \
  -d '{
    "customer": "cus_01J9ZXMQR7P4KBN2VWTF3D6H8A",
    "currency": "USD",
    "collection_method": "send_invoice",
    "issue_date": "2025-06-01",
    "due_date": "2025-07-01",
    "description": "Website redesign — Phase 2",
    "footer": "Payment due within 30 days. Bank transfer preferred.",
    "items": [
      {
        "description": "UI/UX Design",
        "quantity": 40,
        "unit": "HRS",
        "unit_amount": 15000,
        "discount_percent": 0,
        "tax_rate": 18
      },
      {
        "price": "price_01J9ZXMQR7P4KBN2VWTF3D6HXX",
        "quantity": 1,
        "discount_percent": 10
      }
    ]
  }'
```

***

## Response Example

HTTP `201 Created`

```json theme={null}
{
  "data": {
    "id": "in_01JABCDEFGHIJKLMNOPQRSTU01",
    "object": "invoice",
    "number": null,
    "status": "draft",
    "customer": "cus_01J9ZXMQR7P4KBN2VWTF3D6H8A",
    "currency": "USD",
    "collection_method": "send_invoice",
    "issue_date": "2025-06-01",
    "due_date": "2025-07-01",
    "description": "Website redesign — Phase 2",
    "footer": "Payment due within 30 days. Bank transfer preferred.",
    "subtotal": 700000,
    "discount": 50000,
    "taxable": 650000,
    "tax": 108000,
    "total": 758000,
    "amount_due": 0,
    "amount_in_words": null,
    "public_url_token": null,
    "finalized_at": null,
    "paid_at": null,
    "voided_at": null,
    "void_reason": null,
    "created": "2025-06-01T08:00:00.000Z",
    "updated": "2025-06-01T08:00:00.000Z",
    "lines": {
      "data": [
        {
          "id": "ii_01JABCDEFGHIJKLMNOPQRSTU02",
          "object": "invoiceitem",
          "price": null,
          "product": null,
          "description": "UI/UX Design",
          "quantity": 40,
          "unit": "HRS",
          "unit_amount": 15000,
          "amount": 600000,
          "discount_percent": 0,
          "tax_rate": 18,
          "tax_amount": 108000
        },
        {
          "id": "ii_01JABCDEFGHIJKLMNOPQRSTU03",
          "object": "invoiceitem",
          "price": "price_01J9ZXMQR7P4KBN2VWTF3D6HXX",
          "product": "prod_01J9ZXMQR7P4KBN2VWTF3D6HYY",
          "description": "Domain Registration (1 year)",
          "quantity": 1,
          "unit": "NOS",
          "unit_amount": 10000,
          "amount": 10000,
          "discount_percent": 10,
          "tax_rate": 0,
          "tax_amount": 0
        }
      ]
    }
  }
}
```

<Note>
  `number` is always `null` on a newly created invoice. An invoice number is permanently assigned only when you call [POST /api/v1/invoices/:id/finalize](/api-reference/invoices/finalize). This means you can create and discard drafts without gaps in your invoice number series.
</Note>

<Warning>
  **Always send an `Idempotency-Key`.** A network timeout after the server commits but before the response reaches you would, without a key, leave you unable to tell whether the invoice was created. On retry without a key, a second identical draft is created — you then cannot determine which one to finalize, and finalizing both burns two invoice numbers. Use a UUID generated once per logical operation.
</Warning>

***

## Response Fields

<ResponseField name="data.id" type="string">
  Unique public identifier for the new invoice, prefixed `in_`.
</ResponseField>

<ResponseField name="data.object" type="string">
  Always `"invoice"`.
</ResponseField>

<ResponseField name="data.number" type="null">
  Always `null` for a newly created draft. Assigned on finalization.
</ResponseField>

<ResponseField name="data.status" type="string">
  Always `"draft"` on creation.
</ResponseField>

<ResponseField name="data.customer" type="string">
  The `cus_…` public ID of the attached customer.
</ResponseField>

<ResponseField name="data.currency" type="string">
  Three-letter ISO 4217 currency code.
</ResponseField>

<ResponseField name="data.subtotal" type="integer">
  Sum of all `line.amount` values before tax, in minor units.
</ResponseField>

<ResponseField name="data.discount" type="integer">
  Total discount amount across all lines, in minor units.
</ResponseField>

<ResponseField name="data.taxable" type="integer">
  Taxable base amount (subtotal minus discount), in minor units.
</ResponseField>

<ResponseField name="data.tax" type="integer">
  Total tax amount, in minor units.
</ResponseField>

<ResponseField name="data.total" type="integer">
  Grand total (taxable + tax), in minor units.
</ResponseField>

<ResponseField name="data.amount_due" type="integer">
  Always `0` for a draft — no payment is owed until the invoice is finalized.
</ResponseField>

<ResponseField name="data.lines" type="object">
  Object containing a `data` array of the invoice's line items, each with its computed amounts.
</ResponseField>

<ResponseField name="data.lines.data[].id" type="string">
  Unique identifier for the line item, prefixed `ii_`.
</ResponseField>

<ResponseField name="data.lines.data[].object" type="string">
  Always `"invoiceitem"`.
</ResponseField>

<ResponseField name="data.lines.data[].price" type="string | null">
  The `price_…` ID of the catalog price, or `null` for ad-hoc lines.
</ResponseField>

<ResponseField name="data.lines.data[].product" type="string | null">
  The `prod_…` ID of the parent product, or `null` for ad-hoc lines.
</ResponseField>

<ResponseField name="data.lines.data[].unit_amount" type="integer">
  Price per unit in minor units.
</ResponseField>

<ResponseField name="data.lines.data[].amount" type="integer">
  Line total (`quantity × unit_amount`, after discount), in minor units.
</ResponseField>

<ResponseField name="data.lines.data[].tax_amount" type="integer">
  Tax charged on this line, in minor units.
</ResponseField>

***

## Error Codes

| HTTP Status | `code`                     | Meaning                                                                           |
| ----------- | -------------------------- | --------------------------------------------------------------------------------- |
| `401`       | `unauthorized`             | Missing or invalid `Authorization` header.                                        |
| `403`       | `forbidden`                | Token does not carry the `invoices:write` scope.                                  |
| `404`       | `not_found`                | The `customer` ID does not exist or does not belong to your account.              |
| `422`       | `validation`               | Request body failed validation. The `errors` array lists each failing field path. |
| `428`       | `idempotency_key_required` | `Idempotency-Key` header was not sent.                                            |
| `429`       | `rate_limited`             | Per-credential rate limit exceeded. Check the `Retry-After` response header.      |
