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

# Create and send an invoice

> The full invoice lifecycle: creating, editing, and the side effects that come with each

The Invoices endpoint has full parity with the internal Billbooks app, not a stubbed subset. That means creating or editing an invoice through the API triggers the same real side effects a staff member creating one by hand would: stock gets deducted, expenses get linked, client balances move. This guide walks through all of it so nothing surprises you in production.

## Create an invoice

```bash theme={null}
curl -X POST https://app.billbooks.com/api/public-api/v1/invoices \
  -H "Authorization: Bearer bb_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": 4821,
    "date": "2026-08-18",
    "no": "INV-1042",
    "due_terms": "net_30",
    "status": "sent",
    "items": [
      {
        "item_id": 118,
        "qty": 2,
        "rate": 450
      },
      {
        "description": "One-off consulting call",
        "qty": 1,
        "rate": 200
      }
    ]
  }'
```

A few fields worth understanding before you script this:

* `no` is your invoice number. It must be unique per organization. Reuse one and you get a `409 Conflict`.
* Each line needs either an `item_id` (pulled from your catalog) or a plain `description`, at least one of the two. `qty` must be greater than 0, `rate` must be 0 or more, and duplicate `item_id` values in the same request are rejected with `422`.
* `subtotal`, `discount`, `taxtotal`, and `nettotal` are always computed server-side from `items`. If you send those fields in the request body, they're ignored. Only `discount_type`, `discount_value`, and `shipping` actually influence the computed totals.
* `status` defaults to `"draft"` if you omit it. Set it to `"sent"` to send the invoice immediately.

## What happens behind the scenes

Creating an invoice isn't just a database insert. For each line item:

* If the line resolves to an `item_id` whose catalog item has `track_inventory` turned on, stock is deducted and a ledger row is written recording the sale. See [Build your item catalog](/guides/build-your-item-catalog) for how `track_inventory` works.
* If the line carries an `expense_id`, that expense is marked as invoiced and linked to this invoice. See [Log expenses via API](/guides/log-expenses-via-api).
* If `status` resolves to anything other than `"draft"`, the client's running balance is incremented by the invoice's `nettotal`.

None of this is optional or togglable. If you're building a script that creates draft invoices for review before sending, keep in mind the stock and balance effects only fire once you move status off `"draft"`.

## Editing an invoice

```bash theme={null}
curl -X PUT https://app.billbooks.com/api/public-api/v1/invoices \
  -H "Authorization: Bearer bb_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "id": 9931,
    "items": [
      { "item_id": 118, "qty": 3, "rate": 450 }
    ]
  }'
```

<Warning>
  `items` is always a full replace on update, never a patch. Every line you want on the invoice after the edit has to be in the array, including ones that were already there. Any existing line you omit gets deleted, and its stock and expense-linking side effects are reversed as part of that deletion.
</Warning>

Every field other than `items` follows normal partial-patch rules: only what you submit changes. Changing `status` or `client_id` correctly reconciles the client balance delta, which is actually more complete than the internal app's own edit screen (that one can't change status or client at all).

One restriction: through this endpoint, `status` can only be set to `draft` or `sent`. Moving an invoice to `paid`, `partial`, `overdue`, `void`, or `recurring` is rejected with `422`, those states are only reachable from inside the main app (typically as the result of a recorded payment).

## Deleting an invoice

```bash theme={null}
curl -X DELETE "https://app.billbooks.com/api/public-api/v1/invoices?id=9931" \
  -H "Authorization: Bearer bb_live_your_key_here"
```

Deletion is blocked with `409 Conflict` if the invoice has any recorded payment, which is actually a stricter guard than the internal app's own delete function has. If the delete succeeds, all stock and expense-linking side effects from that invoice are reversed line by line, and if the invoice wasn't a draft, the client's balance is decremented by the invoice's balance.

## What's next

* [Manage your client list](/guides/manage-your-client-list) to look up or create the `client_id` you're invoicing
* [Build your item catalog](/guides/build-your-item-catalog) so line items can reference `item_id` and keep stock in sync
* [Errors](/errors) for what a `409` or `422` response looks like in full
