> ## Documentation Index
> Fetch the complete documentation index at: https://docs.capcells.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> Every version of the API contract: what it added, what it deprecates and until when, and what broke.

The contract is versioned by date. A version is the day it first reached production. Every response names the version it was rendered under in the `Contract-Version` header.

## Stating a version

Send the version your client was built against in the same header:

```bash theme={null}
curl https://api.capcells.com/v1/tenants/acme/loans \
  -H "Contract-Version: 2026-10-16" \
  -H "Authorization: Capcells-Ed25519 fp=…,ts=…,sig=…"
```

| You send | You are served |
| - | - |
| No `Contract-Version` | The baseline, `2026-10-15`. It changes only through a window. |
| A version below | That version. |
| Anything else | `400 invalid_contract_version`. Nothing is read or written. |

## What breaks

| Breaking | Not breaking |
| - | - |
| Removing or renaming a route, field, query parameter or enumeration member | Adding a route |
| Changing a field's type, unit or meaning | Adding an optional request or response field |
| Refusing a request that was served | Adding a member to an open enumeration |
| Making an optional request field required | Adding a refusal code or a `details` key |
| Serving a closed enumeration's new member to a caller that does not know it | Rewording prose |

Ignore a response field you do not know.

A breaking change passes through a window. The old behaviour is served until a stated day, at least 90 days after the release that announces it. The day never moves. Until then, every response that relies on it carries three headers:

| Header | Value |
| - | - |
| `Deprecation` | The day, as `@<seconds>` (RFC 9745). |
| `Sunset` | The same day, as an HTTP date (RFC 8594). |
| `Link` | This page's entry, with `rel="deprecation"`. |

## Open and closed enumerations

The reference marks every enumeration `x-enum-open: true` or `false`.

| Kind | Rule | Examples |
| - | - | - |
| Open | May gain a member in any version. Treat a member you do not know as one you do not interpret. | Refusal codes, a loan event's `kind`, every enumeration in a request |
| Closed | Gains a member only for a caller that knows it. | Loan `state`, `election`, `rail`, schedule `kind`, decision `outcome`, the undoing `kind` |

You know a closed member when you stated a version that introduced it, when your request names it (a listing filtered by it), or when the record exists because you used the field or route introduced with it. Otherwise a listing leaves the record out, and a read of that one record is refused `406 contract_version_required`. Its `details.bound` names the version to state.

## Versions

<Update label="2026-10-16" description="Contract-Version, open and closed enumerations">
  **Added**

  * `Contract-Version` on every response, and as an optional request header.
  * `invalid_contract_version` (`400`): a version that was never published.
  * `contract_version_required` (`406`): a record in a closed member your version does not know.
  * `x-enum-open` on every enumeration in the reference.
  * **Client guards** on a product version: `max_open_loans_per_client`, `max_outstanding_per_client`, `refuse_while_any_loan_overdue`. Issuance and preview refuse past them with `client_open_loans`, `client_exposure` or `client_overdue` (`422`).
  * **A limit decision**: `limit: true` on a credit decision asks for the most the band admits; the decision answers `ceiling` and `over_ceiling`.
  * **A line**: `line: true` on a facility created from a limit decision, drawn by every purchase and refilled by repayment; `supersedes` and `superseded_by` when a line is renewed. New refusals `facility_superseded` and `cannot_supersede` (`422`).
  * **A whole return**: a goods return may state more than the outstanding principal. The answer states `returned_whole`, `unwound_principal`, `principal_owed_back`, `interest_owed_back`, `beyond_the_loan` and `still_owed`; the client's obligations list `returns` and `returned`. A return that was refused for exceeding the outstanding principal is now served where the loan received principal.
  * The product frame's field `unit` is open, and gains `loans`.
  * **What falls due on a day**: `GET /v1/tenants/{tenant_id}/dues?on=` lists every Client with something fallen due and unpaid on the date, with each owing Loan's `overdue`, `late` and `instalment` as the payment options answer them and per-currency totals. Narrowed by `only=late` and `min_days_past_due`; one request examines at most twice `limit` Clients.
  * **Your request bounds**: `GET /v1/tenants/{tenant_id}/bounds` answers `concurrent`, `patience_ms`, `per_second`, `burst` and `source`. The reference states the defaults — 16, 2 seconds, 50 per second, 100 — and that requests within them are never refused for a bound.
  * **A market's rules**: `GET /v1/tenants/{tenant_id}/markets/{jurisdiction}` answers the penalty cap's form, the penalty base, capitalisation, whether a late fee may stand beside a penalty rate, acceleration, the tax rules, the disclosure convention, the calendar's last covered day and the facility window, each with `source` and `provisional`. A jurisdiction nothing is supplied for is `404 not_found`.
  * `details.bound` and `details.observed` on `penalty_rate_above_cap`, `outside_window_bound`, `beyond_horizon` and `invalid_identifier`: the bound the market's rules state (or the identifier pattern) and the value that exceeded it.
  * `effective` on every product configuration read, create and preview: every field the frame declares, with its value and `source` (`stated` or `default`). `values` is unchanged.
  * `advisories` on a configuration create, a version create and both previews, with the open codes `first_period_may_fall_next_day` and `no_tolerance`. An advisory refuses nothing.
  * `percent` beside the ratio on a decision's, a resolution's and a facility's `rate`: the decimal percentage a loan states.
  * `rate` is optional on a preview and an issuance under a facility, which then takes the facility's rate; `penalty_rate` is optional under a version whose penalty corridor is a single point, which then takes the point.
  * **Lookup keys**: a word of your own that names the configuration version you sell, the same in every environment. `PUT`, `GET` and `DELETE /v1/tenants/{tenant_id}/product-lookup-keys/{lookup_key}`, and `GET /v1/tenants/{tenant_id}/product-lookup-keys`, which resolves up to ten keys in one read with a repeated `lookup_key`. A key that names another version moves only with `transfer: true`; otherwise `lookup_key_taken` (`409`), with the configuration and version it names in `details`. No selling request changes: sell under the pair the read answers.
  * `Server-Timing` on every response at both doors: `total` always, and `queue`, `conn`, `db` and `derive` once the caller is authenticated. See [reading Server-Timing](/api-reference/overview#reading-server-timing).
  * A dated read answers from one moment of the client's record: a receipt recorded while the read runs is wholly in the answer or wholly out of it. No field or figure changes.

  **Deprecated:** nothing new. `vat_rate_bps` stays deprecated until 2027-01-15, from [2026-10-15](#2026-10-15).

  **Closed enumerations:** no new member. A caller stating this version and one stating none are served the same bodies.

  **Breaking:** one refusal, at the console's policy authoring. A policy version whose approving band's rate has no decimal of at most eight places as a percentage — one third, say — is refused at validation and publication. No loan could ever be issued under such a band, so nothing issuable is lost, and no window applies.
</Update>

<Update label="2026-10-15" description="The baseline">
  What the API served when versions began. A request stating no version is served this.

  **Deprecated**

  | Field | Route | Until | Then |
  | - | - | - | - |
  | `vat_rate_bps` | `PUT /v1/tenants/{tenant_id}/company-profile` | 2027-01-15 | Refused `400`, naming the field |

  Until that day a statement carrying `vat_rate_bps` is accepted with the field ignored. The answer carries `Deprecation: @1799971200`, `Sunset: Fri, 15 Jan 2027 00:00:00 GMT` and a `Link` to this entry.

  **Reached production the same day, before versions existed**

  These closed-enumeration members shipped on 2026-10-15, before the rule above. They are part of the baseline and are served to every caller.

  | Enumeration | New members |
  | - | - |
  | Loan `state` | `held`, `expired`, `voided`, `rescinded`, `withdrawn`, `restructured` |
  | Loan event `kind` | `suspended`, `suspension_ended`, `confirmed`, `voided`, `rescinded`, `withdrawn`, `waived`, `restructured`, `settled_for_less` |
  | Recorded act `kind` | the same nine |
  | Undoing `kind` | `rescinded`, `voided`, `withdrawn` |
  | Credit decision and knockout `outcome` | `not_offered` |

  A listing still leaves `held`, `expired` and `voided` loans out unless `state` names them.
</Update>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.