---
title: "Create an invoice"
method: POST
path: "/public-api/v1/invoices"
tags: ["Invoices"]
---

# Create an invoice

`POST /public-api/v1/invoices`

## Request

- Base URL: `https://app.hellobonsai.com`
- URL: `https://app.hellobonsai.com/public-api/v1/invoices`
- Auth: one of:
  - OAuth 2
    - Authorization code: `https://app.hellobonsai.com/oauth/authorize`
    - Token URL: `https://app.hellobonsai.com/oauth/token`
  - HTTP bearer

Creates an invoice, with its line items inline if you have them.
`company_id`, `contact_id` and `project_id` are all required — an invoice
is always for one client, addressed to one person, against one project.

`kind` decides whether this is a one-off, a recurring invoice or a
subscription. A recurring invoice starts reissuing itself shortly after
it is created, so send `repeat_finish_date` if the series has an end.

The project has to exist already and belong to the same client as
`company_id`. This endpoint never creates one; use
`POST /public-api/v1/projects` first if you need to.

The invoice is created as a draft. Nothing is sent to your client here —
send it from Bonsai when you are ready.

Referencing a company, contact or project that is not in your account
fails with `422 not_accessible` rather than `404`: the invoice is what you
addressed, and the ids inside it are attributes of that request.

## Request body

- object
  - `invoice` object, required
    - `company_id` integer, required: Id of the client company to invoice. Required. Must be a company in your account.
    - `contact_id` integer, required: Id of the contact at that company to address the invoice to. Required. Must be a contact in your account.
    - `project_id` integer, required: Id of the project to invoice. Required, and must already exist and belong to the same client as `company_id`.
    - `currency` string: Three-letter ISO 4217 currency code. Defaults to the project's currency; send it to override.
    - `title` string: Invoice title shown to the client. Defaults to the client name followed by "Invoice".
    - `due` 'upon_receipt' | 'custom': When payment is due. Leave it out to use your account's default payment terms, which may work out to a number of days after issue.
    - `due_date` string, date: The exact date payment is due. Required when `due` is `custom`, and must be left out when `due` is `upon_receipt`.
    - `kind` 'single' | 'recurring' | 'subscription': What kind of invoice to create. Defaults to `single`. `recurring` reissues the invoice on `repeat_interval`; `subscription` does the same and charges the card your client has on file. Bundled invoices are assembled in the Bonsai app from invoices that already exist, so `bundled` is not accepted here.
    - `repeat_interval` 'weekly' | 'every two weeks' | 'every four weeks' | 'monthly' | 'every two months' | 'quarterly' | 'every six months' | 'annually': How often to reissue the invoice. Required for `recurring` and `subscription`, and must be left out for `single`.
    - `repeat_finish_date` string, date: Date to stop reissuing the invoice. Leave it out to keep the series running until you stop it. Must be left out for `single`, and must be no earlier than the first repeat — one `repeat_interval` after the issue date — since a series that ends before then would never reissue at all.
    - `invoice_items` object[]: Line items to put on the invoice. Leave it out for an empty invoice you fill in later, and add lines with the line-item endpoint.
      - `name` string, required: Line item name, as the client will see it. Required.
      - `description` string: Longer description shown beneath the name.
      - `amount` string, required: Quantity billed, as a decimal string. Required, and cannot be negative.
      - `rate` string, required: Price per unit, as a decimal string. Required, and cannot be negative.
      - `unit_type` 'flat' | 'per_hour' | 'per_day' | 'per_item' | 'per_word' | 'per_week' | 'per_month' | 'per_year' | 'per_quarter': What the rate is charged per. Defaults to `flat`.

## Response `201`

The invoice was created as a draft.

- object
  - `data` Invoice, required: A bill you send to a client for work on one project, with its line items.
    - `id` integer, required: Unique identifier for the invoice.
    - `invoice_number` string, nullable, required: Human-readable invoice reference, unique within your account. The list endpoint accepts it as `filter[invoice_number]`.
    - `title` string, nullable, required: Invoice title shown to the client.
    - `currency` string, required: Three-letter ISO 4217 currency code every amount on the invoice is expressed in.
    - `issued_date` string, date, nullable, required: Issue date printed on the invoice. Set when the invoice is created, so a draft has one too; `sent_date` says whether the client has received it.
    - `due_date` string, date, nullable, required: Date payment is due, or `null` when the invoice is payable on receipt.
    - `sent_date` string, date-time, nullable, required: When the invoice was sent to the client, by email or as a shared link, or `null` while it has not been sent. A `drafted` or `scheduled` invoice has none.
    - `project_id` integer, nullable, required: Id of the project the invoice bills.
    - `client_email` string, nullable, required: Email address the invoice is addressed to, captured from the client contact when the invoice was created.
    - `public_url_token` string, nullable, required: Token in the invoice's client-facing URL. Anyone holding that link can view and pay the invoice, so treat this value as a secret.
    - `created_at` string, date-time, required: When the invoice was created.
    - `total_amount` string, required: What the client owes, as a decimal string, after any discount and tax.
    - `discount_amount` string, nullable, required: Discount taken off the subtotal, as a decimal string, or `null` when there is none.
    - `status` 'new' | 'drafted' | 'scheduled' | 'outstanding' | 'overdue' | 'pending' | 'paid' | 'deleted', required: Where the invoice is in its lifecycle: - `new`, `drafted` — not sent to the client yet. - `scheduled` — queued to send automatically on a future date. - `outstanding` — sent and awaiting payment. - `overdue` — sent, unpaid and past `due_date`. - `pending` — a payment has started but has not settled. - `paid` — paid in full. - `deleted` — the invoice has been deleted. An invoice that is `paid` or `pending` can no longer be edited, and neither can its line items.
    - `due_date_option` 'upon_receipt' | 'after_x_days' | 'custom' | 'null', nullable, required: How `due_date` was arrived at: `upon_receipt`, `after_x_days` (a fixed number of days after the issue date) or `custom` (an explicit date).
    - `company_id` integer, nullable, required: Id of the company being invoiced, or `null` when the invoice is addressed to a client that is not a company record.
    - `client_name` string, nullable, required: Name the invoice is addressed to.
    - `subtotal` string, nullable, required: Sum of every line item, before discount and tax, as a decimal string.
    - `tax_amount` string, nullable, required: Total tax on the invoice, as a decimal string, whether it is charged on the whole invoice or per line item.
    - `kind` 'single' | 'recurring' | 'subscription' | 'bundled', required: How the invoice repeats: - `single` — a one-off invoice. Scheduling one to send later does not make it recurring; `status` reports that as `scheduled`. - `recurring` — reissued on `repeat_interval` until you stop it or `repeat_finish_date` arrives. - `subscription` — recurring, and charged automatically to the card the client has on file. - `bundled` — one of several invoices grouped to be sent and paid together. `bundled` is read-only: bundles are assembled in the Bonsai app from invoices that already exist, so it cannot be passed when creating one.
    - `repeat_interval` 'weekly' | 'every two weeks' | 'every four weeks' | 'monthly' | 'every two months' | 'quarterly' | 'every six months' | 'annually' | 'null', nullable, required: How often a `recurring` or `subscription` invoice is reissued, or `null` for a one-off. It records the cadence an invoice was issued under rather than whether its series still runs: stopping a series keeps the interval on every invoice so the series can be started again, including one stopped before it repeated, which then reads `kind` `single`. Only the last invoice a series issues on `repeat_finish_date` comes back `null`. Use `recurrence_active` to tell a running series from a stopped or finished one.
    - `repeat_finish_date` string, date, nullable, required: Date the series stops reissuing, or `null` for a one-off or a series that runs until you stop it. Only the newest invoice of a series carries it: each repeat hands the date on to the invoice it creates, so earlier invoices read `null` even when the series has an end. When the date falls a whole number of `repeat_interval`s after the issue date, the final invoice is issued on it; otherwise the series ends with the last repeat before it. Stopping a series keeps the date so the series can be started again.
    - `series_id` integer, nullable, required: Id of the first invoice of the series this one belongs to, the same for every invoice in it, so you can group them. `null` for an invoice that is not part of a series.
    - `recurrence_active` boolean, required: Whether the series this invoice belongs to still reissues. Unlike `kind` and `repeat_interval`, which describe the invoice, this describes the series, so every invoice in it reports the same answer and an already-issued one turns `false` once the series is stopped. It is `true` exactly when `DELETE /invoices/{invoice_id}/recurrence` has something to stop.
    - `url` string, required: Absolute link to the invoice in the Bonsai app. This is the contractor-facing view, not the client payment page — for that, use `public_url_token`.
    - `invoice_items` InvoiceItem[], required: The invoice's line items, in the order they appear on it.
      - `id` integer, required: Unique identifier for the line item.
      - `name` string, nullable, required: Line item name, as it appears on the invoice.
      - `description` string, nullable, required: Longer description shown beneath the name, or `null`.
      - `amount` string, nullable, required: Quantity billed, as a decimal string.
      - `rate` string, nullable, required: Price per unit, as a decimal string in the invoice currency.
      - `unit_type` 'flat' | 'per_hour' | 'per_day' | 'per_item' | 'per_word' | 'per_week' | 'per_month' | 'per_year' | 'per_quarter' | 'null', nullable, required: What the rate is charged per. Defaults to `flat`.
      - `total` string, required: `amount` multiplied by `rate`, rounded, as a decimal string.
  - `meta` Meta, required: Metadata returned alongside every single-resource response.
    - `request_id` string, required: Identifier for this request. Quote it when contacting support about a specific call.

## Other responses

- `400`: The request body is missing its `invoice` wrapper.
- `401`: The access token is missing, expired or invalid.
- `403`: This token is not allowed to create invoices, or it can only invoice projects it is assigned to and `project_id` is not one of them.
- `422`: The invoice could not be created. `blank` means `company_id`, `contact_id` or `project_id` was missing, or `repeat_interval` was missing on a recurring invoice; `invalid_value` means `due` and `due_date` disagree, `kind` is not one you can create, a recurrence field was sent on a `single` invoice, `repeat_finish_date` falls before the first repeat, or a line item is malformed; `not_accessible` means one of those ids is not in your account, or the project belongs to a different client.
- `429`: Rate limit exceeded. Wait at least `Retry-After` seconds before trying again.

---

[API](https://skmtc.dev/hellobonsai/apis/bonsai-api.md) - [All operations](https://skmtc.dev/hellobonsai/apis/bonsai-api/llms.txt) - [OpenAPI document](https://skmtc.dev/hellobonsai/apis/bonsai-api/revisions/abf12ed6a8d3?raw)
