---
title: "invoice a sales order"
method: POST
path: "/api/v1/sales/orders/{order_id}/invoice"
tags: ["orders"]
---

# invoice a sales order

`POST /api/v1/sales/orders/{order_id}/invoice`

## Request

- Base URL: `https://api.app.tightly.io`
- URL: `https://api.app.tightly.io/api/v1/sales/orders/{order_id}/invoice`
- Auth: HTTP bearer

Raises the account's invoice for the quantities that were actually fulfilled, at the price the order was agreed at, and sends it as an 810 where the account takes one. It is also posted to Xero or QuickBooks as a receivable where a ledger is connected. Tax and freight are figures you send; Tightly never calculates either.

Three states are served apart: the document's own, what the books did (`ledger.state`, null where no ledger is connected) and what the network did with the 810 (`edi.state`, null where the account takes none). Neither the ledger nor the network can fail the invoice.

Call it after the goods have shipped. Do not call it to invoice what was ordered rather than what shipped, and do not call it twice: one live invoice stands per order, and the second call names the one that stands.

Scope: `orders:write`, which includes `orders:read`.

## Path parameters

- `order_id` string, required

## Headers

- `Tightly-Version` string
- `Idempotency-Key` string

## Request body

- object
  - `freight_cents` integer: Freight as invoiced, in whole cents.
  - `tax_cents` integer: Tax as invoiced, in whole cents. Tightly calculates none.
  - `terms` 'net_30' | 'net_60' | 'net_90' | 'due_on_receipt', nullable: The payment terms this invoice carries. Omit it where the account's terms are not on file; the ledger then applies its own standing terms for the contact.

## Response `201`

The invoice, as `GET /api/v1/sales/invoices/{invoice_id}` serves it.

## Other responses

- `400`: `terms_not_known`, `tax_cents_not_whole_cents`, `freight_cents_not_whole_cents`, `tax_cents_negative`, `freight_cents_negative`, or a body that could not be read.
- `401`: No usable key: malformed, unknown, revoked, expired or stopped.
- `403`: The key does not hold Orders (`scope_missing`), or the organisation's plan does not include wholesale, or the key is issued to one account and the order is another's (`account_mismatch`).
- `404`: No order with that id is on file.
- `409`: `order_is_not_an_accounts`, `nothing_fulfilled_to_invoice`, `invoice_already_issued`, `line_without_a_price` (a fulfilled line with no price on it and no sell-in price on the account; nothing is invoiced at $0.00).
- `429`: The key has spent its allowance for this minute. 600 reads and 120 writes a minute, counted per key, refused calls included. `Retry-After` says how long to wait.
- `503`: The database is briefly out of reach, which is what a failover or a full connection pool looks like from outside. Wait the seconds `Retry-After` names and send the request again. A read is safe to repeat. A write is not, because the connection was lost in the middle of it, so read the object back before sending it a second time. Branch on the status rather than on `code`, which is `POSTGRES_UNAVAILABLE` from one of the two seams that raise this and `SERVICE_UNAVAILABLE` from the other.

---

[API](https://skmtc.dev/tightly/apis/tightly-api.md) · [All operations](https://skmtc.dev/tightly/apis/tightly-api/llms.txt) · [OpenAPI document](https://skmtc.dev/tightly/apis/tightly-api/revisions/199f7eb35afb?raw)
