# CloudOverture API reference

The CloudOverture API lets a program work with one company's data: accounts, contacts, and invoices. It calls the same service layer as the app, so posting, the books gate, the lock date, and the send rules run once for every caller. New here? Start with the [quickstart](/quickstart).

## Host and keys

- Every request goes to `https://api.cloudoverture.com/v1/...` over HTTPS.
- Sign each request with `Authorization: Bearer <key>`. Keys are made in the app, in **Settings → Developers**. There are no key-management endpoints.
- The company comes only from the key. No request field can name a company.
- A key can never do more than the member who owns it. Its scopes narrow it further: each area is **No access**, **Read**, or **Read and write**, and sending or voiding an invoice needs its own grant.
- A `co_test_` key works only in a sandbox and on QA. A `co_live_` key works only on a real company. **Try it** on this site accepts only a `co_test_` key.
- Create the key for a company system (for example, a billing system) while signed in as the owner. A key stops working when its member leaves the team.

## Objects

- Every object has `id` (a type prefix, then an opaque string, e.g. `inv_8f3kq2w9d1`), `object`, `created_at`, `updated_at`, and `livemode`: `true` on a real company, `false` in a sandbox and on QA (the `co_test_` keys).
- Money is a **decimal string** with a separate currency code: `"grand_total": "480.00", "currency": "usd"`. A currency code is lowercase, and `usd` is the only one at launch. A string is exact; a JSON number is not. Unit prices carry 4 places, so decimals fit them better than whole cents. Totals carry 2 places.
- The server computes every total and every tax. Sending one returns `400 read_only_field`.
- A timestamp is ISO 8601 in UTC with a `Z`. A calendar date is `YYYY-MM-DD`.
- `PATCH` changes only the fields it sends. `null` clears an optional field. An array (an invoice's `lines`) is replaced whole. An unknown field returns `400 unknown_field`, and the message lists the accepted names.
- `external_id` is your own id for a record. A create with an `external_id` already used returns `409 external_id_exists` with the existing record's `id`.

## Lists

- A list returns `{ "object": "list", "data": [...], "has_more": true, "next_cursor": "..." }`. Pass `next_cursor` as `starting_after` to read the next page.
- `limit` is 25 by default and at most 100. A list is sorted newest first.
- Every list takes `updated_at[gte]`, and the lookups take `active` (active records by default).
- A query value of the wrong type (e.g. `limit=abc`) returns `400 invalid_request`, and `param` names it.
- There is no search endpoint and no bulk endpoint: one call works on one record. To read a lot, page through a list with `updated_at[gte]`.

## Retries

- Every `POST`, `PATCH`, and `DELETE` takes an `Idempotency-Key` header, up to 255 characters. Invoice send and void require it; an invoice create requires it unless it carries an `external_id`.
- The same key with the same body returns the saved first response, a saved `4xx` included. The same key with another body returns `409 idempotency_conflict`. A first try that failed with a `5xx` is not saved, so a retry runs again.
- A send on an invoice that is already sent returns the invoice and does nothing.

## Errors

Every error has one shape: `type`, `code`, `message`, `param`, `display_message`, `doc_url`, and `request_id`. Branch on `code`, never on `message`. Every response carries a `Request-Id` header; quote it to support. Each code has its own page under [/errors](/errors). The errors a correct integration still sees often are `period_locked`, `send_validation_failed`, and `rate_limited`.

## Rate limits

- Per key: 10 requests per second, with bursts up to 20. Per company, across all its keys: 25 requests per second. Send and void: 2 per second per key.
- Every response carries `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset`. A `429` carries `Retry-After` and `reason`.
- Back off like this: wait `Retry-After`, then retry with growing waits plus a small random delay, at most 5 tries.

## Versions

- Versions are named by date. This is `2026-11-01`. A key pins the version that is current when it is made; the `CO-Version` header overrides it for one request, and every response carries `CO-Version`.
- Safe changes ship in any version: a new endpoint, a new optional request field, a new response field, a new enum value, a new error code, a longer id. Write code that tolerates them. A breaking change ships only in a new version.

## If a key leaks

Revoke it in Settings → Developers. Revoking takes effect at once. A leaked key can only do what it was allowed, and only see what its owner can see.


## GET /v1/key

Read this key. Returns the key's name, its company, its scopes, the version that answered, and `livemode`. The quickstart's first call. Any key may call it.

**Scope:** any key · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** and the [common errors](/errors)


Answers 200 with Key:

- `id`: The key's id (`key_...`).
- `object`
- `name`: The key's name.
- `tenant_name`: The company the key belongs to.
- `scopes`: Each area's access: `none`, `read`, or `read_write`.
- `grants`: The protected actions the key may take, e.g. `invoices.send`.
- `version`: The API version that answered.
- `livemode`: `true` for a live key, `false` for a test key.

## GET /v1/accounts

List accounts. Lists accounts, newest first.

**Scope:** `accounts` (Read) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), and the [common errors](/errors)

- query `external_id`: Only the record with this `external_id`.
- query `name`: Only accounts with this name.
- query `updated_at[gte]`: Only records changed at or after this time (ISO 8601, UTC).
- query `limit`: Page size: 25 by default, at most 100.
- query `starting_after`: The previous page's `next_cursor`.

Answers 200 with AccountList:

- `object`
- `data`
- `has_more`: Whether another page follows.
- `next_cursor`: Pass it as `starting_after` to read the next page. Absent on the last page.

## POST /v1/accounts

Create an account. Creates an account. A create with an `external_id` already used returns `409 external_id_exists` with the existing record's id, so a create is safe to retry.

**Scope:** `accounts` (Read and write) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), [`billing_locked`](/errors/billing_locked), [`unknown_field`](/errors/unknown_field), [`read_only_field`](/errors/read_only_field), [`missing_required_input`](/errors/missing_required_input), [`external_id_exists`](/errors/external_id_exists), [`idempotency_conflict`](/errors/idempotency_conflict), and the [common errors](/errors)

- header `Idempotency-Key`: Optional on this write. A unique value for this request, up to 255 characters. A retry with the same key and body returns the saved first response. The same key with another body is `409 idempotency_conflict`.

Body:

- `name`
- `external_id`: Your own id for this record. Unique per company.
- `legal_name`
- `email`
- `phone`
- `website`
- `billing_email`
- `is_customer`
- `is_vendor`
- `is_individual`

Answers 201 with Account:

- `id` (read-only): The account's id: a type prefix, then an opaque string. It never changes.
- `object` (read-only): The type name.
- `created_at` (read-only): When the record was created, in UTC.
- `updated_at` (read-only): When the record last changed, in UTC.
- `livemode` (read-only)
- `external_id`: Your own id for this record. Unique per company.
- `account_number` (read-only): The account's number in the app.
- `name`
- `legal_name`
- `status` (read-only): The account's status in the app.
- `is_customer`
- `is_vendor`
- `is_individual`
- `email`
- `phone`
- `website`
- `billing_email`
- `default_payment_term` (read-only): The account's default payment term (`pterm_...`).

## GET /v1/accounts/{id}

Read an account. Returns one account.

**Scope:** `accounts` (Read) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), [`resource_missing`](/errors/resource_missing), and the [common errors](/errors)

- path `id` (required): The record's id.

Answers 200 with Account:

- `id` (read-only): The account's id: a type prefix, then an opaque string. It never changes.
- `object` (read-only): The type name.
- `created_at` (read-only): When the record was created, in UTC.
- `updated_at` (read-only): When the record last changed, in UTC.
- `livemode` (read-only)
- `external_id`: Your own id for this record. Unique per company.
- `account_number` (read-only): The account's number in the app.
- `name`
- `legal_name`
- `status` (read-only): The account's status in the app.
- `is_customer`
- `is_vendor`
- `is_individual`
- `email`
- `phone`
- `website`
- `billing_email`
- `default_payment_term` (read-only): The account's default payment term (`pterm_...`).

## PATCH /v1/accounts/{id}

Update an account. Changes only the fields it sends. `null` clears an optional field.

**Scope:** `accounts` (Read and write) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), [`billing_locked`](/errors/billing_locked), [`unknown_field`](/errors/unknown_field), [`read_only_field`](/errors/read_only_field), [`resource_missing`](/errors/resource_missing), [`external_id_exists`](/errors/external_id_exists), [`idempotency_conflict`](/errors/idempotency_conflict), and the [common errors](/errors)

- path `id` (required): The record's id.
- header `Idempotency-Key`: Optional on this write. A unique value for this request, up to 255 characters. A retry with the same key and body returns the saved first response. The same key with another body is `409 idempotency_conflict`.

Body:

- `name`
- `external_id`: Your own id for this record. Unique per company.
- `legal_name`
- `email`
- `phone`
- `website`
- `billing_email`
- `is_customer`
- `is_vendor`
- `is_individual`

Answers 200 with Account:

- `id` (read-only): The account's id: a type prefix, then an opaque string. It never changes.
- `object` (read-only): The type name.
- `created_at` (read-only): When the record was created, in UTC.
- `updated_at` (read-only): When the record last changed, in UTC.
- `livemode` (read-only)
- `external_id`: Your own id for this record. Unique per company.
- `account_number` (read-only): The account's number in the app.
- `name`
- `legal_name`
- `status` (read-only): The account's status in the app.
- `is_customer`
- `is_vendor`
- `is_individual`
- `email`
- `phone`
- `website`
- `billing_email`
- `default_payment_term` (read-only): The account's default payment term (`pterm_...`).

## GET /v1/contacts

List contacts. Lists contacts, newest first.

**Scope:** `contacts` (Read) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), and the [common errors](/errors)

- query `account`: Only records of this account (`acct_...`).
- query `email`: Only contacts with this email.
- query `external_id`: Only the record with this `external_id`.
- query `updated_at[gte]`: Only records changed at or after this time (ISO 8601, UTC).
- query `limit`: Page size: 25 by default, at most 100.
- query `starting_after`: The previous page's `next_cursor`.

Answers 200 with ContactList:

- `object`
- `data`
- `has_more`: Whether another page follows.
- `next_cursor`: Pass it as `starting_after` to read the next page. Absent on the last page.

## POST /v1/contacts

Create a contact. Creates a contact at an account.

**Scope:** `contacts` (Read and write) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), [`billing_locked`](/errors/billing_locked), [`unknown_field`](/errors/unknown_field), [`read_only_field`](/errors/read_only_field), [`missing_required_input`](/errors/missing_required_input), [`resource_missing`](/errors/resource_missing), [`external_id_exists`](/errors/external_id_exists), [`idempotency_conflict`](/errors/idempotency_conflict), and the [common errors](/errors)

- header `Idempotency-Key`: Optional on this write. A unique value for this request, up to 255 characters. A retry with the same key and body returns the saved first response. The same key with another body is `409 idempotency_conflict`.

Body:

- `account`: The account this person belongs to (`acct_...`).
- `external_id`: Your own id for this record. Unique per company.
- `first_name`
- `last_name`
- `email`
- `phone`
- `job_title`

Answers 201 with Contact:

- `id` (read-only): The contact's id: a type prefix, then an opaque string. It never changes.
- `object` (read-only): The type name.
- `created_at` (read-only): When the record was created, in UTC.
- `updated_at` (read-only): When the record last changed, in UTC.
- `livemode` (read-only)
- `external_id`: Your own id for this record. Unique per company.
- `account`: The account this person belongs to (`acct_...`).
- `first_name`
- `last_name`
- `email`
- `phone`
- `job_title`
- `is_primary` (read-only): Whether this is the account's primary contact.

## GET /v1/contacts/{id}

Read a contact. Returns one contact.

**Scope:** `contacts` (Read) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), [`resource_missing`](/errors/resource_missing), and the [common errors](/errors)

- path `id` (required): The record's id.

Answers 200 with Contact:

- `id` (read-only): The contact's id: a type prefix, then an opaque string. It never changes.
- `object` (read-only): The type name.
- `created_at` (read-only): When the record was created, in UTC.
- `updated_at` (read-only): When the record last changed, in UTC.
- `livemode` (read-only)
- `external_id`: Your own id for this record. Unique per company.
- `account`: The account this person belongs to (`acct_...`).
- `first_name`
- `last_name`
- `email`
- `phone`
- `job_title`
- `is_primary` (read-only): Whether this is the account's primary contact.

## PATCH /v1/contacts/{id}

Update a contact. Changes only the fields it sends. `null` clears an optional field.

**Scope:** `contacts` (Read and write) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), [`billing_locked`](/errors/billing_locked), [`unknown_field`](/errors/unknown_field), [`read_only_field`](/errors/read_only_field), [`resource_missing`](/errors/resource_missing), [`external_id_exists`](/errors/external_id_exists), [`idempotency_conflict`](/errors/idempotency_conflict), and the [common errors](/errors)

- path `id` (required): The record's id.
- header `Idempotency-Key`: Optional on this write. A unique value for this request, up to 255 characters. A retry with the same key and body returns the saved first response. The same key with another body is `409 idempotency_conflict`.

Body:

- `external_id`: Your own id for this record. Unique per company.
- `first_name`
- `last_name`
- `email`
- `phone`
- `job_title`

Answers 200 with Contact:

- `id` (read-only): The contact's id: a type prefix, then an opaque string. It never changes.
- `object` (read-only): The type name.
- `created_at` (read-only): When the record was created, in UTC.
- `updated_at` (read-only): When the record last changed, in UTC.
- `livemode` (read-only)
- `external_id`: Your own id for this record. Unique per company.
- `account`: The account this person belongs to (`acct_...`).
- `first_name`
- `last_name`
- `email`
- `phone`
- `job_title`
- `is_primary` (read-only): Whether this is the account's primary contact.

## GET /v1/invoices

List invoices. Lists invoices with their lines, newest first.

**Scope:** `invoices` (Read) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), and the [common errors](/errors)

- query `status`: Only invoices in this status, e.g. `draft`.
- query `account`: Only records of this account (`acct_...`).
- query `number`: Only the invoice with this number, e.g. `INV-1042`.
- query `external_id`: Only the record with this `external_id`.
- query `issue_date[gte]`: Only invoices issued on or after this date.
- query `issue_date[lte]`: Only invoices issued on or before this date.
- query `updated_at[gte]`: Only records changed at or after this time (ISO 8601, UTC).
- query `limit`: Page size: 25 by default, at most 100.
- query `starting_after`: The previous page's `next_cursor`.

Answers 200 with InvoiceList:

- `object`
- `data`
- `has_more`: Whether another page follows.
- `next_cursor`: Pass it as `starting_after` to read the next page. Absent on the last page.

## POST /v1/invoices

Create an invoice. Creates a **draft** with its lines. It posts nothing and emails nobody. The server computes every total and every tax: sending one returns `400 read_only_field`.

**Scope:** `invoices` (Read and write) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), [`billing_locked`](/errors/billing_locked), [`unknown_field`](/errors/unknown_field), [`read_only_field`](/errors/read_only_field), [`missing_required_input`](/errors/missing_required_input), [`invalid_line_type`](/errors/invalid_line_type), [`currency_not_supported`](/errors/currency_not_supported), [`idempotency_key_required`](/errors/idempotency_key_required), [`idempotency_conflict`](/errors/idempotency_conflict), [`external_id_exists`](/errors/external_id_exists), [`resource_missing`](/errors/resource_missing), and the [common errors](/errors)

- header `Idempotency-Key`: Required unless the body carries an `external_id`. A retry with the same key and body returns the saved first response. The same key with another body is `409 idempotency_conflict`.

Body:

- `external_id`: Your own id for this record, e.g. your billing system's renewal id. Unique per company.
- `account`: The account billed (`acct_...`).
- `contact`: The contact billed (`cont_...`).
- `issue_date`: The issue date. Send posts the invoice dated to it.
- `due_date`: The due date.
- `currency`: Required. Only `usd` at launch: any other value returns `400 currency_not_supported`.
- `payment_term`: The payment term (`pterm_...`).
- `net_days`: Days until due.
- `reference`: The customer's PO or reference.
- `discount_type`
- `discount_value`: A decimal string.
- `notes_customer`: Notes the customer sees.
- `notes_internal`: Notes only your team sees.
- `terms_and_conditions`: The terms printed on the invoice.
- `lines`: The invoice's lines. On a PATCH the array is replaced whole.

Answers 201 with Invoice:

- `id` (read-only): The invoice's id: a type prefix, then an opaque string. It never changes.
- `object` (read-only): The type name.
- `created_at` (read-only): When the record was created, in UTC.
- `updated_at` (read-only): When the record last changed, in UTC.
- `livemode` (read-only)
- `external_id`: Your own id for this record, e.g. your billing system's renewal id. Unique per company.
- `number` (read-only): The human number, e.g. `INV-1042`. Absent while the invoice is a draft.
- `status` (read-only): The invoice's lifecycle status, e.g. `draft`, `sent`, `voided`.
- `account`: The account billed (`acct_...`).
- `contact`: The contact billed (`cont_...`).
- `issue_date`: The issue date. Send posts the invoice dated to it.
- `due_date`: The due date.
- `currency`: Only `usd` at launch.
- `reference`: The customer's PO or reference.
- `payment_term`: The payment term (`pterm_...`).
- `net_days`: Days until due.
- `discount_type`
- `discount_value`: A decimal string.
- `subtotal` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `discount_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `tax_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `grand_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `amount_paid` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `balance_due` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `notes_customer`: Notes the customer sees.
- `notes_internal`: Notes only your team sees.
- `hosted_url` (read-only): The customer's page for this invoice. Present once the invoice is sent.
- `sent_at` (read-only): When the invoice was sent.
- `voided_at` (read-only): When the invoice was voided.
- `void_reason` (read-only): The reason given for the void.
- `reminders_opted_out` (read-only): `true` when reminders are off, e.g. after a send with `delivery: "none"`.
- `lines`

## GET /v1/invoices/{id}

Read an invoice. Returns one invoice with its lines.

**Scope:** `invoices` (Read) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), [`resource_missing`](/errors/resource_missing), and the [common errors](/errors)

- path `id` (required): The record's id.

Answers 200 with Invoice:

- `id` (read-only): The invoice's id: a type prefix, then an opaque string. It never changes.
- `object` (read-only): The type name.
- `created_at` (read-only): When the record was created, in UTC.
- `updated_at` (read-only): When the record last changed, in UTC.
- `livemode` (read-only)
- `external_id`: Your own id for this record, e.g. your billing system's renewal id. Unique per company.
- `number` (read-only): The human number, e.g. `INV-1042`. Absent while the invoice is a draft.
- `status` (read-only): The invoice's lifecycle status, e.g. `draft`, `sent`, `voided`.
- `account`: The account billed (`acct_...`).
- `contact`: The contact billed (`cont_...`).
- `issue_date`: The issue date. Send posts the invoice dated to it.
- `due_date`: The due date.
- `currency`: Only `usd` at launch.
- `reference`: The customer's PO or reference.
- `payment_term`: The payment term (`pterm_...`).
- `net_days`: Days until due.
- `discount_type`
- `discount_value`: A decimal string.
- `subtotal` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `discount_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `tax_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `grand_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `amount_paid` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `balance_due` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `notes_customer`: Notes the customer sees.
- `notes_internal`: Notes only your team sees.
- `hosted_url` (read-only): The customer's page for this invoice. Present once the invoice is sent.
- `sent_at` (read-only): When the invoice was sent.
- `voided_at` (read-only): When the invoice was voided.
- `void_reason` (read-only): The reason given for the void.
- `reminders_opted_out` (read-only): `true` when reminders are off, e.g. after a send with `delivery: "none"`.
- `lines`

## PATCH /v1/invoices/{id}

Update an invoice. On a draft, changes any field it sends, and `lines` replaces every line. On a sent invoice with no payment, changes only `due_date`, `reference`, and `notes_internal`; these never repost.

**Scope:** `invoices` (Read and write) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), [`billing_locked`](/errors/billing_locked), [`unknown_field`](/errors/unknown_field), [`read_only_field`](/errors/read_only_field), [`invalid_line_type`](/errors/invalid_line_type), [`currency_not_supported`](/errors/currency_not_supported), [`resource_missing`](/errors/resource_missing), [`invoice_not_draft`](/errors/invoice_not_draft), [`invoice_locked`](/errors/invoice_locked), [`invoice_fix_in_progress`](/errors/invoice_fix_in_progress), [`external_id_exists`](/errors/external_id_exists), [`idempotency_conflict`](/errors/idempotency_conflict), and the [common errors](/errors)

- path `id` (required): The record's id.
- header `Idempotency-Key`: Optional on this write. A unique value for this request, up to 255 characters. A retry with the same key and body returns the saved first response. The same key with another body is `409 idempotency_conflict`.

Body:

- `external_id`: Your own id for this record, e.g. your billing system's renewal id. Unique per company.
- `account`: The account billed (`acct_...`).
- `contact`: The contact billed (`cont_...`).
- `issue_date`: The issue date. Send posts the invoice dated to it.
- `due_date`: The due date.
- `currency`: Only `usd`, which changes nothing. Any other value, `null` included, returns `400 currency_not_supported`.
- `payment_term`: The payment term (`pterm_...`).
- `net_days`: Days until due.
- `reference`: The customer's PO or reference.
- `discount_type`
- `discount_value`: A decimal string.
- `notes_customer`: Notes the customer sees.
- `notes_internal`: Notes only your team sees.
- `terms_and_conditions`: The terms printed on the invoice.
- `lines`: The invoice's lines. On a PATCH the array is replaced whole.

Answers 200 with Invoice:

- `id` (read-only): The invoice's id: a type prefix, then an opaque string. It never changes.
- `object` (read-only): The type name.
- `created_at` (read-only): When the record was created, in UTC.
- `updated_at` (read-only): When the record last changed, in UTC.
- `livemode` (read-only)
- `external_id`: Your own id for this record, e.g. your billing system's renewal id. Unique per company.
- `number` (read-only): The human number, e.g. `INV-1042`. Absent while the invoice is a draft.
- `status` (read-only): The invoice's lifecycle status, e.g. `draft`, `sent`, `voided`.
- `account`: The account billed (`acct_...`).
- `contact`: The contact billed (`cont_...`).
- `issue_date`: The issue date. Send posts the invoice dated to it.
- `due_date`: The due date.
- `currency`: Only `usd` at launch.
- `reference`: The customer's PO or reference.
- `payment_term`: The payment term (`pterm_...`).
- `net_days`: Days until due.
- `discount_type`
- `discount_value`: A decimal string.
- `subtotal` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `discount_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `tax_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `grand_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `amount_paid` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `balance_due` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `notes_customer`: Notes the customer sees.
- `notes_internal`: Notes only your team sees.
- `hosted_url` (read-only): The customer's page for this invoice. Present once the invoice is sent.
- `sent_at` (read-only): When the invoice was sent.
- `voided_at` (read-only): When the invoice was voided.
- `void_reason` (read-only): The reason given for the void.
- `reminders_opted_out` (read-only): `true` when reminders are off, e.g. after a send with `delivery: "none"`.
- `lines`

## DELETE /v1/invoices/{id}

Delete a draft invoice. Deletes a draft. A sent invoice is voided, never deleted.

**Scope:** `invoices` (Read and write) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), [`billing_locked`](/errors/billing_locked), [`resource_missing`](/errors/resource_missing), [`invoice_not_draft`](/errors/invoice_not_draft), [`idempotency_conflict`](/errors/idempotency_conflict), and the [common errors](/errors)

- path `id` (required): The record's id.
- header `Idempotency-Key`: Optional on this write. A unique value for this request, up to 255 characters. A retry with the same key and body returns the saved first response. The same key with another body is `409 idempotency_conflict`.

Answers 204.

## POST /v1/invoices/{id}/send

Send an invoice. Runs the books gate, then send rules I1 to I9, then numbers the invoice, posts it dated to `issue_date`, and emails the customer unless `delivery` is `none`. A send of an invoice already sent returns it and does nothing.

**Scope:** `invoices` (Read and write) · **Grant:** `invoices.send` · **Rate limit:** 2 requests per second per key · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), [`billing_locked`](/errors/billing_locked), [`unknown_field`](/errors/unknown_field), [`idempotency_key_required`](/errors/idempotency_key_required), [`idempotency_conflict`](/errors/idempotency_conflict), [`resource_missing`](/errors/resource_missing), [`invoice_fix_in_progress`](/errors/invoice_fix_in_progress), [`books_not_set_up`](/errors/books_not_set_up), [`send_validation_failed`](/errors/send_validation_failed), [`period_locked`](/errors/period_locked), and the [common errors](/errors)

- path `id` (required): The record's id.
- header `Idempotency-Key` (required): A unique value for this request, up to 255 characters. A retry with the same key and body returns the saved first response. The same key with another body is `409 idempotency_conflict`.

Body:

- `delivery`: `email` sends like Send in the app. `none` numbers and posts the invoice and makes `hosted_url`, emails nobody, and turns reminders off.

Answers 200 with Invoice:

- `id` (read-only): The invoice's id: a type prefix, then an opaque string. It never changes.
- `object` (read-only): The type name.
- `created_at` (read-only): When the record was created, in UTC.
- `updated_at` (read-only): When the record last changed, in UTC.
- `livemode` (read-only)
- `external_id`: Your own id for this record, e.g. your billing system's renewal id. Unique per company.
- `number` (read-only): The human number, e.g. `INV-1042`. Absent while the invoice is a draft.
- `status` (read-only): The invoice's lifecycle status, e.g. `draft`, `sent`, `voided`.
- `account`: The account billed (`acct_...`).
- `contact`: The contact billed (`cont_...`).
- `issue_date`: The issue date. Send posts the invoice dated to it.
- `due_date`: The due date.
- `currency`: Only `usd` at launch.
- `reference`: The customer's PO or reference.
- `payment_term`: The payment term (`pterm_...`).
- `net_days`: Days until due.
- `discount_type`
- `discount_value`: A decimal string.
- `subtotal` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `discount_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `tax_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `grand_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `amount_paid` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `balance_due` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `notes_customer`: Notes the customer sees.
- `notes_internal`: Notes only your team sees.
- `hosted_url` (read-only): The customer's page for this invoice. Present once the invoice is sent.
- `sent_at` (read-only): When the invoice was sent.
- `voided_at` (read-only): When the invoice was voided.
- `void_reason` (read-only): The reason given for the void.
- `reminders_opted_out` (read-only): `true` when reminders are off, e.g. after a send with `delivery: "none"`.
- `lines`

## POST /v1/invoices/{id}/void

Void an invoice. Voids the invoice with the reason, the same as Void in the app. The reversal is dated by the app's void rule.

**Scope:** `invoices` (Read and write) · **Grant:** `invoices.void` · **Rate limit:** 2 requests per second per key · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), [`billing_locked`](/errors/billing_locked), [`unknown_field`](/errors/unknown_field), [`missing_required_input`](/errors/missing_required_input), [`idempotency_key_required`](/errors/idempotency_key_required), [`idempotency_conflict`](/errors/idempotency_conflict), [`resource_missing`](/errors/resource_missing), [`invoice_fix_in_progress`](/errors/invoice_fix_in_progress), [`invoice_not_voidable`](/errors/invoice_not_voidable), and the [common errors](/errors)

- path `id` (required): The record's id.
- header `Idempotency-Key` (required): A unique value for this request, up to 255 characters. A retry with the same key and body returns the saved first response. The same key with another body is `409 idempotency_conflict`.

Body:

- `reason`: Why the invoice is voided.

Answers 200 with Invoice:

- `id` (read-only): The invoice's id: a type prefix, then an opaque string. It never changes.
- `object` (read-only): The type name.
- `created_at` (read-only): When the record was created, in UTC.
- `updated_at` (read-only): When the record last changed, in UTC.
- `livemode` (read-only)
- `external_id`: Your own id for this record, e.g. your billing system's renewal id. Unique per company.
- `number` (read-only): The human number, e.g. `INV-1042`. Absent while the invoice is a draft.
- `status` (read-only): The invoice's lifecycle status, e.g. `draft`, `sent`, `voided`.
- `account`: The account billed (`acct_...`).
- `contact`: The contact billed (`cont_...`).
- `issue_date`: The issue date. Send posts the invoice dated to it.
- `due_date`: The due date.
- `currency`: Only `usd` at launch.
- `reference`: The customer's PO or reference.
- `payment_term`: The payment term (`pterm_...`).
- `net_days`: Days until due.
- `discount_type`
- `discount_value`: A decimal string.
- `subtotal` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `discount_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `tax_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `grand_total` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `amount_paid` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `balance_due` (read-only): Computed. A decimal string, e.g. `"480.00"`.
- `notes_customer`: Notes the customer sees.
- `notes_internal`: Notes only your team sees.
- `hosted_url` (read-only): The customer's page for this invoice. Present once the invoice is sent.
- `sent_at` (read-only): When the invoice was sent.
- `voided_at` (read-only): When the invoice was voided.
- `void_reason` (read-only): The reason given for the void.
- `reminders_opted_out` (read-only): `true` when reminders are off, e.g. after a send with `delivery: "none"`.
- `lines`

## GET /v1/products

List products. Lists the catalog's products and services, newest first.

**Scope:** `products` (Read) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), and the [common errors](/errors)

- query `active`: `true` (the default) lists active records, `false` inactive ones. Any other value is `400 invalid_request`.
- query `updated_at[gte]`: Only records changed at or after this time (ISO 8601, UTC).
- query `limit`: Page size: 25 by default, at most 100.
- query `starting_after`: The previous page's `next_cursor`.

Answers 200 with ProductList:

- `object`
- `data`
- `has_more`: Whether another page follows.
- `next_cursor`: Pass it as `starting_after` to read the next page. Absent on the last page.

## GET /v1/products/{id}

Read a product. Returns one product.

**Scope:** `products` (Read) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), [`resource_missing`](/errors/resource_missing), and the [common errors](/errors)

- path `id` (required): The record's id.

Answers 200 with Product:

- `id`: The product's id: a type prefix, then an opaque string. It never changes.
- `object`: The type name.
- `created_at`: When the record was created, in UTC.
- `updated_at`: When the record last changed, in UTC.
- `livemode`
- `item_type`: `product` or `service`.
- `name`
- `sku`
- `description`
- `unit_price`: A decimal string, up to 4 places.
- `unit_of_measure`
- `is_taxable`
- `active`

## GET /v1/payment-terms

List payment terms. Lists payment terms, newest first. Needs `invoices` read.

**Scope:** `invoices` (Read) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), and the [common errors](/errors)

- query `active`: `true` (the default) lists active records, `false` inactive ones. Any other value is `400 invalid_request`.
- query `updated_at[gte]`: Only records changed at or after this time (ISO 8601, UTC).
- query `limit`: Page size: 25 by default, at most 100.
- query `starting_after`: The previous page's `next_cursor`.

Answers 200 with PaymentTermList:

- `object`
- `data`
- `has_more`: Whether another page follows.
- `next_cursor`: Pass it as `starting_after` to read the next page. Absent on the last page.

## GET /v1/tax-rates

List tax rates. Lists sales tax rates, newest first. Needs `invoices` read.

**Scope:** `invoices` (Read) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), and the [common errors](/errors)

- query `active`: `true` (the default) lists active records, `false` inactive ones. Any other value is `400 invalid_request`.
- query `updated_at[gte]`: Only records changed at or after this time (ISO 8601, UTC).
- query `limit`: Page size: 25 by default, at most 100.
- query `starting_after`: The previous page's `next_cursor`.

Answers 200 with TaxRateList:

- `object`
- `data`
- `has_more`: Whether another page follows.
- `next_cursor`: Pass it as `starting_after` to read the next page. Absent on the last page.

## GET /v1/categories

List income categories. Lists the active income categories that invoice lines post to. Needs `invoices` read.

**Scope:** `invoices` (Read) · **Rate limit:** 10 requests per second per key, bursts up to 20 · **Errors:** [`scope_missing`](/errors/scope_missing), [`member_lacks_permission`](/errors/member_lacks_permission), and the [common errors](/errors)

- query `kind`: The category kind. Only `income` in phase 1.
- query `active`: `true` (the default) lists active records, `false` inactive ones. Any other value is `400 invalid_request`.
- query `updated_at[gte]`: Only records changed at or after this time (ISO 8601, UTC).
- query `limit`: Page size: 25 by default, at most 100.
- query `starting_after`: The previous page's `next_cursor`.

Answers 200 with CategoryList:

- `object`
- `data`
- `has_more`: Whether another page follows.
- `next_cursor`: Pass it as `starting_after` to read the next page. Absent on the last page.
