# Changelog

The changes to the public API that integrators must know, newest first. The
contract is the public schema, [`/docs/api/openapi.json`](/docs/api/openapi.json).
A change that is not listed here only adds something: a new operation, a new
optional parameter, a new field in a response, or a new error code.

A breaking change (a removed operation or field, a new required parameter, a
changed type) is always listed here, with what to change.

## 15.10.2026: the payments of an invoice, and a new permission

- `permissions` in `GET /api/companies/{company_id}/` can list a new value,
  `reconciliation.decide`: the right to confirm, reject or undo a match of
  a bank payment to a purchase invoice that OscarAI proposed. Accept and
  ignore a permission you do not know.
- `GET /api/companies/{company_id}/invoices/{invoice_id}/` has a new field,
  `allocations`: every active allocation that settles the invoice (a
  payment, a refund, a credit note), with `bank_transaction_id` when the
  other side is a bank line.
- The invoice list and the invoice have a new field, `posted_at`: when the
  current revision was posted, null while it is not posted.
- Each document of `revision.documents` has two new fields, `file_id` and
  `download_url`, as the document records have.
- Each document of `revision.documents` has a new field, `source`: where
  its stored file came from (`email`, `upload`, `api`, `import`,
  `generated` or `other`; more values may come), null when no file is
  stored.

## 15.10.2026: a company whose data was deleted

- `state` of a company (`GET /api/companies/{company_id}/`, the companies of
  `GET /api/me/`) can be a new value, `deleted`: the service of the company
  ended and its data was deleted. Such a company has no books, and it never
  becomes `ready` again. Accept a state you do not know and treat it as one
  with no books.
- A company the firm closed keeps its registry code: a new company with that
  code answers 409 `company_closed`.

## 14.10.2026: employees and payroll

- New operations: list and read the employees of a company, submit a new
  employee or a change and read the status of the submission, and list and
  read the pay runs.
- `permissions` in `GET /api/companies/{company_id}/` can list a new value,
  `employees.submit`: the right to submit a new employee or a change for the
  firm to review. Accept and ignore a permission you do not know.
- `GET /api/me/` has a new field, `features.employees`: true while the
  employee register is on.

## 14.10.2026: the tax account balance

- New operation `GET /api/companies/{company_id}/tax-account/balance/`
  (`companies_tax_account_balance`, permission `books.read`): the tax
  account of the company at the Estonian Tax and Customs Board (EMTA). It
  gives the balance of the claims (`total`, above zero: the company owes
  it), the interest, the prepayment account (`prepayment_balance`, and every
  account in `account_balances`), the open claims with their due dates, and
  `notices` (a debt, a claim due within 7 days).
- The answer comes from the copy OscarAI keeps, not from a live call:
  `as_of` is when it was read from EMTA, and `access_status` says how the
  last read went (`ok`, `rights_missing` when the company has not granted
  `ACCOUNTING_XROAD` to OscarAI in e-MTA, `failed`, `never`). Before the
  first read every amount is null.

Nothing to change: this only adds an operation.

## 13.10.2026: unit prices with decimals, and a party's invoice language

- An invoice line takes `unit_price`, the unit price in major units as a
  decimal string of at most 6 decimals (`"150.334"`). The books compute the
  net as quantity x unit price, rounded half up to the minor unit of the
  currency. `unit_price_minor` stays valid; when a line gives both, they
  must name the same price (400 `invalid_payload` otherwise).
- The lines of an invoice detail have the new field `unit_price`, also for a
  line recorded with `unit_price_minor` only. `unit_price_minor` is null on a
  line whose price has a fraction of a minor unit.
- The defaults of a party (`defaults` of a party detail) have the new field
  `language`: `et`, `en` or null.

Nothing to change: every new field is optional.

## 09.10.2026: what an own sales invoice carries

- `record_invoice` and `revise_invoice` of an own sales invoice refuse a
  document with the role `original` (400 `invalid_payload`). The original of
  an own sales invoice is the PDF Oscar issues. The role of a document
  defaults to `original`, so a document on a sales draft names the role
  `supporting`. `import_sales_invoice` still takes the original of an
  invoice issued elsewhere.

- `issue_invoice` of a VAT-registered company refuses an invoice without
  the seller's address (400 `seller_address_missing`) or without the
  buyer's address (400 `buyer_address_missing`), as the VAT Act requires
  (KMS § 37 (7) p 2 and p 3). The API takes the seller's address from the
  Business Register; give `seller_address` when the register has none. A
  customer that stands for private persons (`private_persons`) needs no
  address.
- `issue_invoice` of a sales invoice in another currency than the euro that
  charges VAT freezes its euro value at the ECB rate of the tax point, and
  refuses the issue when that rate is not known yet (400
  `vat_exchange_rate_missing`). The detail of an invoice has the new field
  `revision.base_vat`: the VAT of each rate and in total in euro, and the
  rate used.

What to change: send `"role": "supporting"` for a document of a sales
draft, and set the address of a customer (`address` of the party) before
you issue its invoice.

## 08.10.2026: OAuth asks for `write` on purpose

- An OAuth authorization request (`/oauth/authorize`) or a device
  authorization request (`/oauth/device_authorization`) that names no
  `scope` now gets `read` only. Before, it got `read write`.
- The consent page shows the `client_id` of the app. For an app that
  registered itself (a client ID metadata document or dynamic
  registration), it warns and shows no logo.

What to change: an integration that writes sends `scope=read write` in the
authorization request. A token without `write` gets 403
`insufficient_scope` on a write. The `oscar` CLI already sends the scope.

## 08.10.2026: size limits on requests

- A JSON request body is at most 1 MB. A larger one is refused with 413
  `request_too_large`. A file upload (`POST .../files/`) keeps its own limit
  of 25 MB.
- Every text field of a command body (invoices, parties, events and the
  others) is at most 2000 characters, and the schema now says so with
  `maxLength`. A longer text is refused with 400 `validation_error`.
- A free JSON value of a command (`source_payload`, `seller_snapshot`,
  `buyer_snapshot`, `raw`) is at most 64 KB as JSON.
- A list of a command has a maximum: 500 lines and 50 documents in an
  invoice revision, 2000 lines in an event. A longer list is refused with
  400 `validation_error`.
- `GET .../files/{file_id}/content/` now counts against the rate of the
  expensive operations, as an upload does: 429 `throttled` when it is used
  up.

What to change: split a larger invoice or event, and shorten a longer text.

## 27.09.2026: payment providers

- `permissions` in `GET /api/companies/{company_id}/` can list two new
  values: `payments.read`, the right to read the payment provider (Stripe)
  connections of the company, and `payments.manage`, the right to connect a
  payment provider, replace its key, sync it and disconnect it. The
  operations of both are not public yet. Accept and ignore a permission you
  do not know.

## 25.09.2026: the bank feed

- `permissions` in `GET /api/companies/{company_id}/` can list a new value,
  `bank.connect`: the right to connect a bank of the company through open
  banking, reconnect it and disconnect it. Only a browser session holds it,
  so an API key or an OAuth token never gets it. Accept and ignore a
  permission you do not know.

## 25.09.2026: the AI agent

- `permissions` in `GET /api/companies/{company_id}/` can list a new value,
  `agent.use`: the right to use the AI agent of Oscar. Only a browser
  session holds it, so an API key or an OAuth token never gets it. Accept
  and ignore a permission you do not know.
- `GET /api/me/` has a new field, `features`, with the features this
  deployment turns on.

## 25.09.2026: the first public API

- The first public set of operations: the organization and its companies,
  parties, invoices (record, revise, issue, cancel), files (upload, read,
  download the bytes), document records, bank accounts and bank transactions,
  the chart of accounts, VAT codes, periods, dimensions, and the reports.
- Authentication with an organization API key, `Authorization: Bearer osk_live_...`.
- The public schema at `/docs/api/openapi.json`, and these docs.
