# Financial IQ Financial Data API v1

Version: `v1-2026-07`

This guide describes the currently implemented v1 routes only. The source of
truth for field-level request and response schemas is
`docs/api/financial-data-api-v1.openapi.json`.

## Base URLs

- Intended public gateway: `https://api.financialiq.co.uk` (Vercel is already
  configured for this hostname; its DNS record still needs to be changed at
  the domain provider)
- Live transition gateway: `https://corporate.financialiq.co.uk/v1/*`
  (verified against production and suitable for controlled testing only)
- Current transition path:
  `https://{project_ref}.supabase.co/functions/v1/financial-data-api`

The Vercel proxy reads `SUPABASE_URL` (or the existing
`VITE_SUPABASE_URL`) from the deployment environment and forwards `/v1/*` to
the Financial Data API function. The Supabase project URL is intentionally not
hard-coded in `vercel.json`. Do not advertise the corporate transition URL as
the final public API hostname; use it only until `api.financialiq.co.uk` DNS
and certificate verification are complete.

## Authentication

Business API routes use an API key in the `x-api-key` header.

```text
x-api-key: fiq_live_or_test_key
```

API keys are scoped. A key must have the required business scopes, company
allowlist and optional IP allowlist before a route can run.

Personal API routes do not accept API keys. They require a user-authorised
bearer token with the relevant personal consent scope.

```text
Authorization: Bearer user_token_with_personal_consent
```

## Common Headers

- `X-FIQ-Request-Id`: optional caller request id. The API echoes a normalised
  request id in v1 responses.
- `Idempotency-Key`: required for write routes that create business effects.
  Reusing the same key with the same body returns the stored response instead of
  creating a duplicate.

## Implemented Routes

| Method   | Path                                                                                                      | Purpose                                                      |
| -------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| `GET`    | `/v1/agent/capabilities`                                                                                  | Get the agent capability and safety manifest                 |
| `GET`    | `/v1/agent/mcp-tools`                                                                                     | Get MCP-style tool descriptors for agents                    |
| `POST`   | `/v1/agent/mcp`                                                                                           | Execute a guarded MCP JSON-RPC request                       |
| `GET`    | `/v1/auth/scopes`                                                                                         | Get supported API permissions and consent scopes             |
| `POST`   | `/v1/auth/api-keys`                                                                                         | Create a company-scoped business API key                     |
| `GET`    | `/v1/auth/api-keys`                                                                                         | List safe business API key metadata                          |
| `DELETE` | `/v1/auth/api-keys/{api_key_id}`                                                                            | Revoke a business API key                                    |
| `POST`   | `/v1/auth/api-keys/{api_key_id}/rotate`                                                                     | Rotate a business API key                                    |
| `POST`   | `/v1/auth/consent-requests/preview`                                                                       | Preview a personal OAuth consent request                     |
| `POST`   | `/v1/auth/consent-requests`                                                                               | Create a pending personal OAuth consent request              |
| `GET`    | `/v1/auth/consent-requests/{consent_request_id}`                                                          | Get a pending personal OAuth consent request                 |
| `POST`   | `/v1/auth/consent-requests/{consent_request_id}/approve`                                                  | Approve a personal consent request and issue a token         |
| `GET`    | `/v1/auth/personal-access-tokens`                                                                         | List safe personal API token metadata                        |
| `POST`   | `/v1/auth/personal-access-tokens/refresh`                                                                 | Rotate a personal API access token                           |
| `DELETE` | `/v1/auth/personal-access-tokens/{token_id}`                                                              | Revoke a personal API token                                  |
| `GET`    | `/v1/business/companies`                                                                                  | List accessible business companies                           |
| `POST`   | `/v1/business/companies`                                                                                  | Create and initialise a business company                     |
| `GET`    | `/v1/business/companies/{company_id}`                                                                     | Get one accessible business company                          |
| `GET`    | `/v1/business/companies/{company_id}/customers`                                                           | List customers for a business company                        |
| `POST`   | `/v1/business/companies/{company_id}/customers`                                                           | Create a customer for a business company                     |
| `GET`    | `/v1/business/companies/{company_id}/customers/{customer_id}`                                             | Get one customer for a business company                      |
| `PATCH`  | `/v1/business/companies/{company_id}/customers/{customer_id}`                                             | Update safe customer master data                             |
| `GET`    | `/v1/business/companies/{company_id}/customer-volumes`                                                    | Get revenue totals by customer                               |
| `GET`    | `/v1/business/companies/{company_id}/suppliers`                                                           | List suppliers for a business company                        |
| `POST`   | `/v1/business/companies/{company_id}/suppliers`                                                           | Create a supplier for a business company                     |
| `GET`    | `/v1/business/companies/{company_id}/suppliers/{supplier_id}`                                             | Get one supplier for a business company                      |
| `PATCH`  | `/v1/business/companies/{company_id}/suppliers/{supplier_id}`                                             | Update safe supplier master data                             |
| `GET`    | `/v1/business/companies/{company_id}/supplier-volumes`                                                    | Get spend totals by supplier                                 |
| `GET`    | `/v1/business/bank-feed-institutions`                                                                     | List banks available for bank-feed connection                |
| `GET`    | `/v1/business/companies/{company_id}/bank-accounts`                                                       | List bank accounts for a business company                    |
| `POST`   | `/v1/business/companies/{company_id}/bank-accounts`                                                       | Create a manual business bank account                        |
| `GET`    | `/v1/business/companies/{company_id}/bank-accounts/{bank_account_id}`                                     | Get one bank account for a business company                  |
| `PATCH`  | `/v1/business/companies/{company_id}/bank-accounts/{bank_account_id}`                                     | Update one bank account metadata                             |
| `POST`   | `/v1/business/companies/{company_id}/bank-feed-authorization-requests`                                      | Create a short-lived bank-feed approval handoff              |
| `GET`    | `/v1/business/companies/{company_id}/bank-feed-authorization-requests/{request_id}`                          | Check bank-feed approval handoff status                      |
| `GET`    | `/v1/business/companies/{company_id}/bank-statement-lines`                                                | List bank statement lines for a business company             |
| `GET`    | `/v1/business/companies/{company_id}/bank-statement-lines/{statement_line_id}`                            | Get one bank statement line                                  |
| `GET`    | `/v1/business/companies/{company_id}/bank-reconciliations`                                                | List bank reconciliation status records                      |
| `GET`    | `/v1/business/companies/{company_id}/bank-reconciliations/{bank_reconciliation_id}`                       | Get one bank reconciliation status record                    |
| `GET`    | `/v1/business/companies/{company_id}/balance-sheet-reconciliations`                                       | List balance-sheet reconciliation workbooks                  |
| `GET`    | `/v1/business/companies/{company_id}/balance-sheet-reconciliations/{reconciliation_id}`                   | Get one balance-sheet reconciliation workbook                |
| `GET`    | `/v1/business/companies/{company_id}/chart-of-accounts`                                                   | List chart of accounts entries for a business company        |
| `POST`   | `/v1/business/companies/{company_id}/chart-of-accounts`                                                   | Create one chart of accounts entry                           |
| `GET`    | `/v1/business/companies/{company_id}/chart-of-accounts/{account_id}`                                      | Get one chart of accounts entry for a business company       |
| `PATCH`  | `/v1/business/companies/{company_id}/chart-of-accounts/{account_id}`                                      | Update one chart of accounts entry                           |
| `GET`    | `/v1/business/companies/{company_id}/projects`                                                            | List projects for a business company                         |
| `POST`   | `/v1/business/companies/{company_id}/projects`                                                            | Create one business project                                  |
| `GET`    | `/v1/business/companies/{company_id}/projects/{project_id}`                                               | Get one project for a business company                       |
| `PATCH`  | `/v1/business/companies/{company_id}/projects/{project_id}`                                               | Update one project for a business company                    |
| `GET`    | `/v1/business/companies/{company_id}/funds`                                                               | List funds for a business company                            |
| `POST`   | `/v1/business/companies/{company_id}/funds`                                                               | Create one business fund                                     |
| `GET`    | `/v1/business/companies/{company_id}/funds/{fund_id}`                                                     | Get one fund for a business company                          |
| `PATCH`  | `/v1/business/companies/{company_id}/funds/{fund_id}`                                                     | Update one fund for a business company                       |
| `GET`    | `/v1/business/companies/{company_id}/gift-aid-donors`                                                     | List Gift Aid donors for a business company                  |
| `POST`   | `/v1/business/companies/{company_id}/gift-aid-donors`                                                     | Create one Gift Aid donor                                    |
| `GET`    | `/v1/business/companies/{company_id}/gift-aid-donors/{donor_id}`                                          | Get one Gift Aid donor for a business company                |
| `PATCH`  | `/v1/business/companies/{company_id}/gift-aid-donors/{donor_id}`                                          | Update one Gift Aid donor profile                            |
| `GET`    | `/v1/business/companies/{company_id}/gift-aid-declarations`                                               | List Gift Aid declarations for a business company            |
| `POST`   | `/v1/business/companies/{company_id}/gift-aid-declarations`                                               | Create one Gift Aid declaration                              |
| `GET`    | `/v1/business/companies/{company_id}/gift-aid-declarations/{declaration_id}`                              | Get one Gift Aid declaration for a business company          |
| `GET`    | `/v1/business/companies/{company_id}/gift-aid-donations`                                                  | List Gift Aid donations for a business company               |
| `POST`   | `/v1/business/companies/{company_id}/gift-aid-donations`                                                  | Create one Gift Aid donation                                 |
| `GET`    | `/v1/business/companies/{company_id}/gift-aid-donations/{donation_id}`                                    | Get one Gift Aid donation for a business company             |
| `GET`    | `/v1/business/companies/{company_id}/gift-aid-claim-batches`                                              | List Gift Aid claim batches for a business company           |
| `POST`   | `/v1/business/companies/{company_id}/gift-aid-claim-batches`                                              | Create one draft Gift Aid claim batch                        |
| `GET`    | `/v1/business/companies/{company_id}/gift-aid-claim-batches/{claim_batch_id}`                             | Get one Gift Aid claim batch for a business company          |
| `POST`   | `/v1/business/companies/{company_id}/gift-aid-claim-batches/{claim_batch_id}/submission-requests`        | Create a signed-in user approval handoff for Gift Aid        |
| `GET`    | `/v1/business/companies/{company_id}/gift-aid-submission-requests/{request_id}`                          | Get Gift Aid submission approval status                      |
| `GET`    | `/v1/business/companies/{company_id}/cash-offering-setup`                                                 | Get cash offering setup for a business company               |
| `GET`    | `/v1/business/companies/{company_id}/cash-offering-batches`                                               | List cash offering batches for a business company            |
| `POST`   | `/v1/business/companies/{company_id}/cash-offering-batches`                                               | Create a counted cash offering batch                         |
| `GET`    | `/v1/business/companies/{company_id}/cash-offering-batches/{cash_offering_batch_id}`                      | Get one cash offering batch for a business company           |
| `GET`    | `/v1/business/companies/{company_id}/cash-deposit-preparations`                                           | List cash deposit preparations for a business company        |
| `POST`   | `/v1/business/companies/{company_id}/cash-deposit-preparations`                                           | Create a cash deposit preparation                            |
| `GET`    | `/v1/business/companies/{company_id}/cash-deposit-preparations/{cash_deposit_preparation_id}`             | Get one cash deposit preparation for a business company      |
| `POST`   | `/v1/business/companies/{company_id}/cash-deposit-preparations/{cash_deposit_preparation_id}/cancel`      | Cancel a prepared cash deposit preparation                   |
| `GET`    | `/v1/business/companies/{company_id}/donation-bank-inflows`                                               | List donation bank inflows for matching                      |
| `GET`    | `/v1/business/companies/{company_id}/donor-match-queue`                                                   | List donor match queue items for matching                    |
| `GET`    | `/v1/business/companies/{company_id}/donor-match-queue/{queue_item_id}`                                   | Get one donor match queue item                               |
| `GET`    | `/v1/business/companies/{company_id}/donor-match-queue/stats`                                             | Get donor match queue status counts                          |
| `GET`    | `/v1/business/companies/{company_id}/cost-centers`                                                        | List cost centre levels and options                          |
| `POST`   | `/v1/business/companies/{company_id}/cost-centers`                                                        | Create one cost centre level                                 |
| `GET`    | `/v1/business/companies/{company_id}/cost-centers/{cost_center_level_id}/options`                         | List cost centre options for one level                       |
| `POST`   | `/v1/business/companies/{company_id}/cost-centers/{cost_center_level_id}/options`                         | Create one cost centre option                                |
| `GET`    | `/v1/business/companies/{company_id}/cost-centers/{cost_center_level_id}`                                 | Get one cost centre level and its options                    |
| `PATCH`  | `/v1/business/companies/{company_id}/cost-centers/{cost_center_level_id}`                                 | Update one cost centre level                                 |
| `GET`    | `/v1/business/companies/{company_id}/cost-centers/{cost_center_level_id}/options/{cost_center_option_id}` | Get one cost centre option                                   |
| `PATCH`  | `/v1/business/companies/{company_id}/cost-centers/{cost_center_level_id}/options/{cost_center_option_id}` | Update one cost centre option                                |
| `GET`    | `/v1/business/companies/{company_id}/cost-center-budgets`                                                 | Get cost-centre budget-versus-actual rows                    |
| `POST`   | `/v1/business/companies/{company_id}/cost-center-budgets`                                                 | Create one cost-centre budget target                         |
| `GET`    | `/v1/business/companies/{company_id}/cost-center-budgets/{cost_center_budget_target_id}`                  | Get one cost-centre budget target                            |
| `PATCH`  | `/v1/business/companies/{company_id}/cost-center-budgets/{cost_center_budget_target_id}`                  | Update one cost-centre budget target                         |
| `GET`    | `/v1/business/companies/{company_id}/accounting-periods`                                                  | List accounting periods for a business company               |
| `POST`   | `/v1/business/companies/{company_id}/accounting-periods`                                                  | Create one open accounting period                            |
| `GET`    | `/v1/business/companies/{company_id}/accounting-periods/{accounting_period_id}`                           | Get one accounting period for a business company             |
| `PATCH`  | `/v1/business/companies/{company_id}/accounting-periods/{accounting_period_id}`                           | Update one unlocked accounting period                        |
| `GET`    | `/v1/business/companies/{company_id}/reports/summary`                                                     | Get business summary report totals                           |
| `GET`    | `/v1/business/companies/{company_id}/reports/fund-movement`                                               | Get charity fund movements for a period                      |
| `GET`    | `/v1/business/companies/{company_id}/reports/profit-loss`                                                 | Get a profit-and-loss report                                 |
| `GET`    | `/v1/business/companies/{company_id}/reports/trial-balance`                                               | Get a trial balance report                                   |
| `GET`    | `/v1/business/companies/{company_id}/reports/balance-sheet`                                               | Get a balance sheet report                                   |
| `GET`    | `/v1/business/companies/{company_id}/reports/cash-flow`                                                   | Get a cash-flow report                                       |
| `GET`    | `/v1/business/companies/{company_id}/reports/general-ledger`                                              | Get an account-level general ledger report                   |
| `GET`    | `/v1/business/companies/{company_id}/reports/aged-debtors`                                                | Get an invoice-backed aged debtors report                    |
| `GET`    | `/v1/business/companies/{company_id}/reports/aged-creditors`                                              | Get a purchase-invoice-backed aged creditors report          |
| `GET`    | `/v1/business/companies/{company_id}/hmrc-correspondence`                                                 | List HMRC correspondence items                               |
| `GET`    | `/v1/business/companies/{company_id}/hmrc-correspondence/{hmrc_correspondence_id}`                        | Get one HMRC correspondence item                             |
| `GET`    | `/v1/business/companies/{company_id}/vat-returns`                                                         | List VAT returns for a business company                      |
| `GET`    | `/v1/business/companies/{company_id}/vat-returns/{vat_return_id}`                                         | Get one VAT return for a business company                    |
| `POST`   | `/v1/business/companies/{company_id}/vat-returns/{vat_return_id}/submission-requests`                    | Create a signed-in user approval handoff for VAT submission  |
| `GET`    | `/v1/business/companies/{company_id}/vat-submission-requests/{request_id}`                                | Get VAT submission approval status                           |
| `GET`    | `/v1/business/companies/{company_id}/ct600-filings`                                                       | List CT600 filing statuses for a business company            |
| `GET`    | `/v1/business/companies/{company_id}/ct600-filings/{ct600_filing_id}`                                     | Get one CT600 filing status for a business company           |
| `POST`   | `/v1/business/companies/{company_id}/ct600-submission-requests`                                           | Create a signed-in user approval handoff for CT600 submission |
| `GET`    | `/v1/business/companies/{company_id}/ct600-submission-requests/{request_id}`                               | Get CT600 submission approval status                          |
| `POST`   | `/v1/business/companies/{company_id}/companies-house-submission-requests`                                 | Create a signed-in user approval handoff for Companies House |
| `GET`    | `/v1/business/companies/{company_id}/companies-house-submission-requests/{request_id}`                     | Get Companies House submission approval status                |
| `GET`    | `/v1/business/companies/{company_id}/statutory-accounts/versions`                                         | List statutory accounts version metadata                     |
| `GET`    | `/v1/business/companies/{company_id}/statutory-accounts/versions/{statutory_accounts_version_id}`         | Get one statutory accounts version metadata record           |
| `GET`    | `/v1/business/companies/{company_id}/directors`                                                           | List business director register records                      |
| `GET`    | `/v1/business/companies/{company_id}/directors/{director_id}`                                             | Get one business director register record                    |
| `GET`    | `/v1/business/companies/{company_id}/shareholders`                                                        | List business shareholder register records                   |
| `GET`    | `/v1/business/companies/{company_id}/shareholders/{shareholder_id}`                                       | Get one business shareholder register record                 |
| `GET`    | `/v1/business/companies/{company_id}/share-classes`                                                       | List share classes for a business company                    |
| `GET`    | `/v1/business/companies/{company_id}/share-classes/{share_class_id}`                                      | Get one share class for a business company                   |
| `GET`    | `/v1/business/companies/{company_id}/share-issues`                                                        | List share issues for a business company                     |
| `GET`    | `/v1/business/companies/{company_id}/share-issues/{share_issue_id}`                                       | Get one share issue for a business company                   |
| `GET`    | `/v1/business/companies/{company_id}/dividend-declarations`                                               | List dividend declarations for a business company            |
| `GET`    | `/v1/business/companies/{company_id}/dividend-declarations/{dividend_declaration_id}`                     | Get one dividend declaration for a business company          |
| `GET`    | `/v1/business/companies/{company_id}/dividend-declarations/{dividend_declaration_id}/lines`               | List dividend declaration lines                              |
| `GET`    | `/v1/business/companies/{company_id}/dividend-declarations/{dividend_declaration_id}/lines/{dividend_line_id}` | Get one dividend declaration line                            |
| `GET`    | `/v1/business/companies/{company_id}/dividend-declarations/{dividend_declaration_id}/allocations`         | List dividend shareholder allocations                        |
| `GET`    | `/v1/business/companies/{company_id}/dividend-declarations/{dividend_declaration_id}/allocations/{dividend_allocation_id}` | Get one dividend shareholder allocation                      |
| `GET`    | `/v1/business/companies/{company_id}/companies-house/change-events`                                       | List Companies House change-event filing packs               |
| `GET`    | `/v1/business/companies/{company_id}/companies-house/change-events/{change_event_id}`                     | Get one Companies House change-event filing pack             |
| `GET`    | `/v1/business/companies/{company_id}/companies-house/psc-register`                                        | List Companies House PSC register records                    |
| `GET`    | `/v1/business/companies/{company_id}/companies-house/psc-register/{psc_record_id}`                        | Get one Companies House PSC register record                  |
| `GET`    | `/v1/business/companies/{company_id}/companies-house/filings`                                             | List Companies House filing statuses for a business company  |
| `GET`    | `/v1/business/companies/{company_id}/companies-house/filings/{companies_house_filing_id}`                 | Get one Companies House filing status for a business company |
| `GET`    | `/v1/business/companies/{company_id}/journal-transactions`                                                | List journal transaction batches and lines                   |
| `POST`   | `/v1/business/companies/{company_id}/journal-transactions`                                                | Create a balanced draft manual journal                      |
| `GET`    | `/v1/business/companies/{company_id}/journal-transactions/{journal_transaction_id}`                       | Get one journal transaction batch and its lines              |
| `GET`    | `/v1/business/companies/{company_id}/fixed-asset-classes`                                                 | List fixed asset class setup records                         |
| `POST`   | `/v1/business/companies/{company_id}/fixed-asset-classes`                                                 | Create one fixed asset class and ledger setup                |
| `GET`    | `/v1/business/companies/{company_id}/fixed-asset-classes/{fixed_asset_class_id}`                          | Get one fixed asset class setup record                       |
| `POST`   | `/v1/business/companies/{company_id}/fixed-asset-classes/{fixed_asset_class_id}/archive`                  | Archive one fixed asset class setup record                   |
| `GET`    | `/v1/business/companies/{company_id}/fixed-assets`                                                        | List fixed asset register records for a business company     |
| `POST`   | `/v1/business/companies/{company_id}/fixed-assets`                                                        | Create one fixed asset register record                       |
| `GET`    | `/v1/business/companies/{company_id}/fixed-assets/{fixed_asset_id}`                                       | Get one fixed asset register record                          |
| `PATCH`  | `/v1/business/companies/{company_id}/fixed-assets/{fixed_asset_id}`                                       | Correct safe fixed asset register fields                     |
| `POST`   | `/v1/business/companies/{company_id}/fixed-assets/{fixed_asset_id}/dispose`                               | Dispose one fixed asset and post the disposal journal         |
| `POST`   | `/v1/business/companies/{company_id}/fixed-assets/{fixed_asset_id}/reverse-disposal`                      | Reverse one fixed asset disposal                             |
| `GET`    | `/v1/business/companies/{company_id}/payment-links`                                                       | List invoice payment links for a business company            |
| `POST`   | `/v1/business/companies/{company_id}/payment-links`                                                       | Create or reuse an invoice payment link                      |
| `GET`    | `/v1/business/companies/{company_id}/payment-links/{payment_link_id}`                                     | Get one invoice payment link                                 |
| `GET`    | `/v1/business/companies/{company_id}/customer-payments`                                                   | List customer payment records for a business company         |
| `POST`   | `/v1/business/companies/{company_id}/customer-payments`                                                   | Record a Stripe-verified customer payment                    |
| `GET`    | `/v1/business/companies/{company_id}/customer-payments/{customer_payment_id}`                             | Get one customer payment record                              |
| `GET`    | `/v1/business/companies/{company_id}/supplier-payments`                                                   | List supplier payment records for a business company         |
| `GET`    | `/v1/business/companies/{company_id}/supplier-payments/{supplier_payment_id}`                             | Get one supplier payment record                              |
| `GET`    | `/v1/business/companies/{company_id}/payroll-pay-items`                                                   | List payroll pay item catalogue records                      |
| `GET`    | `/v1/business/companies/{company_id}/payroll-pay-items/{payroll_pay_item_id}`                             | Get one payroll pay item catalogue record                    |
| `GET`    | `/v1/business/companies/{company_id}/payroll-deduction-items`                                             | List payroll deduction item catalogue records                |
| `GET`    | `/v1/business/companies/{company_id}/payroll-deduction-items/{payroll_deduction_item_id}`                 | Get one payroll deduction item catalogue record              |
| `GET`    | `/v1/business/companies/{company_id}/payroll-pension-schemes`                                             | List payroll pension scheme setup records                    |
| `GET`    | `/v1/business/companies/{company_id}/payroll-pension-schemes/{payroll_pension_scheme_id}`                 | Get one payroll pension scheme setup record                  |
| `POST`   | `/v1/business/companies/{company_id}/payroll-runs/{payroll_run_id}/submission-requests`                  | Create a signed-in user approval handoff for payroll FPS/NEST |
| `POST`   | `/v1/business/companies/{company_id}/payroll-submissions/{payroll_submission_id}/submission-requests`    | Create a signed-in user approval handoff for EPS       |
| `GET`    | `/v1/business/companies/{company_id}/payroll-submission-requests/{request_id}`                            | Get payroll submission approval status                       |
| `GET`    | `/v1/business/companies/{company_id}/payroll-submissions`                                                 | List payroll submission status records                       |
| `GET`    | `/v1/business/companies/{company_id}/payroll-submissions/{payroll_submission_id}`                         | Get one payroll submission status record                     |
| `GET`    | `/v1/business/companies/{company_id}/p11d-benefits`                                                       | List P11D benefit records                                    |
| `GET`    | `/v1/business/companies/{company_id}/p11d-benefits/{p11d_benefit_id}`                                     | Get one P11D benefit record                                  |
| `GET`    | `/v1/business/companies/{company_id}/cis-subcontractors`                                                  | List CIS subcontractor verification records                  |
| `GET`    | `/v1/business/companies/{company_id}/cis-subcontractors/{cis_subcontractor_id}`                           | Get one CIS subcontractor verification record                |
| `GET`    | `/v1/business/companies/{company_id}/cis-deductions`                                                      | List CIS deduction records                                   |
| `GET`    | `/v1/business/companies/{company_id}/cis-deductions/{cis_deduction_id}`                                   | Get one CIS deduction record                                 |
| `GET`    | `/v1/business/companies/{company_id}/payroll-employees`                                                   | List safe payroll employee directory records                 |
| `GET`    | `/v1/business/companies/{company_id}/payroll-employees/{payroll_employee_id}`                             | Get one safe payroll employee directory record               |
| `GET`    | `/v1/business/companies/{company_id}/payroll-runs`                                                        | List payroll runs for a business company                     |
| `GET`    | `/v1/business/companies/{company_id}/payroll-runs/{payroll_run_id}`                                       | Get one payroll run                                          |
| `GET`    | `/v1/business/companies/{company_id}/payroll-payables`                                                    | List payroll payment obligations for a business company      |
| `GET`    | `/v1/business/companies/{company_id}/payroll-payables/{payroll_payable_id}`                               | Get one payroll payment obligation                           |
| `GET`    | `/v1/business/companies/{company_id}/purchase-invoices`                                                   | List purchase invoices for a business company                |
| `POST`   | `/v1/business/companies/{company_id}/purchase-invoices`                                                   | Create a draft purchase invoice for a business company       |
| `GET`    | `/v1/business/companies/{company_id}/purchase-invoices/{purchase_invoice_id}`                             | Get one purchase invoice for a business company              |
| `POST`   | `/v1/business/companies/{company_id}/purchase-invoices/{purchase_invoice_id}/submit`                      | Submit a draft purchase invoice for approval                 |
| `POST`   | `/v1/business/companies/{company_id}/purchase-invoices/{purchase_invoice_id}/void`                        | Void one unpaid non-draft purchase invoice                   |
| `GET`    | `/v1/business/companies/{company_id}/supplier-payment-runs`                                               | List supplier payment runs for a business company            |
| `POST`   | `/v1/business/companies/{company_id}/supplier-payment-runs`                                               | Create a draft supplier payment run                          |
| `GET`    | `/v1/business/companies/{company_id}/supplier-payment-runs/{supplier_payment_run_id}`                     | Get one supplier payment run and its items                   |
| `POST`   | `/v1/business/companies/{company_id}/supplier-payment-runs/{supplier_payment_run_id}/submit`              | Submit a supplier payment run for approval                   |
| `GET`    | `/v1/business/companies/{company_id}/purchase-orders`                                                     | List purchase orders for a business company                  |
| `POST`   | `/v1/business/companies/{company_id}/purchase-orders`                                                     | Create a draft purchase order for a business company         |
| `GET`    | `/v1/business/companies/{company_id}/purchase-orders/{purchase_order_id}`                                 | Get one purchase order for a business company                |
| `POST`   | `/v1/business/companies/{company_id}/purchase-orders/{purchase_order_id}/submit`                          | Submit a draft purchase order for approval                   |
| `GET`    | `/v1/business/companies/{company_id}/expense-claimants`                                                   | List expense claimants for a business company                |
| `POST`   | `/v1/business/companies/{company_id}/expense-claimants`                                                   | Create an expense claimant for a business company            |
| `GET`    | `/v1/business/companies/{company_id}/expense-claimants/{expense_claimant_id}`                             | Get one expense claimant for a business company              |
| `PATCH`  | `/v1/business/companies/{company_id}/expense-claimants/{expense_claimant_id}`                             | Update safe expense claimant profile fields                  |
| `GET`    | `/v1/business/companies/{company_id}/expense-claims`                                                      | List expense claims for a business company                   |
| `POST`   | `/v1/business/companies/{company_id}/expense-claims`                                                      | Create a draft expense claim for a business company          |
| `GET`    | `/v1/business/companies/{company_id}/expense-claims/{expense_claim_id}`                                   | Get one expense claim for a business company                 |
| `POST`   | `/v1/business/companies/{company_id}/expense-claims/{expense_claim_id}/submit`                            | Submit a draft expense claim for approval                    |
| `POST`   | `/v1/business/companies/{company_id}/expense-claims/{expense_claim_id}/void`                              | Void an unpaid expense claim raised in error                 |
| `GET`    | `/v1/business/companies/{company_id}/transactions`                                                        | List bank transactions for a business company                |
| `GET`    | `/v1/business/companies/{company_id}/transactions/{transaction_id}`                                       | Get one bank transaction for a business company              |
| `POST`   | `/v1/business/companies/{company_id}/transactions`                                                        | Create one manual business transaction                       |
| `PATCH`  | `/v1/business/companies/{company_id}/transactions/{transaction_id}`                                       | Update business transaction metadata/category                |
| `GET`    | `/v1/business/companies/{company_id}/invoices`                                                            | List invoices for a business company                         |
| `POST`   | `/v1/business/companies/{company_id}/invoices`                                                            | Create a draft sales invoice for a business company          |
| `GET`    | `/v1/business/companies/{company_id}/invoices/{invoice_id}`                                               | Get one invoice for a business company                       |
| `GET`    | `/v1/business/companies/{company_id}/invoices/{invoice_id}/delivery-events`                              | Read invoice delivery evidence                               |
| `PATCH`  | `/v1/business/companies/{company_id}/invoices/{invoice_id}`                                               | Correct an unpaid draft invoice safely                       |
| `POST`   | `/v1/business/companies/{company_id}/invoices/{invoice_id}/issue`                                         | Issue a draft invoice and post accounting effects            |
| `POST`   | `/v1/business/companies/{company_id}/invoices/{invoice_id}/void`                                          | Void an issued invoice and post reversal effects             |
| `POST`   | `/v1/business/companies/{company_id}/invoices/{invoice_id}/send`                                          | Request approval to send a business invoice                  |
| `POST`   | `/v1/business/companies/{company_id}/invoices/{invoice_id}/send/execute`                                  | Execute an approved invoice email delivery                  |
| `POST`   | `/v1/business/companies/{company_id}/quotes/{quote_id}/send/execute`                                       | Execute an approved quote email delivery                    |
| `GET`    | `/v1/business/companies/{company_id}/quotes/{quote_id}/delivery-events`                                    | Read quote delivery evidence                                |
| `GET`    | `/v1/business/companies/{company_id}/quotes`                                                              | List quotes for a business company                           |
| `POST`   | `/v1/business/companies/{company_id}/quotes`                                                              | Create a draft quote for a business company                  |
| `GET`    | `/v1/business/companies/{company_id}/quotes/{quote_id}`                                                   | Get one quote for a business company                         |
| `PATCH`  | `/v1/business/companies/{company_id}/quotes/{quote_id}`                                                   | Correct a draft quote safely                                 |
| `POST`   | `/v1/business/companies/{company_id}/quotes/{quote_id}/send`                                              | Request approval to send a business quote                    |
| `POST`   | `/v1/business/companies/{company_id}/quotes/{quote_id}/convert`                                           | Convert a quote into an issued invoice                       |
| `GET`    | `/v1/business/companies/{company_id}/credit-notes`                                                        | List credit notes for a business company                     |
| `POST`   | `/v1/business/companies/{company_id}/credit-notes`                                                        | Create a draft credit note for a business company            |
| `GET`    | `/v1/business/companies/{company_id}/credit-notes/{credit_note_id}`                                       | Get one credit note for a business company                   |
| `POST`   | `/v1/business/companies/{company_id}/credit-notes/{credit_note_id}/issue`                                | Issue a draft credit note and post accounting effects        |
| `POST`   | `/v1/business/companies/{company_id}/credit-notes/{credit_note_id}/cancel`                               | Cancel a credit note raised in error                         |
| `GET`    | `/v1/business/companies/{company_id}/documents`                                                           | List safe document metadata for a business company           |
| `POST`   | `/v1/business/companies/{company_id}/documents`                                                           | Upload a business document for a business company            |
| `GET`    | `/v1/business/companies/{company_id}/documents/{document_id}`                                             | Get safe document metadata for a business company            |
| `PATCH`  | `/v1/business/companies/{company_id}/documents/{document_id}`                                             | Update safe document metadata and links                      |
| `GET`    | `/v1/business/companies/{company_id}/documents/{document_id}/download`                                    | Generate a short-lived business document download link       |
| `GET`    | `/v1/business/companies/{company_id}/approval-requests`                                                   | List approval request statuses for a business company        |
| `GET`    | `/v1/business/companies/{company_id}/approval-requests/{approval_request_id}`                             | Get one approval request status for a business company       |
| `POST`   | `/v1/business/companies/{company_id}/approval-requests/{approval_request_id}/approve`                     | Approve the current approval-request step                    |
| `POST`   | `/v1/business/companies/{company_id}/approval-requests/{approval_request_id}/reject`                      | Reject an approval request with a reason                     |
| `POST`   | `/v1/business/companies/{company_id}/approval-requests/{approval_request_id}/request-info`                | Put an approval on hold while more information is requested  |
| `GET`    | `/v1/business/companies/{company_id}/audit-logs`                                                          | List safe audit-log timeline entries                         |
| `GET`    | `/v1/business/companies/{company_id}/audit-logs/{audit_log_id}`                                           | Get one safe audit-log timeline entry                        |
| `GET`    | `/v1/business/companies/{company_id}/evidence-bundles/{resource_type}/{resource_id}`                      | Get a company-scoped evidence bundle                         |
| `POST`   | `/v1/invoices/drafts`                                                                                     | Create a draft sales invoice                                 |
| `POST`   | `/v1/invoices/{invoice_id}/send`                                                                          | Request approval to send a draft invoice                     |
| `GET`    | `/v1/evidence-bundles/{resource_type}/{resource_id}`                                                      | Get an evidence bundle for a supported resource              |
| `GET`    | `/v1/personal/summary`                                                                                    | Get aggregate personal financial summary totals              |
| `GET`    | `/v1/personal/reports/net-worth`                                                                          | Get aggregate personal net-worth totals                      |
| `GET`    | `/v1/personal/reports/cash-flow`                                                                          | Get transaction-backed personal cash-flow totals             |
| `GET`    | `/v1/personal/reports/budget-performance`                                                                 | Get personal budget performance totals                       |
| `GET`    | `/v1/personal/reports/goal-progress`                                                                      | Get personal goal progress totals                            |
| `GET`    | `/v1/personal/accounts`                                                                                   | List personal accounts                                       |
| `POST`   | `/v1/personal/accounts`                                                                                   | Create a manual personal account                             |
| `GET`    | `/v1/personal/accounts/{account_id}`                                                                      | Get one personal account                                     |
| `PATCH`  | `/v1/personal/accounts/{account_id}`                                                                      | Update safe personal account metadata                        |
| `GET`    | `/v1/personal/assets`                                                                                     | List personal assets                                         |
| `POST`   | `/v1/personal/assets`                                                                                     | Create one personal asset                                    |
| `GET`    | `/v1/personal/assets/{asset_id}`                                                                          | Get one personal asset                                       |
| `PATCH`  | `/v1/personal/assets/{asset_id}`                                                                          | Update one personal asset                                    |
| `GET`    | `/v1/personal/liabilities`                                                                                | List personal liabilities                                    |
| `POST`   | `/v1/personal/liabilities`                                                                                | Create one personal liability                                |
| `GET`    | `/v1/personal/liabilities/{liability_id}`                                                                 | Get one personal liability                                   |
| `PATCH`  | `/v1/personal/liabilities/{liability_id}`                                                                 | Update one personal liability                                |
| `GET`    | `/v1/personal/budgets`                                                                                    | List personal budgets                                        |
| `POST`   | `/v1/personal/budgets`                                                                                    | Create one personal budget                                   |
| `GET`    | `/v1/personal/budgets/{budget_id}`                                                                        | Get one personal budget                                      |
| `PATCH`  | `/v1/personal/budgets/{budget_id}`                                                                        | Update one personal budget                                   |
| `GET`    | `/v1/personal/categories`                                                                                 | List personal categories                                     |
| `POST`   | `/v1/personal/categories`                                                                                 | Create one personal category                                 |
| `GET`    | `/v1/personal/categories/{category_id}`                                                                   | Get one personal category                                    |
| `PATCH`  | `/v1/personal/categories/{category_id}`                                                                   | Update one personal category                                 |
| `GET`    | `/v1/personal/categorization-rules`                                                                       | List personal categorization rules                           |
| `POST`   | `/v1/personal/categorization-rules`                                                                       | Create one personal categorization rule                      |
| `GET`    | `/v1/personal/categorization-rules/{rule_id}`                                                             | Get one personal categorization rule                         |
| `PATCH`  | `/v1/personal/categorization-rules/{rule_id}`                                                             | Update one personal categorization rule                      |
| `GET`    | `/v1/personal/goals`                                                                                      | List personal goals                                          |
| `POST`   | `/v1/personal/goals`                                                                                      | Create one personal goal                                     |
| `GET`    | `/v1/personal/goals/{goal_id}`                                                                            | Get one personal goal                                        |
| `PATCH`  | `/v1/personal/goals/{goal_id}`                                                                            | Update one personal goal                                     |
| `GET`    | `/v1/personal/transactions`                                                                               | List personal transactions                                   |
| `GET`    | `/v1/personal/transactions/{transaction_id}`                                                              | Get one personal transaction                                 |
| `POST`   | `/v1/personal/transactions`                                                                               | Create one manual personal transaction                       |
| `PATCH`  | `/v1/personal/transactions/{transaction_id}`                                                              | Update personal transaction metadata/category                |
| `GET`    | `/v1/personal/business-links`                                                                             | List personal business links                                 |
| `POST`   | `/v1/personal/business-links`                                                                             | Create personal business link                                |
| `GET`    | `/v1/personal/business-links/{link_id}`                                                                   | Get one personal business link                               |
| `DELETE` | `/v1/personal/business-links/{link_id}`                                                                   | Revoke personal business link                                |
| `GET`    | `/v1/personal/business-links/{link_id}/shareholders`                                                      | List personal business shareholder mappings                  |
| `POST`   | `/v1/personal/business-links/{link_id}/shareholders`                                                      | Map personal business shareholder                            |
| `GET`    | `/v1/personal/business-links/{link_id}/shareholders/{shareholder_id}`                                     | Get personal business shareholder mapping                    |
| `PATCH`  | `/v1/personal/business-links/{link_id}/shareholders/{shareholder_id}`                                     | Update personal business shareholder mapping                 |
| `DELETE` | `/v1/personal/business-links/{link_id}/shareholders/{shareholder_id}`                                     | Unmap personal business shareholder                          |
| `GET`    | `/v1/webhooks/deliveries`                                                                                 | List public API webhook delivery status                      |
| `GET`    | `/v1/webhooks/endpoints`                                                                                  | List public API webhook endpoints                            |
| `POST`   | `/v1/webhooks/endpoints`                                                                                  | Create public API webhook endpoint                           |
| `GET`    | `/v1/webhooks/endpoints/{endpoint_id}`                                                                    | Get public API webhook endpoint                              |
| `DELETE` | `/v1/webhooks/endpoints/{endpoint_id}`                                                                    | Revoke public API webhook endpoint                           |
| `POST`   | `/v1/webhooks/endpoints/{endpoint_id}/rotate-secret`                                                      | Rotate public API webhook signing secret                     |

## Agent Capabilities

```text
GET /v1/agent/capabilities
```

This route gives an agent one safe discovery response before it starts work. It
returns the available capability groups, auth modes, current API-key permission
shape, approval boundaries, blocked high-risk actions and links to the
machine-readable OpenAPI contract.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- It requires a valid API key, but it is not company-scoped and does not need a
  `company_id` query parameter for master keys.
- It does not read company books, create records, approve actions, file returns,
  mutate external providers, move money or return secrets.
- The response points agents to
  `/api/financial-data-api-v1.openapi.json`, where each implemented route has
  `x-financial-iq-safety` metadata for scopes, idempotency, write level and
  forbidden actions.
- Personal capability groups are shown as OAuth/user-consent only. API keys
  still cannot access personal routes.
- Blocked actions explicitly include live filing, payment execution, provider
  mutation and OAuth consent issuing.

## Agent MCP Tool Descriptors

```text
GET /v1/agent/mcp-tools
```

This route returns MCP-style tool descriptors generated from the implemented
HTTP API surface. It is for agent planning, registration and routing. It does
not execute tools itself; callers still use the documented HTTP API routes.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- It requires a valid API key, but it is not company-scoped and does not need a
  `company_id` query parameter for master keys.
- It returns tool names, route patterns, input schema hints, required
  permissions/scopes, approval flags and safety boundaries.
- It does not read company books, execute tools, create records, approve
  actions, file returns, mutate external providers, move money or return
  secrets.
- Personal tools are marked as OAuth/user-consent only. API keys still cannot
  access personal routes.
- MCP execution is available through `POST /v1/agent/mcp`; this descriptor
  route remains read-only and is still used for planning and registration.

## Agent MCP Execution

```text
POST /v1/agent/mcp
```

This is an MCP-compatible JSON-RPC transport for the
`financial_iq_api_request` tool. It supports `initialize`, `tools/list` and
`tools/call`. A tool call names one documented `v1/business/companies/...` or
`v1/personal/...` route and supplies its HTTP method, query values and JSON
body.

The transport is only a wrapper around the existing route handlers. Business
calls require the normal API key and company permission; personal calls require
the normal user-authorised bearer token and consent scopes. The underlying
route still enforces idempotency, audit, approval and provider safety rules.
The MCP endpoint does not accept arbitrary URLs, provider credentials or a
second set of financial operations.

## Auth Scope Catalogue

```text
GET /v1/auth/scopes
```

This route returns the permission and consent vocabulary the v1 API already
enforces. It is designed for agents, developers and future consent screens that
need to know what can be requested before trying to call personal or business
routes.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- It requires a valid API key, but it is not company-scoped and does not need a
  `company_id` query parameter for master keys.
- It lists business API-key permissions separately from personal OAuth consent
  scopes.
- It points callers to the consent request preview route for safe user-facing
  consent planning.
- It does not issue API keys, bearer tokens, refresh tokens or consent grants.
- The preview route does not create grants or issue tokens.
- The consent request creation route stores a pending request only; it still
  does not grant consent or issue tokens.
- Business permissions never grant access to personal routes.
- Personal OAuth scopes never grant permission to mutate business books.
- No listed scope permits payment execution, provider mutation or live
  tax/statutory filing.

## Personal Consent Request Preview

```text
POST /v1/auth/consent-requests/preview
```

This route validates a requested set of personal OAuth scopes and returns the
user-facing consent sections an app or agent should show before asking a user
for approval. It is a planning step only.

Important behaviour:

- The route does not require an `Idempotency-Key`.
- It requires a valid API key, but it is not company-scoped and does not need a
  `company_id` query parameter for master keys.
- It accepts `requested_scopes` from `GET /v1/auth/scopes`, plus optional
  `client_name`, `purpose`, `redirect_uri` and opaque `state`.
- It rejects unsupported scopes, including business permissions.
- It returns a preview status, grouped consent sections and the future OAuth
  callback metadata.
- It does not create a consent request, consent grant, bearer token, refresh
  token, API key, personal record or business record.
- It does not read personal finance records, mutate business books, file
  returns, move money, mutate providers or return secrets.

## Personal Consent Requests

```text
POST /v1/auth/consent-requests
GET /v1/auth/consent-requests/{consent_request_id}
POST /v1/auth/consent-requests/{consent_request_id}/approve
```

These routes create and inspect durable pending personal OAuth consent requests.
They are the stored step after previewing consent wording and before a signed-in
user approves access.

Important behaviour:

- Create requires an `Idempotency-Key`. Reusing the same key with the same body
  replays the same consent request; reusing it with a changed body returns a
  conflict.
- The routes require a valid API key, but they are not company-scoped and do
  not need a `company_id` query parameter for master keys.
- Create accepts `requested_scopes` from `GET /v1/auth/scopes`, plus optional
  `client_name`, `purpose`, `redirect_uri` and opaque `state`.
- The raw `state` value is not stored; only a hash and presence marker are
  retained.
- Get returns only requests created by the current API key.
- A created request has status `pending_user_authorisation` and expires after
  30 minutes.
- Approval requires a signed-in user bearer token, not an API key, and returns a
  dedicated `fiq_usr_` personal API access token plus a `fiq_rfr_` refresh token
  once.
- Financial IQ stores only SHA-256 hashes of the issued access and refresh
  tokens; the raw tokens are not recoverable after the approval response.
- The access token is short-lived. The refresh token can rotate it without a new
  approval until the refresh token expires or the user revokes the token record.
- These routes do not create API keys, read personal records, mutate business
  books, move money, mutate providers or file returns.

## Personal Access Tokens

```text
GET /v1/auth/personal-access-tokens
POST /v1/auth/personal-access-tokens/refresh
DELETE /v1/auth/personal-access-tokens/{token_id}
```

These routes let a signed-in user inspect and revoke the dedicated personal API
tokens they have approved. The refresh route lets an agent rotate an already
issued personal API access token using its current `fiq_rfr_` refresh token.

Important behaviour:

- List and revoke require a signed-in user bearer token, not an API key.
- Refresh requires the current raw `fiq_rfr_` refresh token in the JSON body; it
  does not require an API key or signed-in user session.
- A `fiq_usr_` personal API token cannot list or revoke token records.
- List returns only safe metadata: token id, last four characters, scopes,
  status, expiry, refresh expiry, last-used time and consent-request client
  details.
- Raw access tokens, raw refresh tokens and token hashes are never returned by
  list or revoke lifecycle routes.
- Refresh returns a new raw access token and a new raw refresh token once. The
  previous access and refresh tokens are no longer usable after a successful
  refresh.
- Revoke is a soft revoke: the token record is marked `revoked` and retained for
  audit history.
- Revoked tokens can no longer call personal API routes or refresh access.
- Expired or revoked refresh access requires a new consent request and user
  approval.

## Business API Keys

```text
POST /v1/auth/api-keys
```

This route creates a non-master business API key for a signed-in company owner
or administrator. It is the credential handoff for teams that want an agent or
external product to use Financial IQ without opening the Financial IQ screens.

Important behaviour:

- It requires a signed-in user bearer token, not an existing API key or a
  personal API access token.
- The request must include `company_id` and an `Idempotency-Key`.
- Only company-scoped keys can be created. Master keys remain an operator-only
  control and cannot be minted through this route.
- The plaintext key is returned once. Store it securely; later API responses
  expose only safe metadata through the administrative product surfaces.
- Company API-key management authority is enforced by the existing database
  security function, including delegated accountant access where permitted.

```text
POST /v1/auth/api-keys/{api_key_id}/rotate
```

Rotation issues a replacement key and disables the old key in one database
transaction. The replacement secret is returned once; the old key remains in
the database as revoked audit history. The request may optionally replace the
key name, permissions or expiry, but it cannot create a master key or remove
the management and expiry safeguards.

## Business Companies

`POST /v1/business/companies` creates a business company for controlled tenant onboarding. It requires a master API key with the `company_management` permission and an `Idempotency-Key`. The operation creates the owner membership, installs the default chart of accounts, optionally links an authorised firm, records audit evidence, and adds the new company to that master key's allowlist atomically.

```text
GET /v1/business/companies
GET /v1/business/companies/{company_id}
```

These routes let an agent discover which business company records it can use
before it calls company-scoped endpoints.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- Standard API keys return only their own company.
- Master API keys return only companies in their configured allowlist.
- Master API keys with no allowlist are rejected.
- Inaccessible company IDs return not found rather than revealing whether
  another tenant exists.

## Business Customers

```text
GET /v1/business/companies/{company_id}/customers
POST /v1/business/companies/{company_id}/customers
GET /v1/business/companies/{company_id}/customers/{customer_id}
PATCH /v1/business/companies/{company_id}/customers/{customer_id}
```

These routes let an agent find, create or update the customer record needed to
create draft invoices.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST` and `PATCH` require an `Idempotency-Key`.
- The API key must have customer permission.
- The company ID must be one the key is allowed to access.
- Lists support `search` or `q` for customer name lookup.
- Creating a customer requires `name`. Optional fields are customer reference,
  contact email, phone, billing address, VAT number, default VAT rate, default
  payment terms, active flag, related-party flag and FAO.
- Updating a customer can change the same safe master-data fields. It cannot
  move the customer to another company.
- Customer writes do not create or issue invoices, change invoice balances, post
  ledger entries, file tax returns or move money.
- Inaccessible customer IDs return not found rather than revealing data from
  another company.

## Business Customer Volumes

```text
GET /v1/business/companies/{company_id}/customer-volumes
```

This route returns revenue totals by customer for a date range, so an agent can
rank customers without downloading every invoice.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have customer or invoice permission.
- The company ID must be one the key is allowed to access.
- `from_date` and `to_date` are required and must use `YYYY-MM-DD` format.
- `branch` can be supplied to use the existing branch-aware aggregation.
- The response includes one row per customer plus a summary with total
  customers, total revenue and total invoices.
- Customer-volume writes are not implemented in v1.

## Business Suppliers

```text
GET /v1/business/companies/{company_id}/suppliers
POST /v1/business/companies/{company_id}/suppliers
GET /v1/business/companies/{company_id}/suppliers/{supplier_id}
PATCH /v1/business/companies/{company_id}/suppliers/{supplier_id}
```

These routes let an agent find the `supplier_id` needed for purchase and bill
workflows, create a supplier record before creating a bill or purchase, or
update safe supplier master data.

Important behaviour:

- The `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST` creates supplier master data only. It does not create bills, payments
  or ledger postings.
- `PATCH` updates supplier master data only. It does not update supplier bank
  details, create bills, approve bills, pay suppliers, change bill balances,
  post ledger entries, file tax returns or move money.
- `POST` and `PATCH` require an `Idempotency-Key` so retrying the same request
  does not create duplicates or repeat an update.
- The API key must have supplier permission.
- The company ID must be one the key is allowed to access.
- Lists support `search` or `q` for supplier name lookup, plus `is_active`,
  `limit` and `offset`.
- The response does not include supplier bank account numbers, sort codes,
  encrypted payment fields or bank account names.
- The create and update routes do not accept supplier bank account details.
  Those remain outside v1 supplier writes.
- Inaccessible supplier IDs return not found rather than revealing data from
  another company.

## Business Supplier Volumes

```text
GET /v1/business/companies/{company_id}/supplier-volumes
```

This route returns purchase spend totals by supplier for a date range, so an
agent can rank suppliers without downloading every bill.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have supplier or transaction permission.
- The company ID must be one the key is allowed to access.
- `from_date` and `to_date` are required and must use `YYYY-MM-DD` format.
- `branch` can be supplied to use the existing branch-aware aggregation.
- The response includes one row per supplier plus a summary with total
  suppliers, total spend and total bills.
- Supplier-volume writes are not implemented in v1.

## Business Bank Feed Institutions

```text
GET /v1/business/bank-feed-institutions
```

This route lists banks that can be selected when connecting a business bank
feed.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have bank-account permission.
- `country` is optional, defaults to `GB`, and must be a two-letter ISO country
  code.
- The response includes public institution metadata only: ID, name, logo,
  supported countries and transaction history days.
- It does not connect a bank feed, create a requisition, or expose any account
  credentials.

## Business Bank Accounts

```text
GET /v1/business/companies/{company_id}/bank-accounts
POST /v1/business/companies/{company_id}/bank-accounts
GET /v1/business/companies/{company_id}/bank-accounts/{bank_account_id}
PATCH /v1/business/companies/{company_id}/bank-accounts/{bank_account_id}
```

These routes let an agent connect business transactions back to the relevant
bank account without exposing raw banking details. The `POST` route lets an
authorised agent create a manual business bank account record. The `PATCH` route
lets an authorised agent update safe account metadata.

Important behaviour:

- The API key must have bank account permission.
- The company ID must be one the key is allowed to access.
- Manual bank account creation and metadata update require an `Idempotency-Key`.
- Lists support `search` or `q`, `is_active`, `connection_status`,
  `feed_status`, `account_type`, `account_category`, `limit` and `offset`.
- Manual bank account creation accepts `account_name`, optional `bank_name`,
  optional `account_type`, optional `account_category`, optional `currency`,
  optional `current_balance`, optional `chart_of_accounts_id` and optional
  active state.
- Bank account update accepts safe metadata only: `account_name`, `bank_name`,
  `account_type`, `account_category`, `chart_of_accounts_id` and `is_active`.
- `chart_of_accounts_id`, when supplied, must already belong to the business
  company. The API does not silently create chart-of-accounts entries.
- The response does not include account numbers, sort codes, token fields,
  provider IDs, consent references or GoCardless account/requisition IDs.
- Manual bank account creation does not connect a bank feed, import
  transactions, store account numbers or sort codes, reconcile statements, move
  money, create journal entries or post to the ledger.
- Bank account update does not change balances, currency, account numbers, sort
  codes, bank-feed credentials, provider IDs, sync state, reconciliation state,
  transactions, journal entries, ledger postings or money movement.
- Inaccessible bank account IDs return not found rather than revealing data from
  another company.

## Business Bank-Feed Approval Handoffs

```text
POST /v1/business/companies/{company_id}/bank-feed-authorization-requests
GET /v1/business/companies/{company_id}/bank-feed-authorization-requests/{request_id}
```

An API agent can create a short-lived approval handoff for a business bank-feed
connection. The response contains an approval URL for a signed-in user to
complete in Financial IQ. The request expires after 15 minutes and exposes no
provider credentials, account numbers or access tokens. The API creates the
request and reports its status. The signed-in bank page in the corporate product
passes the request ID to the shared personal GoCardless function, which performs
the provider authorisation and transaction synchronisation after the user
approves. The corporate API does not accept provider credentials or recreate
that callback flow.

Creation requires `bank_accounts` permission and an `Idempotency-Key`.

## Business Bank Statements And Reconciliations

```text
GET /v1/business/companies/{company_id}/bank-statement-lines
GET /v1/business/companies/{company_id}/bank-statement-lines/{statement_line_id}
GET /v1/business/companies/{company_id}/bank-reconciliations
GET /v1/business/companies/{company_id}/bank-reconciliations/{bank_reconciliation_id}
```

These routes let an agent inspect the statement-line evidence behind bank
transactions and the reconciliation status for each bank account without
matching, posting or signing off anything.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have `bank_accounts` or `transactions` permission.
- The company ID must be one the key is allowed to access.
- Statement-line lists support `bank_account_id`, `statement_upload_id`,
  `from_date`, `to_date`, `min_amount`, `max_amount`, `unmatched_only`,
  `search`/`q`, `limit` and `offset`.
- Reconciliation lists support `bank_account_id`, `status`, `period_from`,
  `period_to`, `limit` and `offset`.
- Statement-line responses include the signed amount, running balance, statement
  upload ID and matched transaction ID when already matched.
- Reconciliation responses include the period end, status, matched count,
  exception count and sign-off timestamp/user when already reconciled.
- The routes do not import statements, create statement lines, match
  transactions, post bank journals, sign off reconciliations, close accounting
  periods or move money.

## Business Balance Sheet Reconciliations

```text
GET /v1/business/companies/{company_id}/balance-sheet-reconciliations
GET /v1/business/companies/{company_id}/balance-sheet-reconciliations/{reconciliation_id}
```

These routes let an agent inspect month-end balance-sheet reconciliation
workbooks: the account, period, GL balance, source balance, difference, open
items, supporting attachments, comments, signoffs and recent audit events.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have `reports` permission.
- The company ID must be one the key is allowed to access.
- Lists support `accounting_period_id`, `reconciliation_period_id`, `status`,
  `workbook_type`, `difference_only`, `open_items_only`, `search`/`q`, `limit`
  and `offset`.
- Detail responses include the workbook header plus lines, attachments,
  comments, signoffs, recent events and period information.
- The routes do not refresh workspaces, update statement balances, add or remove
  reconciliation lines, attach files, sign off workbooks, approve exceptions,
  lock periods, post journals, file returns or move money.

## Business Chart Of Accounts

```text
GET /v1/business/companies/{company_id}/chart-of-accounts
POST /v1/business/companies/{company_id}/chart-of-accounts
GET /v1/business/companies/{company_id}/chart-of-accounts/{account_id}
PATCH /v1/business/companies/{company_id}/chart-of-accounts/{account_id}
```

These routes let an agent find the category/account IDs used for transaction
coding, reporting and explaining the ledger. The create route lets an agent add
a new setup-only chart account/category. The update route lets an agent correct
safe metadata for an existing account/category.

Important behaviour:

- Read routes do not require an `Idempotency-Key`.
- `POST` and `PATCH` require an `Idempotency-Key` and chart of accounts
  permission.
- Read routes accept chart of accounts permission, or transaction permission as
  a fallback for categorisation workflows. Write routes do not accept the
  transaction permission fallback.
- The company ID must be one the key is allowed to access.
- Lists support `search` or `q`, `account_type`, `financial_statement`,
  `is_active`, `is_system_account`, `limit` and `offset`.
- `POST` creates account structure only. It rejects opening balances, current
  balances, system-account flags, protected code aliases and ledger/transaction
  fields.
- `PATCH` updates safe metadata only: `name`, `description`, `area`,
  `ma_category`, `ma_sub_category`, `parent_code` and `is_active`.
- `PATCH` does not change account codes, account type, statutory category, tax
  treatment, standard category, balances, system-account flags, opening
  balances, ledger postings, transactions or tax submissions.
- Inaccessible account IDs return not found rather than revealing data from
  another company.
- Duplicate account codes return a conflict rather than creating a second
  account.

## Business Projects

```text
GET /v1/business/companies/{company_id}/projects
POST /v1/business/companies/{company_id}/projects
GET /v1/business/companies/{company_id}/projects/{project_id}
PATCH /v1/business/companies/{company_id}/projects/{project_id}
```

These routes let an agent find the `project_id` values used for invoice lines,
bills, reporting and budget tracking. The `POST` and `PATCH` routes change setup
data only.

Important behaviour:

- The read routes do not require an `Idempotency-Key`.
- The create and update routes require project permission and an
  `Idempotency-Key`.
- Read routes accept project permission, or transaction permission as a fallback
  for categorisation workflows.
- The company ID must be one the key is allowed to access.
- Lists support `search` or `q`, `code`, `is_active`, `active`, `limit` and
  `offset`.
- Create accepts `code`, `name`, optional description, dates and budget
  estimates.
- Update accepts the same project fields and only changes that one company
  project.
- Inaccessible project IDs return not found rather than revealing data from
  another company.
- Project creation and update do not create transactions, post ledger entries,
  send documents, move money or submit tax.

## Business Reference Data

```text
GET /v1/business/companies/{company_id}/funds
POST /v1/business/companies/{company_id}/funds
GET /v1/business/companies/{company_id}/funds/{fund_id}
PATCH /v1/business/companies/{company_id}/funds/{fund_id}
GET /v1/business/companies/{company_id}/cost-centers
POST /v1/business/companies/{company_id}/cost-centers
GET /v1/business/companies/{company_id}/cost-centers/{cost_center_level_id}/options
POST /v1/business/companies/{company_id}/cost-centers/{cost_center_level_id}/options
GET /v1/business/companies/{company_id}/cost-centers/{cost_center_level_id}
PATCH /v1/business/companies/{company_id}/cost-centers/{cost_center_level_id}
GET /v1/business/companies/{company_id}/cost-centers/{cost_center_level_id}/options/{cost_center_option_id}
PATCH /v1/business/companies/{company_id}/cost-centers/{cost_center_level_id}/options/{cost_center_option_id}
GET /v1/business/companies/{company_id}/cost-center-budgets
POST /v1/business/companies/{company_id}/cost-center-budgets
GET /v1/business/companies/{company_id}/cost-center-budgets/{cost_center_budget_target_id}
PATCH /v1/business/companies/{company_id}/cost-center-budgets/{cost_center_budget_target_id}
GET /v1/business/companies/{company_id}/accounting-periods
POST /v1/business/companies/{company_id}/accounting-periods
GET /v1/business/companies/{company_id}/accounting-periods/{accounting_period_id}
PATCH /v1/business/companies/{company_id}/accounting-periods/{accounting_period_id}
```

These routes let an agent discover the ledger dimensions and period boundaries
it needs before it creates, categorises or reports on records.

Important behaviour:

- The GET routes are read-only and do not require an `Idempotency-Key`.
- Fund read routes require `funds`, `transactions` or `reports` permission.
- Fund creation and update require `funds` permission and an `Idempotency-Key`.
- Fund creation accepts `name`, optional `code`, `fund_class`, `fund_type`,
  `unrestricted_subtype`, `endowment_subtype`, `purpose`, `restriction_summary`,
  `restriction_reference`, `opened_on`, `display_order` and `is_active`.
- Fund update accepts safe setup fields only: `name`, `code`, `purpose`,
  `restriction_summary`, `restriction_reference`, `opened_on`, `display_order`
  and `is_active`.
- Fund creation and update do not create transactions, post ledger entries,
  change the default fund, close funds, reclassify funds, submit Gift Aid claims
  or move money.
- Cost centre read routes and cost-centre budget read routes require
  `cost_centers` or `transactions` permission.
- Cost centre level creation requires `cost_centers` permission and an
  `Idempotency-Key`. The transaction permission fallback is read-only.
- Cost centre level creation accepts `name`, optional `description`,
  `requirement_type`, `display_order` and `is_active`.
- Cost centre level creation does not create options, budgets, account-specific
  requirements, transactions or ledger entries.
- Cost centre level update requires `cost_centers` permission and an
  `Idempotency-Key`.
- Cost centre level update accepts safe setup fields only: `name`,
  `description`, `display_order` and `is_active`.
- Cost centre level update does not change requirement rules, create options,
  create budgets, create account-specific requirements, create transactions,
  post ledger entries, close accounting periods, submit tax returns or move
  money.
- Cost centre option creation requires `cost_centers` permission and an
  `Idempotency-Key`.
- Cost centre option creation accepts `name`, optional `code`, optional
  `description` and `is_active`. The level ID must be supplied in the path and
  belong to the same company.
- Cost centre option creation does not create budgets, account-specific
  requirements, transactions or ledger entries.
- Cost centre option lists return options for one company-owned level only.
  They are read-only and do not require an `Idempotency-Key`.
- Cost centre option detail returns one option by ID. It is read-only and does
  not require an `Idempotency-Key`.
- Cost centre option update requires `cost_centers` permission and an
  `Idempotency-Key`.
- Cost centre option update accepts safe setup fields only: `name`, `code`,
  `description` and `is_active`. The level ID and option ID must be supplied in
  the path and belong to the same company.
- Cost centre option update does not move options between levels, create
  budgets, create account-specific requirements, create transactions, post
  ledger entries, close accounting periods, submit tax returns or move money.
- Accounting period routes require `accounting_periods`, `reports` or
  `transactions` permission.
- Fund lists support `search` or `q`, `code`, `fund_type`, `fund_class`,
  `is_default`, `is_active`, `active`, `is_closed`, `limit` and `offset`.
- Cost centre lists return levels with nested options and support `search` or
  `q`, `requirement_type`, `is_active`, `option_is_active`, `limit` and
  `offset`.
- Cost centre option lists support `search` or `q`, `code`, `is_active`,
  `active`, `limit` and `offset`.
- Cost-centre budgets require `budget_period_id` and `cost_center_level_id`, and
  can be narrowed with `date_from` and `date_to`.
- Cost-centre budget target creation requires `cost_center_budgets` permission
  and an `Idempotency-Key`.
- Cost-centre budget target creation accepts `budget_period_id`,
  `cost_center_level_id`, `cost_center_option_id`, `target_month`,
  `target_amount` and optional `notes`. `target_month` must be the first day of
  a month.
- Cost-centre budget target creation does not update actuals, create
  transactions, create journal entries, post to the ledger or change accounting
  period locks.
- Cost-centre budget target detail returns one target by ID. It is read-only and
  does not require an `Idempotency-Key`.
- Cost-centre budget target update requires `cost_center_budgets` permission and
  an `Idempotency-Key`.
- Cost-centre budget target update accepts safe setup fields only:
  `target_month`, `target_amount` and `notes`. The target ID must be supplied in
  the path and belong to the same company.
- Cost-centre budget target update does not change the budget period,
  cost-centre assignment, actuals, transactions, journal entries, ledger
  postings, accounting period locks, tax submissions or money movement.
- Accounting period lists support `period_type`, `locked`, `include_monthly`,
  `date_from`, `date_to`, `limit` and `offset`.
- Accounting period creation requires `accounting_periods` permission and an
  `Idempotency-Key`. The `reports` and `transactions` permission fallbacks are
  read-only.
- Accounting period creation accepts `start_date`, `end_date`, optional
  `filing_deadline` and optional `period_type`. `statutory` is accepted as an
  API alias and stored as the platform's annual period type.
- Accounting period creation only creates an open, active period. It does not
  lock or close periods, change the company lock date, post journals, create
  year-end adjustments, file accounts or submit CT600.
- Accounting period creation rejects date ranges that overlap an existing active
  period.
- Accounting period update requires `accounting_periods` permission and an
  `Idempotency-Key`.
- Accounting period update accepts safe setup/date fields only: `start_date`,
  `end_date`, `filing_deadline` and `period_type`. The period must be unlocked.
- Accounting period update rejects date ranges that overlap another active
  period and does not lock, close or delete periods, create monthly breakdowns,
  create transactions, post journals, create year-end adjustments, file
  accounts, submit CT600 or move money.
- Inaccessible IDs return not found rather than revealing data from another
  company.

## Business Gift Aid Donors

```text
GET /v1/business/companies/{company_id}/gift-aid-donors
POST /v1/business/companies/{company_id}/gift-aid-donors
GET /v1/business/companies/{company_id}/gift-aid-donors/{donor_id}
PATCH /v1/business/companies/{company_id}/gift-aid-donors/{donor_id}
```

These routes let an agent read, create and update donor profile records needed
for Gift Aid workflows.

Important behaviour:

- The API key must have Gift Aid permission.
- The company ID must be one the key is allowed to access.
- The GET routes are read-only and do not require an `Idempotency-Key`.
- Donor creation and update require an `Idempotency-Key`.
- Donor creation accepts `first_name`, `last_name`, `address_line_1`, `city`,
  `postcode`, optional `title`, `address_line_2`, `county`, `country`, `email`,
  `telephone`, `external_id`, `notes` and `is_active`.
- Donor update accepts the same profile fields and only changes that one donor.
- Donor write routes do not create Gift Aid declarations, donations, bank
  transactions, claim batches, ledger entries or HMRC submissions.
- Lists support `status=active|inactive`, `external_id`, `search` or `q` for
  last-name search, `limit` and `offset`.
- The response contains donor contact data, so access should only be granted to
  integrations that need Gift Aid records.
- Inaccessible donor IDs return not found rather than revealing data from
  another company.

## Business Gift Aid Declarations

```text
GET /v1/business/companies/{company_id}/gift-aid-declarations
POST /v1/business/companies/{company_id}/gift-aid-declarations
GET /v1/business/companies/{company_id}/gift-aid-declarations/{declaration_id}
```

These routes let an agent read declaration records and create a setup-only Gift
Aid declaration for an existing donor.

Important behaviour:

- Read routes do not require an `Idempotency-Key`.
- Create requires an `Idempotency-Key` and Gift Aid permission.
- The company ID must be one the key is allowed to access.
- Create requires `donor_id`, `declaration_type`, `declaration_date`, `channel`
  and `wording_version`.
- Lists support `donor_id`, `status=active|expired|cancelled`,
  `declaration_type=single|ongoing|retrospective_ongoing`,
  `channel=paper|online|phone|email`, `date_from`, `date_to`, `limit` and
  `offset`.
- The response contains donor contact and declaration evidence data, so access
  should only be granted to integrations that need Gift Aid records.
- Inaccessible declaration IDs return not found rather than revealing data from
  another company.
- Create stores the declaration evidence only. It does not create donations,
  bank transactions, claim batches, HMRC submissions, cancellations or
  replacement declarations.

## Business Gift Aid Donations

```text
GET /v1/business/companies/{company_id}/gift-aid-donations
POST /v1/business/companies/{company_id}/gift-aid-donations
GET /v1/business/companies/{company_id}/gift-aid-donations/{donation_id}
```

These routes let an agent read Gift Aid donation records and create a pending
donation record against existing donor, declaration, fund and optional bank
transaction records.

Important behaviour:

- Read routes do not require an `Idempotency-Key`.
- Create requires an `Idempotency-Key` and Gift Aid permission.
- The company ID must be one the key is allowed to access.
- Create requires `donor_id`, `donation_date` and `gross_amount`. If
  `gift_aid_eligible` is true or omitted, `declaration_id` is also required.
- Create validates that the donor, declaration, fund and linked bank transaction
  belong to the same company before writing anything.
- Lists support `donor_id`, `declaration_id`, `bank_transaction_id`, `fund_id`,
  `claim_status=pending|submitted|accepted|rejected|rejected_fixable`,
  `gift_aid_eligible=true|false`, `date_from`, `date_to`, `limit` and `offset`.
- The response contains donor contact data and donation amounts, so access
  should only be granted to integrations that need Gift Aid records.
- Inaccessible donation IDs return not found rather than revealing data from
  another company.
- Create only inserts a pending donation record. It does not create bank
  transactions, claim batches, claim items, HMRC submissions or
  accepted/rejected claim outcomes.

## Business Gift Aid Claim Batches

```text
GET /v1/business/companies/{company_id}/gift-aid-claim-batches
POST /v1/business/companies/{company_id}/gift-aid-claim-batches
GET /v1/business/companies/{company_id}/gift-aid-claim-batches/{claim_batch_id}
```

These routes let an agent inspect Gift Aid and GASDS claim batches, or create a
new draft batch container for later review. Detail responses include the batch
items; list and create responses keep `items` as `null`.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST` requires an `Idempotency-Key` and only creates a `draft` batch.
- The API key must have Gift Aid permission.
- The company ID must be one the key is allowed to access.
- Lists support
  `status=draft|ready|submitted|accepted|rejected|partially_accepted`,
  `claim_type=gift_aid|gasds|combined`, `batch_reference`, `date_from`,
  `date_to`, `limit` and `offset`.
- Create accepts `batch_reference`, `claim_type`, `period_start`, `period_end`
  and optional `status=draft`. It starts all totals and item counts at zero.
- These routes do not attach donations to a batch, generate HMRC XML, submit,
  amend or delete claim batches, update donation claim statuses, post ledger
  entries, file tax returns or move money.
- Inaccessible claim batch IDs return not found rather than revealing data from
  another company.

### Gift Aid submission approval handoff

```text
POST /v1/business/companies/{company_id}/gift-aid-claim-batches/{claim_batch_id}/submission-requests
GET /v1/business/companies/{company_id}/gift-aid-submission-requests/{request_id}
```

An agent with the `tax_filings` permission can prepare a short-lived approval
request for a ready claim batch with generated XML. The response contains an
approval URL for a signed-in Financial IQ user. The existing Gift Aid screen
then submits through the normal HMRC flow.

Important behaviour:

- The POST requires an `Idempotency-Key`, a ready claim batch and generated XML.
- The request expires after 15 minutes and can only be consumed once.
- The API key cannot submit directly or bypass Gift Aid filing access or HMRC
  checks.
- This is an approval handoff, not an unattended Gift Aid filing endpoint.

## Business Cash Offering Setup

```text
GET /v1/business/companies/{company_id}/cash-offering-setup
```

This route lets an agent check whether cash offering bookkeeping is ready before
it suggests or prepares donation workflows.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have Gift Aid or Transactions permission.
- The company ID must be one the key is allowed to access.
- The response returns configured donation income accounts and a `ready` flag.
- The response does not include donor records, bank details or cash batch lines.
- Cash offering setup writes are not implemented in v1.

## Business Cash Offering Batches

```text
GET /v1/business/companies/{company_id}/cash-offering-batches
POST /v1/business/companies/{company_id}/cash-offering-batches
GET /v1/business/companies/{company_id}/cash-offering-batches/{cash_offering_batch_id}
```

These routes let an agent inspect and create counted cash offering batches.
Detail responses include counted lines; list and create responses keep `lines`
as `null`.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST` requires an `Idempotency-Key`.
- Reads require Gift Aid or Transactions permission. Creation requires both Gift
  Aid and Transactions permission.
- The company ID must be one the key is allowed to access.
- Lists support `branch_id`, comma-separated `branch_ids`,
  `status=unbanked|part_banked|banked|overdue|reversed`, `external_batch_id`,
  `include_banked=true|false`, `date_from`, `date_to`, `limit` and `offset`.
- Create requests require `external_batch_id`, `branch_id`, `collection_date`,
  `count_date`, `confirmed_cash_total`, `petty_cash_bank_account_id` and at
  least one `lines[]` item with `donor_id`, `fund_id` and `amount`.
- Create requests call the guarded `create_cash_offering_batch` database
  function, which validates donors, funds, the petty cash account, duplicate
  external ids, Gift Aid eligibility and balanced accounting journals.
- The response contains donor names on detail lines, so access should only be
  granted to integrations that need donation workflows.
- These routes do not bank or reverse cash offering batches.
- Inaccessible cash offering batch IDs return not found rather than revealing
  data from another company.

## Business Cash Deposit Preparations

```text
GET /v1/business/companies/{company_id}/cash-deposit-preparations
POST /v1/business/companies/{company_id}/cash-deposit-preparations
GET /v1/business/companies/{company_id}/cash-deposit-preparations/{cash_deposit_preparation_id}
POST /v1/business/companies/{company_id}/cash-deposit-preparations/{cash_deposit_preparation_id}/cancel
```

These routes let an agent inspect, create and cancel cash deposit preparations
before they are matched to bank activity. Detail responses include batch
allocations and fund allocations; list and create responses keep `allocations`
as `null`.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST` requires an `Idempotency-Key`.
- The API key must have Transactions permission.
- The company ID must be one the key is allowed to access.
- Creating a preparation requires `external_preparation_id`, `branch_id`,
  `deposit_date`, `bank_account_id`, `confirmed_deposit_total`, and
  `allocations[]` lines containing `batch_id` and `allocated_amount`.
- Creating a preparation uses the guarded deposit-preparation workflow, which
  checks company access, branch, bank account, batch availability and fund
  allocations before writing.
- Cancelling a preparation requires `reason` and uses the guarded cancellation
  workflow. It records cancellation details and does not delete the preparation.
- Cancellation is blocked once the preparation has been banked.
- Lists support `branch_id`, comma-separated `branch_ids`, `bank_account_id`,
  `status=prepared|banked|cancelled`, `external_preparation_id`,
  `include_cancelled=true|false`, `date_from`, `date_to`, `limit` and `offset`.
- The response can contain preparer or canceller names, so access should only be
  granted to integrations that need cash banking workflows.
- These routes do not mark cash as banked or match bank transactions.
- Inaccessible cash deposit preparation IDs return not found rather than
  revealing data from another company.

## Business Donation Bank Inflows

```text
GET /v1/business/companies/{company_id}/donation-bank-inflows
```

This route lets an agent find positive bank credits that may need matching to a
donor or donation record.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have Transactions permission.
- The company ID must be one the key is allowed to access.
- Only positive bank credits are returned.
- By default, only unmatched transactions are returned. Use
  `unmatched_only=false` to include matched credits.
- Lists support `from_date`, `to_date`, `min_amount`, `max_amount`,
  `bank_account_id`, `unmatched_only=true|false`, `limit` and `offset`.
- The response can contain payer names in descriptions or merchant fields, so
  access should only be granted to integrations that need donation matching.
- This route does not create donors, match donations, or change bank
  transactions.

## Business Donor Match Queue

```text
GET /v1/business/companies/{company_id}/donor-match-queue
```

This route lets an agent inspect suggested donor matches for bank credits that
may be donations.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have Transactions permission.
- The company ID must be one the key is allowed to access.
- By default, only pending queue items are returned.
- Lists support `status=pending|matched|skipped|new_donor|resolved`,
  comma-separated `bank_account_ids`,
  `sort_by=transaction_date|confidence|amount`, `sort_direction=asc|desc`,
  `limit` and `offset`.
- `status=resolved` returns both `matched` and `new_donor` queue items.
- The response can contain donor names and bank payment references, so access
  should only be granted to integrations that need donation matching.
- This route does not resolve matches, create donors, create donations, or
  change bank transactions.

## Business Donor Match Queue Item

```text
GET /v1/business/companies/{company_id}/donor-match-queue/{queue_item_id}
```

This route returns one donor match queue item so an agent can inspect the
suggested donor, bank transaction summary and candidate donors before acting.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have Transactions permission.
- The company ID must be one the key is allowed to access.
- Optional comma-separated `bank_account_ids` can scope access to one or more
  bank accounts.
- The response can contain donor names and bank payment references, so access
  should only be granted to integrations that need donation matching.
- This route does not resolve matches, create donors, create donations, or
  change bank transactions.

## Business Donor Match Queue Stats

```text
GET /v1/business/companies/{company_id}/donor-match-queue/stats
```

This route gives an agent a status summary of the donor match queue before it
loads individual queue items.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have Transactions permission.
- The company ID must be one the key is allowed to access.
- Counts include `pending`, `resolved`, `skipped`, `high_confidence` and
  `create_new`.
- `resolved` combines queue items marked `matched` or `new_donor`.
- Results include total counts and counts grouped by bank account.
- Lists support comma-separated `bank_account_ids`.
- This route does not resolve matches, create donors, create donations, or
  change bank transactions.

## Business Report Summary

```text
GET /v1/business/companies/{company_id}/reports/summary
```

This route gives an agent a compact period summary without downloading invoices
or bank transactions one by one.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- The route supports optional `from_date` and `to_date` filters in `YYYY-MM-DD`
  format. When omitted, the period runs from `2020-01-01` to today.
- The response includes total invoiced, cash income, cash expenses, net cash
  movement and matching counts for the period.
- The response returns summary figures only. It does not expose raw transaction
  rows, provider payloads, bank details or source document contents.
- Report writes are not implemented in v1.

## Business Profit And Loss Report

```text
GET /v1/business/companies/{company_id}/reports/profit-loss
```

This route gives an agent a read-only profit-and-loss report for a requested
period, grouped into revenue, cost of sales and operating expenses.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- The route requires `from_date` and `to_date` filters in `YYYY-MM-DD` format.
- Figures are generated from active profit-and-loss chart accounts and posted
  journal entries dated inside the requested period.
- The response includes account-level report rows and rounded totals for
  revenue, cost of sales, gross profit, operating expenses and net profit.
- The response does not expose journal entry IDs, internal actor IDs, raw bank
  feeds, provider payloads, tax filing payloads or supporting document contents.
- This route does not create journal entries, post to the ledger, file tax
  returns, close accounting periods or move money.

## Business Trial Balance Report

```text
GET /v1/business/companies/{company_id}/reports/trial-balance
```

This route gives an agent a read-only trial balance as at a requested date, with
debit, credit and net balances for each active account that has a balance.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- The route requires an `as_at_date` filter in `YYYY-MM-DD` format.
- The route accepts optional `current_period_start` in `YYYY-MM-DD` format for
  year-to-date reporting, and it must be on or before `as_at_date`.
- Figures are generated from the existing trial balance calculation, using
  active chart accounts and posted ledger entries up to the requested date.
- The response includes account-level debit, credit and net balances, total
  debits, total credits, account count and whether the report balances.
- The response does not expose journal entry IDs, journal transaction IDs,
  internal actor IDs, raw provider payloads, bank details, tax filing payloads
  or supporting document contents.
- This route does not create journal entries, post to the ledger, file tax
  returns, close accounting periods or move money.

## Business Balance Sheet Report

```text
GET /v1/business/companies/{company_id}/reports/balance-sheet
```

This route gives an agent a read-only balance sheet as at a requested date,
split into assets, liabilities, equity and any unclassified balance-sheet rows.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- The route requires `as_at_date` and `current_period_start` filters in
  `YYYY-MM-DD` format. `current_period_start` must be on or before `as_at_date`.
- Figures are generated from the existing financial statement calculation, using
  active balance-sheet chart accounts and posted ledger entries up to the
  requested date.
- The response includes account-level balance-sheet rows, current-period profit
  in equity, section totals, total assets, total liabilities, total equity,
  liabilities plus equity, unclassified total and a balance check.
- The response does not expose journal entry IDs, journal transaction IDs,
  internal actor IDs, raw provider payloads, bank details, tax filing payloads
  or supporting document contents.
- This route does not create journal entries, post to the ledger, file tax
  returns, close accounting periods or move money.

## Business Cash Flow Report

```text
GET /v1/business/companies/{company_id}/reports/cash-flow
```

This route gives an agent a read-only indirect cash-flow report for a requested
period. It compares the trial balance before the period with the trial balance
at the period end, then classifies non-cash movements into operating, investing
and financing activities.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- The route requires `from_date` and `to_date` filters in `YYYY-MM-DD` format.
- Figures are generated from the existing trial balance calculation and posted
  ledger data. The response reconciles activity totals back to the net cash
  movement for the period.
- The response includes opening cash, closing cash, net change, operating,
  investing and financing sections, activity total and reconciliation
  difference.
- The response does not expose journal entry IDs, journal transaction IDs,
  internal actor IDs, raw provider payloads, bank details, tax filing payloads
  or supporting document contents.
- This route does not create journal entries, post to the ledger, file tax
  returns, close accounting periods or move money.

## Business General Ledger Report

```text
GET /v1/business/companies/{company_id}/reports/general-ledger
```

This route gives an agent a read-only account-level general ledger report for a
requested period. It returns posted journal lines for one chart account, with
opening and closing balances tied back to the trial balance.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- The route requires `account_id`, `from_date` and `to_date` filters.
- `account_id` must belong to the requested company.
- Figures are generated from posted, current journal entries and the existing
  trial balance calculation.
- The response includes the chart account, opening balance, closing balance,
  returned page totals, matching line count, running balances and ledger lines.
- The response exposes ledger transaction and journal-entry IDs because this is
  the audit-facing ledger detail route. It does not expose raw provider
  payloads, bank details, tax filing payloads or supporting document contents.
- This route does not create, amend, void or delete journal entries, file tax
  returns, close accounting periods or move money.

## Business Aged Debtors Report

```text
GET /v1/business/companies/{company_id}/reports/aged-debtors
```

This route gives an agent a read-only aged debtors report for one business
company as at a requested date. It shows unpaid sales invoices grouped by how
overdue they are, with totals by customer and by ageing bucket.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- The route requires an `as_of_date` filter in `YYYY-MM-DD` format.
- The optional `customer_id` filter narrows the report to one customer.
- Figures are generated from invoice totals and invoice payments recorded up to
  `as_of_date`.
- The response includes current, 1-30, 31-60, 61-90 and over-90-day buckets,
  customer summaries, unpaid invoice rows and total outstanding balance.
- The first version is invoice-backed: it does not include manually entered
  opening-balance debtor items.
- The response does not expose journal entry IDs, journal transaction IDs,
  internal actor IDs, raw provider payloads, bank details, tax filing payloads
  or supporting document contents.
- This route does not create or chase invoices, post to the ledger, file tax
  returns, close accounting periods or move money.

## Business Aged Creditors Report

```text
GET /v1/business/companies/{company_id}/reports/aged-creditors
```

This route gives an agent a read-only aged creditors report for one business
company as at a requested date. It shows unpaid supplier bills grouped by how
overdue they are, with totals by supplier and by ageing bucket.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- The route requires an `as_of_date` filter in `YYYY-MM-DD` format.
- The optional `supplier_id` filter narrows the report to one supplier.
- Figures are generated from purchase invoice totals and supplier payments
  recorded up to `as_of_date`.
- The response includes current, 1-30, 31-60, 61-90 and over-90-day buckets,
  supplier summaries, unpaid purchase invoice rows and total outstanding
  balance.
- The first version is purchase-invoice-backed: it does not include manually
  entered opening-balance creditor items.
- The response does not expose journal entry IDs, journal transaction IDs,
  internal actor IDs, raw provider payloads, bank details, tax filing payloads
  or supporting document contents.
- This route does not create or pay purchase invoices, export supplier payment
  files, post to the ledger, file tax returns, close accounting periods or move
  money.

## Business HMRC Correspondence

```text
GET /v1/business/companies/{company_id}/hmrc-correspondence
GET /v1/business/companies/{company_id}/hmrc-correspondence/{hmrc_correspondence_id}
```

These routes let an agent inspect HMRC letters, notices and inbox items already
recorded for a company. They do not reply to HMRC, mark items reviewed or file
anything.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `tax_area`, `category`, `urgency`, `status`,
  `response_required`, `received_from`, `received_to`, `response_due_from`,
  `response_due_to`, `source_document_id`, `limit` and `offset`.
- Responses include title, received date, tax area, category, urgency, status,
  response due date, amount, HMRC reference, summary, recommended action, source
  document link and review timestamp.
- Responses do not include internal notes, created-by users, reviewed-by users
  or other internal actor IDs.
- These routes do not create, update, review, respond to, archive or submit HMRC
  correspondence, submit anything to HMRC or move money.
- Inaccessible HMRC correspondence IDs return not found rather than revealing
  data from another company.

## Business VAT Returns

```text
GET /v1/business/companies/{company_id}/vat-returns
GET /v1/business/companies/{company_id}/vat-returns/{vat_return_id}
```

These routes let an agent inspect VAT return history and status without filing
anything with HMRC.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `submission_status`, `period_start_from`, `period_start_to`,
  `period_end_from`, `period_end_to`, `include_cancelled`, `limit` and `offset`.
- Cancelled VAT returns are hidden from lists by default. Use
  `include_cancelled=true` when an audit trail needs to see them.
- Responses include VAT boxes 1-9, period dates, submission status, submission
  date and HMRC submission reference.
- The routes do not calculate VAT, create draft VAT returns, cancel VAT returns,
  submit to HMRC directly, post ledger entries, file tax, close accounting
  periods or move money.

### VAT submission approval handoff

```text
POST /v1/business/companies/{company_id}/vat-returns/{vat_return_id}/submission-requests
GET /v1/business/companies/{company_id}/vat-submission-requests/{request_id}
```

An agent with the `tax_filings` permission can create a short-lived approval
request for a draft VAT return. The response contains an approval URL for a
signed-in Financial IQ user. After that user approves and completes the normal
VAT screen, the API can report `consumed`, `submitted` or `failed` status.

Important behaviour:

- The POST requires an `Idempotency-Key` and creates an audited request that
  expires after 15 minutes.
- The API key cannot submit directly. The existing signed-in flow still checks
  company access, filing authority, MFA and fraud-prevention data before HMRC.
- Internal API-key and user identifiers are never returned.
- This is an approval handoff, not an unattended HMRC filing endpoint.

## Business CT600 Filings

```text
GET /v1/business/companies/{company_id}/ct600-filings
GET /v1/business/companies/{company_id}/ct600-filings/{ct600_filing_id}
```

These routes let an agent inspect CT600 filing history and status without
submitting anything to HMRC.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `status`, `environment`, `accounting_period_id`,
  `submitted_from`, `submitted_to`, `accepted_from`, `accepted_to`, `limit` and
  `offset`.
- Responses include filing status, HMRC submission reference, environment,
  accounting period, linked computation, submitted/accepted timestamps and a
  safe error count.
- Responses do not expose raw HMRC response bodies, IRmark values, correlation
  IDs or raw error payloads.
- The routes do not calculate corporation tax, generate iXBRL, create draft
  filings, submit to HMRC, poll HMRC, post ledger entries, file tax, close
  accounting periods or move money.

### CT600 submission approval handoff

```text
POST /v1/business/companies/{company_id}/ct600-submission-requests
GET /v1/business/companies/{company_id}/ct600-submission-requests/{request_id}
```

An agent with the `tax_filings` permission can prepare a short-lived approval
request for a CT600 computation and accounting period. The response contains an
approval URL for a signed-in Financial IQ user. The existing CT600 screen then
generates the iXBRL attachments and uses the normal GovTalk submission path.

Important behaviour:

- The POST requires an `Idempotency-Key`, declarant name and declarant status.
- The request expires after 15 minutes and can only be consumed once.
- The API key cannot submit directly or bypass GovTalk credentials, iXBRL,
  MFA, filing authority or existing duplicate-submission checks.
- This is an approval handoff, not an unattended CT600 filing endpoint.

### Companies House submission approval handoff

```text
POST /v1/business/companies/{company_id}/companies-house-submission-requests
GET /v1/business/companies/{company_id}/companies-house-submission-requests/{request_id}
```

An agent with the `tax_filings` permission can prepare a short-lived approval
request for a statutory accounts period and, optionally, a saved accounts
version. The response contains an approval URL for a signed-in Financial IQ
user. The existing Accounts Production screen then performs the normal
Companies House readiness checks and submission path.

Important behaviour:

- The POST requires an `Idempotency-Key` and a valid accounting period.
- The request expires after 15 minutes and can only be consumed once.
- The API key cannot submit directly or bypass accounts readiness checks,
  filing authority, credentials or duplicate-submission controls.
- This is an approval handoff, not an unattended Companies House filing
  endpoint.

## Business Statutory Accounts Versions

```text
GET /v1/business/companies/{company_id}/statutory-accounts/versions
GET /v1/business/companies/{company_id}/statutory-accounts/versions/{statutory_accounts_version_id}
```

These routes let an agent inspect statutory accounts version history and filing
readiness metadata without regenerating accounts or filing anything.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `accounting_period_id`, `current_only`, `include_inactive`,
  `is_audited`, `saved_from`, `saved_to`, `limit` and `offset`.
- Inactive old versions are hidden by default. Use `include_inactive=true` when
  an audit trail needs to see replaced versions.
- Responses include version number/name/notes, current and active flags, audit
  flag, signing dates, saved timestamp and saved-by user.
- Responses do not expose full accounts JSON or persisted iXBRL documents.
- The routes do not generate statutory accounts, generate iXBRL, save new
  versions, set a current version, file accounts, submit to Companies House,
  close accounting periods or move money.

## Business Directors

```text
GET /v1/business/companies/{company_id}/directors
GET /v1/business/companies/{company_id}/directors/{director_id}
```

These routes let an agent inspect the company director register without
appointing, resigning or changing any director records.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists show current directors by default. Use `include_resigned=true` when an
  audit trail needs resigned directors.
- Lists support `include_resigned`, `search`/`q`, `appointed_from`,
  `appointed_to`, `resigned_from`, `resigned_to`, `limit` and `offset`.
- Responses include name, email, phone, linked director loan account,
  appointment/resignation dates and timestamps.
- Responses do not expose internal Nexus sync IDs or internal source-tracking
  fields.
- The routes do not appoint directors, resign directors, update company
  registers, post to the ledger, close accounting periods or move money.

## Business Shareholders

```text
GET /v1/business/companies/{company_id}/shareholders
GET /v1/business/companies/{company_id}/shareholders/{shareholder_id}
```

These routes let an agent inspect the company shareholder/member register
without changing shareholdings or Companies House records.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists show active shareholders by default. Use `include_inactive=true` when an
  audit trail needs inactive historical members.
- Lists support `include_inactive`, `member_type`, `search`/`q`, `limit` and
  `offset`.
- Responses include legal name, member type, register address, email, notes,
  active status, inactive timestamp, created-by user and timestamps.
- Responses do not expose internal Nexus sync IDs or internal source-tracking
  fields.
- The routes do not create shareholders, update shareholders, change
  shareholdings, update company registers, post to the ledger, close accounting
  periods or move money.

## Business Share Capital

```text
GET /v1/business/companies/{company_id}/share-classes
GET /v1/business/companies/{company_id}/share-classes/{share_class_id}
GET /v1/business/companies/{company_id}/share-issues
GET /v1/business/companies/{company_id}/share-issues/{share_issue_id}
```

These routes let an agent inspect the share-capital backbone behind the
shareholder register without issuing shares or changing company registers.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Share-class lists support `is_numbered`, `search`/`q`, `limit` and `offset`.
- Share-issue lists support `share_class_id`, `shareholder_id`, `issued_from`,
  `issued_to`, `posted_to_ledger`, `search`/`q`, `limit` and `offset`.
- Responses include nominal value, rights summary, share numbering fields, issue
  date, quantity, paid/unpaid amounts, shareholder/share-class IDs and linked
  ledger IDs where already posted.
- Responses do not expose internal ledger-posting error text.
- The routes do not create share classes, allot shares, update shareholders,
  update Companies House records, post to the ledger, close accounting periods
  or move money.

## Business Dividends

```text
GET /v1/business/companies/{company_id}/dividend-declarations
GET /v1/business/companies/{company_id}/dividend-declarations/{dividend_declaration_id}
GET /v1/business/companies/{company_id}/dividend-declarations/{dividend_declaration_id}/lines
GET /v1/business/companies/{company_id}/dividend-declarations/{dividend_declaration_id}/lines/{dividend_line_id}
GET /v1/business/companies/{company_id}/dividend-declarations/{dividend_declaration_id}/allocations
GET /v1/business/companies/{company_id}/dividend-declarations/{dividend_declaration_id}/allocations/{dividend_allocation_id}
```

These routes let an agent inspect declared dividends, per-share-class lines and
shareholder allocations without approving, cancelling, posting or paying a
dividend.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Dividend declaration lists support `status`, `declaration_type`,
  `declared_from`, `declared_to`, `record_from`, `record_to`, `payment_from`,
  `payment_to`, `search`/`q`, `limit` and `offset`.
- Dividend line and allocation lists hide archived rows by default. Use
  `include_archived=true` when an audit trail needs replaced dividend
  calculations.
- Dividend line detail returns one line by ID under the requested declaration
  and company.
- Dividend allocation lists support `shareholder_id`, `include_archived`,
  `limit` and `offset`.
- Dividend allocation detail returns one allocation by ID under the requested
  declaration and company.
- Responses include declaration status, dates, currency, total amount,
  journal/recurring IDs, supporting-paperwork timestamp, share-class line
  amounts, shareholder allocations and archive markers where requested.
- The routes do not declare dividends, approve dividends, cancel dividends,
  regenerate allocations, generate paperwork, post journals, make payments,
  close accounting periods or move money.

## Business Companies House Change Events

```text
GET /v1/business/companies/{company_id}/companies-house/change-events
GET /v1/business/companies/{company_id}/companies-house/change-events/{change_event_id}
```

These routes let an agent inspect saved Companies House change-event filing
packs, such as registered-office changes, director appointments/resignations,
share allotments and PSC changes, without filing anything.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `change_type`, `status`, `form_code`, `effective_from`,
  `effective_to`, `filed_from`, `filed_to`, `limit` and `offset`.
- Responses include the Companies House form code/name, status, effective date,
  saved review payload, validation warnings, manual filing reference and
  timestamps.
- `filing_enabled` is always `false` in v1 because electronic Companies House
  change filing is not enabled.
- The routes do not create change events, update filing packs, submit to
  Companies House, update the statutory register, close accounting periods, post
  to the ledger or move money.

## Business Companies House PSC Register

```text
GET /v1/business/companies/{company_id}/companies-house/psc-register
GET /v1/business/companies/{company_id}/companies-house/psc-register/{psc_record_id}
```

These routes let an agent inspect the company PSC register without changing the
register or filing anything at Companies House.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists show active PSCs by default. Use `include_ceased=true` when an audit
  trail needs to include ceased PSCs.
- Lists support `psc_type`, `source`, `personal_code_status`,
  `identity_verified`, `include_ceased`, `search`/`q`, `notified_from`,
  `notified_to`, `ceased_from`, `ceased_to`, `limit` and `offset`.
- Responses include PSC name, control type, nature of control, ownership
  percentage, notified/ceased dates, identity verification status, personal code
  status, source, notes and timestamps.
- Responses do not expose internal Nexus sync IDs or internal source-tracking
  fields.
- The routes do not create PSC records, update PSC records, generate filing
  packs, submit to Companies House, close accounting periods or move money.

## Business Companies House Filings

```text
GET /v1/business/companies/{company_id}/companies-house/filings
GET /v1/business/companies/{company_id}/companies-house/filings/{companies_house_filing_id}
```

These routes let an agent inspect Companies House accounts filing history and
status without submitting anything to Companies House.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `status`, `environment`, `accounting_period_id`,
  `submitted_from`, `submitted_to`, `acknowledged_from`, `acknowledged_to`,
  `limit` and `offset`.
- Responses include filing status, Companies House submission reference,
  environment, filing format, accounting period, submitted/acknowledged
  timestamps, latest poll summary and a safe rejection reason count.
- Responses do not expose raw Companies House response bodies, stored filing
  file paths, persisted iXBRL paths, raw rejection payloads or credential data.
- The routes do not generate statutory accounts, generate iXBRL, create draft
  filings, submit to Companies House, poll Companies House, post ledger entries,
  close accounting periods or move money.

## Business Journal Transactions

```text
GET /v1/business/companies/{company_id}/journal-transactions
POST /v1/business/companies/{company_id}/journal-transactions
GET /v1/business/companies/{company_id}/journal-transactions/{journal_transaction_id}
```

These routes let an agent inspect accounting ledger batches and prepare balanced
draft manual journals for review. The `POST` route creates a draft journal only;
it does not post or finalise the ledger.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST` requires transactions permission and an `Idempotency-Key`.
- The company ID must be one the key is allowed to access.
- `GET` lists support `status`, `source_type`, `transaction_type`, `reference`,
  `from_date`, `to_date`, `include_superseded`, `limit` and `offset`.
- `GET` lists default to current journal versions only. Use
  `include_superseded=true` to include old amendment versions.
- `POST` accepts `transaction_date`, `description`, optional `reference`,
  optional `transaction_type`, and `lines` containing `account_id`, one of
  `debit_amount` or `credit_amount`, and optional description/project/cost
  centre/fund/user reference fields.
- `POST` validates that debit and credit totals balance and that account,
  project, fund and cost-centre references belong to the requested company
  before creating an idempotency record or draft journal.
- Draft journals are always created with `status=draft`,
  `posting_origin=api_draft`, and no source provenance.
- Responses include safe journal header fields, debit/credit totals, balance
  status and journal lines. They do not expose internal idempotency keys, import
  batch identifiers or actor details.
- These routes do not post, amend, void, delete, file tax, close accounting
  periods, set source provenance or move money.
- Inaccessible journal transaction IDs return not found rather than revealing
  data from another company.

## Business Fixed Asset Classes

```text
GET /v1/business/companies/{company_id}/fixed-asset-classes
POST /v1/business/companies/{company_id}/fixed-asset-classes
GET /v1/business/companies/{company_id}/fixed-asset-classes/{fixed_asset_class_id}
POST /v1/business/companies/{company_id}/fixed-asset-classes/{fixed_asset_class_id}/archive
```

These routes let an agent inspect and manage fixed asset class setup records.
Creating a class also creates the linked ledger setup accounts using FIQ's
system-assigned code workflow.

Important behaviour:

- `GET` routes are read-only and can be used by keys with reports,
  transactions or chart_of_accounts permission.
- `POST /fixed-asset-classes` requires chart_of_accounts permission and an
  `Idempotency-Key`.
- `POST /archive` requires chart_of_accounts permission, a `reason` and an
  `Idempotency-Key`.
- Create accepts `name`, optional `code_prefix`, `asset_type`,
  `default_depreciation_method`, `default_useful_life_months`,
  `default_residual_value` and `description`.
- Lists support `asset_type`, `include_inactive`, `limit` and `offset`.
- Archive marks the class and generated ledger setup records inactive. It does
  not hard-delete records. Archive is refused while active fixed assets still
  use the class.
- These routes do not create fixed asset register records, post depreciation,
  post journals, close accounting periods, move money or file tax submissions.
- Inaccessible fixed asset class IDs return not found rather than revealing
  data from another company.

## Business Fixed Assets

```text
GET /v1/business/companies/{company_id}/fixed-assets
POST /v1/business/companies/{company_id}/fixed-assets
GET /v1/business/companies/{company_id}/fixed-assets/{fixed_asset_id}
PATCH /v1/business/companies/{company_id}/fixed-assets/{fixed_asset_id}
POST /v1/business/companies/{company_id}/fixed-assets/{fixed_asset_id}/dispose
POST /v1/business/companies/{company_id}/fixed-assets/{fixed_asset_id}/reverse-disposal
```

These routes let an agent inspect, create, correct, dispose and reverse disposal
of fixed asset register records. The write routes use the fixed-asset safety
workflow, so they respect company access, accounting period locks, journal
posting rules and audit evidence.

Important behaviour:

- `GET` routes are read-only, require reports permission and do not require an
  `Idempotency-Key`.
- `POST` and `PATCH` routes require transactions permission and an
  `Idempotency-Key`.
- `PATCH` requires a correction `reason` for the audit trail.
- `/dispose` requires `disposal_date`, `disposal_proceeds`,
  `proceeds_account_id` and `reason`. It posts the fixed asset disposal journal
  and marks the asset disposed as one all-or-nothing action.
- `/reverse-disposal` requires `reason`. It posts the reversal journal and
  reopens the asset as one all-or-nothing action.
- The company ID must be one the key is allowed to access.
- Lists support `status`, `asset_class_id`, `supplier_id`, `cost_center_id`,
  `fund_id`, `project_id`, `is_disposed`, `acquired_from`, `acquired_to`,
  `limit` and `offset`.
- `POST` validates asset class and optional supplier, cost-centre, fund and
  project references against the requested company.
- `PATCH` can correct safe asset fields such as description, asset number,
  acquisition basis, useful life, location and notes.
- Responses include asset class metadata, acquisition values, disposal status,
  analysis dimensions and the latest saved depreciation schedule. They do not
  calculate or post depreciation on the fly.
- The lifecycle routes post journals but do not move money, delete assets, post
  depreciation, close accounting periods or file tax submissions.
- Inaccessible fixed asset IDs return not found rather than revealing data from
  another company.

## Business Payment Links

```text
GET /v1/business/companies/{company_id}/payment-links
POST /v1/business/companies/{company_id}/payment-links
GET /v1/business/companies/{company_id}/payment-links/{payment_link_id}
```

These routes let an agent inspect invoice payment links. The `POST` route can
also create or reuse a safe public `/pay/...` link for an invoice without
creating a checkout session or charging the customer.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST` requires an `Idempotency-Key` and invoice permission.
- The API key must have invoice permission.
- The company ID must be one the key is allowed to access.
- `POST` requires `invoice_id`. Optional fields are `expiry_days` (1-365) and
  `require_email_verification`.
- `POST` reuses an active unexpired link for the same invoice instead of
  creating duplicates.
- `POST` will not create links for inaccessible, cancelled, voided or fully paid
  invoices.
- `POST` creates the payment link record and marks the invoice as publicly
  payable by that link.
- Lists support `invoice_id`, `is_active`, `limit` and `offset`.
- Responses include the link code, `/pay/...` path, expiry, active state, view
  count and a small invoice summary.
- Responses do not include provider account IDs, checkout session IDs, card
  details, webhook payloads or payment transaction internals.
- These routes do not create checkout sessions, charge customers, accept card
  details, record payments, update invoice payment state, post journals,
  reconcile payouts or move money.
- Inaccessible payment link IDs return not found rather than revealing data from
  another company.

## Business Customer Payments

```text
GET /v1/business/companies/{company_id}/customer-payments
POST /v1/business/companies/{company_id}/customer-payments
GET /v1/business/companies/{company_id}/customer-payments/{customer_payment_id}
```

These routes let an agent inspect customer payment records linked to sales
invoices. The `POST` route can also record an already-completed Stripe payment
against an invoice that has already been posted to the ledger.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST` requires an `Idempotency-Key` and invoice permission.
- The company ID must be one the key is allowed to access.
- Customer payments are company-scoped through the linked sales invoice.
- `POST` requires `invoice_id` and `stripe_payment_intent`. Optional fields are
  `provider` (`stripe` only), `stripe_account`, `payment_date`, `reference` and
  `notes`.
- `POST` will not record a payment against an invoice that has not yet been
  posted to the ledger.
- `POST` does not accept caller-supplied `amount` or `fee`. Financial IQ fetches
  the gross amount and processing fee from Stripe before writing to the books.
- `POST` updates invoice payment state, writes the customer payment evidence row
  and posts the standard payment and processing-fee journals.
- Lists support `invoice_id`, `customer_id`, `bank_account_id`,
  `bank_transaction_id`, `journal_transaction_id`, `payment_date_from`,
  `payment_date_to`, `include_archived`, `limit` and `offset`.
- Lists hide archived payment records by default. Use `include_archived=true` to
  include audit-history rows.
- Responses include the payment amount/date, invoice summary, customer summary,
  bank-account summary, linked bank transaction summary and linked journal
  transaction summary where available.
- Responses do not include internal notes, actor IDs, provider payloads,
  provider payment IDs, Stripe account IDs or card details.
- These routes do not charge customers, create checkout sessions, create payment
  links, reconcile bank transactions, reconcile payouts, submit tax returns or
  move money.
- Inaccessible customer payment IDs return not found rather than revealing data
  from another company.

## Business Supplier Payments

```text
GET /v1/business/companies/{company_id}/supplier-payments
GET /v1/business/companies/{company_id}/supplier-payments/{supplier_payment_id}
```

These routes let an agent inspect supplier payment records linked to purchase
invoices without initiating a supplier payment, exporting a bank file or
changing the ledger.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have invoice permission.
- The company ID must be one the key is allowed to access.
- Supplier payments are directly company-scoped and also include linked bill and
  supplier summaries.
- Lists support `purchase_invoice_id`, `supplier_id`, `bank_account_id`,
  `bank_transaction_id`, `journal_transaction_id`, `payment_batch_id`, `status`,
  `payment_method`, `payment_date_from`, `payment_date_to`, `include_archived`,
  `limit` and `offset`.
- Lists hide archived payment records by default. Use `include_archived=true` to
  include audit-history rows.
- Responses include the payment amount/date/method/status, bill summary,
  supplier summary, bank-account summary, linked bank transaction summary and
  linked journal transaction summary where available.
- Responses do not include internal notes, actor IDs, provider references,
  Yapily/provider payment IDs or supplier bank details.
- These routes do not initiate supplier payments, export bank payment files,
  approve payment runs, mark bills as paid, post journals, reconcile bank
  transactions or move money.
- Inaccessible supplier payment IDs return not found rather than revealing data
  from another company.

## Business Payroll Pay Items

```text
GET /v1/business/companies/{company_id}/payroll-pay-items
GET /v1/business/companies/{company_id}/payroll-pay-items/{payroll_pay_item_id}
```

These routes let an agent inspect the payroll pay item catalogue, such as
salary, bonus or overtime components. They do not edit payroll setup or
calculate a pay run.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `code`, `calculation_method`, `is_active`, `taxable`, `niable`,
  `pensionable`, `limit` and `offset`.
- `calculation_method` can be `manual`, `fixed_amount` or `percentage_of_gross`.
- Boolean filters accept true/false style values.
- Responses include code, name, description, calculation method, default amount,
  taxable/NI/pension flags, active state and audit timestamps.
- Responses do not include internal actor IDs.
- These routes do not create, update or delete pay items, calculate payroll,
  submit payroll, export bank files or move money.
- Inaccessible payroll pay item IDs return not found rather than revealing data
  from another company.

## Business Payroll Deduction Items

```text
GET /v1/business/companies/{company_id}/payroll-deduction-items
GET /v1/business/companies/{company_id}/payroll-deduction-items/{payroll_deduction_item_id}
```

These routes let an agent inspect the payroll deduction item catalogue, such as
PAYE, employee National Insurance, student loans, pension deductions or other
deduction rules. They do not edit payroll setup or calculate a pay run.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `code`, `deduction_type`, `calculation_method`, `is_active`,
  `limit` and `offset`.
- `deduction_type` can be `paye`, `employee_ni`, `student_loan`,
  `postgraduate_loan`, `employee_pension`, `attachment_of_earnings` or `other`.
- `calculation_method` can be `manual`, `fixed_amount` or `percentage_of_gross`.
- Boolean filters accept true/false style values.
- Responses include code, name, description, deduction type, calculation method,
  default amount, active state and audit timestamps.
- Responses do not include internal actor IDs.
- These routes do not create, update or delete deduction items, calculate
  payroll, submit payroll, export bank files or move money.
- Inaccessible payroll deduction item IDs return not found rather than revealing
  data from another company.

## Business Payroll Pension Schemes

```text
GET /v1/business/companies/{company_id}/payroll-pension-schemes
GET /v1/business/companies/{company_id}/payroll-pension-schemes/{payroll_pension_scheme_id}
```

These routes let an agent inspect payroll pension scheme setup, including the
provider, scheme name, submission method, relief method, contribution basis and
default contribution rates or fixed amounts. They do not edit pension setup,
calculate payroll or submit pension files.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `provider`, `submission_method`, `status`, `relief_method`,
  `contribution_basis`, `enforce_ae_minimum`, `limit` and `offset`.
- `provider` can currently be `nest`.
- `submission_method` can be `api`, `sftp` or `file_upload`.
- `status` can be `active` or `inactive`.
- `relief_method` can be `relief_at_source`, `net_pay` or `salary_sacrifice`.
- `contribution_basis` can be `qualifying_earnings`, `basic_pay`, `total_pay`,
  `tier1`, `tier2` or `tier3`.
- Boolean filters accept true/false style values.
- Responses include scheme identity, submission method, status, relief method,
  contribution basis, contribution rates/fixed amounts, AE-minimum flag and
  audit timestamps.
- Responses do not include credential secret references or internal actor IDs.
- These routes do not create, update or delete pension schemes, calculate
  payroll, submit payroll, submit pension files, export bank files or move
  money.
- Inaccessible payroll pension scheme IDs return not found rather than revealing
  data from another company.

## Business Payroll Submissions

```text
GET /v1/business/companies/{company_id}/payroll-submissions
GET /v1/business/companies/{company_id}/payroll-submissions/{payroll_submission_id}
```

These routes let an agent inspect RTI and NEST submission status records. They
show status metadata only. They do not expose the raw request or response
payloads sent to HMRC or NEST.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `payroll_run_id`, `submission_channel`, `submission_type`,
  `submission_environment`, `status`, `submitted_from`, `submitted_to`, `limit`
  and `offset`.
- `submission_channel` can be `hmrc_rti` or `nest`.
- `submission_environment` can be `sandbox` or `production`.
- `status` can be `queued`, `submitted`, `accepted`, `rejected`, `retrying` or
  `cancelled`.
- Responses include submission identity, payroll run, channel, type,
  environment, status, correlation/external references, submitted/responded
  timestamps, retry timing, error code/message and audit timestamps.
- Responses do not include raw request payloads, raw response payloads or
  internal actor IDs.
- These routes do not create, queue, retry, cancel or submit payroll
  submissions, calculate payroll, export bank files or move money.
- Inaccessible payroll submission IDs return not found rather than revealing
  data from another company.

### Payroll submission approval handoff

```text
POST /v1/business/companies/{company_id}/payroll-runs/{payroll_run_id}/submission-requests
POST /v1/business/companies/{company_id}/payroll-submissions/{payroll_submission_id}/submission-requests
GET /v1/business/companies/{company_id}/payroll-submission-requests/{request_id}
```

An agent with the `tax_filings` permission can prepare a short-lived approval
request for an eligible payroll run. The response contains an approval URL for
a signed-in Financial IQ user. The existing payroll screen then uses the
normal HMRC or NEST controls.

Important behaviour:

- The POST requires an `Idempotency-Key`, either `hmrc_rti`/`fps` or
  `nest`/`contribution_schedule`, and an explicit `sandbox` or `production`
  environment.
- HMRC FPS requests may target calculated, approved or finalised runs. NEST
  contribution schedule requests require a finalised and posted payroll run.
- The request expires after 15 minutes and can only be consumed once.
- The API key cannot submit directly or bypass payroll finalisation, HMRC/NEST
  credentials, MFA or provider controls.
- This is an approval handoff, not an unattended payroll submission endpoint.
- An EPS approval request targets an existing queued, retrying or rejected EPS
  submission by ID and opens the signed-in Employer Payment Summary screen.

## Business P11D Benefits

```text
GET /v1/business/companies/{company_id}/p11d-benefits
GET /v1/business/companies/{company_id}/p11d-benefits/{p11d_benefit_id}
```

These routes let an agent inspect P11D benefit records and Class 1A National
Insurance evidence for a company. They do not create benefits, mark benefits as
reviewed or submit P11D/EXB filings.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `tax_year`, `employee_id`, `benefit_type`, `status`, `limit` and
  `offset`.
- `tax_year` must use `YYYY/YY` format, for example `2025/26`.
- `status` can be `draft`, `reviewed` or `excluded`.
- Responses include benefit identity, employee ID, tax year, benefit type,
  description, cash equivalent, amount made good, taxable value, Class 1A
  National Insurance estimate, status and audit timestamps.
- Responses do not include internal notes or internal actor IDs.
- These routes do not create, update, exclude, review or submit P11D benefits,
  submit EXB filings, calculate payroll, export bank files or move money.
- Inaccessible P11D benefit IDs return not found rather than revealing data from
  another company.

## Business CIS Subcontractors

```text
GET /v1/business/companies/{company_id}/cis-subcontractors
GET /v1/business/companies/{company_id}/cis-subcontractors/{cis_subcontractor_id}
```

These routes let an agent inspect CIS subcontractor verification status for a
company. They do not verify subcontractors with HMRC, create subcontractors or
submit CIS returns.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `supplier_id`, `verification_status`, `limit` and `offset`.
- `verification_status` can be `gross`, `registered` or `unregistered`.
- Responses include subcontractor identity, supplier ID, verification status,
  verification timestamp and audit timestamps.
- Responses do not include UTRs, HMRC verification numbers, internal notes or
  internal actor IDs.
- These routes do not create, update or verify subcontractors, submit CIS
  returns, calculate CIS deductions, export bank files or move money.
- Inaccessible CIS subcontractor IDs return not found rather than revealing data
  from another company.

## Business CIS Deductions

```text
GET /v1/business/companies/{company_id}/cis-deductions
GET /v1/business/companies/{company_id}/cis-deductions/{cis_deduction_id}
```

These routes let an agent inspect CIS deduction records for a company. They do
not calculate deductions, edit payment records or submit CIS returns.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `subcontractor_id`, `supplier_id`, `tax_month`, `status`,
  `payment_date_from`, `payment_date_to`, `limit` and `offset`.
- `tax_month` must use `YYYY-MM`. `status` can be `draft`, `reviewed` or
  `submitted`.
- Responses include subcontractor ID, supplier ID, tax month, payment date,
  gross amount, materials amount, VAT amount, deduction rate, deduction amount,
  net payment, status and audit timestamps.
- Responses do not include internal notes or internal actor IDs.
- These routes do not create, update, review, submit or recalculate CIS
  deductions, export bank files or move money.
- Inaccessible CIS deduction IDs return not found rather than revealing data
  from another company.

## Business Payroll Employees

```text
GET /v1/business/companies/{company_id}/payroll-employees
GET /v1/business/companies/{company_id}/payroll-employees/{payroll_employee_id}
```

These routes let an agent identify payroll employee records safely. They are a
directory view only, not a payroll-personal-data export.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `employee_code`, `employment_status`, `pay_frequency`,
  `start_date_from`, `start_date_to`, `leave_date_from`, `leave_date_to`,
  `limit` and `offset`.
- `employment_status` can be `draft`, `active`, `leaver` or `paused`.
- `pay_frequency` can be `weekly`, `fortnightly`, `four_weekly` or `monthly`.
- Responses include employee code, display name, employment status, pay
  frequency, start date, leave date and audit timestamps.
- Responses do not include dates of birth, National Insurance numbers, bank
  details, tax codes, NI categories, employee emails, payroll notes, pay
  amounts, pension settings, starter declarations or internal actor IDs.
- These routes do not create, update or delete employees, calculate payroll,
  submit RTI, submit pension files, export bank files or move money.
- Inaccessible payroll employee IDs return not found rather than revealing data
  from another company.

## Business Payroll Runs

```text
GET /v1/business/companies/{company_id}/payroll-runs
GET /v1/business/companies/{company_id}/payroll-runs/{payroll_run_id}
```

These routes let an agent inspect payroll run status, period dates, submission
state and safe run-level totals. They do not calculate, approve, finalise,
submit or post payroll.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `status`, `correction_of_run_id`, `period_start_from`,
  `period_start_to`, `pay_date_from`, `pay_date_to`, `limit` and `offset`.
- `status` can be `draft`, `calculated`, `approved`, `finalised`, `submitted`,
  `accepted`, `rejected`, `corrected` or `cancelled`.
- Responses include run reference, run number, pay period, pay date, run status,
  RTI and pension submission statuses, linked journal transaction ID where
  already posted, approval/finalisation dates and a safe totals summary.
- Responses do not include payroll notes, actor IDs, raw calculation snapshots,
  employee lines, employee bank details, National Insurance numbers or tax
  codes.
- These routes do not calculate payroll, approve payroll, finalise payroll,
  submit RTI, submit pension files, post journals, export bank files or move
  money.
- Inaccessible payroll run IDs return not found rather than revealing data from
  another company.

## Business Payroll Payables

```text
GET /v1/business/companies/{company_id}/payroll-payables
GET /v1/business/companies/{company_id}/payroll-payables/{payroll_payable_id}
```

These routes let an agent inspect payroll payment obligations from finalised pay
runs: net wages, PAYE/NIC and workplace pension amounts. They do not record a
payroll payment, export a bank file or initiate an open-banking payment.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have reports permission.
- The company ID must be one the key is allowed to access.
- Lists support `payroll_run_id`, `employee_id`, `payable_type`, `status`,
  `due_from`, `due_to`, `include_archived`, `limit` and `offset`.
- `payable_type` can be `net_wages`, `paye_nic` or `pension`.
- `status` can be `unpaid`, `paid` or `cancelled`.
- Lists hide archived payment obligations by default. Use
  `include_archived=true` to include audit-history rows.
- Responses include payable amount, due date, status, payee name, payment
  journal IDs where already recorded, payroll-run summary and a minimal employee
  summary where relevant.
- Responses do not include employee bank details, National Insurance numbers,
  tax codes, employee emails, payroll notes, internal actor IDs or raw payroll
  calculation snapshots.
- These routes do not record payroll payments, initiate bank payments, export
  bank files, post journals, reverse payments, submit RTI, submit pension files
  or move money.
- Inaccessible payroll payable IDs return not found rather than revealing data
  from another company.

## Business Purchase Invoices

```text
GET /v1/business/companies/{company_id}/purchase-invoices
POST /v1/business/companies/{company_id}/purchase-invoices
GET /v1/business/companies/{company_id}/purchase-invoices/{purchase_invoice_id}
POST /v1/business/companies/{company_id}/purchase-invoices/{purchase_invoice_id}/submit
POST /v1/business/companies/{company_id}/purchase-invoices/{purchase_invoice_id}/void
```

These routes let an agent inspect supplier bills, create draft supplier bills
submit a draft bill for human approval, and void an unpaid posted supplier bill
without paying, refunding or deleting it.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST /purchase-invoices` creates a draft purchase invoice only and requires an
  `Idempotency-Key`.
- `POST /purchase-invoices/{purchase_invoice_id}/submit` creates or reuses an
  approval request for a draft bill and requires an `Idempotency-Key`.
- `POST /purchase-invoices/{purchase_invoice_id}/void` requires a `reason` and
  an `Idempotency-Key`; it voids an unpaid non-draft supplier bill through the
  existing guarded reversal workflow.
- The API key must have invoice permission, matching the existing purchase
  invoice permission model.
- Submit also requires approval-workflow permission.
- After guarded activation, the approval request, submitted run state,
  submission action and API audit are committed together. A missing guarded
  bridge returns a retryable `503` rather than using the legacy path; master
  keys must also be allowlisted for the company.
- Guarded submit rechecks the exact active key, current direct or delegated
  authority and master-key company allowlist, then saves the approval request,
  pending invoice state, submission action and API audit together. If the
  guarded bridge is enabled but unavailable, the route fails closed with a
  retryable `503` rather than using split writes.
- Guarded voiding uses the canonical supplier-bill reversal workflow. It
  rechecks the exact active key and authority, serialises the bill, preserves
  the original posted journal, creates at most one reversal, and records the
  API audit in the same transaction. Missing guarded infrastructure fails
  closed with a retryable `503`.
- The company ID must be one the key is allowed to access.
- The draft create body must include `supplier_id`, `supplier_invoice_number`,
  `invoice_date` and at least one line item.
- The submit body may include `reason`, `requested_by_name` and
  `requested_by_email`.
- The void body only accepts `reason` for the audit trail.
- Purchase invoice creation validates linked suppliers, accounts, projects,
  funds, documents and cost centres against the requested company.
- Lists support `status`, `supplier_id`, `from_date`, `to_date`, `limit` and
  `offset`.
- Inaccessible purchase invoice IDs return not found rather than revealing data
  from another company.
- Draft creation and submit do not approve, post to ledger, pay, submit tax,
  send documents or make bank payments. Submit creates an approval request only.
- Void may post accounting reversal effects where applicable. It does not
  delete the bill, pay a supplier, refund money, submit tax, send documents or
  make bank payments.
- Supported webhook events include `business.purchase_invoice.voided`.

## Business Supplier Payment Runs

```text
GET /v1/business/companies/{company_id}/supplier-payment-runs
POST /v1/business/companies/{company_id}/supplier-payment-runs
GET /v1/business/companies/{company_id}/supplier-payment-runs/{supplier_payment_run_id}
POST /v1/business/companies/{company_id}/supplier-payment-runs/{supplier_payment_run_id}/submit
POST /v1/business/companies/{company_id}/supplier-payment-runs/{supplier_payment_run_id}/execution-requests
GET /v1/business/companies/{company_id}/supplier-payment-execution-requests/{request_id}
```

These routes let an agent inspect supplier pay runs, create draft pay runs from
selected bills and submit a draft run for human approval without approving,
exporting or executing payments.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST /supplier-payment-runs` creates a draft run and requires an
  `Idempotency-Key`.
- `POST /supplier-payment-runs/{supplier_payment_run_id}/submit` creates or
  reuses an approval request and requires an `Idempotency-Key`.
- The API key must have `suppliers` or `invoices` permission.
- Submit also requires approval-workflow permission.
- The company ID must be one the key is allowed to access.
- Create requires `purchase_invoice_ids` and accepts optional
  `scheduled_payment_date` and `notes`.
- Submit may include `reason`, `requested_by_name` and `requested_by_email`.
- Lists support `status`, `provider`, `scheduled_from`, `scheduled_to`, `limit`
  and `offset`.
- Detail responses include included bill item IDs, supplier IDs, amount,
  currency, membership status and execution status.
- Responses do not expose supplier bank details, payment authorisation URLs or
  raw Yapily/Open Banking payment IDs.
- The run inspection and submit routes do not approve, cancel, export, initiate
  bank payments, mark bills as paid, post journals, reconcile bank transactions
  or move money.
- An agent can create an execution approval handoff for an already-approved run
  using `POST .../execution-requests`. The signed-in user then continues from
  Financial IQ and authorises each bank batch through the existing Yapily flow.
  The handoff is one-use, expires after 15 minutes and records consume/start/
  failure status in the audit trail. The API never receives bank credentials or
  directly initiates the provider payment.
- Inaccessible payment run IDs return not found rather than revealing data from
  another company.

## Business Purchase Orders

```text
GET /v1/business/companies/{company_id}/purchase-orders
POST /v1/business/companies/{company_id}/purchase-orders
GET /v1/business/companies/{company_id}/purchase-orders/{purchase_order_id}
POST /v1/business/companies/{company_id}/purchase-orders/{purchase_order_id}/submit
```

These routes let an agent inspect purchase orders, create draft purchase orders
and submit a draft order for human approval without approving, completing,
receiving, billing or paying it.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST /purchase-orders` creates a draft purchase order and requires an
  `Idempotency-Key`.
- `POST /purchase-orders/{purchase_order_id}/submit` creates or reuses an
  approval request and requires an `Idempotency-Key`.
- After guarded activation, submission is atomic: the approval request, pending
  order state, submission action and API audit are committed together. A missing
  guarded database bridge returns a retryable `503` rather than using the legacy
  path; master keys must also be allowlisted for the company.
- The API key must have `projects` permission, with `transactions` accepted as
  the existing fallback used by the legacy purchase-order API.
- Submit also requires approval-workflow permission.
- The company ID must be one the key is allowed to access.
- Lists support `status`, `supplier_id`, `project_id`, `from_date`, `to_date`,
  `expected_delivery_from`, `expected_delivery_to`, `limit` and `offset`.
- Creates require `supplier_id` and at least one line item. Optional
  `project_id` and `chart_of_accounts_id` values are inherited by line items
  when a line does not provide its own value.
- Creates calculate `subtotal`, `vat_amount` and `total_amount` from the line
  items. Caller-supplied totals are ignored.
- The response includes supplier id/name, project summary, chart-of-account
  summary, totals, dates, approval summary and line items. It does not expose
  supplier bank details, encrypted fields, supplier email or internal notes.
- The submit body may include `reason`, `requested_by_name` and
  `requested_by_email`.
- Inaccessible purchase order IDs return not found rather than revealing data
  from another company.
- v1 purchase order writes do not approve, complete, receive goods, create
  supplier bills, post to ledger, pay, submit tax, send documents or make bank
  payments. Submit creates an approval request only.

## Business Expense Claimants

```text
GET /v1/business/companies/{company_id}/expense-claimants
POST /v1/business/companies/{company_id}/expense-claimants
GET /v1/business/companies/{company_id}/expense-claimants/{expense_claimant_id}
PATCH /v1/business/companies/{company_id}/expense-claimants/{expense_claimant_id}
```

These routes let an agent identify, create or update staff, director or
volunteer claimants for expense-claim workflows without exposing or setting
repayment bank details.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST` creates a claimant and `PATCH` updates safe claimant profile fields.
  Both require an `Idempotency-Key`.
- The API key must have invoice permission, matching the existing expense
  claimant permission model.
- The company ID must be one the key is allowed to access.
- Lists return active claimants by default and support `search` or `q`,
  `is_active`, `active`, `include_inactive`, `limit` and `offset`.
- Creates require `name` and `email`, accept optional `phone` and `is_active`,
  and store email addresses lowercase. Claimant emails are unique per company.
- Updates accept the same safe profile fields: `name`, `email`, `phone` and
  `is_active`.
- The response includes claimant id, name, email, phone and active status. It
  does not expose bank account name, account number, sort code or encrypted
  fields.
- Inaccessible expense claimant IDs return not found rather than revealing data
  from another company.
- v1 expense claimant writes do not accept repayment bank details, create
  expense claims, approve claims, repay claimants, post to ledger, submit tax,
  close periods or make bank payments.

## Business Expense Claims

```text
GET /v1/business/companies/{company_id}/expense-claims
POST /v1/business/companies/{company_id}/expense-claims
GET /v1/business/companies/{company_id}/expense-claims/{expense_claim_id}
POST /v1/business/companies/{company_id}/expense-claims/{expense_claim_id}/submit
POST /v1/business/companies/{company_id}/expense-claims/{expense_claim_id}/void
```

These routes let an agent inspect or draft staff, director or volunteer expense
claims, submit a draft claim for human approval before any repayment or ledger
workflow happens, or void an unpaid claim that was raised in error.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST /expense-claims` creates a draft claim and requires an
  `Idempotency-Key`.
- `POST /expense-claims/{expense_claim_id}/submit` creates or reuses an
  approval request and requires an `Idempotency-Key`.
- After guarded activation, the approval request, pending claim state,
  submission action and API audit are committed together. A missing guarded
  bridge returns a retryable `503` rather than using the legacy path; master
  keys must also be allowlisted for the company.
- `POST /expense-claims/{expense_claim_id}/void` requires a reason and an
  `Idempotency-Key`. It voids an unpaid, non-reimbursed claim and marks linked
  journal or supplier-bill state as voided where applicable.
- After guarded activation, the claim, linked accounting records and API audit
  are committed together. A missing guarded bridge returns a retryable `503`
  rather than using the legacy path; master keys must also be allowlisted for
  the company.
- The API key must have invoice permission, matching the existing expense claim
  API gate.
- Submit also requires approval-workflow permission.
- The company ID must be one the key is allowed to access.
- Lists support `status`, `claimant_id`, `approval_status`, `payment_status`,
  `fund_id`, `from_date`, `to_date`, `include_voided`, `limit` and `offset`.
- Voided claims are hidden unless `include_voided=true` is supplied.
- Creates require an active `claimant_id`, `claim_date` and at least one line
  item. Line totals are calculated from quantity, unit price and VAT rate.
- Creates validate claimant, supplier, account, fund, project and document IDs
  against the requested company before writing.
- The response includes claim totals, status, claimant id/name, fund summary and
  line items. It does not expose claimant bank details or encrypted fields.
- The submit body may include `reason`, `requested_by_name` and
  `requested_by_email`.
- The void body accepts `reason` only. Claims with paid, partial or reimbursed
  payment activity are rejected and need a separate controlled reversal flow.
- Inaccessible expense claim IDs return not found rather than revealing data
  from another company.
- v1 expense claim writes do not approve claims, repay claimants, create
  supplier bills, post to ledger, submit tax, close periods or make bank
  payments. Submit creates an approval request only. Void does not move money,
  refund payments, file tax or delete the original claim.
- Supported webhook events include `business.expense_claim.voided`.

## Business Transactions

```text
GET /v1/business/companies/{company_id}/transactions
POST /v1/business/companies/{company_id}/transactions
GET /v1/business/companies/{company_id}/transactions/{transaction_id}
PATCH /v1/business/companies/{company_id}/transactions/{transaction_id}
```

These routes let an agent inspect business bank transactions. `POST` creates one
manual, unposted business transaction record. `PATCH` updates safe metadata and
categorisation on an existing transaction.

Important behaviour:

- Read routes do not require an `Idempotency-Key`.
- Create and update routes require an `Idempotency-Key`.
- The API key must have transaction permission.
- The company ID must be one the key is allowed to access.
- Lists support `bank_account_id`, `category_id`, `transaction_type`,
  `posting_status`, `reconciliation_status`, `from_date`, `to_date`,
  `uncategorised`, `limit` and `offset`.
- Manual creation requires a company-owned `bank_account_id`,
  `transaction_date`, `description` and signed `gross_amount`. Debits are
  negative and credits are positive.
- Optional create `chart_of_accounts_id` must belong to the requested company.
- Update can change `description`, `merchant_name`, `reference`,
  `chart_of_accounts_id`, `customer_id`, `supplier_id`, `project_id`, `fund_id`,
  `receipt_attached` and `receipt_required`.
- Update checks every linked customer, supplier, project, fund and chart of
  accounts category belongs to the requested company before writing.
- The response does not include raw provider metadata, external provider
  transaction IDs or statement-line internals.
- Inaccessible transaction IDs return not found rather than revealing data from
  another company.
- Manual creation and update do not import bank-feed data, reconcile statements,
  post ledger entries, calculate or post VAT, file tax returns or move money.
  Update also cannot change the transaction amount, date, bank account,
  currency, provider data, posting state or reconciliation state.

## Business Invoices

```text
GET /v1/business/companies/{company_id}/invoices
POST /v1/business/companies/{company_id}/invoices
GET /v1/business/companies/{company_id}/invoices/{invoice_id}
PATCH /v1/business/companies/{company_id}/invoices/{invoice_id}
POST /v1/business/companies/{company_id}/invoices/{invoice_id}/issue
POST /v1/business/companies/{company_id}/invoices/{invoice_id}/void
POST /v1/business/companies/{company_id}/invoices/{invoice_id}/send
POST /v1/business/companies/{company_id}/invoices/{invoice_id}/send/execute
```

These routes let an agent inspect existing sales invoices before deciding
whether to create a new draft, correct an existing draft, issue an approved
draft, void an issued invoice, or request a send approval action.
`POST /invoices` is the business-scoped draft invoice creation route.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST /invoices` creates a draft invoice only and requires an
  `Idempotency-Key`.
- `PATCH /invoices/{invoice_id}` updates only unpaid, unposted draft invoices
  in one database transaction. The invoice change, line history and required
  API audit entry either all succeed or all roll back. It requires an
  `Idempotency-Key` and the `expected_lock_version` returned by the latest
  invoice read, so an agent cannot overwrite a newer edit.
- A patch may correct header fields without resending lines. If `line_items`
  is omitted, existing line IDs and line history are unchanged. If
  `line_items` is supplied, it atomically replaces the active draft lines;
  archived lines are never revived.
- Public API draft creation and line replacement currently accept sales lines
  only. Existing expense-deduction lines keep their type, signed values and line
  IDs during a header-only correction.
- Recurring invoices support monthly, quarterly and yearly frequency and must
  provide exactly one of `recurring_end_date` or `max_occurrences`.
- `POST /invoices/{invoice_id}/issue` issues a sales-line draft invoice,
  accepts no request body, and requires an `Idempotency-Key`. Its audited mode
  posts ledger, VAT and inventory effects in the same database transaction as
  the required API audit entry and rechecks the active API key, permissions and
  company authority there, including master-key allowlists.
- Issuing uses active invoice lines only. Archived line history retained by a
  prior draft correction cannot reach the ledger, VAT records, stock movements
  or cost-centre allocations.
- Drafts containing expense-deduction lines cannot yet be issued through the
  public API because their issue-time VAT split needs a separate guarded path.
- The audited mode has a migration-first activation gate. Until the database
  migrations are confirmed and
  `FINANCIAL_API_V1_AUDITED_INVOICE_ISSUE_ENABLED=true` is set, simple
  sales-line drafts continue through the existing issue bridge. Corrected
  drafts with archived line history and expense-deduction drafts are rejected
  before that bridge is called.
- Once the gate is enabled, the API never falls back to the previous bridge. If
  the audited database function is unavailable it returns `503` without
  changing the invoice, so the caller can safely retry with the same
  `Idempotency-Key`.
- `POST /invoices/{invoice_id}/void` voids a supported sales-line invoice,
  records the supplied `reason`, posts the standard reversal journal where
  applicable, reverses VAT/stock effects where applicable, and requires an
  `Idempotency-Key`. Its audited mode rechecks the active API key, current
  direct or delegated company authority, and master-key allowlists inside the
  same database transaction as the reversal and required API audit entry.
- Expense-deduction invoices cannot yet be voided through the public API. Their
  mixed output/input VAT reversal needs a separately guarded path.
- Invoice voiding has a migration-first activation gate. Until
  `FINANCIAL_API_V1_AUDITED_INVOICE_VOID_ENABLED=true` is set, standard company
  keys continue through the hardened compatibility bridge. Master-key and
  expense-deduction voids fail closed before that bridge is called.
- Once the void gate is enabled, an unavailable audited database function
  returns `503` without falling back or changing the invoice. The same
  `Idempotency-Key` can be retried after activation completes.
- `POST /invoices/{invoice_id}/send` creates or reuses an approval request to
  send a draft invoice and requires an `Idempotency-Key`.
- Guarded mode rechecks the active API key, current direct or delegated company
  authority, master-key allowlist, draft invoice and active approval step in one
  database transaction. The approval request, submitted action and required API
  audit are committed together; the invoice remains a draft until a separate
  approval action completes.
- Guarded mode is activated by
  `FINANCIAL_API_V1_GUARDED_INVOICE_SEND_APPROVAL_ENABLED=true`. Before
  activation, standard keys retain the compatibility path and master keys fail
  closed. After activation, an unavailable database bridge returns `503` and
  never falls back to the weaker path.
- Concurrent requests using different idempotency keys are serialised per
  invoice and reuse one pending approval request. Webhook queue failure does not
  undo the committed approval state.
- The API key must have invoice permission.
- Send approval also requires approval-workflow permission.
- The company ID must be one the key is allowed to access.
- Lists support `status`, `customer_id`, `from_date`, `to_date`, `limit` and
  `offset`.
- Creation and update validate customer, revenue account, inventory item,
  project, cost-centre, line-template, fund and charity-activity references
  against the requested company.
- The send body may include `reason`, `requested_by_name` and
  `requested_by_email`.
- Inaccessible invoice IDs return not found rather than revealing data from
  another company.
- v1 invoice writes do not send invoices externally, delete records, refund
  payments, file tax, close accounting periods or make bank payments. Send
  creates an approval request only.

## Business Quotes And Credit Notes

```text
GET /v1/business/companies/{company_id}/quotes
POST /v1/business/companies/{company_id}/quotes
GET /v1/business/companies/{company_id}/quotes/{quote_id}
PATCH /v1/business/companies/{company_id}/quotes/{quote_id}
POST /v1/business/companies/{company_id}/quotes/{quote_id}/send
POST /v1/business/companies/{company_id}/quotes/{quote_id}/convert
GET /v1/business/companies/{company_id}/credit-notes
POST /v1/business/companies/{company_id}/credit-notes
GET /v1/business/companies/{company_id}/credit-notes/{credit_note_id}
POST /v1/business/companies/{company_id}/credit-notes/{credit_note_id}/issue
POST /v1/business/companies/{company_id}/credit-notes/{credit_note_id}/cancel
```

These routes let an agent inspect sales documents around the invoice workflow
and perform guarded quote conversion. `POST /quotes` creates draft quotes only.
`POST /quotes/{quote_id}/convert` converts an eligible quote into an issued
invoice and posts the normal accounting entries through the existing product
workflow. `POST /credit-notes` creates draft credit notes only.
`POST /credit-notes/{credit_note_id}/issue` issues a draft credit note and posts
the related ledger, VAT and invoice-allocation effects.
`POST /credit-notes/{credit_note_id}/cancel` cancels a credit note raised in
error without deleting it.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST /quotes` creates a draft quote only and requires an `Idempotency-Key`.
- `PATCH /quotes/{quote_id}` corrects a draft quote and requires both an
  `Idempotency-Key` and the latest `expected_lock_version` returned by a quote
  read. Header-only corrections keep the current line IDs. Supplying
  `line_items` replaces the active set while retaining the prior rows as hidden
  history. Sent, converted, accepted, declined, pending-approval, approved or
  on-hold quotes cannot be changed through this route. A rejected draft can be
  corrected and submitted for approval again.
- `POST /quotes/{quote_id}/send` creates or reuses an approval request to send
  a draft quote and requires an `Idempotency-Key`. Once
  `FINANCIAL_API_V1_GUARDED_QUOTE_SEND_APPROVAL_ENABLED=true` is set, the
  approval request, quote status, submission action and required API audit are
  written together by the database safety bridge. Before activation, standard
  company keys retain the existing route and master keys fail closed.
- `POST /quotes/{quote_id}/convert` creates an issued invoice, marks the quote
  as converted, posts the product's normal accounting entries and requires an
  `Idempotency-Key`.
- Guarded quote conversion rechecks the exact active key, both quote and invoice
  permissions, current direct or delegated company access, and master-key
  allowlists inside the same database transaction as conversion and the required
  API audit entry.
- Guarded conversion validates active quote lines, stored totals, customer and
  company coding before invoking the canonical product conversion. Archived
  quote history cannot become invoice, VAT or ledger activity.
- Quote-line writes and conversion share the parent quote lock. A concurrent
  coding edit either commits before validation or waits and is rejected after
  conversion, so the issued invoice uses one stable quote snapshot.
- Quote conversion has a migration-first activation gate. Until
  `FINANCIAL_API_V1_GUARDED_QUOTE_CONVERSION_ENABLED=true` is set, standard
  company keys continue through the existing conversion bridge. Master-key
  conversion fails closed until the guarded bridge is active.
- Once the gate is enabled, an unavailable guarded database function returns a
  retryable `503` without falling back or changing quote, invoice or accounting
  state. The caller can retry the same `Idempotency-Key` after activation.
- Guarded quote-send approval uses the same migration-first rule: an unavailable
  database bridge returns a retryable `503` without falling back to split writes;
  webhook delivery is handled separately and cannot undo the committed approval.
- Concurrent quote-send approval requests are serialised on the quote, so two
  agents receive one pending approval request, one submission action and one
  audit record.
- Current company authority is checked before an idempotent response is replayed,
  so revoked users cannot retrieve prior conversion data through replay.
- `POST /credit-notes` creates a draft credit note only and requires an
  `Idempotency-Key`.
- `POST /credit-notes/{credit_note_id}/issue` requires an `Idempotency-Key`,
  accepts no request body, and only works for draft credit notes.
- `POST /credit-notes/{credit_note_id}/cancel` requires a reason and an
  `Idempotency-Key`. It marks the credit note as cancelled, preserves the
  original posted journal and creates one posted reversal where applicable,
  then archives linked invoice-allocation and VAT state.
- Quote routes require quote permission.
- Quote conversion also requires invoice permission.
- Quote send approval also requires approval-workflow permission.
- Credit-note routes require invoice permission, matching the existing
  credit-note permission model.
- The company ID must be one the key is allowed to access.
- Quote lists support `status`, `customer_id`, `from_date`, `to_date`, `limit`
  and `offset`.
- Quote creation and correction validate customer, revenue account, project,
  cost-centre and fund references against the requested company.
- Draft quote correction rechecks the exact active key and current direct,
  delegated or master-key authority in the same database transaction as the
  quote change and audit entry. It is activated only after the migration is
  confirmed by setting `FINANCIAL_API_V1_GUARDED_QUOTE_UPDATE_ENABLED=true`;
  a missing bridge fails closed with a retryable `503` and never falls back.
- The quote send body may include `reason`, `requested_by_name` and
  `requested_by_email`.
- The quote conversion body may include `invoice_date`, `due_date` and
  `payment_terms_days` only. Invoice customer, currency and line items come
  from the quote.
- Credit-note lists support `status`, `customer_id`, `original_invoice_id`,
  `from_date`, `to_date`, `limit` and `offset`.
- Credit-note creation validates customer, original invoice, revenue account,
  project, cost-centre and fund references against the requested company.
- Credit-note issue posts the accounting journal, records VAT effects where
  applicable, applies the credit to the original invoice when one is linked, and
  only then marks the credit note as issued.
- Credit-note issue and cancellation use migration-first guarded database
  bridges. Until their activation flags are deliberately enabled, existing
  behaviour remains available; once enabled, a missing bridge fails closed with
  a retryable `503` and never falls back to split writes.
- The guarded paths recheck the exact active API key, direct or delegated
  company authority, and master-key company allowlist in the same transaction
  as the accounting changes and public API audit record. Concurrent issue or
  cancellation requests are serialised so only one accounting posting or
  reversal is created.
- Credit-note cancel accepts `reason` only. It does not delete the credit note,
  refund the customer, move money, file tax or send documents.
- v1 quote writes do not send quotes directly, accept, decline, file tax, close
  accounting periods or make bank payments. Send creates an approval request
  only. Conversion is the exception: it creates an issued invoice and posts the
  normal accounting entries.
- v1 credit-note writes do not send credit notes externally, refund customers,
  file tax, close accounting periods or make bank payments.
- Inaccessible quote and credit-note IDs return not found rather than revealing
  data from another company.

## Business Documents

```text
GET /v1/business/companies/{company_id}/documents
POST /v1/business/companies/{company_id}/documents
GET /v1/business/companies/{company_id}/documents/{document_id}
PATCH /v1/business/companies/{company_id}/documents/{document_id}
GET /v1/business/companies/{company_id}/documents/{document_id}/download
```

These routes let an agent inspect the metadata around uploaded business
documents and their links to invoices, bills, purchase orders or bank
transactions. `POST /documents` uploads a JSON/base64 document and returns safe
metadata only. `GET /download` generates a temporary link to the underlying file
so an agent can inspect supporting evidence without seeing the storage bucket or
private file path.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST` requires an `Idempotency-Key`.
- `PATCH` requires an `Idempotency-Key` and updates metadata/links only.
- The API key must have document permission.
- The company ID must be one the key is allowed to access.
- Download links expire after 300 seconds and are generated only after the
  document has been confirmed to belong to the requested company.
- Uploads require `file_name` and base64 `file_content`. Optional fields are
  `file_type`, `status`, `document_type`, `document_classification`,
  `description`, `notes`, `customer_id`, `supplier_id`,
  `linked_purchase_invoice_id`, `linked_purchase_order_id` and
  `linked_bank_transaction_id`.
- Uploads validate customer, supplier, purchase invoice, purchase order and bank
  transaction links against the requested company.
- Updates accept `status`, `document_type`, `document_classification`,
  `description`, `notes`, `customer_id`, `supplier_id`, `linked_invoice_id`,
  `linked_purchase_invoice_id`, `linked_purchase_order_id` and
  `linked_bank_transaction_id`. Links are revalidated against the requested
  company and nullable fields can be cleared with `null`.
- Lists support `status`, `document_type`, `document_classification`,
  `customer_id`, `supplier_id`, `linked_invoice_id`,
  `linked_purchase_invoice_id`, `linked_purchase_order_id`,
  `linked_bank_transaction_id`, `limit` and `offset`.
- Metadata and upload responses do not return signed download URLs, file paths,
  storage bucket names, raw OCR text or document content hashes.
- Download responses return a short-lived `signed_url`, expiry timestamp and
  safe document metadata. They do not return file paths, storage bucket names,
  raw OCR text or document content hashes.
- v1 document uploads do not create invoices or bills, approve expenses, post to
  the ledger, file tax, close accounting periods or make bank payments.
- v1 document updates do not replace files, change storage paths, alter OCR raw
  text, create invoices or bills, approve expenses, post to the ledger, file
  tax, close accounting periods or make bank payments.
- Inaccessible document IDs return not found rather than revealing data from
  another company.
- File replacement and destructive document deletion are not implemented in v1.

## Business Approval Requests

```text
GET /v1/business/companies/{company_id}/approval-requests
GET /v1/business/companies/{company_id}/approval-requests/{approval_request_id}
POST /v1/business/companies/{company_id}/approval-requests/{approval_request_id}/approve
POST /v1/business/companies/{company_id}/approval-requests/{approval_request_id}/reject
POST /v1/business/companies/{company_id}/approval-requests/{approval_request_id}/request-info
```

These routes let an agent check the status of an approval request, approve the
current assigned approval step, reject a pending approval with a reason, or put
a pending approval on hold while more information is requested. They cover the
approval requests created by purchase invoices, purchase orders, expense claims,
supplier payment runs, invoice send requests and quote send requests.

Important behaviour:

- `GET` routes are read-only and do not require an `Idempotency-Key`.
- `POST /approval-requests/{approval_request_id}/approve` requires an
  `Idempotency-Key` and writes an approval action audit row.
- `POST /approval-requests/{approval_request_id}/reject` requires an
  `Idempotency-Key`, a `reason`, and writes a rejection audit row.
- `POST /approval-requests/{approval_request_id}/request-info` requires an
  `Idempotency-Key`, a `message`, and writes an information-request audit row.
- The API key must have approval workflow permission.
- The company ID must be one the key is allowed to access.
- Lists support `status`, `resource_type`, `resource_id`, `current_step_id`,
  `limit` and `offset`.
- Approve accepts an optional `comment` and requires the resolved API-key actor
  to be assigned to the current approval step.
- Reject requires the same current-step assignment and records the reason on
  the approval request.
- Request-info requires the same current-step assignment and records the
  message on the approval request while setting its status to `on_hold`.
- If the step is complete, the request advances to the next applicable step or
  becomes approved. If the request becomes approved, the linked resource status
  is updated to approved.
- Approve, reject and request-info do not send invoices or quotes, export
  supplier payment runs, initiate bank payments, post journals, file tax returns
  or move money.
- Inaccessible approval request IDs return not found rather than revealing data
  from another company.

## Business Audit Logs

```text
GET /v1/business/companies/{company_id}/audit-logs
GET /v1/business/companies/{company_id}/audit-logs/{audit_log_id}
```

These routes let an agent inspect the immutable timeline of recorded business
actions without exposing the underlying before/after payloads.

Important behaviour:

- The routes are read-only and do not require an `Idempotency-Key`.
- The API key must have evidence permission.
- The company ID must be one the key is allowed to access.
- Lists support `action`, `resource_type`, `resource_id`, `created_from`,
  `created_to`, `limit` and `offset`.
- Responses include action, resource type, resource ID and created timestamp.
- Responses do not include raw metadata, old values, new values, IP addresses,
  user agents, profile data or internal actor IDs.
- These routes do not create, update, delete or export audit logs, replay
  actions, change books, submit anything externally or move money.
- Inaccessible audit-log IDs return not found rather than revealing data from
  another company.

## Create Draft Invoice

```text
POST /v1/invoices/drafts
```

Required body fields:

- `customer_id`
- `line_items`

Important behaviour:

- The invoice is forced to draft status.
- The route requires an `Idempotency-Key`.
- Successful writes produce a public API audit row.
- A repeated idempotent request returns the original response.

## Request Invoice Send Approval

```text
POST /v1/invoices/{invoice_id}/send
```

This route asks Financial IQ to create an approval request for sending a draft
invoice. It deliberately does not send the invoice directly.

Optional body fields:

- `reason`
- `requested_by_name`
- `requested_by_email`

Important behaviour:

- The route requires an `Idempotency-Key`.
- The caller must have invoice and approval-workflow permission.
- The response is `202 Accepted` when an approval request is created or reused.
- After the guarded activation gate, the request, submission action and required
  API audit are atomic. A missing guarded bridge returns `503` with
  `Retry-After: 30`, so an agent can retry safely.

## Execute Approved Invoice Delivery

```text
POST /v1/business/companies/{company_id}/invoices/{invoice_id}/send/execute
```

This route completes delivery only after the supplied invoice-specific approval
request has status `approved`. It requires `invoices` and
`approval_workflow` permissions, an `Idempotency-Key`, and an
`approval_request_id`. It reuses the existing audited email sender and records
provider delivery evidence; it does not create or approve the request, post
ledger entries, or move money.

## Evidence Bundles

```text
GET /v1/evidence-bundles/{resource_type}/{resource_id}
```

This route returns a safe evidence bundle for supported business resources. The
first implemented resource type is `invoice`.

Important behaviour:

- The caller must have `business.evidence.read` scope.
- The route is read-only and does not require an `Idempotency-Key`.
- The response uses `bundle_status` to distinguish `complete`, `partial`,
  `missing`, `not_supported` and `lookup_failed`.
- Document evidence returns metadata only. It does not return signed document
  URLs by default.
- Lookup failures return a safe `lookup_failed` status and must not expose raw
  database or provider messages.

## Personal Financial Summary

```text
GET /v1/personal/summary
```

This route gives a user-authorised agent a compact view of the user's personal
financial position without downloading each underlying record.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- It requires a bearer user token with `personal.accounts.read`,
  `personal.assets.read`, `personal.liabilities.read`, `personal.budgets.read`
  and `personal.goals.read` consent.
- API keys cannot call personal routes.
- Totals are built from active personal accounts, active assets, active
  liabilities, active budgets and personal goals.
- The response includes net worth, account balance, available balance, asset
  value, liability balance, budget progress and goal progress.
- The response includes grouped totals only. It does not include account
  numbers, sort codes, bank-feed tokens, encrypted token fields, hashes, raw
  provider payloads, individual transactions, bank statements or business ledger
  data.
- This route does not create, update or delete records, import transactions,
  connect bank feeds, move money, post ledger entries, raise invoices, file tax
  submissions or share data with a business company.

## Personal Net-Worth Report

```text
GET /v1/personal/reports/net-worth
```

This route gives a user-authorised agent a focused net-worth report built from
personal account balances, assets and liabilities.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- It requires a bearer user token with `personal.accounts.read`,
  `personal.assets.read` and `personal.liabilities.read` consent.
- API keys cannot call personal routes.
- The response includes total net worth, gross assets, debt-to-asset percentage,
  account balance totals, asset totals and liability totals.
- The response includes category/type group totals only; it does not include
  account numbers, sort codes, bank-feed tokens, encrypted token fields, hashes,
  raw provider payloads, individual transactions, bank statements, budgets,
  goals or business ledger data.
- This route does not create, update or delete records, import transactions,
  connect bank feeds, move money, post ledger entries, raise invoices, file tax
  submissions or share data with a business company.

## Personal Cash-Flow Report

```text
GET /v1/personal/reports/cash-flow
```

This route gives a user-authorised agent money-in, money-out and net cash-flow
totals for a defined personal transaction period.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- It requires a bearer user token with `personal.transactions.read` consent.
- API keys cannot call personal routes.
- `from_date` and `to_date` are required and must use `YYYY-MM-DD` format.
- Optional filters are `account_id`, `category_id` and `uncategorised=true`.
- The response includes totals, monthly totals, category totals and account ID
  totals only.
- The response does not include transaction descriptions, merchant names, raw
  provider payloads, external transaction IDs, account names, account numbers,
  sort codes, bank-feed tokens or business ledger data.
- This route does not create, update or delete transactions, categorise
  transactions, connect bank feeds, move money, post ledger entries, raise
  invoices, file tax submissions or share data with a business company.

## Personal Budget Performance Report

```text
GET /v1/personal/reports/budget-performance
```

This route gives a user-authorised agent budgeted, spent and remaining totals
for a defined personal budget period.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- It requires a bearer user token with `personal.budgets.read` consent.
- API keys cannot call personal routes.
- `from_date` and `to_date` are required and must use `YYYY-MM-DD` format.
- Optional filters are `category_id`, `budget_type`, `period_type` and
  `is_active`.
- The response includes overall totals, budget-type totals, category totals,
  month totals and safe budget summaries.
- The response does not include transaction rows, bank details, account numbers,
  sort codes, bank-feed tokens, raw provider payloads or business ledger data.
- This route does not create, update or delete budgets or transactions,
  categorise transactions, connect bank feeds, move money, post ledger entries,
  raise invoices, file tax submissions or share data with a business company.

## Personal Goal Progress Report

```text
GET /v1/personal/reports/goal-progress
```

This route gives a user-authorised agent target, current and remaining totals
for the user's personal financial goals.

Important behaviour:

- The route is read-only and does not require an `Idempotency-Key`.
- It requires a bearer user token with `personal.goals.read` consent.
- API keys cannot call personal routes.
- Optional filters are `search` or `q`, `category_id`, `linked_account_id`,
  `goal_type`, `is_achieved`, `is_shared`, `from_target_date` and
  `to_target_date`.
- The response includes overall totals, goal-type totals, category totals and
  safe goal summaries.
- The response does not include goal descriptions, transaction rows, bank
  details, account numbers, sort codes, bank-feed tokens, raw provider payloads
  or business ledger data.
- This route does not create, update or delete goals or transactions, categorise
  transactions, connect bank feeds, move money, post ledger entries, raise
  invoices, file tax submissions or share data with a business company.

## Personal Accounts

```text
GET /v1/personal/accounts
POST /v1/personal/accounts
GET /v1/personal/accounts/{account_id}
PATCH /v1/personal/accounts/{account_id}
```

These routes let a user-authorised agent read the user's personal account
balances and account metadata. The `POST` route lets an authorised agent create
a manual personal account record. The `PATCH` route updates safe display and
classification metadata.

Important behaviour:

- The read routes require a bearer user token with `personal.accounts.read`
  consent.
- The create and update routes require a bearer user token with
  `personal.accounts.write` consent and an `Idempotency-Key` header.
- API keys cannot call personal routes.
- Create accepts `account_name`, optional `bank_name`, optional `account_type`,
  optional `account_category`, optional `currency`, optional `current_balance`
  and optional active state.
- Update accepts `account_name`, `bank_name`, `account_type`,
  `account_category`, `currency` and active state only.
- The response does not include account numbers, sort codes, bank-feed tokens,
  encrypted token fields, or hashes.
- Account creation does not connect a bank feed, import transactions, store
  account numbers or sort codes, move money, post ledger entries or file tax
  submissions.
- Account updates do not change balances, connect bank feeds, import
  transactions, store account numbers or sort codes, move money, post ledger
  entries or file tax submissions.
- Inaccessible account IDs return not found rather than revealing another user's
  data.

## Personal Assets

```text
GET /v1/personal/assets
POST /v1/personal/assets
GET /v1/personal/assets/{asset_id}
PATCH /v1/personal/assets/{asset_id}
```

These routes let a user-authorised agent read the user's personal asset list,
such as property, vehicles, investments, savings and other asset records. The
`POST` route lets an authorised agent create a personal asset record. The
`PATCH` route updates one owned asset record.

Important behaviour:

- The read routes require a bearer user token with `personal.assets.read`
  consent.
- The create and update routes require a bearer user token with
  `personal.assets.write` consent and an `Idempotency-Key` header.
- API keys cannot call personal routes.
- Lists support `search` or `q`, `asset_type`, `is_active`, `min_value`,
  `max_value`, `limit` and `offset`.
- Create accepts `name`, `asset_type`, `current_value`, optional purchase
  details, optional description and active state.
- Update accepts the same asset fields and only changes that one owned asset.
- Inaccessible asset IDs return not found rather than revealing another user's
  data.
- Asset creation does not create transactions, move money, post ledger entries
  or file tax submissions.
- Asset updates do not create transactions, move money, post ledger entries or
  file tax submissions.

## Personal Liabilities

```text
GET /v1/personal/liabilities
POST /v1/personal/liabilities
GET /v1/personal/liabilities/{liability_id}
PATCH /v1/personal/liabilities/{liability_id}
```

These routes let a user-authorised agent read the user's personal liabilities,
such as mortgages, loans, credit cards and other debt records. The `POST` route
lets an authorised agent create a personal liability record. The `PATCH` route
updates one owned liability record.

Important behaviour:

- The read routes require a bearer user token with `personal.liabilities.read`
  consent.
- The create and update routes require a bearer user token with
  `personal.liabilities.write` consent and an `Idempotency-Key` header.
- API keys cannot call personal routes.
- Lists support `search` or `q`, `liability_type`, `is_active`, `min_balance`,
  `max_balance`, `limit` and `offset`.
- Create accepts `name`, `liability_type`, `current_balance`, optional
  interest/payment details, start/maturity dates and active state.
- Update accepts the same liability fields and only changes that one owned
  liability.
- Inaccessible liability IDs return not found rather than revealing another
  user's data.
- Liability creation does not create transactions, move money, post ledger
  entries or file tax submissions.
- Liability updates do not create transactions, move money, post ledger entries
  or file tax submissions.

## Personal Budgets

```text
GET /v1/personal/budgets
POST /v1/personal/budgets
GET /v1/personal/budgets/{budget_id}
PATCH /v1/personal/budgets/{budget_id}
```

These routes let a user-authorised agent read personal budget targets and
current spend against those targets. The `POST` route lets an authorised agent
create a personal budget target. The `PATCH` route updates one owned budget
target.

Important behaviour:

- The read routes require a bearer user token with `personal.budgets.read`
  consent.
- The create and update routes require a bearer user token with
  `personal.budgets.write` consent and an `Idempotency-Key` header.
- API keys cannot call personal routes.
- Lists support `category_id`, `budget_type`, `period_type`, `is_active`,
  `from_date`, `to_date`, `limit` and `offset`.
- Create accepts `budget_amount`, `start_date`, `end_date`, optional
  `category_id` or `category_name`, budget type, period, month and rollover
  settings.
- Update accepts the same budget fields and only changes that one owned budget.
- Linked categories and templates must belong to the bearer-token user.
- Inaccessible budget IDs return not found rather than revealing another user's
  data.
- Budget creation does not create transactions, move money, post ledger entries
  or file tax submissions.
- Budget updates do not create transactions, move money, post ledger entries or
  file tax submissions.

## Personal Categories

```text
GET /v1/personal/categories
POST /v1/personal/categories
GET /v1/personal/categories/{category_id}
PATCH /v1/personal/categories/{category_id}
```

These routes let a user-authorised agent understand the categories used in the
user's personal transaction ledger. The `POST` route lets an authorised agent
create a category before using it in budgets or transaction categorisation. The
`PATCH` route updates one owned category.

Important behaviour:

- The read routes require a bearer user token with `personal.categories.read`
  consent.
- The create and update routes require a bearer user token with
  `personal.categories.write` consent and an `Idempotency-Key` header.
- API keys cannot call personal routes.
- Lists support `search` or `q`, `category_type`, `parent_id`, `is_active`,
  `limit` and `offset`.
- Create accepts `name`, `category_type`, optional `parent_id`, `icon`, `color`,
  `is_active` and `sort_order`.
- Update accepts the same category fields and only changes that one owned
  category.
- Parent categories must belong to the bearer-token user and use the same
  `category_type`.
- Inaccessible category IDs return not found rather than revealing another
  user's data.
- Category creation and update do not create transactions, budgets, postings or
  tax submissions.

## Personal Categorization Rules

```text
GET /v1/personal/categorization-rules
POST /v1/personal/categorization-rules
GET /v1/personal/categorization-rules/{rule_id}
PATCH /v1/personal/categorization-rules/{rule_id}
```

This route lets a user-authorised agent inspect the user's personal
categorisation rules before suggesting or applying transaction changes. The
write routes let an authorised agent create or safely adjust rules for future
categorisation.

Important behaviour:

- The read routes require a bearer user token with both
  `personal.transactions.read` and `personal.categories.read` consent and do not
  require an `Idempotency-Key`.
- The create and update routes require a bearer user token with both
  `personal.transactions.write` and `personal.categories.read` consent and an
  `Idempotency-Key` header.
- API keys cannot call personal routes.
- Lists support `search` or `q`, `category_id`, `match_type`, `pattern_type`,
  `is_active`, `limit` and `offset`.
- Create and update only accept `merchant_pattern`, `category_id`, `match_type`,
  `pattern_type` and `is_active`.
- `category_id` must belong to the bearer-token user and point to an active
  personal category.
- The response includes the merchant or keyword pattern, match settings,
  category id, category label, confidence score, success rate and usage count.
- Usage count, success rate and confidence score are system-managed and cannot
  be set through this API.
- The response does not include transaction rows, account numbers, sort codes,
  bank-feed tokens, raw provider payloads, business ledger data or another
  user's rules.
- These routes do not categorise existing transactions, create or update
  transactions, move money, post ledger entries, raise invoices, file tax
  submissions or share data with a business company.

## Personal Goals

```text
GET /v1/personal/goals
POST /v1/personal/goals
GET /v1/personal/goals/{goal_id}
PATCH /v1/personal/goals/{goal_id}
```

These routes let a user-authorised agent read the user's personal financial
goals, such as savings targets, debt payoff targets or income buffers. The
`POST` route lets an authorised agent create a planning goal. The `PATCH` route
updates one owned goal.

Important behaviour:

- The read routes require a bearer user token with `personal.goals.read`
  consent.
- The create and update routes require a bearer user token with
  `personal.goals.write` consent and an `Idempotency-Key` header.
- API keys cannot call personal routes.
- Lists support `search` or `q`, `category_id`, `linked_account_id`,
  `goal_type`, `is_achieved`, `is_shared`, `from_target_date`, `to_target_date`,
  `limit` and `offset`.
- Create accepts `name`, `goal_type`, `target_amount`, optional category, linked
  account, target dates, contribution settings and priority.
- Update accepts the same goal fields plus `is_achieved`, and only changes that
  one owned goal.
- Linked categories and accounts must belong to the bearer-token user.
- Inaccessible goal IDs return not found rather than revealing another user's
  data.
- Goal creation and update do not create transactions, move money, post ledger
  entries or file tax submissions.

## Personal Transactions

```text
GET /v1/personal/transactions
GET /v1/personal/transactions/{transaction_id}
POST /v1/personal/transactions
PATCH /v1/personal/transactions/{transaction_id}
```

These routes let a user-authorised agent read the user's personal transaction
ledger without exposing raw bank-feed provider payloads. The POST route lets an
agent create one manual personal transaction record. The PATCH route updates
safe categorisation and metadata only.

Important behaviour:

- Read routes require a bearer user token with `personal.transactions.read`
  consent.
- The create route requires `personal.transactions.write` consent and an
  `Idempotency-Key`.
- The update route requires `personal.transactions.write` consent and an
  `Idempotency-Key`.
- API keys cannot call personal routes.
- Lists support `account_id`, `category_id`, `transaction_type`, `from_date`,
  `to_date`, `uncategorised`, `limit` and `offset`.
- Manual transaction creation requires an owned `bank_account_id`,
  `transaction_date`, `description` and signed `gross_amount`. Debits are
  negative; credits are positive.
- Optional `personal_category_id` must belong to the authenticated user.
- Updates accept only `description`, `merchant_name`, `reference`,
  `personal_category_id`, `receipt_attached` and `receipt_required`. Categories
  are validated against the authenticated user and can be cleared with `null`.
- The response does not include raw provider metadata, external provider
  transaction IDs or statement-line internals.
- Inaccessible transaction IDs return not found rather than revealing another
  user's data.
- Manual transaction creation does not connect a bank feed, import provider
  transactions, move money, reconcile statements, post ledger entries, change
  business books or file tax returns.
- Personal transaction updates do not change the amount, date, account,
  currency, source, provider data or reconciliation state.

## Personal Business Links

```text
GET    /v1/personal/business-links
POST   /v1/personal/business-links
GET    /v1/personal/business-links/{link_id}
DELETE /v1/personal/business-links/{link_id}
GET    /v1/personal/business-links/{link_id}/shareholders
POST   /v1/personal/business-links/{link_id}/shareholders
GET    /v1/personal/business-links/{link_id}/shareholders/{shareholder_id}
PATCH  /v1/personal/business-links/{link_id}/shareholders/{shareholder_id}
DELETE /v1/personal/business-links/{link_id}/shareholders/{shareholder_id}
```

These routes manage explicit links between the authenticated user's personal
finance space and companies they have chosen to connect.

Important behaviour:

- Read routes require a bearer user token with `personal.business_links.read`
  consent.
- Personal business link detail returns one user-owned link by ID. It is
  read-only and does not require an `Idempotency-Key`.
- Shareholder mapping reads return the mappings already attached to one
  user-owned personal business link.
- Shareholder mapping detail reads return one mapped shareholder by ID. They
  are read-only and do not require an `Idempotency-Key`.
- Write routes require a bearer user token with `personal.business_links.write`
  consent.
- Write routes require an `Idempotency-Key`.
- Shareholder mapping updates can only change `weight_override`. A value of
  `null` clears the override.
- Shareholder unmapping deletes only the user's personal mapping row. It does
  not delete business shareholders, alter company registers or change the books.
- API keys cannot call personal routes.
- Business scopes do not grant personal access.
- The response can identify linked companies and shareholder mappings, but it
  does not allow the caller to mutate business books.
- Revocation is a soft revoke: the link is marked inactive rather than deleting
  historical audit evidence.

## Webhook Endpoint Management

```text
GET    /v1/webhooks/deliveries
GET    /v1/webhooks/endpoints
POST   /v1/webhooks/endpoints
GET    /v1/webhooks/endpoints/{endpoint_id}
DELETE /v1/webhooks/endpoints/{endpoint_id}
POST   /v1/webhooks/endpoints/{endpoint_id}/rotate-secret
```

Creating an endpoint requires:

- `name`
- `target_url`, which must use HTTPS
- `enabled_events`

Supported events today:

- `business.invoice.created`
- `business.invoice.updated`
- `business.invoice.issued`
- `business.invoice.voided`
- `business.quote.updated`
- `business.purchase_invoice.voided`
- `business.expense_claim.voided`
- `business.credit_note.issued`
- `business.credit_note.cancelled`
- `business.approval.created`
- `agent.action.requires_approval`
- `*`

Signing secrets are returned only when an endpoint is created or rotated. Store
the secret immediately; future reads only return a preview.

## Error Shape

v1 routes return a JSON envelope rather than raw database or provider messages.

```json
{
  "error": {
    "message": "A human-readable error message"
  },
  "meta": {
    "api_version": "v1-2026-07",
    "request_id": "request-id"
  }
}
```

Server-side logs use the edge redaction layer, and client responses must not
include raw provider tokens, SQL messages or personal data.

## Operational Guidance

### Tenant Boundary

Business routes always include the target `{company_id}` in the path. The API
key must be allowed to access that company, and referenced records such as
customers, suppliers, bank accounts, categories, projects, funds and documents
must also belong to that company. If a caller asks for another company's data,
the API returns a safe not-found or access error instead of revealing whether
the record exists.

Personal routes do not accept API keys. They require a user-authorised bearer
token and only return records owned by that user or explicitly linked to that
user. Business scopes never grant personal access.

### Pagination

List routes use `limit` and `offset` where documented. The default limit varies
by route, and most business lists are capped between 200 and 500 rows per
request. Callers should page forward by increasing `offset` by the returned
`limit` until `has_more` is false.

### Idempotency

Write routes that create or change business effects require `Idempotency-Key`.
Reuse the same key only for the same route and the same request body. Reusing a
key with a different body is rejected, and reusing a key with the same body
returns the stored response instead of creating a duplicate.

```text
Idempotency-Key: invoice-create-2026-07-09-001
X-FIQ-Request-Id: agent-run-123
```

For retrying writes after a network failure, send the same idempotency key and
the same body. Do not retry a write with a new key unless the caller has first
confirmed that the original request did not create a record.

### Rate Limits And Retries

API-key and bearer-token routes are rate limited. Unless a tighter key-specific
limit is configured, callers should expect 60 requests per minute and 1,000
requests per hour. When a bucket is exhausted, the API returns HTTP 429 and a
`Retry-After` header.

Retry only transient failures such as HTTP 429 or 5xx responses, use exponential
backoff, and keep the same `Idempotency-Key` for write retries. Do not retry
validation errors or access errors without changing the request.

### Webhook Safety

Webhook signing secrets are displayed once on endpoint creation or rotation.
The list and detail routes return only a secret preview. Store the full secret
outside the application, rotate it if exposed, and revoke endpoints that are no
longer in use.

### Rollback And Deprecation

The API version is explicit in every response as `v1-2026-07`. Rollback for a
bad API release should disable the new public gateway or route traffic back to
the previous deployed function version before revoking caller credentials. Data
created through idempotent write routes should be voided, cancelled or soft
revoked through the relevant business workflow rather than deleted.

Breaking changes must ship under a new API version. Existing v1 routes should
remain backward compatible; if a route is withdrawn, publish the replacement
route, owner contact and sunset date before disabling access.

### Support Handoff

Support requests should include the endpoint, method, timestamp, API version,
safe `X-FIQ-Request-Id`, response status and redacted response body. Do not send
API keys, bearer tokens, webhook signing secrets, bank details, payroll
identifiers or raw provider payloads in support messages.

## Outside The Current API Boundary

The routes above are the implemented v1 surface. The following capabilities
remain outside the current API boundary or require separate release evidence.
Do not describe them as unattended execution until their provider credentials,
consent model, audit controls, rollback path and release approval are complete.

## Capabilities Requiring Separate Release Or Provider Work

These are capability areas, not promised route names. The route guide must not
publish additional paths until implementation, OpenAPI and tests exist.
