Guide

Changes

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

This page as Markdown

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 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; 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.