> ## Documentation index
> Fetch the complete index at: /llms.txt
> Use it to discover every page before exploring further.

# Changes

> What the API promises about change, and what has changed, newest first.

## What you can rely on

- Changes are additive. A new field, argument, enum value or operation can appear at any time; a query you wrote keeps working.
- Nothing is removed without notice. A field or argument that is going away is marked deprecated in the schema, with the reason and what to use instead, and stays for at least six months after that.
- A new enum value can appear in a field you already read. Handle a value you do not know as "other" rather than failing.
- Error codes and `extensions.reason` values are part of the contract; a new reason can appear, an existing one is not renamed.
- The Markdown pages and `llms.txt` follow the same rules, so an assistant that learned the API keeps its footing.

Deprecations appear in the reference as a tag on the field, and here with their date.

## Not in the API yet

What this documentation does not promise, because the product does not do it yet. Each will appear above, dated, when it lands.

- A "changed since" filter. A system that mirrors the portfolio reads it in full, as [Keeping in sync](/docs/sync.md) shows.
- An assessment's own revisions. Every assessment is revision 0, and a value read again replaces the figure on the same one.
- The `INGESTED` bill status, the `QUEUED`, `OCR_RUNNING`, `MATCH_FAILED` and `CONFIRMED` extraction states, and the `MAILED_PDF`, `EMAIL`, `SFTP_FEED` and `UPLOAD` ways a unit makes its bill available: in the schema, recorded for no unit and set by nothing today. Handle them as "other".

## 16 October 2026

- Two changes need a change to your calls. Every review decision and every change to an appeal now names the version it was made from: `acceptBill`, `flagBillForClient`, `rejectBill` and `undoBillReview` take `expectedVersion`, the bill's `version` as you read it, and `updateAppealStatus` and `recordAppealOutcome` take the appeal's. When the bill or the appeal changed after you read it, the call is refused with `CONFLICT` (`extensions.reason` is `STALE_VERSION`) and nothing is written. And `Deadline.parcel` can be null: a unit's own appeal window, which belongs to no parcel, is listed once with its unit.
- A bill the unit re-issues with other charges (on a page that lists none, another total) is the bill's next `revision`, under a `key` of its own and waiting for review; the revision it replaces reads `SUPERSEDED`, a new status, and counts nowhere. A payment posted or interest grown still updates the bill in place. `correctBill` records a person's correction of a reading as the next revision, checked like any reading, and `enterBill` enters a bill by hand from a tax bill page no reading turned into one.
- Documents: `startDocumentUpload` gives a link, good once for fifteen minutes, to `PUT` a PDF, CSV or Excel file to; a bill PDF from a county the product reads exactly is read as a bill, and anything else is kept on the parcel as evidence. `createDocumentLink` gives a five-minute path that opens a document without a header. A document is kept seven years; after that its route answers 410 `file_removed`, and one that cannot be opened at the moment answers 503 `documents_unavailable`.
- Your own payment records: `recordPayment` and `importPayments` (up to 2,000 rows a call, with `dryRun`) record payments, refunds and adjustments beside the county's figures, never into them. `TaxBillInstallment.payments` and `paidPerRecords`, `TaxBill.paidPerRecords`, `Deadline.paidPerRecords` and `OutstandingTotal.installmentsPaidPerRecords` say what they show.
- Bills: `annualTotal`, `chargesTotal`, `installmentsTotal` and `penaltyIncluded` give the county's stated total, the charges and the installments apart. `PAID` and `CLOSED` are deprecated and never returned: a status is the review state alone, and what is paid is `amountPaid` and `paymentKnown`, from the county, and `payments` and `paidPerRecords`, from your records. `Validation.resolutionNote` and `Validation.history` keep who resolved each check and why; `Extraction.fields` and `pages` say where each figure was printed.
- Dates are kept: `Deadline.id` names one, `acknowledgeDeadline` marks it handled, and `supersedes` says what a moved date was. `deadlineGeneration` says when the dates were last worked out. `Deadline.paidPerRecords` needs `BILL_READ`, as `exposure` does.
- Appeals: `updateAppeal` changes what an open or filed appeal asks for and when its window closes, and its note at any status; `Appeal.version` and `ruleDeadlineOn` are new. The evidence pack (`APPEAL_EVIDENCE_PACK`) is made for named appeals with `ReportFilter.appealIds`, and as a `ZIP` holds the documents themselves. `rerunReport` asks again for a report that failed or expired.
- Owners and units: `legalEntitiesInPortfolio` breaks the portfolio down by owner of record; `setLegalEntityTaxId` and `clearLegalEntityTaxId` keep an owner's tax identification number encrypted and show only `taxIdLast4`. `assessingJurisdictions`, `Jurisdiction.rules` and `Jurisdiction.appealRequirements` give each unit's dated rules with their evidence and what an appeal there needs; `reportRule` and `ruleReports` tell retrieve-IQ a rule looks wrong and show what became of it. `ParcelInput.state` and `ParcelImportRowInput.state` name a county outside California.
- Engagements: the record says when the client approved it, under what reference and by whom, and when it was revoked and why. Revoking one stops your access the moment it is recorded; a report or an upload under way at that moment is not kept, and its message says so.
- The file feed is version 3: a bill a failed check holds back, or one a reviewer rejected, keeps its rows with its amounts empty, `figures_withheld` true and `withheld_reason` saying why, and its charges likewise; `validation_warnings` lists only the checks still standing.

## 14 October 2026

- Every operation's entry names every scope it needs. A change that returns a record needs the scope to read it as well as the scope to make it: `acceptBill` needs `BILL_REVIEW` and `BILL_READ`, `upsertParcel` needs `PARCEL_WRITE` and `PARCEL_READ`, `openAppeal` needs `APPEAL_WRITE` and `PARCEL_READ`. `ingestionStatus`, `billInbox`, `inboxSummary` and `portfolioSummary` need `PARCEL_READ` beside their own scope. Each MCP tool's entry names its scopes.
- A key's scopes hold wherever its work runs: a report is produced with the engagement's scopes capped by the key's, `requestReport` refuses a key or an engagement without the scopes a report reads with (`reportCatalog` lists them as `scopes`), and a queued report fails if that access ends before it runs. `Deadline.exposure`, the balance at stake on a payment, needs `BILL_READ`; the dates still need only `PARCEL_READ`, and a calendar feed made without `BILL_READ` carries the dates alone.
- Revoking a key ends what it made: a calendar feed it created answers 403 from then on, and a report it requested that has not run yet fails. An unknown, revoked or expired key answers 401, as a missing token does; switch on `extensions.code` all the same.
- `deadlines` and the calendar feed cover the whole portfolio; the first 500 parcels by parcel number was the earlier limit. `portfolioSummary` is computed from the portfolio as it stands when it is read.
- `jurisdictions` asks for a state, a level or a search, takes `level` and `first` (500 when omitted, up to 5,000), and answers in state and name order. `team` lists the people who can sign in, to a person signed in to the app and not to a key. `acceptBill` refuses a bill with an error-level check standing on any of its installments, not only the ones named. `countiesInPortfolio` breaks a tie on the amount owed by name. `upsertParcel` and `importParcels` refuse the state's own name as a county. `ReportJob.error` is a sentence for the person who asked, not a note for the operator. `Portal` no longer carries the survey's own notes, rule codes and reader codes.
- The examples select only what their scopes allow, and the Keeping in sync read is two queries, each held under the [limits](/docs.md#limits); the earlier one did not fit.
- Descriptions say what is reserved rather than what is planned: every bill and assessment is `revision` 0 and a fetch refreshes it in place; `INGESTED`, `PAID` and `CLOSED` are set by nothing today; a rejected bill is not reopened by a later fetch; a decided appeal cannot be changed.
- The file feed as it is written: `fetched_at` moves whenever an account is read again, so two nights rarely match byte for byte; `validation_warnings` sits on the bill's first installment row and lists the checks no person has resolved, cleared ones included; `line_items.csv` joins to that same row; a client whose fetch failed is left out of the files; the feed's ids differ in form from the API's.
- The Python first request uses the standard library only.

## 13 October 2026

- This documentation: every query, mutation, type, field and argument described, the scope each operation needs on its entry, an example for each operation, and the tool list of the MCP server.
- Common tasks, Keeping in sync and AI assistants pages added.
- The schema is served at `/docs/schema.graphql`.
