<!--
GENERATED FILE — DO NOT EDIT BY HAND.
Produced by scripts/gen_api_reference.py from the live OpenAPI schema,
filtered to routes marked "public": true in tests/e2e/coverage/routes.json.
Hand edits are destroyed on the next run and are caught by
scripts/check_docs_drift.py.
-->

# Accounting Orbit — API reference

Generated from the live OpenAPI schema. Only the **public subset** is listed:
the routes an API key may reach. Everything else in the product is
session-cookie only and is intentionally absent from this page.

## Every request

- Base URL: `https://api.accountingorbit.com` (self-hosted: your backend origin).
- Auth header on every request: `Authorization: Bearer ao_...`.
  Each endpoint below also exposes an optional `authorization` header
  parameter in the raw schema; it is the same header, and it is omitted from
  the per-endpoint parameter tables rather than repeated on every entry.
- All requests are tenant-scoped by the key. There is no tenant parameter.
- `application/json` unless the endpoint says `multipart/form-data`.
- Validation failures return `422` with the FastAPI error shape; see
  [/docs/conventions.md](/docs/conventions.md) for the full error, pagination,
  idempotency and rate-limit rules, and [/docs/auth.md](/docs/auth.md) for
  obtaining and rotating keys.

## Endpoints

**Bank imports & transactions**

- [`POST /api/v1/bank/import`](#post-api-v1-bank-import)
- [`POST /api/v1/bank/import-csv`](#post-api-v1-bank-import-csv)
- [`POST /api/v1/bank/import-pdf`](#post-api-v1-bank-import-pdf)
- [`POST /api/v1/bank/import-qif`](#post-api-v1-bank-import-qif)
- [`GET /api/v1/bank/orphans`](#get-api-v1-bank-orphans)
- [`GET /api/v1/bank/pending`](#get-api-v1-bank-pending)
- [`GET /api/v1/bank/statements`](#get-api-v1-bank-statements)
- [`GET /api/v1/bank/statements/{batch_id}`](#get-api-v1-bank-statements-batch_id)
- [`GET /api/v1/bank/transactions`](#get-api-v1-bank-transactions)
- [`POST /api/v1/bank/transactions/{bank_txn_id}/confirm`](#post-api-v1-bank-transactions-bank_txn_id-confirm)

**Bills (AP)**

- [`GET /api/v1/bills`](#get-api-v1-bills)
- [`POST /api/v1/bills`](#post-api-v1-bills)
- [`PATCH /api/v1/bills/{bill_id}`](#patch-api-v1-bills-bill_id)
- [`POST /api/v1/bills/{bill_id}/approve`](#post-api-v1-bills-bill_id-approve)
- [`POST /api/v1/bills/{bill_id}/pay`](#post-api-v1-bills-bill_id-pay)
- [`POST /api/v1/bills/{bill_id}/post`](#post-api-v1-bills-bill_id-post)
- [`POST /api/v1/bills/{bill_id}/void`](#post-api-v1-bills-bill_id-void)

**Customers**

- [`GET /api/v1/customers`](#get-api-v1-customers)
- [`POST /api/v1/customers`](#post-api-v1-customers)

**Invoices (AR)**

- [`GET /api/v1/invoices`](#get-api-v1-invoices)
- [`POST /api/v1/invoices`](#post-api-v1-invoices)
- [`PATCH /api/v1/invoices/{invoice_id}`](#patch-api-v1-invoices-invoice_id)
- [`POST /api/v1/invoices/{invoice_id}/approve`](#post-api-v1-invoices-invoice_id-approve)
- [`POST /api/v1/invoices/{invoice_id}/pay`](#post-api-v1-invoices-invoice_id-pay)
- [`GET /api/v1/invoices/{invoice_id}/pdf`](#get-api-v1-invoices-invoice_id-pdf)
- [`POST /api/v1/invoices/{invoice_id}/post`](#post-api-v1-invoices-invoice_id-post)
- [`POST /api/v1/invoices/{invoice_id}/refund`](#post-api-v1-invoices-invoice_id-refund)
- [`POST /api/v1/invoices/{invoice_id}/send`](#post-api-v1-invoices-invoice_id-send)
- [`POST /api/v1/invoices/{invoice_id}/void`](#post-api-v1-invoices-invoice_id-void)

**Parties**

- [`GET /api/v1/parties/{party_id}/statement-pdf`](#get-api-v1-parties-party_id-statement-pdf)

**Payments**

- [`POST /api/v1/payments/{payment_id}/reverse`](#post-api-v1-payments-payment_id-reverse)

**Receipts**

- [`GET /api/v1/receipts`](#get-api-v1-receipts)
- [`POST /api/v1/receipts/batch`](#post-api-v1-receipts-batch)
- [`POST /api/v1/receipts/manual`](#post-api-v1-receipts-manual)
- [`GET /api/v1/receipts/orphans`](#get-api-v1-receipts-orphans)
- [`GET /api/v1/receipts/pending`](#get-api-v1-receipts-pending)
- [`POST /api/v1/receipts/{receipt_id}/confirm`](#post-api-v1-receipts-receipt_id-confirm)
- [`GET /api/v1/receipts/{receipt_id}/pdf`](#get-api-v1-receipts-receipt_id-pdf)
- [`POST /api/v1/receipts/{receipt_id}/reject`](#post-api-v1-receipts-receipt_id-reject)
- [`POST /api/v1/receipts/{receipt_id}/update`](#post-api-v1-receipts-receipt_id-update)

**Reports & exports**

- [`GET /api/v1/reports`](#get-api-v1-reports)
- [`GET /api/v1/reports/export-csv`](#get-api-v1-reports-export-csv)
- [`GET /api/v1/reports/export-pdf`](#get-api-v1-reports-export-pdf)

**Financial statements**

- [`GET /api/v1/statements/balance-sheet`](#get-api-v1-statements-balance-sheet)
- [`GET /api/v1/statements/cash-flow`](#get-api-v1-statements-cash-flow)
- [`GET /api/v1/statements/customer/{party_id}`](#get-api-v1-statements-customer-party_id)
- [`GET /api/v1/statements/income-statement`](#get-api-v1-statements-income-statement)
- [`GET /api/v1/statements/retained-earnings`](#get-api-v1-statements-retained-earnings)
- [`GET /api/v1/statements/trial-balance`](#get-api-v1-statements-trial-balance)

**Vendors**

- [`GET /api/v1/vendors`](#get-api-v1-vendors)
- [`POST /api/v1/vendors`](#post-api-v1-vendors)

---

## Bank imports & transactions

### `POST /api/v1/bank/import`

Import Json.

**Parameters**

None.

**Request body** (`application/json`, required)

| field | type | required | default |
|---|---|---|---|
| `gl_account_id` | integer | yes |  |
| `rows` | array<object> | yes |  |
| `rows.amount` | number \| string | yes |  |
| `rows.description` | string | no | `""` |
| `rows.external_id` | string, nullable | no |  |
| `rows.txn_date` | string (date) | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `imported` | integer | yes |  |
| `overlap_warning` | object, nullable | no |  |
| `skipped` | integer | yes |  |
| `updated` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/bank/import-csv`

Import Csv.

CSV columns: date, amount, description, external_id?

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `gl_account_id` | query | integer | yes |  |

**Request body** (`multipart/form-data`, required)

| field | type | required | default |
|---|---|---|---|
| `file` | binary (file upload) | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `batch_id` | integer, nullable | no |  |
| `control_matched` | boolean | yes |  |
| `control_total` | string | yes |  |
| `imported` | integer | yes |  |
| `imported_total` | string | yes |  |
| `overlap_warning` | object, nullable | no |  |
| `skipped` | integer | yes |  |
| `updated` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/bank/import-pdf`

Import Pdf.

PDF bank statement.

Gemini is the only extractor — no rows means no rows, never a lower-quality
substitute. Rows carry a content-derived external_id so re-uploading the
same statement dedupes instead of double-importing. Control total = sum of
parsed rows vs sum imported.

The pipeline lives in import_statement_bytes() so the generic uploader can
reach the same code instead of growing a parallel one.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `gl_account_id` | query | integer | yes |  |

**Request body** (`multipart/form-data`, required)

| field | type | required | default |
|---|---|---|---|
| `file` | binary (file upload) | yes |  |

**Response `200`** (`application/json`)

_Not declared in the OpenAPI schema — this operation has no declared response model. Treat the body as an untyped JSON object._

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/bank/import-qif`

Import Qif.

Import Quicken Interchange Format (QIF) bank transactions.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `gl_account_id` | query | integer | yes |  |

**Request body** (`multipart/form-data`, required)

| field | type | required | default |
|---|---|---|---|
| `file` | binary (file upload) | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `batch_id` | integer, nullable | no |  |
| `error` | string, nullable | no |  |
| `imported` | integer | yes |  |
| `overlap_warning` | object, nullable | no |  |
| `skipped` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/bank/orphans`

List Orphan Bank Transactions.

Return one page of bank transactions without an attached receipt.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `limit` | query | integer | no | `200` |
| `offset` | query | integer | no | `0` |

**Response `200`** (`application/json`)

_Not declared in the OpenAPI schema — this operation has no declared response model. Treat the body as an untyped JSON object._

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/bank/pending`

List Pending Transactions.

List all pending (greyed) transactions — preview only, never matchable.

**Parameters**

None.

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `count` | integer | yes |  |
| `note` | string | yes |  |
| `pending` | array<object> | yes |  |
| `pending.amount` | string | yes |  |
| `pending.description` | string | yes |  |
| `pending.id` | integer, nullable | no |  |
| `pending.status` | string | yes |  |
| `pending.txn_date` | string | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/bank/statements`

List Statements.

Every import batch (statement/CSV/QIF/sync) with per-batch rollups.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `limit` | query | integer | no | `100` |
| `offset` | query | integer | no | `0` |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `has_more` | boolean | yes |  |
| `items` | array<object> | yes |  |
| `items.account_name` | string, nullable | no |  |
| `items.confirmed` | boolean | yes |  |
| `items.created_at` | string, nullable | no |  |
| `items.excluded_count` | integer | yes |  |
| `items.filename` | string, nullable | no |  |
| `items.id` | integer, nullable | no |  |
| `items.imported_count` | integer, nullable | no |  |
| `items.matched_count` | integer | yes |  |
| `items.period_end` | string, nullable | no |  |
| `items.period_start` | string, nullable | no |  |
| `items.reviewed_count` | integer | yes |  |
| `items.skipped_count` | integer, nullable | no |  |
| `items.source` | string, nullable | no |  |
| `items.total_in` | string | yes |  |
| `items.total_out` | string | yes |  |
| `items.txn_count` | integer | yes |  |
| `items.unreviewed_count` | integer | yes |  |
| `limit` | integer | yes |  |
| `offset` | integer | yes |  |
| `total` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/bank/statements/{batch_id}`

Statement Detail.

One statement's transactions — the per-row review workspace.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `batch_id` | path | integer | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `created_at` | string, nullable | no |  |
| `filename` | string, nullable | no |  |
| `id` | integer, nullable | no |  |
| `period_end` | string, nullable | no |  |
| `period_start` | string, nullable | no |  |
| `source` | string, nullable | no |  |
| `transactions` | array<object> | yes |  |
| `transactions.amount` | string | yes |  |
| `transactions.category` | string, nullable | no |  |
| `transactions.category_is_provisional` | boolean | no | `false` |
| `transactions.category_source` | string, nullable | no |  |
| `transactions.description` | string | yes |  |
| `transactions.dispute_status` | string, nullable | no |  |
| `transactions.exclude_reason` | string, nullable | no |  |
| `transactions.external_id` | string, nullable | no |  |
| `transactions.flag_note` | string, nullable | no |  |
| `transactions.flagged_for_review` | boolean | yes |  |
| `transactions.gl_account_id` | integer | yes |  |
| `transactions.group_id` | string, nullable | no |  |
| `transactions.id` | integer, nullable | no |  |
| `transactions.import_batch_id` | integer, nullable | no |  |
| `transactions.imported_at` | string (date-time) | yes |  |
| `transactions.is_fee` | boolean | yes |  |
| `transactions.last_edited_at` | string (date-time), nullable | no |  |
| `transactions.linked_transaction_id` | integer, nullable | no |  |
| `transactions.matched_entry_id` | integer, nullable | no |  |
| `transactions.matched_posting_id` | integer, nullable | no |  |
| `transactions.modified_at` | string (date-time), nullable | no |  |
| `transactions.needs_review` | boolean | yes |  |
| `transactions.parent_txn_id` | integer, nullable | no |  |
| `transactions.processor` | string, nullable | no |  |
| `transactions.raw_data` | string, nullable | no |  |
| `transactions.receipt_file` | string, nullable | no |  |
| `transactions.receipt_status` | string | yes |  |
| `transactions.review_reason` | string, nullable | no |  |
| `transactions.review_status` | string | yes |  |
| `transactions.reviewed_at` | string (date-time), nullable | no |  |
| `transactions.reviewed_by` | string, nullable | no |  |
| `transactions.revision_detected_at` | string (date-time), nullable | no |  |
| `transactions.revision_of_id` | integer, nullable | no |  |
| `transactions.source_connection_id` | integer, nullable | no |  |
| `transactions.status` | string | yes |  |
| `transactions.suggested_account_id` | integer, nullable | no |  |
| `transactions.tenant_id` | integer | yes |  |
| `transactions.txn_date` | string (date) | yes |  |
| `transactions.txn_status` | string | yes |  |
| `transactions.txn_type` | string | yes |  |
| `transactions.viewed_at` | string (date-time), nullable | no |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/bank/transactions`

List Transactions.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `account_code` | query | string, nullable | no |  |
| `category` | query | string, nullable | no |  |
| `end_date` | query | string (date), nullable | no |  |
| `gl_account_id` | query | integer, nullable | no |  |
| `limit` | query | integer | no | `200` |
| `match_status` | query | string, nullable | no |  |
| `max_amount` | query | number \| string, nullable | no |  |
| `min_amount` | query | number \| string, nullable | no |  |
| `offset` | query | integer | no | `0` |
| `q` | query | string, nullable | no |  |
| `receipt_status` | query | string, nullable | no |  |
| `review_status` | query | string, nullable | no |  |
| `sort_dir` | query | string | no | `"asc"` |
| `sort_field` | query | string | no | `"txn_date"` |
| `start_date` | query | string (date), nullable | no |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `has_more` | boolean | yes |  |
| `items` | array<object> | yes |  |
| `items.amount` | number | yes |  |
| `items.category` | string, nullable | no |  |
| `items.category_is_provisional` | boolean | no | `false` |
| `items.category_source` | string, nullable | no |  |
| `items.description` | string | yes |  |
| `items.dispute_status` | string, nullable | no |  |
| `items.exclude_reason` | string, nullable | no |  |
| `items.external_id` | string, nullable | no |  |
| `items.flag_note` | string, nullable | no |  |
| `items.flagged_for_review` | boolean | yes |  |
| `items.gl_account_id` | integer | yes |  |
| `items.group_id` | string, nullable | no |  |
| `items.id` | integer, nullable | no |  |
| `items.import_batch_id` | integer, nullable | no |  |
| `items.imported_at` | string (date-time) | yes |  |
| `items.is_fee` | boolean | yes |  |
| `items.last_edited_at` | string (date-time), nullable | no |  |
| `items.linked_transaction_id` | integer, nullable | no |  |
| `items.matched_entry_id` | integer, nullable | no |  |
| `items.matched_posting_id` | integer, nullable | no |  |
| `items.modified_at` | string (date-time), nullable | no |  |
| `items.needs_review` | boolean | yes |  |
| `items.parent_txn_id` | integer, nullable | no |  |
| `items.processor` | string, nullable | no |  |
| `items.raw_data` | string, nullable | no |  |
| `items.receipt_file` | string, nullable | no |  |
| `items.receipt_status` | string | yes |  |
| `items.review_reason` | string, nullable | no |  |
| `items.review_status` | string | yes |  |
| `items.reviewed_at` | string (date-time), nullable | no |  |
| `items.reviewed_by` | string, nullable | no |  |
| `items.revision_detected_at` | string (date-time), nullable | no |  |
| `items.revision_of_id` | integer, nullable | no |  |
| `items.source_connection_id` | integer, nullable | no |  |
| `items.status` | string | yes |  |
| `items.suggested_account_id` | integer, nullable | no |  |
| `items.tenant_id` | integer | yes |  |
| `items.txn_date` | string (date) | yes |  |
| `items.txn_status` | string | yes |  |
| `items.txn_type` | string | yes |  |
| `items.viewed_at` | string (date-time), nullable | no |  |
| `limit` | integer | yes |  |
| `offset` | integer | yes |  |
| `total` | integer | yes |  |
| `transactions` | array<object> | yes |  |
| `transactions.amount` | number | yes |  |
| `transactions.category` | string, nullable | no |  |
| `transactions.category_is_provisional` | boolean | no | `false` |
| `transactions.category_source` | string, nullable | no |  |
| `transactions.description` | string | yes |  |
| `transactions.dispute_status` | string, nullable | no |  |
| `transactions.exclude_reason` | string, nullable | no |  |
| `transactions.external_id` | string, nullable | no |  |
| `transactions.flag_note` | string, nullable | no |  |
| `transactions.flagged_for_review` | boolean | yes |  |
| `transactions.gl_account_id` | integer | yes |  |
| `transactions.group_id` | string, nullable | no |  |
| `transactions.id` | integer, nullable | no |  |
| `transactions.import_batch_id` | integer, nullable | no |  |
| `transactions.imported_at` | string (date-time) | yes |  |
| `transactions.is_fee` | boolean | yes |  |
| `transactions.last_edited_at` | string (date-time), nullable | no |  |
| `transactions.linked_transaction_id` | integer, nullable | no |  |
| `transactions.matched_entry_id` | integer, nullable | no |  |
| `transactions.matched_posting_id` | integer, nullable | no |  |
| `transactions.modified_at` | string (date-time), nullable | no |  |
| `transactions.needs_review` | boolean | yes |  |
| `transactions.parent_txn_id` | integer, nullable | no |  |
| `transactions.processor` | string, nullable | no |  |
| `transactions.raw_data` | string, nullable | no |  |
| `transactions.receipt_file` | string, nullable | no |  |
| `transactions.receipt_status` | string | yes |  |
| `transactions.review_reason` | string, nullable | no |  |
| `transactions.review_status` | string | yes |  |
| `transactions.reviewed_at` | string (date-time), nullable | no |  |
| `transactions.reviewed_by` | string, nullable | no |  |
| `transactions.revision_detected_at` | string (date-time), nullable | no |  |
| `transactions.revision_of_id` | integer, nullable | no |  |
| `transactions.source_connection_id` | integer, nullable | no |  |
| `transactions.status` | string | yes |  |
| `transactions.suggested_account_id` | integer, nullable | no |  |
| `transactions.tenant_id` | integer | yes |  |
| `transactions.txn_date` | string (date) | yes |  |
| `transactions.txn_status` | string | yes |  |
| `transactions.txn_type` | string | yes |  |
| `transactions.viewed_at` | string (date-time), nullable | no |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/bank/transactions/{bank_txn_id}/confirm`

Confirm.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `bank_txn_id` | path | integer | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `amount` | string | yes |  |
| `category` | string, nullable | no |  |
| `category_is_provisional` | boolean | no | `false` |
| `category_source` | string, nullable | no |  |
| `description` | string | yes |  |
| `dispute_status` | string, nullable | no |  |
| `exclude_reason` | string, nullable | no |  |
| `external_id` | string, nullable | no |  |
| `flag_note` | string, nullable | no |  |
| `flagged_for_review` | boolean | yes |  |
| `gl_account_id` | integer | yes |  |
| `group_id` | string, nullable | no |  |
| `id` | integer, nullable | no |  |
| `import_batch_id` | integer, nullable | no |  |
| `imported_at` | string (date-time) | yes |  |
| `is_fee` | boolean | yes |  |
| `last_edited_at` | string (date-time), nullable | no |  |
| `linked_transaction_id` | integer, nullable | no |  |
| `matched_entry_id` | integer, nullable | no |  |
| `matched_posting_id` | integer, nullable | no |  |
| `modified_at` | string (date-time), nullable | no |  |
| `needs_review` | boolean | yes |  |
| `parent_txn_id` | integer, nullable | no |  |
| `processor` | string, nullable | no |  |
| `raw_data` | string, nullable | no |  |
| `receipt_file` | string, nullable | no |  |
| `receipt_status` | string | yes |  |
| `review_reason` | string, nullable | no |  |
| `review_status` | string | yes |  |
| `reviewed_at` | string (date-time), nullable | no |  |
| `reviewed_by` | string, nullable | no |  |
| `revision_detected_at` | string (date-time), nullable | no |  |
| `revision_of_id` | integer, nullable | no |  |
| `source_connection_id` | integer, nullable | no |  |
| `status` | string | yes |  |
| `suggested_account_id` | integer, nullable | no |  |
| `tenant_id` | integer | yes |  |
| `txn_date` | string (date) | yes |  |
| `txn_status` | string | yes |  |
| `txn_type` | string | yes |  |
| `viewed_at` | string (date-time), nullable | no |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

---

## Bills (AP)

### `GET /api/v1/bills`

List Bills.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `limit` | query | integer | no | `100` |
| `offset` | query | integer | no | `0` |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `has_more` | boolean | yes |  |
| `items` | array<object> | yes |  |
| `items.amount_paid` | string | yes |  |
| `items.approved_at` | string (date-time), nullable | no |  |
| `items.approved_by` | string, nullable | no |  |
| `items.created_at` | string (date-time) | yes |  |
| `items.created_by` | integer, nullable | no |  |
| `items.currency` | string | yes |  |
| `items.due_date` | string (date) | yes |  |
| `items.id` | integer, nullable | no |  |
| `items.is_approved` | boolean | yes |  |
| `items.issue_date` | string (date) | yes |  |
| `items.journal_entry_id` | integer, nullable | no |  |
| `items.memo` | string, nullable | no |  |
| `items.number` | string | yes |  |
| `items.receipt_file` | string, nullable | no |  |
| `items.status` | string | yes |  |
| `items.subtotal` | string | yes |  |
| `items.tax_total` | string | yes |  |
| `items.tenant_id` | integer | yes |  |
| `items.total` | string | yes |  |
| `items.vendor_id` | integer | yes |  |
| `items.version` | integer | yes |  |
| `limit` | integer | yes |  |
| `offset` | integer | yes |  |
| `total` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/bills`

Create Bill.

**Parameters**

None.

**Request body** (`application/json`, required)

| field | type | required | default |
|---|---|---|---|
| `currency` | string | no | `"USD"` |
| `draft` | boolean | no | `false` |
| `due_date` | string (date), nullable | no |  |
| `issue_date` | string (date) | yes |  |
| `lines` | array<object> | yes |  |
| `lines.description` | string | yes |  |
| `lines.expense_account_id` | integer, nullable | no |  |
| `lines.quantity` | number \| string | no | `"1"` |
| `lines.revenue_account_id` | integer, nullable | no |  |
| `lines.tax_rate_id` | integer, nullable | no |  |
| `lines.unit_price` | number \| string | yes |  |
| `memo` | string, nullable | no |  |
| `number` | string | no | `""` |
| `receipt_file` | string, nullable | no |  |
| `vendor_id` | integer | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `amount_paid` | string | yes |  |
| `approved_at` | string (date-time), nullable | no |  |
| `approved_by` | string, nullable | no |  |
| `created_at` | string (date-time) | yes |  |
| `created_by` | integer, nullable | no |  |
| `currency` | string | yes |  |
| `due_date` | string (date) | yes |  |
| `id` | integer, nullable | no |  |
| `is_approved` | boolean | yes |  |
| `issue_date` | string (date) | yes |  |
| `journal_entry_id` | integer, nullable | no |  |
| `memo` | string, nullable | no |  |
| `number` | string | yes |  |
| `receipt_file` | string, nullable | no |  |
| `status` | string | yes |  |
| `subtotal` | string | yes |  |
| `tax_total` | string | yes |  |
| `tenant_id` | integer | yes |  |
| `total` | string | yes |  |
| `vendor_id` | integer | yes |  |
| `version` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `PATCH /api/v1/bills/{bill_id}`

Patch Bill.

DEL4 — edit an unpaid bill (dates, memo).

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `bill_id` | path | integer | yes |  |

**Request body** (`application/json`, required)

| field | type | required | default |
|---|---|---|---|
| `due_date` | string (date), nullable | no |  |
| `expected_version` | integer, nullable | no |  |
| `issue_date` | string (date), nullable | no |  |
| `memo` | string, nullable | no |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `amount_paid` | string | yes |  |
| `approved_at` | string (date-time), nullable | no |  |
| `approved_by` | string, nullable | no |  |
| `created_at` | string (date-time) | yes |  |
| `created_by` | integer, nullable | no |  |
| `currency` | string | yes |  |
| `due_date` | string (date) | yes |  |
| `id` | integer, nullable | no |  |
| `is_approved` | boolean | yes |  |
| `issue_date` | string (date) | yes |  |
| `journal_entry_id` | integer, nullable | no |  |
| `memo` | string, nullable | no |  |
| `number` | string | yes |  |
| `receipt_file` | string, nullable | no |  |
| `status` | string | yes |  |
| `subtotal` | string | yes |  |
| `tax_total` | string | yes |  |
| `tenant_id` | integer | yes |  |
| `total` | string | yes |  |
| `vendor_id` | integer | yes |  |
| `version` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/bills/{bill_id}/approve`

Approve Bill.

P54 — mark a bill as approved. WL-10 duty separation enforced.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `bill_id` | path | integer | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `amount_paid` | string | yes |  |
| `approved_at` | string (date-time), nullable | no |  |
| `approved_by` | string, nullable | no |  |
| `created_at` | string (date-time) | yes |  |
| `created_by` | integer, nullable | no |  |
| `currency` | string | yes |  |
| `due_date` | string (date) | yes |  |
| `id` | integer, nullable | no |  |
| `is_approved` | boolean | yes |  |
| `issue_date` | string (date) | yes |  |
| `journal_entry_id` | integer, nullable | no |  |
| `memo` | string, nullable | no |  |
| `number` | string | yes |  |
| `receipt_file` | string, nullable | no |  |
| `status` | string | yes |  |
| `subtotal` | string | yes |  |
| `tax_total` | string | yes |  |
| `tenant_id` | integer | yes |  |
| `total` | string | yes |  |
| `vendor_id` | integer | yes |  |
| `version` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/bills/{bill_id}/pay`

Pay Bill.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `bill_id` | path | integer | yes |  |

**Request body** (`application/json`, required)

| field | type | required | default |
|---|---|---|---|
| `amount` | number \| string | yes |  |
| `cash_account_code` | string | no | `"1000"` |
| `idempotency_key` | string, nullable | no |  |
| `payment_date` | string (date) | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `amount` | string | yes |  |
| `bill_id` | integer, nullable | no |  |
| `cash_account_id` | integer | yes |  |
| `created_at` | string (date-time) | yes |  |
| `direction` | string | yes |  |
| `id` | integer, nullable | no |  |
| `idempotency_key` | string, nullable | no |  |
| `invoice_id` | integer, nullable | no |  |
| `journal_entry_id` | integer, nullable | no |  |
| `party_id` | integer | yes |  |
| `party_kind` | string | yes |  |
| `payment_date` | string (date) | yes |  |
| `tenant_id` | integer | yes |  |
| `version` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/bills/{bill_id}/post`

Post Bill.

H9 — post a draft bill to the GL (draft → open).

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `bill_id` | path | integer | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `amount_paid` | string | yes |  |
| `approved_at` | string (date-time), nullable | no |  |
| `approved_by` | string, nullable | no |  |
| `created_at` | string (date-time) | yes |  |
| `created_by` | integer, nullable | no |  |
| `currency` | string | yes |  |
| `due_date` | string (date) | yes |  |
| `id` | integer, nullable | no |  |
| `is_approved` | boolean | yes |  |
| `issue_date` | string (date) | yes |  |
| `journal_entry_id` | integer, nullable | no |  |
| `memo` | string, nullable | no |  |
| `number` | string | yes |  |
| `receipt_file` | string, nullable | no |  |
| `status` | string | yes |  |
| `subtotal` | string | yes |  |
| `tax_total` | string | yes |  |
| `tenant_id` | integer | yes |  |
| `total` | string | yes |  |
| `vendor_id` | integer | yes |  |
| `version` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/bills/{bill_id}/void`

Void Bill.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `bill_id` | path | integer | yes |  |

**Request body** (`application/json`, optional)

Type: `object, nullable`

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `amount_paid` | string | yes |  |
| `approved_at` | string (date-time), nullable | no |  |
| `approved_by` | string, nullable | no |  |
| `created_at` | string (date-time) | yes |  |
| `created_by` | integer, nullable | no |  |
| `currency` | string | yes |  |
| `due_date` | string (date) | yes |  |
| `id` | integer, nullable | no |  |
| `is_approved` | boolean | yes |  |
| `issue_date` | string (date) | yes |  |
| `journal_entry_id` | integer, nullable | no |  |
| `memo` | string, nullable | no |  |
| `number` | string | yes |  |
| `receipt_file` | string, nullable | no |  |
| `status` | string | yes |  |
| `subtotal` | string | yes |  |
| `tax_total` | string | yes |  |
| `tenant_id` | integer | yes |  |
| `total` | string | yes |  |
| `vendor_id` | integer | yes |  |
| `version` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

---

## Customers

### `GET /api/v1/customers`

List Customers.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `limit` | query | integer | no | `200` |
| `offset` | query | integer | no | `0` |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `has_more` | boolean | yes |  |
| `items` | array<object> | yes |  |
| `items.address_line1` | string, nullable | no |  |
| `items.address_line2` | string, nullable | no |  |
| `items.city` | string, nullable | no |  |
| `items.country` | string, nullable | no |  |
| `items.created_at` | string (date-time) | yes |  |
| `items.credit_limit` | string, nullable | no |  |
| `items.credit_period_days` | integer | yes |  |
| `items.default_currency` | string | yes |  |
| `items.default_rate` | string, nullable | no |  |
| `items.display_name` | string, nullable | no |  |
| `items.email` | string, nullable | no |  |
| `items.id` | integer, nullable | no |  |
| `items.is_active` | boolean | yes |  |
| `items.is_client` | boolean | yes |  |
| `items.is_contractor` | boolean | yes |  |
| `items.name` | string | yes |  |
| `items.party_type` | string | yes |  |
| `items.payable_account_id` | integer, nullable | no |  |
| `items.phone` | string, nullable | no |  |
| `items.postal_code` | string, nullable | no |  |
| `items.receivable_account_id` | integer, nullable | no |  |
| `items.state` | string, nullable | no |  |
| `items.tax_id` | string, nullable | no |  |
| `items.tenant_id` | integer | yes |  |
| `items.updated_at` | string (date-time) | yes |  |
| `items.w9_received` | boolean | yes |  |
| `items.w9_requested_at` | string (date-time), nullable | no |  |
| `items.ytd_1099_amount` | string | yes |  |
| `limit` | integer | yes |  |
| `offset` | integer | yes |  |
| `total` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/customers`

Create Customer.

**Parameters**

None.

**Request body** (`application/json`, required)

| field | type | required | default |
|---|---|---|---|
| `email` | string, nullable | no |  |
| `name` | string | yes |  |
| `terms_days` | integer | no | `30` |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `address_line1` | string, nullable | no |  |
| `address_line2` | string, nullable | no |  |
| `city` | string, nullable | no |  |
| `country` | string, nullable | no |  |
| `created_at` | string (date-time) | yes |  |
| `credit_limit` | string, nullable | no |  |
| `credit_period_days` | integer | yes |  |
| `default_currency` | string | yes |  |
| `default_rate` | string, nullable | no |  |
| `display_name` | string, nullable | no |  |
| `email` | string, nullable | no |  |
| `id` | integer, nullable | no |  |
| `is_active` | boolean | yes |  |
| `is_client` | boolean | yes |  |
| `is_contractor` | boolean | yes |  |
| `name` | string | yes |  |
| `party_type` | string | yes |  |
| `payable_account_id` | integer, nullable | no |  |
| `phone` | string, nullable | no |  |
| `postal_code` | string, nullable | no |  |
| `receivable_account_id` | integer, nullable | no |  |
| `state` | string, nullable | no |  |
| `tax_id` | string, nullable | no |  |
| `tenant_id` | integer | yes |  |
| `updated_at` | string (date-time) | yes |  |
| `w9_received` | boolean | yes |  |
| `w9_requested_at` | string (date-time), nullable | no |  |
| `ytd_1099_amount` | string | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

---

## Invoices (AR)

### `GET /api/v1/invoices`

List Invoices.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `limit` | query | integer | no | `200` |
| `offset` | query | integer | no | `0` |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `has_more` | boolean | yes |  |
| `items` | array<object> | yes |  |
| `items.amount_paid` | string | yes |  |
| `items.approved_at` | string (date-time), nullable | no |  |
| `items.approved_by` | string, nullable | no |  |
| `items.created_at` | string (date-time) | yes |  |
| `items.created_by` | integer, nullable | no |  |
| `items.currency` | string | yes |  |
| `items.customer_id` | integer | yes |  |
| `items.due_date` | string (date) | yes |  |
| `items.estimate_id` | integer, nullable | no |  |
| `items.id` | integer, nullable | no |  |
| `items.is_approved` | boolean | yes |  |
| `items.is_estimate` | boolean | yes |  |
| `items.issue_date` | string (date) | yes |  |
| `items.journal_entry_id` | integer, nullable | no |  |
| `items.memo` | string, nullable | no |  |
| `items.number` | string | yes |  |
| `items.receipt_file` | string, nullable | no |  |
| `items.sent_at` | string (date-time), nullable | no |  |
| `items.sent_to` | string, nullable | no |  |
| `items.status` | string | yes |  |
| `items.subtotal` | string | yes |  |
| `items.tax_total` | string | yes |  |
| `items.tenant_id` | integer | yes |  |
| `items.total` | string | yes |  |
| `items.version` | integer | yes |  |
| `items.viewed_at` | string (date-time), nullable | no |  |
| `limit` | integer | yes |  |
| `offset` | integer | yes |  |
| `total` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/invoices`

Create Invoice.

**Parameters**

None.

**Request body** (`application/json`, required)

| field | type | required | default |
|---|---|---|---|
| `currency` | string | no | `"USD"` |
| `customer_id` | integer | yes |  |
| `draft` | boolean | no | `false` |
| `due_date` | string (date), nullable | no |  |
| `issue_date` | string (date) | yes |  |
| `lines` | array<object> | yes |  |
| `lines.description` | string | yes |  |
| `lines.expense_account_id` | integer, nullable | no |  |
| `lines.quantity` | number \| string | no | `"1"` |
| `lines.revenue_account_id` | integer, nullable | no |  |
| `lines.tax_rate_id` | integer, nullable | no |  |
| `lines.unit_price` | number \| string | yes |  |
| `memo` | string, nullable | no |  |
| `number` | string | no | `""` |
| `receipt_file` | string, nullable | no |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `amount_paid` | string | yes |  |
| `approved_at` | string (date-time), nullable | no |  |
| `approved_by` | string, nullable | no |  |
| `created_at` | string (date-time) | yes |  |
| `created_by` | integer, nullable | no |  |
| `currency` | string | yes |  |
| `customer_id` | integer | yes |  |
| `due_date` | string (date) | yes |  |
| `estimate_id` | integer, nullable | no |  |
| `id` | integer, nullable | no |  |
| `is_approved` | boolean | yes |  |
| `is_estimate` | boolean | yes |  |
| `issue_date` | string (date) | yes |  |
| `journal_entry_id` | integer, nullable | no |  |
| `memo` | string, nullable | no |  |
| `number` | string | yes |  |
| `receipt_file` | string, nullable | no |  |
| `sent_at` | string (date-time), nullable | no |  |
| `sent_to` | string, nullable | no |  |
| `status` | string | yes |  |
| `subtotal` | string | yes |  |
| `tax_total` | string | yes |  |
| `tenant_id` | integer | yes |  |
| `total` | string | yes |  |
| `version` | integer | yes |  |
| `viewed_at` | string (date-time), nullable | no |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `PATCH /api/v1/invoices/{invoice_id}`

Patch Invoice.

DEL4 — edit an unpaid, unsent invoice (dates, memo).

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `invoice_id` | path | integer | yes |  |

**Request body** (`application/json`, required)

| field | type | required | default |
|---|---|---|---|
| `due_date` | string (date), nullable | no |  |
| `expected_version` | integer, nullable | no |  |
| `issue_date` | string (date), nullable | no |  |
| `memo` | string, nullable | no |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `amount_paid` | string | yes |  |
| `approved_at` | string (date-time), nullable | no |  |
| `approved_by` | string, nullable | no |  |
| `created_at` | string (date-time) | yes |  |
| `created_by` | integer, nullable | no |  |
| `currency` | string | yes |  |
| `customer_id` | integer | yes |  |
| `due_date` | string (date) | yes |  |
| `estimate_id` | integer, nullable | no |  |
| `id` | integer, nullable | no |  |
| `is_approved` | boolean | yes |  |
| `is_estimate` | boolean | yes |  |
| `issue_date` | string (date) | yes |  |
| `journal_entry_id` | integer, nullable | no |  |
| `memo` | string, nullable | no |  |
| `number` | string | yes |  |
| `receipt_file` | string, nullable | no |  |
| `sent_at` | string (date-time), nullable | no |  |
| `sent_to` | string, nullable | no |  |
| `status` | string | yes |  |
| `subtotal` | string | yes |  |
| `tax_total` | string | yes |  |
| `tenant_id` | integer | yes |  |
| `total` | string | yes |  |
| `version` | integer | yes |  |
| `viewed_at` | string (date-time), nullable | no |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/invoices/{invoice_id}/approve`

Approve Invoice.

P54 — mark an invoice as approved.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `invoice_id` | path | integer | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `amount_paid` | string | yes |  |
| `approved_at` | string (date-time), nullable | no |  |
| `approved_by` | string, nullable | no |  |
| `created_at` | string (date-time) | yes |  |
| `created_by` | integer, nullable | no |  |
| `currency` | string | yes |  |
| `customer_id` | integer | yes |  |
| `due_date` | string (date) | yes |  |
| `estimate_id` | integer, nullable | no |  |
| `id` | integer, nullable | no |  |
| `is_approved` | boolean | yes |  |
| `is_estimate` | boolean | yes |  |
| `issue_date` | string (date) | yes |  |
| `journal_entry_id` | integer, nullable | no |  |
| `memo` | string, nullable | no |  |
| `number` | string | yes |  |
| `receipt_file` | string, nullable | no |  |
| `sent_at` | string (date-time), nullable | no |  |
| `sent_to` | string, nullable | no |  |
| `status` | string | yes |  |
| `subtotal` | string | yes |  |
| `tax_total` | string | yes |  |
| `tenant_id` | integer | yes |  |
| `total` | string | yes |  |
| `version` | integer | yes |  |
| `viewed_at` | string (date-time), nullable | no |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/invoices/{invoice_id}/pay`

Pay Invoice.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `invoice_id` | path | integer | yes |  |

**Request body** (`application/json`, required)

| field | type | required | default |
|---|---|---|---|
| `amount` | number \| string | yes |  |
| `cash_account_code` | string | no | `"1000"` |
| `idempotency_key` | string, nullable | no |  |
| `payment_date` | string (date) | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `amount` | string | yes |  |
| `bill_id` | integer, nullable | no |  |
| `cash_account_id` | integer | yes |  |
| `created_at` | string (date-time) | yes |  |
| `direction` | string | yes |  |
| `id` | integer, nullable | no |  |
| `idempotency_key` | string, nullable | no |  |
| `invoice_id` | integer, nullable | no |  |
| `journal_entry_id` | integer, nullable | no |  |
| `party_id` | integer | yes |  |
| `party_kind` | string | yes |  |
| `payment_date` | string (date) | yes |  |
| `tenant_id` | integer | yes |  |
| `version` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/invoices/{invoice_id}/pdf`

Invoice Pdf.

PDF preview — same document the customer receives.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `invoice_id` | path | integer | yes |  |

**Response `200`** (`application/pdf`)

Type: `string (binary)`

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/invoices/{invoice_id}/post`

Post Invoice.

H9 — post a draft invoice to the GL (draft → open).

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `invoice_id` | path | integer | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `amount_paid` | string | yes |  |
| `approved_at` | string (date-time), nullable | no |  |
| `approved_by` | string, nullable | no |  |
| `created_at` | string (date-time) | yes |  |
| `created_by` | integer, nullable | no |  |
| `currency` | string | yes |  |
| `customer_id` | integer | yes |  |
| `due_date` | string (date) | yes |  |
| `estimate_id` | integer, nullable | no |  |
| `id` | integer, nullable | no |  |
| `is_approved` | boolean | yes |  |
| `is_estimate` | boolean | yes |  |
| `issue_date` | string (date) | yes |  |
| `journal_entry_id` | integer, nullable | no |  |
| `memo` | string, nullable | no |  |
| `number` | string | yes |  |
| `receipt_file` | string, nullable | no |  |
| `sent_at` | string (date-time), nullable | no |  |
| `sent_to` | string, nullable | no |  |
| `status` | string | yes |  |
| `subtotal` | string | yes |  |
| `tax_total` | string | yes |  |
| `tenant_id` | integer | yes |  |
| `total` | string | yes |  |
| `version` | integer | yes |  |
| `viewed_at` | string (date-time), nullable | no |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/invoices/{invoice_id}/refund`

Refund Invoice.

Refund a (partly) paid invoice: issues a mirroring credit memo and pays
it out in cash in one step.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `invoice_id` | path | integer | yes |  |

**Request body** (`application/json`, required)

| field | type | required | default |
|---|---|---|---|
| `amount` | number \| string, nullable | no |  |
| `cash_account_code` | string | no | `"1000"` |
| `number` | string, nullable | no |  |
| `payment_date` | string (date) | yes |  |
| `reason` | string, nullable | no |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `credit_memo_id` | integer | yes |  |
| `credit_memo_number` | string | yes |  |
| `refund_payment_id` | integer | yes |  |
| `refunded` | string | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/invoices/{invoice_id}/send`

Send Invoice.

Email the invoice PDF to the customer with an open-tracking pixel.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `invoice_id` | path | integer | yes |  |

**Request body** (`application/json`, required)

| field | type | required | default |
|---|---|---|---|
| `to_email` | string, nullable | no |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `sent_at` | string | yes |  |
| `status` | string | yes |  |
| `to` | string | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/invoices/{invoice_id}/void`

Void Invoice.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `invoice_id` | path | integer | yes |  |

**Request body** (`application/json`, optional)

Type: `object, nullable`

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `amount_paid` | string | yes |  |
| `approved_at` | string (date-time), nullable | no |  |
| `approved_by` | string, nullable | no |  |
| `created_at` | string (date-time) | yes |  |
| `created_by` | integer, nullable | no |  |
| `currency` | string | yes |  |
| `customer_id` | integer | yes |  |
| `due_date` | string (date) | yes |  |
| `estimate_id` | integer, nullable | no |  |
| `id` | integer, nullable | no |  |
| `is_approved` | boolean | yes |  |
| `is_estimate` | boolean | yes |  |
| `issue_date` | string (date) | yes |  |
| `journal_entry_id` | integer, nullable | no |  |
| `memo` | string, nullable | no |  |
| `number` | string | yes |  |
| `receipt_file` | string, nullable | no |  |
| `sent_at` | string (date-time), nullable | no |  |
| `sent_to` | string, nullable | no |  |
| `status` | string | yes |  |
| `subtotal` | string | yes |  |
| `tax_total` | string | yes |  |
| `tenant_id` | integer | yes |  |
| `total` | string | yes |  |
| `version` | integer | yes |  |
| `viewed_at` | string (date-time), nullable | no |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

---

## Parties

### `GET /api/v1/parties/{party_id}/statement-pdf`

Statement Pdf.

D11 — customer statement PDF with open invoices + aging.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `party_id` | path | integer | yes |  |
| `as_of` | query | string (date), nullable | no |  |

**Response `200`** (`application/pdf`)

Type: `string (binary)`

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

---

## Payments

### `POST /api/v1/payments/{payment_id}/reverse`

Reverse Payment.

PAY2 — reverse a payment (failed/bounced/stopped) and return the
invoice/bill to its prior unpaid/partial status.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `payment_id` | path | integer | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `bill_id` | integer, nullable | no |  |
| `invoice_id` | integer, nullable | no |  |
| `payment_id` | integer | yes |  |
| `reversed` | boolean | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

---

## Receipts

### `GET /api/v1/receipts`

List Receipts.

List receipts from journal entries tagged with receipt source types.

ROADMAP42 BUG-1: this endpoint used to take no ``offset`` and hard-capped
``limit`` at 200, so a tenant with more than 200 receipts could never reach
row 201 — ``total`` honestly reported thousands while only the first 200
were addressable. It now takes a real ``offset`` and shares the same
``paginate()`` helper (and the same 1000-row ceiling) as every neighbouring
list endpoint.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `end` | query | string (date), nullable | no |  |
| `limit` | query | integer | no | `50` |
| `offset` | query | integer | no | `0` |
| `sort_by` | query | string | no | `"entry_date"` |
| `sort_direction` | query | string | no | `"desc"` |
| `start` | query | string (date), nullable | no |  |

**Response `200`** (`application/json`)

_Not declared in the OpenAPI schema — this operation has no declared response model. Treat the body as an untyped JSON object._

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/receipts/batch`

Batch Upload Receipts.

Upload multiple receipt images/PDFs at once.

Each file goes through: dedup → integrity → Gemini extraction → save as pending.
Returns summary: receipts, duplicates, errors, per-file details.

**Parameters**

None.

**Request body** (`multipart/form-data`, required)

| field | type | required | default |
|---|---|---|---|
| `files` | array<binary (file upload)> | yes |  |

**Response `200`** (`application/json`)

_Not declared in the OpenAPI schema — this operation has no declared response model. Treat the body as an untyped JSON object._

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/receipts/manual`

Create Manual Receipt.

Create a receipt/bill manually, optionally with image. Accepts JSON body or multipart FormData.

**Parameters**

None.

**Response `200`** (`application/json`)

_Not declared in the OpenAPI schema — this operation has no declared response model. Treat the body as an untyped JSON object._

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/receipts/orphans`

List Orphan Receipts.

Confirmed (posted) receipts that never matched a bank transaction.

Pending receipts live on the Receipts page until confirmed; only after
confirm + failed auto-match does a receipt become an orphan.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `limit` | query | integer | no | `200` |
| `offset` | query | integer | no | `0` |

**Response `200`** (`application/json`)

_Not declared in the OpenAPI schema — this operation has no declared response model. Treat the body as an untyped JSON object._

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/receipts/pending`

List Pending Receipts.

List receipts awaiting human review (paginated with optional filters).

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `date_from` | query | string (date), nullable | no |  |
| `date_to` | query | string (date), nullable | no |  |
| `limit` | query | integer | no | `200` |
| `max_amount` | query | number \| string, nullable | no |  |
| `min_amount` | query | number \| string, nullable | no |  |
| `offset` | query | integer | no | `0` |
| `q` | query | string, nullable | no |  |
| `sort_dir` | query | string | no | `"desc"` |
| `sort_field` | query | string | no | `"created_at"` |
| `source` | query | string, nullable | no |  |

**Response `200`** (`application/json`)

_Not declared in the OpenAPI schema — this operation has no declared response model. Treat the body as an untyped JSON object._

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/receipts/{receipt_id}/confirm`

Confirm Receipt.

Human confirms a pending receipt → posts to ledger, then links the
matching bank transaction (explicit linked_bank_txn_id or auto-match).

Optional override: {"category": "Office Supplies", "total": 19.99,
"linked_bank_txn_id": 42}

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `receipt_id` | path | integer | yes |  |

**Request body** (`application/json`, optional)

Type: `object, nullable`

**Response `200`** (`application/json`)

_Not declared in the OpenAPI schema — this operation has no declared response model. Treat the body as an untyped JSON object._

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/receipts/{receipt_id}/pdf`

Receipt Pdf.

Download a confirmed receipt as PDF.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `receipt_id` | path | integer | yes |  |

**Response `200`** (`application/json`)

_Not declared in the OpenAPI schema — this operation has no declared response model. Treat the body as an untyped JSON object._

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/receipts/{receipt_id}/reject`

Reject Receipt.

Reject a pending receipt — wrong classification, not a receipt, etc.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `receipt_id` | path | integer | yes |  |
| `reason` | query | string | no | `""` |

**Response `200`** (`application/json`)

_Not declared in the OpenAPI schema — this operation has no declared response model. Treat the body as an untyped JSON object._

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/receipts/{receipt_id}/update`

Update Receipt.

Update a posted receipt — modifies receipt_data and syncs journal entry.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `receipt_id` | path | integer | yes |  |

**Request body** (`application/json`, required)

| field | type | required | default |
|---|---|---|---|
| `date` | string, nullable | no |  |
| `items` | array<object>, nullable | no |  |
| `merchant` | string, nullable | no |  |
| `total` | number, nullable | no |  |

**Response `200`** (`application/json`)

_Not declared in the OpenAPI schema — this operation has no declared response model. Treat the body as an untyped JSON object._

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

---

## Reports & exports

### `GET /api/v1/reports`

List Reports.

List generated report snapshots for this tenant.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `report_type` | query | string, nullable | no |  |

**Response `200`** (`application/json`)

Type: `array<object>`

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/reports/export-csv`

Export Csv.

Export income statement + ratios as CSV.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `end` | query | string (date) | yes |  |
| `start` | query | string (date) | yes |  |

**Response `200`** (`text/csv`)

Type: `string`

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/reports/export-pdf`

Export Pdf.

Export income statement as PDF.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `end` | query | string (date) | yes |  |
| `start` | query | string (date) | yes |  |

**Response `200`** (`application/pdf`)

Type: `string (binary)`

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

---

## Financial statements

### `GET /api/v1/statements/balance-sheet`

Get Balance Sheet.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `as_of` | query | string (date) | yes |  |
| `tenant_id` | query | integer, nullable | no |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `_meta` | object, nullable | no |  |
| `as_of` | string | yes |  |
| `assets` | object | yes |  |
| `assets.current_assets` | array<object> | yes |  |
| `assets.current_assets.amount` | number | yes |  |
| `assets.current_assets.code` | string | yes |  |
| `assets.current_assets.name` | string | yes |  |
| `assets.current_liabilities` | array<object> | yes |  |
| `assets.current_liabilities.amount` | number | yes |  |
| `assets.current_liabilities.code` | string | yes |  |
| `assets.current_liabilities.name` | string | yes |  |
| `assets.equity` | array<object> | yes |  |
| `assets.equity.amount` | number | yes |  |
| `assets.equity.code` | string | yes |  |
| `assets.equity.name` | string | yes |  |
| `assets.fixed_assets` | array<object> | yes |  |
| `assets.fixed_assets.amount` | number | yes |  |
| `assets.fixed_assets.code` | string | yes |  |
| `assets.fixed_assets.name` | string | yes |  |
| `assets.long_term_liabilities` | array<object> | yes |  |
| `assets.long_term_liabilities.amount` | number | yes |  |
| `assets.long_term_liabilities.code` | string | yes |  |
| `assets.long_term_liabilities.name` | string | yes |  |
| `assets.other_assets` | array<object> | yes |  |
| `assets.other_assets.amount` | number | yes |  |
| `assets.other_assets.code` | string | yes |  |
| `assets.other_assets.name` | string | yes |  |
| `balanced` | boolean | yes |  |
| `expense_source_connected` | boolean, nullable | no |  |
| `liabilities_plus_equity` | number | yes |  |
| `total_assets` | number | yes |  |
| `total_equity` | number | yes |  |
| `total_liabilities` | number | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/statements/cash-flow`

Get Cash Flow.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `end` | query | string (date) | yes |  |
| `start` | query | string (date) | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `_meta` | object, nullable | no |  |
| `adjustments` | object | yes |  |
| `adjustments.amortization` | number | yes |  |
| `adjustments.depreciation` | number | yes |  |
| `adjustments.disposal_gain_loss` | number | yes |  |
| `adjustments.working_capital` | number | yes |  |
| `adjustments.working_capital_lines` | array<object> | yes |  |
| `adjustments.working_capital_lines.amount` | number | yes |  |
| `adjustments.working_capital_lines.code` | string | yes |  |
| `adjustments.working_capital_lines.name` | string | yes |  |
| `cash_from_financing` | number | yes |  |
| `cash_from_investing` | number | yes |  |
| `cash_from_operations` | number | yes |  |
| `expense_source_connected` | boolean, nullable | no |  |
| `financing_lines` | array<object> | yes |  |
| `financing_lines.amount` | number | yes |  |
| `financing_lines.code` | string | yes |  |
| `financing_lines.name` | string | yes |  |
| `investing_lines` | array<object> | yes |  |
| `investing_lines.amount` | number | yes |  |
| `investing_lines.code` | string | yes |  |
| `investing_lines.name` | string | yes |  |
| `net_change_in_cash` | number | yes |  |
| `net_change_in_cash_classified` | number | yes |  |
| `net_income` | number | yes |  |
| `non_cash_transactions` | array<object> | yes |  |
| `non_cash_transactions.accounts` | array<object> | yes |  |
| `non_cash_transactions.accounts.amount` | number | yes |  |
| `non_cash_transactions.accounts.code` | string | yes |  |
| `non_cash_transactions.accounts.name` | string | yes |  |
| `non_cash_transactions.entry_id` | integer | yes |  |
| `period` | object | yes |  |
| `period.end` | string | yes |  |
| `period.start` | string | yes |  |
| `reconciled` | boolean | yes |  |
| `reconciliation_difference` | number | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/statements/customer/{party_id}`

Customer Statement.

Canonical customer statement route — returns PDF + aging summary.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `party_id` | path | integer | yes |  |
| `as_of` | query | string (date), nullable | no |  |

**Response `200`** (`application/pdf`)

Type: `string (binary)`

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/statements/income-statement`

Get Income Statement.

tenant_id lets multi-entity views query a sibling tenant the user
belongs to (membership verified); defaults to the JWT tenant.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `end` | query | string (date) | yes |  |
| `start` | query | string (date) | yes |  |
| `tenant_id` | query | integer, nullable | no |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `_meta` | object, nullable | no |  |
| `cogs` | number | yes |  |
| `ebit` | number | yes |  |
| `expense_source_connected` | boolean, nullable | no |  |
| `expenses` | array<object> | yes |  |
| `expenses.amount` | number | yes |  |
| `expenses.code` | string | yes |  |
| `expenses.name` | string | yes |  |
| `gross_profit` | number | yes |  |
| `interest_expense` | number | yes |  |
| `net_income` | number | yes |  |
| `net_sales` | number | yes |  |
| `operating_expenses` | number | yes |  |
| `operating_income` | number | yes |  |
| `other_income` | number | yes |  |
| `period` | object | yes |  |
| `period.end` | string | yes |  |
| `period.start` | string | yes |  |
| `pretax_income` | number | yes |  |
| `revenue` | array<object> | yes |  |
| `revenue.amount` | number | yes |  |
| `revenue.code` | string | yes |  |
| `revenue.name` | string | yes |  |
| `tax_expense` | number | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/statements/retained-earnings`

Get Retained Earnings.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `end` | query | string (date) | yes |  |
| `start` | query | string (date) | yes |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `closing_retained_earnings` | number | yes |  |
| `dividends` | number | yes |  |
| `net_income` | number | yes |  |
| `opening_retained_earnings` | number | yes |  |
| `period` | object | yes |  |
| `period.end` | string | yes |  |
| `period.start` | string | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `GET /api/v1/statements/trial-balance`

Get Trial Balance.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `as_of` | query | string (date) | yes |  |
| `start_date` | query | string (date), nullable | no |  |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `_meta` | object, nullable | no |  |
| `as_of` | string | yes |  |
| `balanced` | boolean | yes |  |
| `basis` | string | yes |  |
| `closing_balanced` | boolean, nullable | no |  |
| `expense_source_connected` | boolean, nullable | no |  |
| `lines` | array<object> | yes |  |
| `lines.closing` | number, nullable | no |  |
| `lines.closing_credit` | number, nullable | no |  |
| `lines.closing_debit` | number, nullable | no |  |
| `lines.code` | string | yes |  |
| `lines.credit` | number | yes |  |
| `lines.debit` | number | yes |  |
| `lines.is_subtotal` | boolean, nullable | no |  |
| `lines.name` | string | yes |  |
| `lines.opening` | number, nullable | no |  |
| `lines.opening_credit` | number, nullable | no |  |
| `lines.opening_debit` | number, nullable | no |  |
| `lines.period_credit` | number, nullable | no |  |
| `lines.period_debit` | number, nullable | no |  |
| `lines.type` | string | yes |  |
| `opening_balanced` | boolean, nullable | no |  |
| `start` | string, nullable | no |  |
| `total_closing_credit` | number, nullable | no |  |
| `total_closing_debit` | number, nullable | no |  |
| `total_credit` | number | yes |  |
| `total_debit` | number | yes |  |
| `total_opening_credit` | number, nullable | no |  |
| `total_opening_debit` | number, nullable | no |  |
| `total_period_credit` | number, nullable | no |  |
| `total_period_debit` | number, nullable | no |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

---

## Vendors

### `GET /api/v1/vendors`

List Vendors.

**Parameters**

| name | in | type | required | default |
|---|---|---|---|---|
| `limit` | query | integer | no | `200` |
| `offset` | query | integer | no | `0` |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `has_more` | boolean | yes |  |
| `items` | array<object> | yes |  |
| `items.address_line1` | string, nullable | no |  |
| `items.address_line2` | string, nullable | no |  |
| `items.city` | string, nullable | no |  |
| `items.country` | string, nullable | no |  |
| `items.created_at` | string (date-time) | yes |  |
| `items.credit_limit` | string, nullable | no |  |
| `items.credit_period_days` | integer | yes |  |
| `items.default_currency` | string | yes |  |
| `items.default_rate` | string, nullable | no |  |
| `items.display_name` | string, nullable | no |  |
| `items.email` | string, nullable | no |  |
| `items.id` | integer, nullable | no |  |
| `items.is_active` | boolean | yes |  |
| `items.is_client` | boolean | yes |  |
| `items.is_contractor` | boolean | yes |  |
| `items.name` | string | yes |  |
| `items.party_type` | string | yes |  |
| `items.payable_account_id` | integer, nullable | no |  |
| `items.phone` | string, nullable | no |  |
| `items.postal_code` | string, nullable | no |  |
| `items.receivable_account_id` | integer, nullable | no |  |
| `items.state` | string, nullable | no |  |
| `items.tax_id` | string, nullable | no |  |
| `items.tenant_id` | integer | yes |  |
| `items.updated_at` | string (date-time) | yes |  |
| `items.w9_received` | boolean | yes |  |
| `items.w9_requested_at` | string (date-time), nullable | no |  |
| `items.ytd_1099_amount` | string | yes |  |
| `limit` | integer | yes |  |
| `offset` | integer | yes |  |
| `total` | integer | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.

### `POST /api/v1/vendors`

Create Vendor.

**Parameters**

None.

**Request body** (`application/json`, required)

| field | type | required | default |
|---|---|---|---|
| `email` | string, nullable | no |  |
| `name` | string | yes |  |
| `terms_days` | integer | no | `30` |

**Response `200`** (`application/json`)

| field | type | required | default |
|---|---|---|---|
| `address_line1` | string, nullable | no |  |
| `address_line2` | string, nullable | no |  |
| `city` | string, nullable | no |  |
| `country` | string, nullable | no |  |
| `created_at` | string (date-time) | yes |  |
| `credit_limit` | string, nullable | no |  |
| `credit_period_days` | integer | yes |  |
| `default_currency` | string | yes |  |
| `default_rate` | string, nullable | no |  |
| `display_name` | string, nullable | no |  |
| `email` | string, nullable | no |  |
| `id` | integer, nullable | no |  |
| `is_active` | boolean | yes |  |
| `is_client` | boolean | yes |  |
| `is_contractor` | boolean | yes |  |
| `name` | string | yes |  |
| `party_type` | string | yes |  |
| `payable_account_id` | integer, nullable | no |  |
| `phone` | string, nullable | no |  |
| `postal_code` | string, nullable | no |  |
| `receivable_account_id` | integer, nullable | no |  |
| `state` | string, nullable | no |  |
| `tax_id` | string, nullable | no |  |
| `tenant_id` | integer | yes |  |
| `updated_at` | string (date-time) | yes |  |
| `w9_received` | boolean | yes |  |
| `w9_requested_at` | string (date-time), nullable | no |  |
| `ytd_1099_amount` | string | yes |  |

**Errors:** `401` invalid_api_key · `403` insufficient_scope / cookie_auth_required · `404` not found or not in your tenant · `422` validation · `429` rate limited.
