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

# Changelog

> Every change to the /v1 contract and, once refreshes run on a schedule, every data refresh.

Investorlift Data Services (the God Mode API). This log has one entry per `/v1` contract change. Once refreshes run on a
schedule, it has one entry per data refresh.

`/v1` is additive only. Investorlift aims to give 30 days' email notice before it removes or renames a documented route,
field or tool. It aims to give 90 days' notice before it retires a paid plan (Developer Agreement Section 12.3). This log
records a removal on the day it ships.

Each change is one bullet. The bullet starts with a prefix that says the kind of change. It links the endpoint or the
page it touched, when it has one. A plumbing change, a setting or a metric often has none. The prefixes:

| Prefix      | Means                                                                                                    |
| ----------- | -------------------------------------------------------------------------------------------------------- |
| **Added**   | A new endpoint, field, filter, parameter or error code. You can ignore it until you want it.             |
| **Changed** | Existing behaviour is different. Read these.                                                             |
| **Removed** | A field or route is gone. `/v1` is additive only, so these are rare, and this log always announces them. |
| **Data**    | A market, a dataset or a refresh. No contract change.                                                    |
| **Docs**    | The documentation site or the reference. No contract change.                                             |

<Update label="2026-09-17" description="API 0.33.1, 0.33.0, 0.32.0, 0.31.0" tags={["Added","Changed","Data","Docs"]} rss={{"title":"2026-09-17: API 0.33.1, 0.33.0, 0.32.0, 0.31.0","description":"2 added, 3 changed, 2 data, 1 docs"}}>
  * **Changed** API 0.33.1: the `build` kind and its counters describe the six bounds of the pipeline rule, and nothing
    on the wire changes yet. The served rows of every market still follow the 0.13.0 rule, every lot-to-build resale by a
    registered investor, until that market's next data build. From its first build after 2026-09-17 a market keeps only a
    single-family parcel held over 90 days, its house complete by the sale year. On the stage tables of 2026-09-14 that
    keeps 5,044 of Phoenix's 10,006 builds and 5,747 of Houston's 10,030, and the rest read `other` again. Every `build`
    description names the rule a market follows (`meta.coverage[].loaded_at`), `n_lot_to_build_built` is described
    against `n_builds`, and `docs/god-mode-api/PLAN-build-kind.md` section 1 defines the bounds.
    [Deal](https://developers.investorlift.com/api-reference/objects/deal)
  * **Changed** API 0.33.0: a [`POST /v1/properties/search`](https://developers.investorlift.com/api-reference/endpoints/properties-search)
    with a `zip` list and no `point`, `bbox` or `property_id` chooses its markets from the ZIP codes. The API searches only the markets
    whose parcels carry them, so the scan names those markets and walks their index alone. A ZIP no market carries is `422 outside_coverage`,
    and the problem names it in `zips_unknown`. Before this release the same request answered an empty page, or timed out for a Seattle
    or Houston ZIP under the default sort. Beside a geometry the list stays a filter, as documented. The MCP tool `godmode_property_search`
    follows the route.
  * **Changed** The permit-window rule applies in every parcel market, where until 2026-09-17 it applied to a Washington market alone. Where
    a jurisdiction issued no permit in the 12 months before the permit snapshot, its parcels carry the five windowed permit aggregates as
    null. Then `meta.coverage[].parcel.permits.jurisdictions[].windows_measured` is false for it. The next Phoenix build marks Scottsdale,
    Goodyear and Gilbert. The field descriptions say so, and no field, filter or error code changes.
  * **Added** API 0.32.0: the lender family's investor stubs carry the name to print under the rule of 0.29.0. `LenderInvestorRef`
    gains `display_name`. That stub sits behind `top_borrowers[].investor` and `top_investors[].investor` on a lender profile
    ([`GET /v1/lenders/{lender_id}`](https://developers.investorlift.com/api-reference/endpoints/lenders-get)) and behind `investor` on a
    borrower row ([`GET /v1/lenders/{lender_id}/borrowers`](https://developers.investorlift.com/api-reference/endpoints/lenders-borrowers)).
    A loan row's `deal` block gains `investor_display_name` beside `investor_name` ([`GET /v1/lenders/{lender_id}/loans`](https://developers.investorlift.com/api-reference/endpoints/lenders-loans)), null exactly when `investor_name` is null.
    The profile computes the stubs' names on read from the ids its detail carries. The MCP loans tool's concise row drops the field with
    `investor_name`, and the borrowers and lender tools carry it in both formats.
  * **Data** Coverage recorded from production on 2026-09-17: hou dataset\_version 1789490988 -> 1789629654; hou deeds: served (2026-08-11) -> served (2026-08-25); hou listings: served (2026-08-31) -> served (2026-09-07); hou addresses: not\_published -> served (2026-08-25); phx dataset\_version 1789533180 -> 1789636470; phx addresses: not\_published -> served (2026-08-27); sea (WA: King, Pierce, Snohomish) is served, deeds to 2026-08-26. The Coverage pages and coverage.json are regenerated from the recording. No contract change.
  * **Data** The Seattle metro (King, Pierce and Snohomish counties, WA) is served on production since 2026-09-17, dataset version 1789654598. It moves from the planned list to the served markets in `api/src/coverage/intent.ts` with the stage `beta`. The planned list is empty. No contract change.
  * **Added** API 0.31.0: the four investor stubs that carried `name` alone carry the name to print too, under the rule of
    0.29.0 (given-first for a cleanly parsed person, equal to the deed spelling otherwise, null exactly when the name is null):
    `buyer.display_name` and `intermediate.display_name` on an Investorlift wholesale listing
    ([`GET /v1/wholesale/listings`](https://developers.investorlift.com/api-reference/endpoints/wholesale-listings)),
    `top_buyers[].display_name` on a wholesaler ([`GET /v1/wholesalers/{wholesaler_id}`](https://developers.investorlift.com/api-reference/endpoints/wholesalers-get)),
    `investor_links[].investor_display_name` on an agent ([`GET /v1/agents/{agent_id}`](https://developers.investorlift.com/api-reference/endpoints/agents-get))
    and `holder.display_name` on an agent's listing row ([`GET /v1/agents/{agent_id}/listings`](https://developers.investorlift.com/api-reference/endpoints/agents-listings)).
    An unregistered company that bought a wholesale listing prints its name. The MCP tools carry the buyer's, the top buyers', the holder's and the
    link's field in both formats (the intermediate stays detailed-only, as before); the wholesale listings tool's twenty-row concise
    budget widened from 20 KiB to 22 KiB (maximal rows measured 21,584 bytes).
  * **Docs** The Developer Agreement (Beta) is version `data-services-beta-v2026-09-17` and the Privacy Notice is reworded with it:
    where both said a registry investor's "display name", they say its registry name, the deed spelling, and the same name
    printed given-first, so the legal texts and the API's `display_name` field do not share a term for two different things.
    No right or obligation changes; the revision is a new agreement version under Section 21.1, published here and in the
    console once the console's agreement id is repointed.
</Update>

<Update label="2026-09-16" description="API 0.31.1, 0.30.0, 0.20.0, 0.29.0, 0.28.1, 0.28.0, 0.27.1, 0.27.0, 0.26.0, 0.25.0, 0.24.2, 0.24.1, 0.24.0, 0.23.0, 0.22.3, 0.22.2, 0.22.1, 0.22.0, 0.21.0, 0.19.0" tags={["Added","Changed","Data","Docs"]} rss={{"title":"2026-09-16: API 0.31.1, 0.30.0, 0.20.0, 0.29.0, 0.28.1, 0.28.0, 0.27.1, 0.27.0, 0.26.0, 0.25.0, 0.24.2, 0.24.1, 0.24.0, 0.23.0, 0.22.3, 0.22.2, 0.22.1, 0.22.0, 0.21.0, 0.19.0","description":"16 added, 7 changed, 2 data, 13 docs"}}>
  * **Docs** API 0.31.1: every text behind the generated reference now reads as ASD-STE100 Simplified Technical English. That
    is the description of every field and parameter, the 38 operation descriptions, the response and header descriptions,
    the error catalogue (each code's title, description and recovery sentence), the MCP tool descriptions, server
    instructions, prompts and resources, and the introduction of the OpenAPI document. The recovery sentence of every error
    body reads the same way, so a client shows the same words the catalogue carries. No field, code, status, limit, default
    or example changed. The rules that write the public document follow the new sentences. The preamble of this changelog
    reads the same way; the dated entries are a record and keep their text.
  * **Docs** The documentation site now reads as ASD-STE100 Simplified Technical English (the DX-07 decision): every
    hand-written page of the Guides, MCP and API reference tabs. A sentence has at most 25 words, or 20 in a procedure.
    The voice is active, the words come from the approved list, and one term names one thing. No fact, field, code, limit
    or example changed. The three legal pages and the dated entries of this changelog are records and keep their text.
    The section heading "Reading the response" is now "Read the response" on every endpoint page, and the early-access
    note reads the same on every page that carries it. `scripts/check-ste.mjs` holds the pages to the rules as a step of
    `pnpm verify` and of CI; `docs/god-mode-api/DOCS-STE.md` is the rule sheet.
  * **Added** API 0.30.0: [`GET /v1/coverage`](https://developers.investorlift.com/api-reference/endpoints/coverage) answers which data is
    available where. For every loaded market it returns one row per dataset a market can lack. The dataset keys are `deeds`,
    `listings`, `agents`, `wholesale`, `auction`, `str`, `parcels`, `financing`, `permits`, `owner_profile`, `history`,
    `cash_sale`, `lenders` and `addresses`. Each row carries one `status`: `served`, `partial`, `not_measured` or
    `not_published`. Beside it are the `dated` and `stale` flags, the `as_of` date, and the `gate` a call meets where the API
    does not serve the dataset. The row also carries the `granularity` and the `scope` at that grain, and the market row
    carries `label` and `stage` (Houston reads `beta`).
  * **Added** API 0.30.0, the scope of the same route. The scope lists every county with its deed end and every short-term
    rental jurisdiction with its reason. It also lists every permit issuer and every history ZIP. Each county also says if
    the lender registry covers it.
  * **Added** API 0.30.0, the place lookup of the same route. The place inputs are `lat` with `lng`, `county` (a FIPS or a
    name), `zip` (through the Census ZIP-to-county crosswalk) and `market`. With one of them the answer is the markets that cover the place, with a
    `place_status` per dataset. A place nothing covers answers `200` with `markets: []` and `place.status` `outside`, never
    `422`. `planned[]` lists the markets Investorlift intends to serve, as labelled intent (`label_kind: intent`, the Seattle
    metro, building).
  * **Added** API 0.30.0, the price and the MCP half: the route costs zero credits and takes any key. It is a probe, so behind
    the gateway lock it needs no `X-On-Behalf-Of`, like [`GET /v1/dataset`](https://developers.investorlift.com/api-reference/endpoints/dataset).
    Over `/mcp`, `godmode_coverage` runs the same plan, with the place inputs (`latitude`, `longitude`, `county`, `zip`,
    `market`) and `response_format`. Its rows carry `label`, `stage`, `datasets` (the matrix), `str_cities` and `history_zips`
    beside the fields they always carried. `data.place` and `data.planned` ride beside `data.markets`, and the concise format
    caps the jurisdiction, issuer and ZIP lists at ten. `godmode://coverage` and `godmode://markets/{market}` serve the same
    rows, and the property-history tool now names `history_zips`.
  * **Changed** API 0.30.0: `meta.coverage[].lenders` gains `dated` and `n_parcels_uncovered`. No description of the public
    document or the public manifest names two loaded markets or two states any more, and a test holds it. The tools list
    ratchet moved once, to 188,416 bytes.
  * **Docs** The developer site gains a Coverage section, generated from a recording of production. `npm run coverage:record`
    writes `api/coverage.snapshot.json` and `npm run docs:sync` renders it.
    [Which data is available where](https://developers.investorlift.com/coverage/markets) carries the markets table and the
    matrix. The matrix gives every as-of date with the snapshot and the stale marks. The legend and the planned markets follow,
    under the intent sentence. [Counties, cities and ZIP codes](https://developers.investorlift.com/coverage/places) lists every
    county, short-term rental jurisdiction, history ZIP and permit issuer.
  * **Docs** [`coverage.json`](https://developers.investorlift.com/coverage.json) is the machine twin of those pages. The first
    recording is production on 2026-09-17 (API 0.20.0 there). It shows agents not published and the address resolve not yet
    released on production. The hand-written pages stop typing coverage facts. The market sentence on the introduction and the
    coverage guide is a generated region filled from the recording. Every hand-typed as-of date, covered county, history ZIP and
    served city became a pointer to `meta.coverage[]` or the Coverage pages.
  * **Docs** A test fails the next coverage fact typed by hand. The coverage guide no longer describes `GET /health` as
    reachable from the public host.
  * **Added** API 0.29.0: `display_name` on every investor object, the name to print beside `name`, which is the deed spelling:
    the investor summary of every deal row ([`GET /v1/deals`](https://developers.investorlift.com/api-reference/endpoints/deals-list),
    [`GET /v1/deals/{deal_id}`](https://developers.investorlift.com/api-reference/endpoints/deals-get), an investor's deals, a parcel's
    deals), the list row and the match row ([`GET /v1/investors`](https://developers.investorlift.com/api-reference/endpoints/investors-list),
    [`GET /v1/buyers/match`](https://developers.investorlift.com/api-reference/endpoints/buyers-match)), the profile
    ([`GET /v1/investors/{investor_id}`](https://developers.investorlift.com/api-reference/endpoints/investors-get)), the search hit
    ([`GET /v1/investors/search`](https://developers.investorlift.com/api-reference/endpoints/investors-search)) and a deal's `seller` and
    `end_buyer`. For a person whose deed spelling parsed cleanly it is GIVEN \[MIDDLE] SURNAME \[SUFFIX] in upper case ("DANA RIVERA"
    from the deed's "RIVERA DANA"); for an entity, a trust, a public body, an institutional investor or an ambiguous spelling it
    equals `name`; it is served wherever `name` is and is null exactly when `name` is null. It is assembled when the row is read
    from the member the registry already parsed, so no data refresh was needed and every market carries it at once. `name` keeps
    its value and is now described as the deed spelling, SURNAME GIVEN for a person: match, join and search on `name`, print
    `display_name`. The MCP tools carry the field in both formats ([Tools](https://developers.investorlift.com/mcp/tools)); the
    twenty-row concise budget the deals tool is tested against was widened from 20 KiB to 21 KiB to keep it there.
  * **Changed** API 0.28.1: behind the gateway lock a request to a probe route ([`GET /v1/dataset`](https://developers.investorlift.com/api-reference/endpoints/dataset),
    the freshness probe) is served without `X-On-Behalf-Of`; every other `/v1` path and `/mcp` still answer 400
    `on_behalf_of_required` without it. A probe charges nothing and counts against no credit line, so it needs no developer
    identity, and the gateway's subscription-less forward of the probe (pricing plan) is served instead of refused (issue #26).
  * **Added** API 0.28.0: `meta.coverage[].parcel.permits.jurisdictions[].windows_measured` says whether a jurisdiction's permit feed is
    current. It is false when the jurisdiction issued no permit in the 12 months before the permit snapshot, so the parcels there carry
    null 24-month and 36-month permit windows (`n_permits_24m`, `n_open_permits_12m`, `major_work_36m`, `tags_24m`, `job_value_24m`: a
    stale feed, not a permit-free parcel); true where it did; null where the market does not record the flag (every Arizona and Texas
    jurisdiction today). The five window fields name that second reason in their null clauses. The negative permit filters of
    `POST /v1/properties/search` (`filters.permits.open = false`, `filters.permits.major_work_36m = false`) leave an unmeasured parcel out
    instead of answering it as permit-free; a parcel with no permit at all is answered as before, and the Phoenix and Houston results
    are unchanged (every jurisdiction there is measured). In the Seattle metro ten King County city feeds are unmeasured today (Redmond,
    Kirkland, Renton, Sammamish, Mercer Island, Auburn, Issaquah, Kenmore, Bothell, SeaTac; the vendor crawled them once around
    2025-04-09).
  * **Changed** In Washington, a short-term rental parcel's `business_use` follows the operator's own declaration on the licence (the
    City of Seattle roll records whether the unit is the operator's primary residence); the owner-occupied inference decides it everywhere
    else, and where the roll carries no declaration. Phoenix and Houston values are unchanged. The field's description says so; no shape change.
  * **Data** The Seattle metro is the third market, `sea`, served on stage: King 53033, Pierce 53053 and Snohomish 53061 (Washington, the
    Seattle-Tacoma-Bellevue MSA) as one `metro` market with the default 20-mile point tolerance, 1,321,876 parcels, `data_end` 2026-08-26
    (King 2026-08-25, Pierce 2026-08-26, Snohomish 2026-08-25), registry run 2, `dataset_version` 1789595959 after the last swap of the day
    (1789572701 when the registry half was served; every sidecar swap restamps it): 198,176 deals (15,185 flips), 51,803 investors, 554,680
    listings, the buyer signal, 2,074 Investorlift wholesale listings from 183 wholesalers, and the auction channel under Washington's rules
    (RCW 61.24 trustee's sales without redemption; sheriff's deeds in two kinds; the treasurer's `Public Action` deed as `TAX_SALE`; a hard-money
    funder standing beside the bidder no longer hides the bidder's purchase). Washington is a disclosure state, so every pair carries both prices
    and no deal is `SELL_ONLY`. The short-term rental block is served for the City of Seattle (the FAS roll s7df-xba4, 5,140 active units, matched
    at 92.7 percent with 99.64 percent of exact matches within 100 m; 8,363 parcels carry a licensed unit and the block is explained on all
    224,605 Seattle parcels) and `meta.coverage[].str`
    explains all 81 Washington jurisdictions (every incorporated place of the three counties is a Census TIGER polygon). The parcel family is
    served from the BatchData archive: the parcel layer on all 1,321,876 parcels (a listing status on 86.5 percent, a last sale price on 82.3 percent, an AVM on 92.3
    percent; the recorders' nominal $200 / $313 / \$370 served null), permits 4,309,331 on 689,890 parcels from the 2026-09-01 FULL (the vendor's
    King labels corrected; the 24-month windows of a jurisdiction with no permit in the 12 months before the snapshot served null), financing on
    1,318,098 parcels (887,892 open liens, dated 2026-06-25), owner profiles on 1,307,411 (as of 2026-09-09), the history change log for ZIP 98133
    (165,126 events over 62 weeks, 862 listing cycles), and the lender registry (21,302 served lenders of 57,175 identities, 3,740,525 loan rows,
    4,142,060 borrower rows; the same id for the same lender in every market), and the street-address resolve (1,309,532 of 1,321,876 parcels
    parsed, 99.1 percent). Not served for `sea`: agents (no Washington licence roll reaches a
    commercial requester; 422 `agents_unavailable`). The plan, the measurements and the research are `docs/seattle/PLAN.md` and
    `docs/seattle/research/`. Production serves it since 2026-09-17 (the 2026-09-17 entry).
  * **Docs** The auction-kind descriptions gain their Washington clauses (`TRUSTEE_SALE`, `SHERIFF_SALE`, `TAX_SALE`; `SHERIFFS_DEED` "in an
    Arizona or Washington market"), the county FIPS examples and the `metro` description name the Seattle metro (the coverage tool lists the
    markets from the database and needed no change), every `state` description of a market row (the coverage row, `GET /v1/dataset`, the
    investor objects) says AZ, TX or WA, the `last_sale_price`, `prior_sale_price` and `last_sale_price_per_sqft` null clauses name the
    Washington nominal-consideration rule, and the site's index, coverage guide and handbook say three markets. No contract change: the generated documents are
    regenerated without a version bump. The attribution line of the Terms and attribution page gains "the City of Seattle (public domain)"
    after Tempe, a Section 8.2 change of the Developer Agreement (Beta); `meta.terms` is unchanged ("municipal short-term rental registries"
    already covers it). The 8.2 notice goes to every account that has accepted the Agreement when the Seattle roll is first served; the
    acceptance ledger is the console's, not this repository's, and the notice is not sent by the build. Recorded 2026-09-17: production
    first served Seattle at 11:12 UTC. The console's ledger held no acceptance of any Data Services agreement at 19:30 UTC that day, so the
    notice went to no account. The first account that accepts the Agreement sees the line as published.
  * **Docs** API 0.27.1: seven operations that had no recorded example in the reference carry one, recorded on stage on 2026-09-16
    and published in the deals-only shape like every other example (item 22 of the SFR Analytics API review of 2026-09-15):
    [`GET /v1/dataset`](https://developers.investorlift.com/api-reference/endpoints/dataset) (every loaded market),
    [`POST /v1/properties/search`](https://developers.investorlift.com/api-reference/endpoints/properties-search) (the three parcels nearest the Phoenix golden point within 2 miles, the
    default datasets), [`GET /v1/properties/{property_id}/financing`](https://developers.investorlift.com/api-reference/endpoints/properties-financing) and
    [`GET /v1/properties/{property_id}/permits`](https://developers.investorlift.com/api-reference/endpoints/properties-permits) at the golden parcel (no open lien, one involuntary lien, no
    permit: the shapes a parcel with nothing on record carries), [`GET /v1/properties/{property_id}/history`](https://developers.investorlift.com/api-reference/endpoints/properties-history)
    (the ten newest events, `limit=10`) and [`GET /v1/properties/{property_id}/listing-history`](https://developers.investorlift.com/api-reference/endpoints/properties-listing-history) at a
    Scottsdale condominium of the history lake's ZIP half a mile from the point, which the golden parcel sits outside of, and
    [`GET /v1/markets/{market}/changes`](https://developers.investorlift.com/api-reference/endpoints/markets-changes) for ZIP 85251 at the default 52 weeks. The comps route is the one
    documented `/v1` operation still without an example. No contract change.
  * **Added** API 0.27.0 (provisional): over `/mcp`, `meta.credits` on every tool result behind the gateway carries `used`, `line`
    and `period_end` beside `charged`: the period's exact count after the call as the API's own ledger holds it (the figure the
    line is enforced on; null when the ledger did not answer or nothing is counted for the request, never a figure derived from the
    charge), the `X-Credits-Line` the gateway forwarded and the period end it resets on. `godmode_coverage` carries an `account`
    block beside `data.markets[]`: `plan`, `tier` and `period_end`; `credits` with `grant`, `line`, `used`, `remaining` (`grant`
    minus `used`, negative on Growth and Scale into the overage down to `grant` minus `line`, and on a hard line by at most the
    last page's cost; the API's exact counter, which differs from `X-Credits-Remaining`, the gateway's balance, by the gateway's
    one-minute cache and by any call charged for a client that left mid-request, until the period resets) and, on Free, the
    lifetime `budget` (`line`, `spent`, `remaining`); `limits` with `largest_limit`, the small plans' `geometry` bound,
    `max_weeks` and `name_search` (`day_cap`, `used_today`, `resets_in_seconds`); `requests` (`per_minute`, `per_day`, a copy of
    the gateway's catalogue); and `counters`,
    which says whether the counts were read (`read`, `off`, `unavailable`). Off the gateway the block reads `plan` null and `tier`
    `internal` with no credits. A standing read the ledger did not answer is counted under `credits_ledger_errors_total{op="standing"}`,
    a new label apart from `read`, whose every increment stays a 503 to a customer. Item 20, step 1, of the SFR Analytics API
    review of 2026-09-15; step 2, `GET /v1/account`, needs the gateway repository.
    [Keys and headers](https://developers.investorlift.com/mcp/keys)
  * **Added** API 0.26.0: holdings on the investor row. `holds_min` and `holds_max` join the shared filters: the investors whose market-wide
    `investor.n_holds` (confirmed holdings plus un-resold purchases inside the area loaded for the market) sits inside the band,
    inclusive, 0 to 1,000,000 each; for example `holds_min=20` for the larger portfolios, `holds_max=0` for the investors holding
    nothing, and buyers without an investor id pass unless `registered_only=true`. They apply wherever the shared filters do (`GET /v1/investors`,
    `GET /v1/buyers/match`, `GET /v1/deals` with its summary, pins and cells) and on the MCP tools that carry the filters
    (`godmode_investors_nearby`, `godmode_buyers_match`, `godmode_deals_search`, `godmode_deals_summary`). `sort=holds_desc` on
    `GET /v1/investors` and `godmode_investors_nearby` orders the rows by `in_radius.n_holds`, the holdings inside the geometry,
    as every sort of that list orders by what happened there. `GET /v1/investors/{id}` gains `median_purchase_year`: the median
    year the investor bought the parcels it still holds, a whole year computed over the `n_holds` rows when the profile is read,
    null when it holds nothing or no holding is dated; `godmode_investor_lookup` carries it in both formats. The band reads a
    new index on the investors' holdings count (the 132nd of the served set, carried by the pipeline and the publish path). No
    sort is tier-gated and none becomes so.
    [List investors in an area](https://developers.investorlift.com/api-reference/endpoints/investors-list)
  * **Added** API 0.25.0: `by_channel` on [`GET /v1/deals/summary`](https://developers.investorlift.com/api-reference/endpoints/deals-summary)
    and, through the same plan, on `godmode_deals_summary`: five filtered counts over the summary's row set, each on the deal row's own channel field,
    `from_wholesaler` (`bought_from_wholesaler` true), `from_investor` (`bought_from_investor` true: the seller carried an investor
    id), `distressed` (`bought_distressed` true), `auction` (`bought_auction_kind` set) and `other_or_unknown` (the rows in none of
    the four, a deal whose purchase deed is unknown included). The four named buckets are overlapping counts, not a partition: a known
    wholesaler who is also a registered investor, or a distressed deed bought at auction, counts twice, so the five do not sum to
    `n_deals`, and `n_deals` minus `other_or_unknown` is the rows in at least one. `other_or_unknown` is not a count of purchases from a
    homeowner, because a contract assignment leaves no deed. `auction` is null, never 0, where any market the location covers has
    `meta.coverage[].auction_counted` false, the rule the `bought_auction_kind` filter already answers 422 under. The counts are five
    more aggregates over the one scan the summary already makes (stage, 5 miles at the golden point, 31,987 rows: 231 and 263 ms
    against 240 and 237 ms before, the same plan). The summary tool's description names the block and stays under its cap; the
    recorded `getDealSummary` example was re-rendered for its own request against the day's stage tables so it carries a real block.
  * **Changed** API 0.24.2: a `/v1` problem response no longer carries the dataset freshness headers `ETag`, `Last-Modified`,
    `X-Dataset-Version` and `X-Data-End`; they belong to a 2xx and the 304. The handler set them before the query ran, so an error
    raised after that point (a 403 at the name-search day cap, a 404 from the query, a 422, a 500, a 503, a 504) carried a validator
    that described no body and the freshness of an answer that was never served, and a shared cache was handed a 503 wearing a
    dataset's freshness. `Cache-Control: private, no-cache` stays on a problem, as before. A compressed export (`Accept-Encoding:
    gzip`) whose stream fails after the count now answers its problem without the stream's `X-Row-Cap`, `Content-Disposition` and
    `Content-Encoding`, so the body is a readable problem rather than a file labelled gzip. No field, parameter or code changes.
  * **Docs** API 0.24.2: the response headers are declared in the reference, each described once under `components.headers` and
    referenced from the responses that carry it ([The response envelope](https://developers.investorlift.com/guides/concepts/envelope#response-headers)).
    Every response of the operators' document declares `X-Request-Id`; every `/v1` response the limiter counted, errors included, the
    `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` trio (the 503 the in-flight cap answers before the request is
    counted carries none), which the public document leaves out because the gateway never forwards it; every `/v1` 2xx and the 304
    `ETag`, `X-Dataset-Version` and `X-Data-End`, and every `/v1` 2xx `X-Rows`; `Retry-After` on every 429, on the 503 of every `/v1`
    route and on the 403 of `GET /v1/investors/search` (the name-search day cap); `X-Row-Cap` on the 200 and the 422 of the routes
    that export CSV (on the public document, the loans of a lender alone); and `GET /v1/dataset` declares its 304 with the freshness
    headers it repeats. The public document describes `X-Request-Id` as the edge sends it (also `zp-rid`; absent on a refusal the
    gateway answers itself) and moves its two credit headers under the same `components.headers`. No header is new: the set is the
    nine headers the CORS policy already exposed plus the export cap (item 26 of the SFR Analytics API review of 2026-09-15). The
    one wire change of the release is the **Changed** line above.
  * **Docs** API 0.24.1: every field that reads as of a date names the date. Fifty-eight descriptions are reworded, fifty-six
    fields, filters and blocks of the reference and the agent listings and coverage tool texts. The ones that said "today" or
    "right now" (`on_market`, `owner_test` and the agent's `role` on a listing, `deal_type`, `holding_kind`, `ownership_confirmed`,
    the property `owner` block, `n_active`, `n_pending` and `n_listed` on the investor's listings block, the `listing_status`
    filter, among others) now name the listing data end (`meta.coverage[].listings_data_end`) or the deed data end
    (`meta.coverage[].data_end`), the way `days_on_market` does; `n_active`, `n_pending`, `n_on_market`, `n_listed_12m` and
    `n_sold_12m` on an agent's listings block, `n_active` on an agent search hit and the `holder` of an agent's listing row name
    the listing slice the agent registry was built from (`meta.coverage[].agents_data_end`, equal to `listings_data_end` while
    the registry is current), which is the slice the registry counts them at; the windows that said "the last N months" or
    "still is" (the auction and REO outcome counts, the wholesale `PENDING` and `NO_DEED_120` outcomes, the permit and history
    filters and summaries of the property search, the `w_wholesale_buyer` and `w_activity` weights of the buyer match,
    `str_status`, a lender's `churn_measured`, the profile's 24-month counts, among others) name theirs. Four tests hold the line
    from this date: no public document, no hand-written page above its "Partners and staff" section (fenced examples excluded),
    no public tool description and no changelog entry names a served table, the pipeline's vocabulary, a market's table set,
    the schema, a swap table's suffix, or the runner's flag and the placeholder it fills (`test/public-openapi.test.ts`,
    `test/docs-site.test.ts`; the entries dated before this one keep their wording); every field or parameter description that
    reads relative to the present names its anchor in that sentence, or sits in a block, or inside one, that carries a field
    named for the date (`data_end`, `listings_data_end`, an `as_of`), with the calendar flags of the short-term rental feed
    (`StrCoverageSource.stale`, `StrCoverage.feed_stale`, `StrCoverageJurisdiction.feed_stale`, `InvestorStr.stale_jurisdictions`)
    allowed by name and the hidden ops probes left out (`test/openapi-docs.test.ts`); no served SQL reads the clock, by function,
    keyword, one-argument `age()` or the `'now'` and `'today'` input strings (`test/v1-sql-clock.test.ts`). The coverage tool's
    description says what `contact_via_mcp` means without naming the scope or the header behind it, and says its `stale` flag is
    the one value it serves that is measured against the request's day. No contract change.
  * **Added** API 0.24.0: `page.capped` on the four name searches,
    [`GET /v1/investors/search`](https://developers.investorlift.com/api-reference/endpoints/investors-search),
    [`GET /v1/agents/search`](https://developers.investorlift.com/api-reference/endpoints/agents-search),
    [`GET /v1/wholesalers/search`](https://developers.investorlift.com/api-reference/endpoints/wholesalers-search) and
    [`GET /v1/lenders/search`](https://developers.investorlift.com/api-reference/endpoints/lenders-search): true when more names
    matched than `limit` allowed and the page was cut at it, false when every hit is on the page, so a page holding exactly `limit`
    hits can be told from the whole set (a ranked lookup issues no cursor, so the response could not say so before). The statement
    fetches one row past the page, as the keyset lists and the address resolve do; no search counts its hits twice. The MCP lookup
    tools `godmode_investor_lookup`, `godmode_agent_lookup`, `godmode_wholesaler_lookup` and `godmode_lender_lookup` carry the same
    `capped` beside `candidates` on an unresolved name (item 27 of the SFR Analytics API review of 2026-09-15).
  * **Changed** API 0.24.0: on [`GET /v1/investors/search`](https://developers.investorlift.com/api-reference/endpoints/investors-search)
    the matched member of a many-member investor is the one with the most purchase deeds (a `trigram` hit: the most similar spelling,
    then the most deeds), where it was the first matching key alphabetically; `n_deals_min` and `include_public` apply inside every
    search step, so a broad query that came back with fewer than `limit` rows while more matched now fills the page; and the fuzzy
    step runs when fewer than 10 investors matched by the better methods, where it counted deed spellings. On
    [`GET /v1/lenders/search`](https://developers.investorlift.com/api-reference/endpoints/lenders-search) the fuzzy step keeps one
    spelling per lender, the most similar, before its cap of 200, where it capped spelling rows, so a capped step holds 200 distinct
    lenders and `page.capped` is exact on every search. Both are why the flag above can be trusted; additive under the rule (no
    field, route or code moved).
  * **Added** API 0.23.0: `meta.geometry` beside `meta.reference_point` on every route that takes a location: `GET /v1/deals`, `GET /v1/deals/summary`, `GET /v1/deals/cells`, `GET /v1/deals/pins`,
    `GET /v1/investors`, `GET /v1/wholesale-listings`, `GET /v1/str-parcels`, `GET /v1/buyers/match`, `GET /v1/properties/{property_id}/comps`,
    `POST /v1/properties/search` and, when a geometry, a `zip` or a `city` was given, `GET /v1/lenders` and `GET /v1/lenders/{id}/loans`. One flat object keyed as the query is
    (`kind`, `lat`, `lng`, `radius_miles`, `bbox`, `property_id`, `zip`, `city`), every field typed null when the kind has no such
    value, so it can be copied back as the query. It is the location as it ran, defaults filled: `radius_miles` 2 on the GET lists
    when none was sent and 1 around `property_id` on the parcel search; a `property_id` request echoed as `kind: "radius"` around the
    parcel centroid with `property_id` filled; a viewport as the `[west, south, east, north]` tuple; a ZIP list under `zip` as sent
    and a city as the folded upper-case value that was compared; `lat` and `lng` the reference point that rode beside a viewport or a
    place, null without one. On the parcel search a ZIP-only body echoes `kind: "place"` and a county-only body echoes nothing; a
    `zip` or `county_fips` list beside a geometry is applied as sent and not echoed. The comps echo the subject as `kind: "radius"` around
    its centroid with `property_id` the subject and `radius_miles` the value the comps were drawn from. The lender list and loans apply a
    geometry as the H3 res-8 cells whose centre lies inside it, as before, and echo a `zip` or a `city` as `kind: "place"`; a `county` is not
    echoed: the echo describes the shape asked. REST only: the MCP tools' `meta` carries
    `reference_point` alone. `GET /v1/markets/{market}/changes` carries `weeks_applied` on `data` beside `first_week` and `last_week`:
    the window that was applied, the weeks asked for or 52 on Free and Starter, so the plan clamp is reported rather than silent.
    [The response envelope](https://developers.investorlift.com/api-reference/objects/envelope)
  * **Changed** over `/mcp`, `godmode_market_changes` refuses `weeks` above 52 on Free and Starter as `403 plan_limit` naming the cap
    (`limit: "weeks"`, `cap: 52`, `errors[0].param` `weeks`), as every paged tool refuses a `limit` over the plan's page, instead of
    clamping the series in silence; REST keeps the clamp and reports it in `weeks_applied`. The tool's description and its `weeks`
    input say so, and the `plan_limit` catalogue row names the change series' weeks.
    [Read a ZIP's weekly changes](https://developers.investorlift.com/api-reference/endpoints/markets-changes)
  * **Docs** the changes page said `first_week` and `last_week` were the edges of the whole record; they are the edges of the returned
    window (`weeks[0]` and the last entry), and the lake's own edges are `meta.coverage[].parcel.history.first_week` / `last_week`. The
    two field descriptions, the page and its gotchas say so, and the envelope pages describe `meta.geometry`.
    [Read a ZIP's weekly changes](https://developers.investorlift.com/api-reference/endpoints/markets-changes)
  * **Added** API 0.22.3: the MCP prompt `underwrite_property`, the seventh beside `find_buyers` and `market_snapshot` (item 23
    of the SFR Analytics API review of 2026-09-15). For one house at a price (`asking_price`, then `property`: a `prop_` id, a
    `latitude,longitude` pair or a street address with its ZIP or city after it) it walks the model through
    `godmode_property_lookup` (the parcel, its owner and deeds), `godmode_property_financing` (the open liens, equity and LTV at
    the slice date), `godmode_property_comps` (the comparable sales, most similar first, with the flip exits among them),
    `godmode_deals_search` (the flips within a mile bought in the twelve months to the market's `data_end`, read off the
    coverage), `godmode_property_search` (`count_only` over the parcel's ZIP with the last sale within 10 percent of the price
    in the same twelve months: the ZIP's liquidity) and, where the market's `godmode_coverage` row carries the lender registry
    and its `lenders.counties` lists the parcel's county, `godmode_lenders_top` and `godmode_lender_loans` for the hard-money
    lenders active in the ZIP; it names the figures to carry into the underwrite, the `meta.dated[]` stamps to repeat and the
    caveats (the parcel layer is published for one market today, and inside it the financing slice and the lender registry
    cover Maricopa alone, read off the coverage row; Texas prices are mostly null; nothing here is a valuation). Both hosts
    list it, so both manifests move; the OpenAPI introduction's MCP sentence counts seven prompts. No `/v1` change.
    [Prompts and chaining](https://developers.investorlift.com/mcp/workflows)
  * **Docs** API 0.22.2: corrections from the SFR Analytics API review of 2026-09-15 (items 30 and 31). The `DealKind`
    description states the flip test as the pipeline applies it (held 30 to 365 days, and either both deeds priced and resold
    for 10 to 150% over the purchase price or an unpriced purchase resold at a price by a registry investor; a resale under 30
    days, or a chain resale or round trip within 90, is wholesale; about one flip in six has no investor id), the `kind` legend,
    `by_kind.wholesale`, both `n_wholesales` fields and `n_quick_resales` state the wholesale bound in the same words, so both
    OpenAPI documents move, and the document carries `externalDocs` pointing at this site. On the site, [The ideas you need](https://developers.investorlift.com/guides/ideas)
    carries the same sentence under the deal kinds, and the deal object page's `wholesale` row reads the same; [Plans and limits](https://developers.investorlift.com/guides/plans-and-limits)
    says every plan sees every covered market, coverage being a label on the response and not a line on the bill, and names the
    two products sold outside the plans (`GET /v1/str-parcels` per jurisdiction under an order form, the Market Buyer File per
    metro when it ships); [I know the name, find the investor](https://developers.investorlift.com/guides/walkthroughs/find-by-name)
    links the merged-id and retired-id handling of the profile walkthrough; and the prompts page no longer says five of the six
    prompts are on the public host. A unit test now pins every catalogue count a hand-written page or snippet spells out (tools,
    prompts, resources, the families and the subsets) to the registry, the way the OpenAPI introduction's sentence already was,
    and fails on a subset it has no constant for. No contract change.
  * **Docs** a walkthrough for the top buyers of a place,
    [Top buyers in a place](https://developers.investorlift.com/guides/walkthroughs/top-buyers): one call,
    `GET /v1/investors?zip=<one ZIP>&bought_after=<data_end minus 90 days>&sort=events_desc`, with the point form (`lat`, `lng`,
    `radius_miles`) and `godmode_investors_nearby` with the same inputs as the alternatives; `bought_after` as an absolute date the
    reader computes from `meta.coverage[].data_end` (the data end runs weeks behind today, so a window counted from today is short by
    that lag); `in_radius.n_purchase_events` as the trailing count beside the row's market-wide `n_deals`, `n_purchase_events_12m` and
    `days_since_last_purchase`; the operator rule (rows sharing `operator_id` are one operation, counts per id); the contact facts every
    key sees (`contact.n_skip_trace_targets`, `has_person_member`, `has_home_address`, the identifying fields null on the public host);
    and what the list is not (a relative window, a ring count the API does not compute, a ZIP list or a city on Free and Starter,
    buyers without an investor id). `GET /v1/investors` gains a Related line to it. Item 19 of the SFR Analytics API review of
    2026-09-15. No contract change.
  * **Docs** API 0.22.1: a walkthrough for new construction,
    [New construction near here](https://developers.investorlift.com/guides/walkthroughs/new-construction): `POST /v1/properties/search`
    around a point, or around the parcel `GET /v1/properties/resolve?address` answers for a street address, with the `active-listing`,
    `new-construction` and `corporate-owned` quicklists, the `listing` and `valuation` blocks and `sort: listing_price_asc`; how to
    read the list price, the days on market (counted to the listing feed's as-of date, `meta.coverage[].listings_data_end`, never to
    today), the assessor's `year_built`, `owner.kind` for the builder and the dated valuation; what the list is not (Phoenix alone
    today, with Houston answering `parcels_unavailable`; MLS-listed only; a standing parcel as the roll dates it, so a to-be-built plan
    and a spec home still carried as a lot are absent; a dated AVM); and, for the internal host, the one-builder narrowing with
    `filters.owner.name_contains_words`. The `godmode_property_search` description names the same question ("new-construction homes
    for sale within a radius") among what it answers and no longer restates the dataset list its `datasets` field carries, so both
    manifests move (item 8 of the SFR Analytics API review of 2026-09-15). No contract change.
  * **Added** API 0.22.0: a ZIP list or a postal city as the fourth location beside the point, the viewport and the parcel on `GET /v1/deals`, `GET /v1/deals/summary`, `GET /v1/deals/cells`,
    `GET /v1/deals/pins`, `GET /v1/investors`, `GET /v1/wholesale-listings` and `GET /v1/str-parcels`. `zip` is a comma list or
    repeated key of 5-digit ZIPs (up to 50); `city` is the parcel's postal city as the county records it, any case (folded upper
    case; never the short-term rental jurisdiction of `meta.coverage[].str.jurisdictions[]`). One place per request, and not
    beside `radius_miles`, `bbox` or `property_id` (400 `geometry_conflict`); `lat` + `lng` may ride along as the reference
    point only, so `distance_miles`, `nearest_distance_miles` and `sort=distance` keep their rule and `meta.reference_point`
    echoes the point, while a place with no point takes the list's default order (`sort=distance` there is 400
    `sort_requires_point`); no shape is echoed. A ZIP or a city no parcel of a loaded market carries is 422 `outside_coverage`
    naming it (`zips_unknown` or `city` in the body) and the loaded markets, and a list with one unknown ZIP is refused whole, so
    a page is never mistaken for the full set. On Free and Starter the place is exactly one ZIP: a ZIP list or a city there is
    403 `plan_limit` naming the limit. That place rule runs on the summary, the cells and the pins as on the list and the
    investors, while the radius and viewport bound of those plans stays where it was (the deals list, the investors and the
    match carry it; the summary, cells and pins never did), so a summary or cells request served under 0.21.0 is served
    unchanged. A city, or more than six ZIPs, queues behind other large queries like a radius over five miles.
    `GET /v1/buyers/match` takes no place: `zip` or `city` there is 400 `geometry_conflict`, because the match ranks
    near a point. The MCP tools `godmode_deals_search`, `godmode_deals_summary`, `godmode_investors_nearby` and
    `godmode_wholesale_listings` take `zip` and `city` under the same rules and `godmode_buyers_match` refuses them; the
    concepts resource lists the place beside the three geometries, and the catalogue budget took its fourth raise, by 8 KiB to
    176 KiB (173,651 bytes measured; `api/test/mcp-registry.test.ts`). Two indexes on the parcel table, `(market, zip)` and
    `(market, city)`, were built on stage the same day and are carried by the pipeline and the publish path for every later
    build.
    [List deals around a location](https://developers.investorlift.com/api-reference/endpoints/deals-list)
  * **Added** API 0.21.0: [`GET /v1/properties/resolve`](https://developers.investorlift.com/api-reference/endpoints/properties-resolve)
    takes a street address: `address` (the street line as the county records it) with `zip` or `city` and an optional `unit`,
    exclusive with `lat`/`lng` and `apn`. The line is normalised by the pipeline's own normaliser (`il_norm_address`) and probed against a served address
    table (one row per parcel with the parts of its situs line, keyed on ZIP, house number and street; built
    over the live parcel table of each market and swapped like the other sidecars) with the direction-less street as the fallback
    rung, the ZIP narrowing the probe or, without one, the city. One row resolves; several rows resolve when exactly one row's unit
    matches the unit given (inside the line or in `unit`), otherwise 422 `ambiguous_address` lists up to 25 `candidates[]` with
    `truncated`. `ResolveCandidate` gains `unit`. `meta.coverage[]` (and `/health`, `godmode_coverage`, `godmode://markets`)
    gains `address_as_of`, the deed data end the address table was read from, null where it is not published; an address is
    then 422 `addresses_unavailable`. `godmode_property_lookup` takes the same form (`address`, `zip`, `city`, `unit`; an
    ambiguous address is `resolved: false` with the units as candidates), and the concepts resource points a model from an
    address to a `property_id`. Two error codes (43), both bound to the resolve operation in the OpenAPI document along with
    `ambiguous_apn`, which was declared on every 422 route before.
  * **Added** API 0.20.0: `GET /v1/properties/{property_id}/comps` and the MCP tool `godmode_property_comps`: the comparable sales around one parcel, most similar first. Every priced sale inside
    `radius_miles` (default 1, at most 5) and the last `days_back` days before the market's `data_end` (default 180, at most
    1,095) on a parcel within `beds_tolerance` (1), `sqft_pct` (20) and `year_tolerance` (15) of the subject, one sale per
    parcel, from two sources the API already serves: the deed registry's arm's-length last purchase (`DEED`, every market) and
    the SOLD listing records where the market's listing tables are published (`MLS`, with `days_on_market` and
    `list_price`); `source` picks `deed`, `mls` (422 `listings_unavailable` where none) or `both` (the default; the MLS record
    wins over a deed recorded within 45 days of it). Each comp carries `distance_miles`, `price_per_sqft`, the building facts,
    `cash_sale_proxy` and `last_sale_lender` from the parcel layer where the market has one and its last sale is the same
    transaction, the investor exit the deal registry records within 45 days of the sale (`deal`: id, kind, investor, gross profit,
    hold days) or null, and `similarity` (0 to 1: the distance, size, bedroom, age and recency gaps, each over its own
    tolerance, weighted 0.35, 0.25, 0.15, 0.10 and 0.15). `summary` carries the counts by source, the medians, an indicative
    `price_per_sqft_value` (the median times the subject's sqft: no time or condition adjustment), the range, the window,
    the sources read and the tolerances applied (a fact the subject lacks applies none). Priced at 2 credits plus 1 per comp
    parcel new to the account. The MCP catalogue is twenty-six tools, inside the
    168 KiB budget of 0.19.0 (`api/test/mcp-registry.test.ts`).
    [Read a parcel's comps](https://developers.investorlift.com/api-reference/endpoints/properties-comps)
  * **Added** API 0.19.0: rankings by place and outcomes of the lender registry (phase 5 of docs/god-mode-api/PLAN-lenders.md; 0.18.0 went
    to the cash-sale proxy fix merged the same night), on
    the same dated slice (2026-06-25, recordings through 2026-06-11, Maricopa County alone inside the Phoenix market). The
    geography table gains the H3 cell kinds (res 7 and 8), the market-wide month kind and the periods `12m`, `all`, a calendar
    year from 2002 and a calendar month among the 36 to the slice month beside `24m` (a stored `rank` over every identity in
    the bucket, persons below the gate and placeholders included, `share_by_volume`, `n_borrowers`, `n_foreclosed`;
    3,862,332 rows on stage against 70,119, cell8 1,293,029, zip 1,186,445, cell7 686,395, city 521,563, county 143,865, month 31,035). `GET /v1/lenders` takes every period class
    inside a ZIP, city or county (market-wide `24m` and a calendar month, which reads the month kind; 400 `validation_error`
    code `period_not_served` otherwise) and a geometry (`lat` + `lng` + `radius_miles`, `bbox` or `property_id`, the shared
    fields; not beside a place, 400 `geography_conflict`): a geometry is resolved to the H3 res-8 cells whose centre lies inside
    it (about 0.3 sq mi each), so its edge is a hex boundary, not the circle, and the lender's cell rows are summed over the
    three windows alone (`share`, `share_by_volume`, `n_unpriced`, `n_borrowers` and `n_deals_financed` null there, not measured
    over an arbitrary set of cells; `in_geometry` `{res, n_cells, n_cells_lent}` on every row; outside every market 422
    `outside_coverage`, in another market 400 `geometry_market`); the ranking row carries `share_by_volume` and `n_borrowers`.
    Two routes, zero-priced: `GET /v1/lenders/{id}/rankings` (`group_by` month, zip, city, county or cell with `res` 8 or 7,
    `period`, `min_n` 10 on the lender's own count, a keyset page; the Lender ranking bucket `\{key, n, volume,
    n_purchase_money, n_investor, rank, share, partial, floor, as_of, dated\}`, the month series served as a span with every
    month present, zero-filled, and `partial` on the slice month and year, `floor` on a year before `history_floor_year`) and
    `GET /v1/lenders/{id}/cells` (`res`, a window period, `product[]` narrowing `n`; the Lender cell `\{cell, n, volume,
    n_purchase_money, n_investor, n_foreclosed, as_of, dated\}`, up to 5,000 a page in cell order, served from the result cache
    on a repeat). The profile carries `takebacks` (the take-back deeds since 2016 joined to the registry through its member
    keys: `n_takebacks`, `n_takebacks_24m`, `by_instrument[]` `TRUSTEES_DEED` / `SHERIFFS_DEED` / `DEED_IN_LIEU`,
    `n_loans_foreclosed`, `n_takebacks_unlinked`, `foreclosure_share` null under 20 captured loans,
    `median_days_takeback_to_resale`, `is_auction_lender` read from the auction build's lender list alone, never the buyer-side
    exclusion vocabulary; deeds, never delinquency, the grantee usually the trustee or servicer; null while
    `takebacks_measured` is false) and `rankings` gains `share_by_volume_24m`, `n_zips_ranked_first_24m`,
    `n_cities_ranked_first_24m` and `n_counties_ranked_first_24m` (the lender's own 10 or more and rank 1; the county count
    null while `counties_measured` is false); the loan row carries `foreclosed` (`instrument`, `deed_date`, `buyer_is_lender`:
    the earliest take-back deed on the parcel more than 90 days after the recording that precedes any priced resale and any
    later mortgage; a shorter gap is the auction buyer's own deed, not a foreclosure of this loan) and
    `GET /v1/lenders/{id}/loans` takes `foreclosed`, `cell` (an H3 index at res 7 or 8, the resolution read off its bits) and
    the same geometry, the CSV three cells (Foreclosed on, Foreclosed instrument, Foreclosed by lender); the borrower row
    carries `other_lenders[]` (the stubs behind `other_lender_ids[]`, which stays); the wholesale listing carries
    `lenders_active[]` (the five lenders with the most investor loans in the listing's ZIP over the registry's window, from the
    ZIP rankings at page time; empty where the ZIP has none, null on a hidden address and where the registry or its borrower
    match is not published; no CSV cell) and its responses carry the lenders and financing stamps in `meta.dated[]`;
    `meta.coverage[].lenders` carries `takebacks_measured` and `counties_measured`. Over MCP `godmode_lenders_nearby` joins the
    catalogue on both hosts (the ranked lenders inside a geometry, the investors\_nearby shape; `/rankings` and `/cells` are not
    tools, the pins and cells precedent) and `godmode_lenders_top` takes `period`: twenty-five tools; the catalogue budget took
    its third 8 KiB raise, to 172,032 (166,431 bytes measured, the arithmetic in `test/mcp-registry.test.ts`);
    the wholesale tools' concise row leaves `lenders_active` to the detailed format, the loans tool's `foreclosed` and the
    borrowers tool's `other_lenders` likewise. Every phase-5 field is null and every phase-5 parameter and route 422
    `lenders_unavailable` on a registry built before the place rankings or a host without the h3 extension
    (`MarketCache.hasLenderPlaces`; `scripts/sql/roles.sql` creates `h3` and `h3_postgis`, an operator step of
    docs/data-services/RUNBOOK-copy.md and deploy/README.md); the publish path: seq 42, 45 and 46's md5s moved, `indexes.sql`
    stays 124; nothing is on production until the family's next copy. None of it is priced, and the early-access sentence of
    0.14.0 still applies.
    [The Lender ranking bucket](https://developers.investorlift.com/api-reference/objects/lender-ranking-bucket)
  * **Changed** API 0.19.0: `outcome` on a loan tied to a deal reads `FORECLOSED` where a take-back deed on the parcel (a
    trustee's deed, a sheriff's deed or a deed in lieu, dated more than 90 days after the recording: a shorter gap is the
    auction buyer's own deed, not a foreclosure of this loan) precedes any priced resale and any later
    mortgage of another lender, and a distressed deed no longer counts as the resale of the chain (a trustee's deed carries a
    credit-bid price and read `RESOLVED_BY_RESALE` before): on stage 231 captured loans' `outcome` moved against the
    phase-4 set: 40 to `FORECLOSED` (30 from `RESOLVED_BY_RESALE`, 7 from `OPEN`, 3 from `REFINANCED`), and 191 more left
    `RESOLVED_BY_RESALE` once a distressed deed stopped counting as the resale (151 to `REFINANCED`, 23 to `UNKNOWN`, 17 to `OPEN`);
    `months_to_resolution` is then measured to the take-back deed, `by_outcome_24m` on the profile carries the value, and
    acceptance 12's "UNKNOWN on at most 10%" was re-read after the move (793 of 58,940 captured, 1.35%, from 770 before). `geography.by_county[]`,
    `by_city[]` and `by_zip[]` on the profile count dated instruments alone in `n_all`, now that the geography table's `all`
    period is the dated ones (an instrument without a recording date is counted in the profile's `n_undated`, not in any
    place), and a place whose instruments are all undated no longer appears.
    [The Lender loan](https://developers.investorlift.com/api-reference/objects/lender-loan)
  * **Data** The phase-5 stage build (from the runner as the chain's `lenders`, `parcel_serve` and `public_copy` stages on the db.m7g.2xlarge, runs 35050752528 and 35053303132: 35b step 412 at 90.8 s, 35c 413 at 1,874.3 s after a first attempt (411, 1,447.6 s) raised on the geo share rounded to 0, 30a 414 at 123.3 s, 22c 21.2 s: 35c alone under caffeinate after a 1/256 dry run of the one-pass
    roll-up, then 22c; the report lines pinned at this build: LAKEVIEW 170 (165 trustee's deeds, 5 in lieu) take-back deeds (165 expected),
    BANK OF NEW YORK MELLON 187 (159 trustee's, 26 in lieu, 2 sheriff's; the plan's 77 sit on BANK OF NEW YORK's own identity) (159 + 77), WELLS FARGO 165 (133 trustee's, 27 in lieu, 5 sheriff's over the two member spellings) (107 + 26), CSTE HOLDINGS
    462 deeds excluded pending review (462), the auction lender table 233 keys
    (120 DIL\_GRANTEE\_2P, 113 CREDIT\_BID\_2P), `n_takebacks_unlinked`
    6,168 of 6,586 (418 linked to a loan row of the lender; 126 served identities read `is_auction_lender`) market-wide): 32,046 identities served, lender registry run 16,
    dataset\_version 1789530735 (the market row restamped 1789532610).
</Update>

<Update label="2026-09-15" description="API 0.18.0, 0.17.0, 0.16.0, 0.15.0, 0.14.0" tags={["Added","Changed","Data","Docs"]} rss={{"title":"2026-09-15: API 0.18.0, 0.17.0, 0.16.0, 0.15.0, 0.14.0","description":"5 added, 7 changed, 1 data, 2 docs"}}>
  * **Changed** API 0.18.0: `cash_sale_proxy` on the parcel row is null on every parcel of a market whose delivery records a
    purchase mortgage on fewer than one priced last sale in five, instead of true on every priced sale. The 2026 deliveries
    carry that column empty, so the `cash-buyer` quicklist and the `sale.cash_sale` filter had been returning every priced
    sale of the last three years as a cash sale (299,430 of 299,430 in Phoenix on the stage tables of 2026-09-14). Where the
    flag is unmeasured both now answer 422 `cash_sale_unavailable`; the measured cash signals (the auction block,
    `bought_auction_kind`, `buys_at_auction`, `buys_reo`) are unchanged. The served value moves on the next 30a build of the
    market.
    [Parcel search row](https://developers.investorlift.com/api-reference/objects/parcel-search-row)

  * **Added** API 0.18.0: `meta.coverage[].parcel.sale_mortgage_measured`, true when the delivery records a purchase mortgage on
    at least one priced last sale in five of the market; and the error code `cash_sale_unavailable`.
    [Errors](https://developers.investorlift.com/guides/concepts/errors#cash_sale_unavailable)

  * **Added** API 0.17.0: identity and class of the lender registry (phase 4 of docs/god-mode-api/PLAN-lenders.md), on the
    same dated slice (2026-06-25, recordings through 2026-06-11, Maricopa County alone inside the Phoenix market). Lender ids
    now follow the alias table (`gm_lender_alias`, the seventh lender table: `old_id`, `new_id`, `reason` and `lender_run`,
    the run that retired the id): an id retired by a merge redirects to the current lender with `meta.resolved_from` on
    `GET /v1/lenders/{id}`, `GET /v1/lenders/{id}/loans` and `GET /v1/lenders/{id}/borrowers` and their MCP tools
    (`godmode_lender_lookup`, `godmode_lender_loans`, `godmode_lender_borrowers`), the current id bound by the scan, the
    cursor and the CSV filename; a retired id with no successor is `410 gone` (`superseded_by` null, the reason and the
    lender registry run in the body; the three id routes and the parcel search declare the status). The walk runs only after the route's own
    lookup found no row and only while the registry carries the alias table, so a registry built before this phase answers
    404 for an old id as it did, and a lender live in several markets answers them all as `markets[]` on the profile (never
    `market_required` from the walk; the list routes keep it). The two lender-id filters walk it too: `financed_by` on
    `GET /v1/investors`, `GET /v1/deals` (and `/pins`, `/cells`, `/summary`) and `GET /v1/buyers/match`, and
    `filters.financing.lender_id` on `POST /v1/properties/search`, bind the current id behind a merged one and stamp
    `meta.resolved_from` after any `investor_id` chain, answer `410 gone` for a retired id and, as the `investor_id` filter
    does, `404 not_found` for an id no registry run ever issued (until now the id was bound as sent and matched nothing).
    The profile carries `class_basis` (`NAME`: the registry's name rules read on the folded name; `DICTIONARY`: one of the
    seven hand-kept CLASS rows overrides them, for example
    BBVA USA as a BANK), `successor` (the acquirer or parent a link row records: `id`, `name`, `lender_class`,
    `is_hard_money`, `kind` `ACQUISITION` or `SUBSIDIARY`; the books are never merged, so a lender's loans stay under its
    own id; null when none is recorded or the successor has no profile in the market; 19 link rows seeded by hand, Chase
    Manhattan and National City into JPMORGAN CHASE BANK and PNC BANK, Wells Fargo Financial and SunTrust Mortgage into
    WELLS FARGO BANK and TRUIST BANK, M\&I into BMO HARRIS BANK among them, every acquirer or parent a served identity of
    phx), `nmls` (`id`, `url`: the NMLS Consumer Access page; null where uncurated; 69 ids curated by hand, most of the top
    80 served lenders by loans in the window and the golden lender KIAVI FUNDING, each verified on 2026-09-15 against the
    lender's own licensing disclosure or NMLS Consumer Access, the top 500 the target and the build printing the backlog)
    and `id_history[]` (`old_id`, `reason`, `lender_run`: the ids merged into the lender, oldest run first, each still
    answering with a redirect); `class_basis` and `nmls` ride the Lender object too, the lead market's; every one of them
    is null (the history empty) on a registry built before this phase. `meta.resolved_from` admits a `len_` id beside the
    investor and agent ids, and `410 gone` names the three kinds. Over MCP the lookup forwards `meta.resolved_from`, its
    `lender_id` input says an old id is followed, and the concise format keeps the four fields; the catalogue measures
    160,132 bytes against the 163,840 budget (the arithmetic in `test/mcp-registry.test.ts`). On stage the build retired
    118 ids with alias rows (120 rows in `gm_lender_alias`: the 108 served ids dictionary round two (116 estimated before the run)
    merged into their canonicals, the person-gate crossings, and the Sierra Pacific pair carried since the phase-1 build);
    the seventh table rides the parcel family's publish path (22c seq 48 beside seq 42's moved md5, rules 1515 to 1517,
    `schema.sql` forty-four tables, `indexes.sql` 124, `assert_no_contact.sql` forty-three `COLUMNS` rows); nothing is on
    production until the family's next copy. None of it is priced, and the early-access sentence of 0.14.0 still applies.
    [The Lender](https://developers.investorlift.com/api-reference/objects/lender)

  * **Changed** API 0.17.0: the lender classes on `gm_financing`, `gm_financing_mortgage`, `gm_mortgage_history`,
    `gm_parcel`, `gm_lender` and `gm_lender_loan` moved to the v2 name rules of docs/god-mode-api/PLAN-lenders.md 2.2
    (the government, bank, trust, lending and person vocabularies in that order, read on the folded name, so KS STATEBANK,
    MANUFACTURERS & TRADERS TR CO and SCME MORTGAGE BANKERS read BANK while a person named Banks does not, a securitisation
    trust reads NONBANK, a custodial IRA account name reads PRIVATE under the person gate, a five-or-more-token name joined
    by AND or a marital phrase reads INDIVIDUAL, and HOUSING & URBAN FINANCIAL is no HUD form). Measured read-only over the
    76,216 phx names before the run with the function the run applies: 4,327 recordings and 3,154 open positions on 212
    names INDIVIDUAL to BANK, 4,965 and 400 on 265 names INDIVIDUAL to GOVERNMENT, 12,015 and 6,607 on 2,747 names
    INDIVIDUAL to NONBANK, 11,676 and 5,436 on 134 names NONBANK to BANK; 43 rows of 1,713,124 leave BANK (20 names,
    0.0025%, inside the 0.1% band 35a guards); the 23-row crosstab is in the build log with the run's own, which must
    equal it. Placeholder lender names (NOT AVAILABLE, NOT PROVIDED, CORELOGIC SOLEX, UNKNOWN: 4 spellings, 349 recorded and 1,062 open rows on stage (230 and 424 on the four named spellings before the run; the function reads the hyphen and the empty forms too)) are served as `lender_name` null with `lender_class` null on the financing block, the open
    liens and the mortgage history, and carry no lender id; the rows stay (their amounts and dates are real) and
    `parcel.financing.n_lenders` no longer counts them. The person gate is re-baselined under v2: 669 served person
    profiles (INDIVIDUAL and PRIVATE) to 493 (503 measured before the run), 10 ids retired
    with alias rows (10 measured: persons named Banks and two trust-company typos v2 reads below the gate) and
    2,729 issued (about 2,757 measured) as names crossed the gate; the `private-lender-financed` quicklist
    moved from 38,141 parcels to 30,629 (30a's report). Every id a dictionary merge retired answers a redirect
    where it answered 404: the 116 served ids round two merged into their canonicals (the HUD-shaped and Department
    spellings into SECRETARY OF HOUSING AND URBAN DEVELOPMENT, DEPARTMENT OF HOUSING & URBAN DEV `len_992e364eeaeb` among
    them; the 42 Arizona Industrial Development Authority typo spellings with 3 or more rows; the Sierra Pacific leftovers;
    MORGAN STANLEY MORTGAGE CAPITAL; FIRST INTERNATIONAL BANK & TRUST; the ambiguous truncations decided where every
    candidate names one lender), and the Sierra Pacific pair (`len_6ac531f2070d` and `len_04303f5320d7`) retired at the
    phase-1 build, whose two alias rows now carry the run that retired them (4, not the computing run).
    [Financing](https://developers.investorlift.com/api-reference/objects/financing)

  * **Changed** API 0.17.0: over MCP, `godmode_property_lookup` carries `meta.dated[]` (the `lenders` and `financing`
    stamps) on a detailed lookup whose `deals[]` rows carry the financing block of 0.16.0, as
    `GET /v1/properties/{property_id}` and the deal and investor tools already do, so the tool's meta now agrees with
    0.16.0's contract; a concise lookup, which leaves the block to the detailed format, carries none, and a registry
    without the deed link gives `financing` null with no stamp. The tool's description names the block and the stamps.
    Nothing is priced. (Issue #46.)
    [The twenty-four tools](https://developers.investorlift.com/mcp/tools)

  * **Data** The phase-4 stage build (phx\_step\_log 404 for 35a, 406 for 35b, 407 for 35c, 408 for 30a and 409 for 35a again with the review's guards, 22c at 21:35 and 21:40 ET on 2026-09-15 and 2026-09-16; the two guard raises of 403 and 405 in the build log: the read-only class crosstab, then 35a, 35b, 35c and 30a, then 22c
    with a test `gm_lender` suppression row, `assert_no_contact.sql` and `check-no-person-names.mjs` over the copy):
    32,046 identities served, lender registry run 14, dataset\_version 1789520874.

  * **Added** API 0.16.0: the linkage blocks and the borrowers of the lender registry (phase 3 of
    docs/god-mode-api/PLAN-lenders.md), on the same dated slice (2026-06-25, recordings through 2026-06-11, Maricopa
    County alone inside the Phoenix market). The recorded deeds are joined to every loan: each loan row of
    `GET /v1/lenders/{id}/loans` carries `purpose` (`PURCHASE` when a priced deed on the parcel is dated in the 45 days up
    to the recording, `NOT_PURCHASE` when the registry covers those days and none falls in them, `UNKNOWN` otherwise;
    decided by the deeds, never by the file's label), `deed` (`dt`, `price`, `loan_to_price`) on a `PURCHASE` row, `deal`
    (the investor deal the loan is tied to: `deal_id`, `kind`, `link_window` `STRICT` or `LOOSE`, `is_purchase_loan`,
    `secondary_loans_n`, the investor, the prices, `gross_profit`, `hold_days`), `outcome` (`RESOLVED_BY_RESALE`,
    `REFINANCED`, `OPEN`, `UNKNOWN`; `FORECLOSED` reserved for a later release) with `months_to_resolution`, and the
    borrowers as keys and ids (`borrower_keys[]`, 12-hex salted hashes served to every key, `borrower_match`,
    `investor_id`, `co_borrower_investor_ids[]`); the route filters on `purpose`, `outcome`, `deal_kind` and
    `investor_id`, and the CSV gains the eleven cells (Purpose, Deed date, Deed price, Loan to price, Deal id, Deal kind,
    Purchase loan, Outcome, Investor id, Borrower keys, Borrower match). The profile (`GET /v1/lenders/{id}`) carries three
    blocks built at build time and null where their input is not built for the market: `borrowers` (`n_borrowers`,
    `n_borrowers_24m`, `n_resolved_24m`, `borrower_measured_share`, `n_repeat_24m`, `n_new_12m`, `n_churned_12m`,
    `churn_measured`, ten `top_borrowers` never carrying a person's name, ten `competing_lenders`,
    `person_names_redacted`), `investor_lending` (the investor loans and their share, the investors by kind, the deals
    financed by what happened on them, the auction and wholesale purchases among them, ten `top_investors`,
    `is_investor_lender`) and `flips_financed` (the flips whose purchase loan was the lender's in the 36 months to 45 days
    before `recordings_through`, `capture_share`, `n_flips_by_known_borrowers`, `share_of_financed_flips`, the resale
    margins `median_gross_profit` with `p25` and `p75`, `median_spread_ratio`, `median_hold_days`, `median_loan_to_price`,
    null under 20 resold flips with both prices, `by_year`), beside `by_purpose_24m`, `by_outcome_24m`,
    `median_months_to_payoff`, `terms.loan_to_price`, `rankings.rank_purchase_24m`, the flat counts
    `n_purchase_money_24m`, `n_investor_loans_24m`, `n_borrowers`, `n_borrowers_24m` and `is_investor_lender`, `by_year[]`'s
    `n_purchase` and `n_investor`, `by_month[]`'s `n_purchase_money`, and `borrowers_measured` beside the three measured
    flags; `is_hard_money` is now true by behaviour too (20 or more loans since 2015 of which a quarter or more sit
    within 45 days of a registered investor's purchase, the lender not a bank: `hard_money_basis` `BEHAVIOUR` or `BOTH`,
    89 identities on stage). `GET /v1/lenders/{id}/borrowers`, the Lender borrower object: one
    row per borrower key with the name (a company's to every key, a person's under the `contact` scope with `party_key`),
    the investor stub, `borrower_match`, the loans by window, `volume_24m`, `first_loan_on`, `last_loan_on`,
    `n_lenders_used_24m`, `other_lender_ids[]`, `churned`, `churn_measured`, `moved_to_lender_id` (a lower bound of
    leaving on a feed that sees one loan in five) and `n_deals_financed`; `period` (`24m`, `all`), `q` (a company name;
    persons never match), `registered_only`, `churned`, `investor_kind`, `min_loans`, sorts `loans_desc`, `volume_desc`,
    `last_loan_desc`, `churned_first`; the audit line under the contact scope as on the loans route. `GET /v1/lenders`
    takes `purpose=purchase` (refused beside `position` or `product`), `investor_only` and `sort=purchase_desc`, and the
    ranking row carries `n_purchase_money`, `n_investor` and `n_deals_financed`. The investor profile
    (`GET /v1/investors/{id}`) carries `financing` (`n_loans`, `n_loans_12m`, `n_loans_24m`, `n_lenders`, ten
    `lenders_used`, `last_loan`, `n_deals_measured_24m`, `n_deals_financed_24m`, `share_deals_financed_24m`,
    `uses_hard_money`, `as_of`, `dated`) and the deal row carries `financing` (`purchase_loan_status` `FOUND`,
    `NONE_CAPTURED` or `NOT_MEASURED`, `purchase_loan`, `secondary_loans_n`, `link_window`, `outcome`, `as_of`, `dated`),
    both served to every key and null where the registry or its linkage is not published; `NONE_CAPTURED` is documented as
    not cash, since the open-lien table sees about one purchase loan in five on holds and one in fifteen on flips
    (`cash-buyer` keeps reading the sale). `GET /v1/investors`, `GET /v1/buyers/match` and `GET /v1/deals` take
    `financed_by=len_...` and `uses_private_lender=true` (a loan in the registry's 24-month window from a hard-money,
    investor, INDIVIDUAL or PRIVATE lender), and the buyers match adds the `financing` reason (`w_financing`, weight 0 by
    default: the investor borrows from a lender that also lends in the subject's ZIP). Every one of them answers
    `422 lenders_unavailable` where the registry, its deed link or its borrower match is not published (a registry built
    before this release keeps serving phases 1 and 2 with the new fields null). Over MCP `godmode_lender_borrowers` joins
    the catalogue on both hosts: twenty-four tools; a list tool, so a person's name and every `party_key` are null on its
    rows and `meta.contact` reads `not_on_lists`; the loans tool keeps `purpose`, `deed`, `deal`, `outcome`,
    `investor_id` in the concise format and drops the two borrower arrays, `months_to_resolution`, `borrower_match`, the
    deed's date and the deal's own facts (the default page stays under 20,480 bytes); the lookup caps the three blocks'
    lists at five (three flip years); the concise deal row of the deal tools and the parcel leaves `financing` to the detailed
    format (twenty maximal rows carrying its null key measured 20,736 bytes against the 20,480-byte page); the catalogue budget took
    a second raise, to 163,840 bytes, with the arithmetic in `test/mcp-registry.test.ts`. None of it is priced, and the
    early-access sentence of 0.14.0 still applies. On stage the build linked 44,132 deals STRICT
    (2,059 flips bought since 2022 with a purchase loan over 495 lenders),
    4,599,640 borrower rows over 28,640 lenders, and 10.7% of the
    history rows since 2019 to a registered investor; the sixth table `gm_lender_borrower` rides the parcel family's
    publish path (22c seq 47, rules 1514, 1532 to 1534 and 1591 to 1597, `indexes.sql` 122); nothing is on production until the
    family's first copy.
    [The Lender borrower](https://developers.investorlift.com/api-reference/objects/lender-borrower)

  * **Added** API 0.15.0: the loan-level contact projection and the parcel-side links of the lender registry (phase 2 of
    docs/god-mode-api/PLAN-lenders.md). `GET /v1/lenders/{id}/loans` answers `Accept: text/csv`: every filtered row as
    `lender-<id>-loans.csv` in the requested sort, `limit` and `cursor` ignored, the set counted before the first row
    streams, at most 50,000 rows (422 `csv_cap_exceeded` above it, a public code from today on this operation alone), at
    0 credits; on `api.investorlift.com` from Growth (403 `plan_limit` below it) with `recorded_from` required (400
    `validation_error`, code `window_required`, without it; `recorded_to` stays optional) from the gateway release that
    forwards `Accept` on this route (until it ships the public host answers the JSON page whatever `Accept` says; this
    release is the origin's half), on the internal host with neither gate. Every loan row carries `borrowers[]` under the `contact` scope: a RECORDED row's borrowers as the recorded
    history names them (an OPEN row reads null), with `contact_redacted` false; a deals-only key's row omits both keys, and
    the CSV's Borrowers cell (the names joined by `; `) is a contact cell dropped the same way. The Financing object carries
    the registry id beside every lender name, `summary.first_lien_lender_id`, `summary.last_mortgage_lender_id`,
    `open_liens[].lender_id` and `mortgage_history[].lender_id` (null where the lender is below the person gate, on a
    placeholder row and where the market has no registry), so a parcel's financing block opens the lender and its loans;
    `POST /v1/properties/search` takes `filters.financing.lender_id` beside `lender_name_contains`, the parcels whose last
    recorded mortgage's lender carries the id, every spelling at once, over a new `gm_parcel` index (400 `validation_error`
    when no market of the location has a registry). `GET /v1/lenders` serves `position=1` with `product`, the first liens of
    the named products from precomputed counts, which 0.14.0 refused as `not_precomputed`. Over MCP `godmode_lender_loans`
    joins the catalogue on both hosts: twenty-three tools; a list tool, so the borrower keys are left out of its rows and
    `meta.contact` reads `not_on_lists`. None of it is priced, and the early-access sentence of 0.14.0 still applies. The two
    `gm_parcel` columns behind the filter and the quicklist (`last_mortgage_lender_id`, `last_mortgage_hard_money`, from
    30a) ride the parcel family's publish path.
    [List one lender's loans](https://developers.investorlift.com/api-reference/endpoints/lenders-loans)

  * **Changed** API 0.15.0: the `private-lender-financed` quicklist reads the lender registry's hard-money flag beside the
    class: a parcel whose last recorded mortgage came from a PRIVATE or INDIVIDUAL lender, or from a hard-money lender of
    the registry (`is_hard_money`), 30,666 to 38,141 parcels on stage (7,475 more, both counted 2026-09-15: the class alone on
    the 0.14.0 build, then the class or `last_mortgage_hard_money` after 30a wrote the flag; 7,555 parcels carry it).
    `cash-buyer` is unchanged: it reads the sale, never the history.
    [Quicklists](https://developers.investorlift.com/guides/concepts/quicklists)

  * **Changed** API 0.15.0: `amount_min` and `amount_max` on `GET /v1/lenders/{id}/loans` accept any non-negative integer;
    the 2,000,000,000 ceiling of 0.14.0 is gone (a value above it answered 400 `validation_error` before).
    [List one lender's loans](https://developers.investorlift.com/api-reference/endpoints/lenders-loans)

  * **Added** API 0.14.0: the lender registry, built from the 2026-06-25 financing slice (recordings through 2026-06-11) and
    dated like it, Maricopa County only inside the Phoenix market (Pinal is not measured): `GET /v1/lenders/search` (any
    spelling of a lender's name to one stable `len_` id), `GET /v1/lenders` (the lenders of a market or of one ZIP, city or
    county ranked by loans recorded in the 24 months to the slice date, with `hard_money`, `position`, `product` and
    `sort=volume_desc`; persons and GOVERNMENT hidden unless asked), `GET /v1/lenders/{id}` (the profile: every spelling, the
    class and the hard-money flag, the loans counted once across the open liens and the recorded history, the open book,
    `by_year` and `by_month` against the market, the product, term, label and property-type mix, `terms` with their
    coverage, `geography`, `coverage`, `rankings` and a `summary`, one profile per market; every volume and the amount
    quartiles leave out amounts above \$5,000,000, a facility amount recorded on every parcel it covers, which
    `n_amount_excluded_24m` counts and `amount_outlier` marks on the loan row) and `GET /v1/lenders/{id}/loans`
    (every instrument, one row per instrument by default, JSON only), with the Lender, Lender ranking row and Lender loan
    objects, their reference pages and the walkthrough; `meta.coverage[].lenders` (`as_of`, `recordings_through`,
    `counties`, `history_capture_share`, `n_lender_ids`: the lender ids served, beside `parcel.financing.n_lenders`, which
    counts raw spellings and now says so) and the `lenders` block of `meta.dated[]`; 422 `lenders_unavailable` where the
    registry is not published, 422 `outside_coverage` for a ZIP, city or county outside the covered counties. The recorded
    history captures about one open loan in five, so every origination count is over the union of the two tables, a
    history count is a floor and a bridge lender is the most undercounted. Lender names are business records served to
    every key; a person-named lender has a profile only with 10 or more loans on 5 or more parcels and is hidden unless
    `include_persons=true`; nothing carries a lender's phone or email. Over MCP `godmode_lender_lookup` and
    `godmode_lenders_top` join the catalogue on both hosts: twenty-two tools. None of it is priced: no credits are charged,
    the responses carry no `X-Credits-Charged`, and a legacy MCP call to the two tools weighs 0; when the paid plans publish
    the lender routes are included from Growth and refused below it at the origin, announced here first. In early access for
    Investorlift's team and trusted partners while the developer tier is in beta, to be restricted further before the tier
    opens to every key. The five tables ride the parcel family's publish path (22c seq 42 to 46, rules 1510 to 1531 and
    1590, the `gm_lender` takedown kind); nothing is on production until the family's first copy.
    [The Lender object](https://developers.investorlift.com/api-reference/objects/lender)

  * **Changed** API 0.14.0: the three spellings under which the financing slice records HUD (`SECRETARY/HOUSING & URBAN
    DEV`, `SECRETARY OF HOUSING AND URBAN` and a third truncation) and 24 rarer HUD forms read `lender_class` GOVERNMENT,
    not INDIVIDUAL or NONBANK: the slash and the truncation had defeated the class rule, which gains one alternation (27
    spellings, 21,245 recordings and 1,198 open liens on the stage build of 2026-09-15; BANK unchanged). No id changes. The
    `private-lender-financed` quicklist (43,623 to 30,666 parcels on stage) and the `financing.lender_class` filter stop
    counting those rows as a person's, and the rows leave the default lender lists with the other GOVERNMENT lenders.
    [Financing](https://developers.investorlift.com/api-reference/objects/financing)

  * **Changed** API 0.14.0: the recovery sentence of `400 market_required` (on every caller, investor and agent ids
    included) and the descriptions of `invalid_id`, `not_found` and `outside_coverage` now name the lender list and the
    `len_` id kind beside the kinds they already named; the codes and statuses are unchanged.
    [Errors](https://developers.investorlift.com/guides/concepts/errors)

  * **Docs** the `position` of a recorded mortgage (`mortgage_history[]` on the Financing object) is a slot in the
    recorded history, 1 the oldest kept slot and the highest filled slot the newest, so a parcel with slot 5 filled has
    lost older mortgages; the reference read "1 is the most recent". `sale_date` on the same record is the loan's own
    date as the file writes it, not the sale the mortgage financed. No contract change.
    [Financing](https://developers.investorlift.com/api-reference/objects/financing)

  * **Docs** the legal short forms name the lender family: Developer Agreement 9.1 and Privacy Notice 8 list a person named as
    a borrower on a mortgage or as a party to a lien among the withheld fields, and an entity named as a borrower and the
    lender of record on a recorded mortgage (which can be a person's name, with the count and balance of the loans recorded
    under it) among the shown ones; Privacy Notice 10 adds the lender record to the removals it offers and says a suppressed
    lender's name leaves every loan, property and history-event record with the amounts and dates kept; Privacy Notice 14
    adds a lender's name as recorded to the identifiers; Acceptable Use Policy 3.6 says lender data describes recorded
    activity, not any borrower's creditworthiness; Developer Agreement 7.2 adds len\_ ids to the identifiers a developer may
    keep as references. Under the unpublished version `data-services-beta-v2026-09-13`, for
    counsel's read. No contract change.
    [Terms](https://developers.investorlift.com/guides/terms)
</Update>

<Update label="2026-09-14" description="API 0.13.0, 0.11.0" tags={["Added","Changed","Data"]} rss={{"title":"2026-09-14: API 0.13.0, 0.11.0","description":"2 added, 1 changed, 1 data"}}>
  * **Added** API 0.13.0: the listing-agent and Investorlift listing routes are back in the public document
    (`openapi.public.json`, the reference at developers.investorlift.com) and on the developer tier, in early access for
    Investorlift's team and trusted partners while the tier is in beta: `GET /v1/agents/search`, `GET /v1/agents/{id}`,
    `GET /v1/agents/{id}/listings`, `GET /v1/wholesale-listings`, `GET /v1/wholesale-listings/{id}`,
    `GET /v1/investors/{id}/wholesale-purchases`, `GET /v1/wholesalers/search`, `GET /v1/wholesalers/{id}` and
    `GET /v1/wholesalers/{id}/listings`, with the Listing agent, Investorlift listing and Wholesaler objects, their reference
    pages and the two walkthroughs; 422 `agents_unavailable` rejoins the public catalogue. Over MCP the five listing-agent
    and Investorlift-listing tools (`godmode_agent_lookup`, `godmode_agent_listings`, `godmode_wholesale_listings`,
    `godmode_wholesale_listing_get`, `godmode_wholesaler_lookup`) and the `investorlift_listing_outcome` prompt are served
    at api.investorlift.com, and `market_snapshot` keeps its Investorlift step there: twenty tools and six prompts on both
    hosts. None of it is priced: no credits are charged, the responses carry no `X-Credits-Charged`, and a legacy MCP call
    to those tools weighs 0. The fields that identify a natural person stay withheld on this host as on every other route.
    These routes will be restricted further before the tier opens to every key, announced here first. `GET /v1/deals/pins`
    stays on the internal host. The gateway's route table follows in the api-gateway repository.
    [API reference](https://developers.investorlift.com/api-reference/introduction)
  * **Changed** API 0.13.0: deals gain the kind `build`: a lot or a teardown bought by a registered investor, a house
    built on it during the hold, then sold. Those deals were `other`; a client that switches on `kind` should treat a
    value it does not know as `other`. On the stage tables of 2026-09-14 that is 10,006 of Phoenix's 89,129 `other` deals and
    10,030 of Houston's 42,160, served since the publish of 2026-09-17. The classifier label `deal_kind` gains `BUILD`. A purchase of a builder's
    finished home that is later resold stays `other`. `kind` filters accept `build`; `meta.kinds` on pins lists it last, so
    the existing indexes 0 to 4 are unchanged. No deal id changes. (`docs/god-mode-api/PLAN-build-kind.md`; the bounds of
    2026-09-15 narrow this set: see 0.33.1, 2026-09-17.)
    [Deal](https://developers.investorlift.com/api-reference/objects/deal)
  * **Added** API 0.13.0: `by_kind.build` and a per-year `build` count on `GET /v1/deals/summary`, `n_builds` on
    `GET /v1/deals/cells`, and `n_builds` on `GET /v1/investors/{id}`. Each reads 0 for a market whose tables predate the
    kind. [Deals summary](https://developers.investorlift.com/api-reference/endpoints/deals-summary)
  * **Data** Production serves the refreshed Phoenix market: `phx` data\_end 2026-08-27 (was 2026-08-12), registry\_run 10,
    dataset\_version 1789409315; `hou` unchanged at data\_end 2026-08-11 (registry\_run 5, re-stamped 1789408252 by the swap).
    Published from the stage RDS with `scripts/copy-gm-from-stage.sh` (37 tables, a 1.72 GB dump, 1,188 s end to end; the six
    served indexes of `scripts/sql/indexes.sql` and the float8 `gm_score` of `functions.sql` reached prod with it). API 0.11.0 was
    released to stage and to production the same day (build log, 2026-09-14).
</Update>

<Update label="2026-09-13" description="API 0.10.0, 0.11.0" tags={["Added","Changed","Data","Docs"]} rss={{"title":"2026-09-13: API 0.10.0, 0.11.0","description":"1 added, 5 changed, 2 data, 2 docs"}}>
  * **Changed** API 0.10.0: the MCP tools describe the 29 shared filters and the geometry and paging fields in one sentence
    each (`FILTER_SHORT_DESCRIPTIONS` beside the filter table in `api/src/schemas/common.ts`); the REST reference keeps the full
    texts and `openapi.json` is byte-identical. `tools/list` is 142,577 bytes compact, from 152,409, and the registry test pins
    it under 147,456 with no tool over 26,624. Four tool descriptions (`godmode_deals_search`, `godmode_investors_nearby`,
    `godmode_investor_deals`, `godmode_agent_listings`) no longer repeat the limit and cursor rules their fields carry.
    (Efficiency review 2026-09-13, finding F04.) [MCP tools](https://developers.investorlift.com/mcp/tools)
  * **Added** API 0.10.0: an in-process result cache for `GET /v1/deals/summary`, `GET /v1/deals/cells` and the
    `godmode_deals_summary` tool, keyed on the operation, the canonical validated query and the markets' `dataset_version`, so a
    data refresh changes every key and empties the store; `RESULT_CACHE_MAX_MB` sizes it (default 32, 0 disables). A cached
    answer's `meta.generated_at` is the time it was served. `GET /health` gains `result_cache`; `/metrics` gains
    `result_cache_lookups_total{outcome}`, `result_cache_bytes` and `result_cache_entries`. (Finding F05.)
    [Running the API, section 5](https://github.com/Investorlift-Engineering/data-services/blob/main/docs/god-mode-api/README.md)
  * **Changed** API 0.10.0: the request pool caps a query at 15 s client-side, above the 10 s server `statement_timeout`, so a
    socket the server stopped answering on fails as 503 `database_unavailable` and leaves the pool instead of holding its slot
    until TCP gives up; the CSV stream carries its own 65 s budget above the 60 s wall clock. The pool keeps two warm
    connections for ten minutes and parses the RDS certificate bundle once per process. CSV exports are written as the header
    followed by about 64 KB chunks; the bytes are unchanged. The startup guard tests that schema `core` exists before asking
    for USAGE on it, so the API starts against a served-tier database with no BatchData feed. (Findings F70, F28, F48, F19, F104.)
  * **Data** The stage-to-prod publish can run inside the VPC from the self-hosted runner: `publish-to-production.yml` (modes
    `no-swap`, `swap-only`, `full`, `reload`; the dump parked in S3 between the two halves; gzip level 1 through the new
    `DUMP_COMPRESS` setting of `publish-dataset.sh`, whose laptop path and `--from-dump` rollback are unchanged). Fenced behind
    the publish login, the runner route and the bucket that devops still has to create (deploy/README.md, "Devops asks from
    the efficiency review (2026-09-13)"). No contract change. (Finding F13.)
  * **Docs** Runbook entries for the stage instance's EBS balance check and the fenced db.m7g.2xlarge refresh bracket, and the planned cutover of the served tier
    to a dedicated RDS. Nothing
    built. (Findings F102, F104.)
  * **Changed** API 0.10.0: `POST /v1/properties/search` sort `id` (the default without a point or `property_id`) orders by market,
    then parcel id, the table's primary key order, so a first page over a wide location and every page after it walk the index
    (stage EXPLAIN over county 04013: cost 291,655 as a parallel seq scan and sort of 1.6M rows, 228 as an index walk). A cursor
    minted under the old single-key order is refused as 400 `invalid_cursor` with "Restart from page 1 without a cursor."; cursors
    stay opaque. (Finding F03.) [Search parcels](https://developers.investorlift.com/api-reference/endpoints/properties-search)
  * **Changed** API 0.10.0, no wire change: every radius is queried as the bounding box plus the sphere-distance bound instead of
    `ST_DWithin`, so distance-sorted pages run as KNN index walks (the 2 mi deals page from cost 33,502 to 7,725, the 20 mi page from
    2,150,105 to 1,573); the `str_status`, `str_business_use`, `bought_on_investorlift`, `source=investorlift`, `buys_wholesale`,
    `buys_at_auction` and `buys_reo` filters are hashed sets over the markets in play instead of per-row probes; nullable sorts render
    `nulls last` natively and the sort indexes are declared to match; distance and primary-key cursors compare as one row; and the CSV
    exports count over the scan alone before streaming (the 20 mi deals count from 2,152,417 to 66,435). The live suites assert the plan
    shapes and that every CSV count equals its streamed rows. (Findings F01, F02, F20, F21, F22.)
  * **Data** Six served indexes reach prod at the next publish through `scripts/sql/indexes.sql` (98 expected): three partial
    `(market, id)` indexes on `gm_investor` for the `buys_wholesale`, `buys_at_auction` and `buys_reo` filters, and three
    trigram indexes on `gm_parcel` for the parcel search's `legal.subdivision`, `owner.name_contains_words` and
    `financing.lender_name_contains` filters. Stage carries them since 2026-09-13. No contract change. (Findings F21, F26.)
  * **Docs** the Developer Agreement (Beta) and the Privacy notice are version `data-services-beta-v2026-09-13`: Allowances are defined in credits with the overage ceiling and clause 3.3 says how a hard
    allowance and an overage plan behave; 1.1 lets an entity be identified by email within 10 Business Days of acceptance;
    1.2 and Notice section 16 make the beta US-only; 11.1 bills the plan fee in advance and overage in arrears and, from
    later the same day, says the Free plan requires a payment method on file that is never charged on Free and is
    charged as 11.1 provides on a change to a paid plan (Robert: a card on file raises the cost of fake accounts), while
    the Notice's Subscription and billing bullet says Stripe holds the payment method for every plan, the Free plan
    included; Notice
    section 13 gains the credit-ledger row (400 days after the last charged request) and section 10 is narrowed to
    investor, property and deal removals. Decided 2026-09-13 by Robert, by delegation to the session, not by counsel, whose
    read is still owed before the first outside key; in force under 21.1 on acceptance or 30 days after the email for every
    account that accepted v2026-09-11; the Free plan's new shape is a 3.1 change and triggers no 11.4 notice. The
    Acceptable use and abuse policy's rules are unchanged; its title block names the new version as the Agreement that
    incorporates it. No contract change.
    [Terms and attribution](https://developers.investorlift.com/guides/terms)
  * **Changed** API 0.11.0 (the section 7 proof of docs/data-services/PLAN-zuplo-pricing.md, 2026-09-13): a request the
    credit ledger refuses at the line (403 `quota_exceeded`, before the query, by the charge script or on a legacy
    `tools/call`) now writes `credits_line` and `credits_period_end` on its usage line, the line and the period the
    counter was checked against, with the four credits figures null (nothing was charged); the first build wrote all six
    null, so the period a request was refused against was only in the problem body's `used` and `line`. No `/v1` contract
    change; the shape is in docs/god-mode-api/README.md and RUNBOOK.md, "Credits".
</Update>

<Update label="2026-09-12" description="API 0.11.0" tags={["Changed"]} rss={{"title":"2026-09-12: API 0.11.0","description":"1 changed"}}>
  * **Changed** API 0.11.0 (the section 7 proof of docs/data-services/PLAN-zuplo-pricing.md, 2026-09-12): a zero-priced
    operation (`GET /v1/deals/summary`, `/deals/cells`, `/properties/resolve`, `/markets/{market}/changes`,
    `/investors/search`, `/dataset`, and an uncharged MCP tool) no longer carries `X-Credits-Charged: 0` through the
    gateway; it carries no credits header, as the public document has always declared. The twelve metered operations keep
    the header, `0` on a re-read and an empty page; an MCP tool result behind the gateway still carries
    `meta.credits: { charged }` either way.
    [API reference](https://developers.investorlift.com/api-reference/introduction)
</Update>

<Update label="2026-09-11" description="API 0.9.0, 0.8.0, 0.7.0, 0.6.2" tags={["Added","Changed","Removed","Data","Docs"]} rss={{"title":"2026-09-11: API 0.9.0, 0.8.0, 0.7.0, 0.6.2","description":"22 added, 11 changed, 1 removed, 2 data, 16 docs"}}>
  * **Added** API 0.9.0: the credit ledger behind the
    gateway lock. Every `/v1` JSON 2xx served through the gateway carries `X-Credits-Charged`: one credit per deal,
    investor or parcel the account receives for the first time plus the route's base (2 on `GET /v1/buyers/match`), 0 on a
    re-read and on an empty page; none on the routes that charge nothing (summary, cells, resolve, the change series,
    `GET /v1/investors/search`, `GET /v1/dataset`); a `tools/call` on `/mcp` charges what its route charges. The count is
    exact per billing period against the line the gateway forwards (`X-Gateway-Customer`, `X-Credits-Grant`, `X-Credits-Line`,
    `X-Credits-Period-End`), a spent period answers 403 `quota_exceeded` with the page unsent, and the Free plan's first
    month cannot be bought twice by re-subscribing. New problem codes: 403 `plan_limit` (the request is over what the plan
    allows: a radius over 5 miles or a viewport over 10 miles across on the Free and Starter plans, a county-wide or
    multi-ZIP parcel search there, an MCP page over the plan's largest, the name-search day cap, with `Retry-After` to
    midnight UTC) and 503 `ledger_unavailable` with `Retry-After: 5` (the ledger is unreachable; nothing was charged, nothing
    was served). Behind the lock a `limit` above the plan's largest page (25 / 25 / 100 / 200 / 500) is lowered to it on
    REST, `page.limit` says what was used, and refused by name on `/mcp`; `GET /v1/markets/{market}/changes` serves at most
    52 weeks on the two small plans. The internal host is untouched. New setting `CREDITS_MODE` (`ledger`, or `free` to
    answer 0 and write nothing); `PUBLIC_HOSTS` now requires `REDIS_URL`, and the chart refuses the public Ingress with
    Redis off. 403 `quota_exceeded` and 403 `subscription_required`, added below as the gateway's own codes, are now also
    raised by the API itself behind the lock (`producer` both; `quota_exceeded` now reads credits and names both stops), so
    the operators' `openapi.json` lists them under a `/v1` 403 again, `payment_overdue` being the one code it leaves out;
    and neither `X-Credits-Charged` nor `X-Rows` is ever on an error response, a 500 raised in the send included.
    [Errors](https://developers.investorlift.com/guides/concepts/errors#plan_limit)

  * **Added** API 0.9.0: `GET /health` carries
    `credits: { backend, mode, errors, steps }`, the credit ledger's backend, the `CREDITS_MODE` lever and the failed
    operations and charge steps since start; `GET /metrics` gains `gateway_requests_total{route}`,
    `credits_steps_total{kind}`, `credits_charged_total{kind}`, `credits_ledger_errors_total{op}` and
    `credits_refusals_total{code}`, every label value present from start and none of them a customer, plan or
    subscription. The usage line (O4) gains `customer`, `credits`, `credits_new`, `credits_kind` and `credits_mode`. No
    `/v1` contract change; the alert rules and the reconciliation queries are in docs/god-mode-api/RUNBOOK.md, "Credits".

  * **Added** API 0.9.0: on `POST /mcp` every tool result served
    through the gateway carries `meta.credits: { charged }`, what the call cost: the credit ledger's figure on a client
    negotiating protocol 2026-07-28 (which also receives `X-Credits-Charged`), and on an initialize-era client the static
    weight the gateway meters (the base plus the asked page clamped to 50 and the plan's largest; base plus one for a
    record; nothing on the coverage, summary and change-series tools), counted against the billing period before the call
    runs and refused at the line with 403 `quota_exceeded`; such a client is billed per call, served or refused, and its
    re-reads are not free. Through the gateway `tools/list` returns fifteen tools: the five listing-agent and
    Investorlift-listing tools are served on the internal host alone, and a call to one there is an unknown-tool error.
    `prompts/list` there returns five prompts: `investorlift_listing_outcome`, whose every step runs on an
    Investorlift-listing tool, is served on the internal host alone, and `market_snapshot` there stops before its
    Investorlift summary step. `api/mcp-manifest.public.json` is that catalogue and those prompts and is what the site publishes; the server instructions, the concepts
    resource and every charged tool's description say what a call costs, and behind the gateway the instructions are the
    public text, which says what is not served on that host and names no scope. Nothing changes on the internal host.
    [The tools](https://developers.investorlift.com/mcp/tools)

  * **Added** API 0.9.0: the problem code 403 `payment_overdue`,
    returned by the gateway at api.investorlift.com when a subscription's last payment failed and the seven-day grace
    period has passed (the key is blocked until the card is updated under Manage Billing in the console; nothing is
    charged for a refused request), never by the API itself; in the catalogue and on every 403 of the public document so
    its `type` URL lands on the Errors page like every other code.
    [Errors](https://developers.investorlift.com/guides/concepts/errors#payment_overdue)

  * **Added** to the public document the response headers `X-Credits-Charged` (set by the origin: the credits the response
    cost) and `X-Credits-Remaining` (set at the edge: the balance after it, absent on Enterprise), declared on every 2xx of
    the twelve metered operations (the deal, investor, buyer-match, property and parcel routes; not cells, summary, resolve,
    the change series, investor search or the dataset probe).
    [API reference](https://developers.investorlift.com/api-reference/introduction)

  * **Changed** API 0.9.0: the description of 403 `quota_exceeded`
    and its What-to-do row on the Errors page speak of credits rather than requests and rows and name both stops: the
    plan's credits for the billing period (the allowance, or on Growth and Scale the overage ceiling) and the Free plan's
    lifetime credits (`stop` `budget` in the body); the body carries `used` and `line`. The code, the status and who
    raises it are unchanged.
    [Errors](https://developers.investorlift.com/guides/concepts/errors#quota_exceeded)

  * **Changed** API 0.9.0 (the section 7 proof of docs/data-services/PLAN-zuplo-pricing.md): a `tools/call` from an
    initialize-era MCP client whose static weight would pass the period's line is refused 403 `quota_exceeded` before it
    is counted or metered; the first build refused only once the counter had reached the line, so a call one credit short
    was served and counted past it.
    [Errors over MCP](https://developers.investorlift.com/mcp/errors)

  * **Added** API 0.9.0 (the same proof): the usage line gains `credits_line` and `credits_period_end`, the forwarded
    `X-Credits-Line` and `X-Credits-Period-End` as the credit ledger's period counter used them, so a period reconciles and
    a dispute is read from the line itself; null where no counter was read. No `/v1` contract change; the fields and the
    queries are in docs/god-mode-api/README.md and RUNBOOK.md, "Credits".

  * **Changed** API 0.9.0 (the same proof, row 9): a client that has left before the credit ledger step is not charged.
    On `/v1` and on a modern `tools/call`, a charge whose request the client abandoned during the query (a `499` in the
    origin's log) no longer runs: nothing is written, no `X-Credits-Charged` is set, the 499 usage line reads `credits`
    null, and the skipped step is counted under the new series `credits_skipped_total{reason="client_gone"}`; a legacy
    `tools/call` the client abandoned is not counted at its weight either. A client that leaves while the step is in
    flight gets a 499 line that waits for the step (at most one second) and carries what it took, the ledger drift the
    runbook reports; the first build could write that line before the step and charge behind it, leaving the origin's
    counter one page ahead of the gateway's meter. No `/v1` contract change; docs/god-mode-api/README.md section 7 and
    RUNBOOK.md, "Credits".

  * **Docs** (the same proof): Plans and limits, Errors and Errors over MCP say that a re-read is free while the plan has
    credit, and that at the line of a hard plan every charged route answers 403 `quota_exceeded`, re-reads included, until
    the period resets or the plan changes, while the zero-priced routes and the MCP handshake keep answering; the API
    refuses before it prices the page, and the gateway's own check refuses too. No contract change.
    [Plans and limits](https://developers.investorlift.com/guides/plans-and-limits)

  * **Changed** what a deals-only key receives on the four parcel payloads (`POST /v1/properties/search`,
    `GET /v1/properties/{property_id}/financing`, `/history` and `/listing-history`, and their MCP tools), served on the
    internal host since API 0.8.0: the contact-gated keys are left out of the object instead of carried as null, and the
    payloads no longer carry `contact_redacted`. The keys are the search row's `owner.names` and `owner.mailing`, a
    mortgage's `borrowers`, a lien's `parties`, a listing cycle's `agent_name` and `agent_license` (the licence number
    joins the projection the same day: it is a contact field on the listing block and in the history payload already) and
    the person-naming columns of a history event's `before`, `after` and `deed` payloads. A client that tested
    `contact_redacted === true` or `owner.names === null` on a deals-only key now tests for the missing key. A key with
    the contact scope receives every field as before, with `contact_redacted: false`. The published examples, the MCP tool
    descriptions, the MCP instructions and `meta.contact` follow the same rule.
    [Parcel search](https://developers.investorlift.com/api-reference/endpoints/properties-search)

  * **Removed** from the public document (`openapi.public.json`, the reference at developers.investorlift.com) on
    2026-09-11, before the first outside key: `GET /v1/deals/pins`, `GET /v1/agents/search`, `GET /v1/agents/{id}`,
    `GET /v1/agents/{id}/listings`, `GET /v1/wholesale-listings`, `GET /v1/wholesale-listings/{id}`,
    `GET /v1/investors/{id}/wholesale-purchases`, `GET /v1/wholesalers/search`, `GET /v1/wholesalers/{id}` and
    `GET /v1/wholesalers/{id}/listings`, ten operations the developer tier never sells, with their tags, schemas and reference pages; the two codes only
    they raise, 422 `pin_cap_exceeded` and 422 `agents_unavailable`, move to the Errors page's partners section. The
    routes stay on the internal host and in the operators' `openapi.json`; the blocks the served rows carry
    (`wholesale_listing`, `wholesale_purchases`, `wholesale_listings[]`, the listing agents) are unchanged.
    `GET /v1/str-parcels` stays in the public document under a By contract tag, sold per jurisdiction under an order form.
    [API reference](https://developers.investorlift.com/api-reference/introduction)

  * **Docs**: Plans and limits is written around credits: what
    counts (one credit per deal, investor or parcel the first time the account receives it, on any route; 0 on a re-read,
    on cells, summary, resolve, the change series, `GET /v1/dataset` and a sub-resource of a parcel already held; 2 plus 1
    per new investor on the buyers match; the name search uncharged and capped per day), the five plans with their credits
    a month (Free's first month included), what happens past the allowance (a stop, or on Growth and Scale an overage of up
    to three times the allowance), requests a minute and a day, the largest page, how wide a search may be on Free and
    Starter, the seven-day grace period and the two credit headers; no price is printed. Rate limits, the envelope, the
    spreadsheet walkthrough, Pricing, Your keys and the three MCP pages follow it: `X-Rows` is the size of the body, a
    `tools/call` costs what its route costs on a client negotiating 2026-07-28 and a legacy client is billed per call,
    refused or served; Errors over MCP gains `plan_limit`, `quota_exceeded` and `ledger_unavailable` rows. No contract
    change.
    [Plans and limits](https://developers.investorlift.com/guides/plans-and-limits)

  * **Changed** API 0.9.0: `meta.terms` on every `/v1` and `/mcp` response is now a short attribution in the form of the
    line the Developer Agreement requires developers to show beside the Data ("Data: Investorlift Data Services.
    Public-record and MLS listing data licensed through BatchData; ...") and still ends by pointing at the Terms and
    attribution page, which carries the full required line with the city credits and now the Investorlift Data Services
    Developer Agreement (Beta), version `data-services-beta-v2026-09-11`, beside the new Privacy notice and Acceptable use
    and abuse policy pages. Every page names [support@investorlift.com](mailto:support@investorlift.com) for removal requests; the MCP concepts text points at the Privacy notice's removal section instead, because no MCP surface may carry an email address.
    The link at the end of the sentence follows `DOCS_URL`, like the problem `type` URLs and `GET /docs`, instead of a
    hard-coded host (finding 5 of the PR #13 review).
    [Terms and attribution](https://developers.investorlift.com/guides/terms)

  * **Added** API 0.9.0: `GET /v1/dataset`, the freshness probe:
    `dataset_version`, `data_end`, the counties and `loaded_at` of every loaded market, plus the one-line version label,
    read from the market cache with no query; any key; carries an `ETag` for a `304` handshake.
    [Read the dataset version](https://developers.investorlift.com/api-reference/endpoints/dataset)

  * **Added** the response headers `X-Rows` (rows in the body), `X-Dataset-Version` and `X-Data-End` (`market=value`,
    comma-joined) on every `/v1` JSON response, and in the headers a browser page may read; `/mcp` already carried the
    last two.
    [Coverage and freshness](https://developers.investorlift.com/guides/concepts/coverage)

  * **Added** the problem codes 403 `quota_exceeded` and 403 `subscription_required`: returned by the gateway at
    api.investorlift.com when a plan's monthly budget is spent or the key has no plan, never by the API itself, in the
    catalogue so their `type` URLs land on the Errors page like every other code. `unauthorized` and `rate_limited` are
    now marked as sent by both the gateway and the API. The MCP resource `godmode://problems` carries the whole catalogue,
    the two gateway codes included, and its description now says so and points at each code's `producer`.
    [Errors](https://developers.investorlift.com/guides/concepts/errors#quota_exceeded)

  * **Added** `producer` on every entry of `GET /docs/problems` and of the `godmode://problems` resource: `origin`,
    `gateway` or `both`, the fact the Errors page prints under each code, so a program can tell which side answers a code
    without reading its English. The operators' `openapi.json` no longer lists the two gateway codes under a `/v1` 403 or
    shows their bodies, since the internal host never sends them; the public document does.
    [Errors](https://developers.investorlift.com/guides/concepts/errors#quota_exceeded)

  * **Added** the settings `PUBLIC_HOSTS`, `GATEWAY_SECRETS`, `GATEWAY_KEY_NAMES` (the gateway lock: on a public origin
    hostname every request without the gateway's token is 404, the open routes are 404 with it, CSV is 406, and only a
    gateway key without a contact scope and with `X-On-Behalf-Of` is served), `ON_BEHALF_OF_RPM` (the 240 a minute
    per key and user, now a setting), `GATEWAY_ON_BEHALF_OF_RPM` (1,800 behind the lock) and `EDGE_REQUEST_ID_HEADER`
    (`zp-rid`). Every one is empty or at today's value by default, so the internal host behaves as before. A
    `GATEWAY_KEY_NAMES` entry with no `API_KEYS` record, or whose record carries `contact` or `mcp_contact`, stops the API
    at startup rather than refusing every request after the flip.

  * **Added** a `usage: true` log line per `/v1` and `/mcp` response with the operation, the outcome, the key, the
    developer and, behind the gateway lock, the consumer, plan and subscription the gateway named: the origin's side of
    the monthly reconciliation against the gateway's metering ledger. No contract change.

  * **Changed** `meta.terms` on every `/v1` and `/mcp` response now ends by pointing at the Terms and attribution page of
    the documentation site (`https://developers.investorlift.com/guides/terms`) instead of at a file path inside the
    API's repository, which a reader of the public host cannot open. The attribution sentence before it is unchanged.
    [Terms and attribution](https://developers.investorlift.com/guides/terms)

  * **Docs** the published `openapi.json` is now the public tier's document (`api/openapi.public.json`): one server,
    `https://api.investorlift.com`, the `/v1` routes alone, no header the gateway sets, no CSV, and none of the fields a
    deals-only key never sees; every runnable request on the site names that host; the machine-readable error catalogue
    is `problems.json` on the site.
    [Authentication](https://developers.investorlift.com/guides/concepts/authentication)

  * **Docs** the site is written for a developer with a `zpka_` key from the developer console: three landing pages
    (`/get-a-key`, `/keys`, `/pricing`) are the only pages that name the console, the navbar's one button is "Get an API
    key", and a new Plans and limits page states what a plan allows each month, each minute and per page, what counts as
    a request, the `403 quota_exceeded` and `429 rate_limited` it meets at a limit, fair use and support; no price is
    printed on the site, the console's pricing page is the one place.
    [Plans and limits](https://developers.investorlift.com/guides/plans-and-limits)

  * **Docs** the `gm_` keys, the `contact` and `mcp_contact` scopes, `X-On-Behalf-Of`, the five internal rate-limit
    buckets and their per-response limit headers, the CSV export and the bring-your-own request id moved to a "Partners and
    staff" subsection at the foot of the page they were on (Authentication, Rate limits, Request ids, Give me a
    spreadsheet, Keys and headers, Errors over MCP); the public reader's text above them describes the one host and the
    one key. The MCP pages name `https://api.investorlift.com/mcp` in every client entry and the Messages API connector
    as available.
    [Authentication](https://developers.investorlift.com/guides/concepts/authentication)

  * **Docs** the Errors page confines the four codes a `zpka_` key never meets (`on_behalf_of_required`, `scope_required`,
    `csv_cap_exceeded`, `not_ready`) to its closing Partners and staff section, each marked as the internal host's alone,
    and `problems.json` on the site carries the public tier's codes only, each with its `producer`; `GET /docs/problems`
    on the internal host still serves every code.
    [Errors](https://developers.investorlift.com/guides/concepts/errors#partners-and-staff)

  * **Docs** the public document carries the parcel family of 0.8.0 (`POST /v1/properties/search`, the financing, permits,
    history and listing-history sub-resources, `GET /v1/markets/{market}/changes`) as a `zpka_` key receives it: without
    the owner's names and mailing, the borrowers, the lien parties and the agent named on a listing cycle, and without the
    owner-name filter, which only a contact key may send. The search page's request is generated from its JSON body and
    rendered against the public host like every other.
    [Property search](https://developers.investorlift.com/api-reference/endpoints/properties-search)

  * **Added** API 0.8.0: the parcel products over
    `data_services.gm_parcel` and its sidecars. `POST /v1/properties/search` (a location, thirteen filter groups, quicklists, the blocks
    named in `datasets`, keyset paging, `count_only`), `GET /v1/properties/{property_id}/financing` (the AVM with equity and LTV, every
    open lien, the recorded mortgage history and the involuntary liens), `GET /v1/properties/{property_id}/permits` (every building
    permit with the normalised tags and the per-parcel aggregates), `GET /v1/properties/{property_id}/history` (the week-by-week
    change timeline), `GET /v1/properties/{property_id}/listing-history` (the listing cycles) and `GET /v1/markets/{market}/changes`
    (the weekly change series of one ZIP). Borrower names on the financing route and the agent named on a listing cycle need the `contact`
    scope, like owner names. The publish path carries the seventeen new tables since 2026-09-11 (22c, `scripts/sql`, `publish-dataset.sh`);
    they reach prod with the next stage-to-prod copy.
    [Property search](https://developers.investorlift.com/api-reference/endpoints/properties-search)

  * **Added** `meta.dated[]` on the envelope and `meta.coverage[].parcel_as_of` / `meta.coverage[].parcel`: the financing, lien and
    valuation blocks are a snapshot valued at 2026-06-25 (the Recorder & Mortgage and AVM datasets are not in the current BatchData
    delivery) and every response that carries one says so, one `meta.dated[]` entry per block (`valuation`, `financing`, `liens`) with its
    as-of date; `require_current: true` refuses with the new 422 `dated_refused`.
    [Dated data](https://developers.investorlift.com/guides/concepts/dated-data)

  * **Added** `contact_redacted` on `PropertyHistory`: the event payloads' contact columns (owner names, mailing address, listing agent
    names, licences and keys, the listing office, borrowers, lien parties) are null without the contact scope, on REST and on
    `godmode_property_history`, and a contact key's read is audited like the financing and listing-history routes.

  * **Added** five problem codes: 422 `parcels_unavailable`, 422 `dated_refused`, 422 `history_unavailable`, 400 `quicklist_unavailable`,
    400 `dataset_unavailable`.
    [Errors](https://developers.investorlift.com/guides/concepts/errors)

  * **Added** six MCP tools over the same plans (`godmode_property_search`, `godmode_property_financing`, `godmode_property_permits`,
    `godmode_property_history`, `godmode_listing_history`, `godmode_market_changes`): twenty tools in the catalogue. The MCP
    `meta.coverage[]` row every tool carries keeps its ten fields; the parcel layer's `parcel_as_of` is read from `godmode_coverage`
    and the `godmode://markets` resource, not from every result. The financing, history and listing-history tools can carry contact
    fields under the same gate as the four existing single-record lookups.
    [The twenty tools](https://developers.investorlift.com/mcp/tools)

  * **Changed** `godmode_investors_nearby` concise rows (the default format) no longer carry the contact block's four identifying
    fields (`contact.primary_address`, `contact.mailing_addresses`, `contact.person_members`, `contact.skip_trace_targets`). A list
    tool never serves contact, so on this row they were always null; dropping them keeps the default page of 20 rows under the
    20,480-byte concise budget (20,536 bytes at the Phoenix golden point on 2026-09-11). The seven contact counts and booleans,
    `contact_redacted` and `meta.contact` are unchanged, and the detailed format still carries the four fields.
    [The twenty tools](https://developers.investorlift.com/mcp/tools)

  * **Docs** the parcel search page's request is generated and checked against the route's body schema; the coverage, envelope,
    authentication and MCP overview pages and the MCP worked examples cover the parcel family.
    [Property search](https://developers.investorlift.com/api-reference/endpoints/properties-search)

  * **Data** Phoenix (market `phx`): the permit snapshot effective 2026-09-01 (2,377,484 permits on 663,041 Maricopa parcels), the
    financing slice valued at 2026-06-25, the Owner Profile block as of 2026-09-09, and the history lake over ZIP 85251 from the
    2025-07-24 FULL onward (the counts are in `pipeline/scottsdale-mvp/reports/build-log.md`).

  * **Added** API 0.7.0: every investor carries `auction`, the
    foreclosure-auction and REO purchase block. On `InvestorListItem` (so on every list row and match row) the nine-field
    summary: auction and REO purchase counts, the 24-month counts and the two active flags, the last purchase days, the
    resale share. On `InvestorProfile` the full block: the kinds bought at (`TRUSTEE_SALE`, `SHERIFF_SALE`, and the
    shown-but-not-counted `HOA_TRUSTEE_SALE`, `TAX_SALE`, `TAX_LIEN_DEED`), the Texas venue (Auction.com, ServiceLink,
    Xome), the outcomes at 12 months, the median discount to the later resale, the REO grantor classes. Null where the
    market's counts are not published.
    [The Investor object](https://developers.investorlift.com/api-reference/objects/investor)

  * **Added** `bought_auction_kind` on every `Deal`: the auction kind of the purchase deed when it was a third-party auction
    purchase, null otherwise; and the cell `Bought at auction` on the deals CSV and the investor history CSV. The investors
    CSV and the match CSV gain seven auction / REO cells. None is a contact column.
    [The Deal object](https://developers.investorlift.com/api-reference/objects/deal)

  * **Added** the shared filters `buys_at_auction` and `buys_reo` (investor level) and `bought_auction_kind` (deal level),
    and the problem code 422 `auction_unavailable`: a market whose registry predates the counts answers it to the three
    filters, and `meta.coverage[].auction_counted` (also on `GET /health`) says so in advance.
    [Filters](https://developers.investorlift.com/guides/concepts/filters)

  * **Added** MCP: `godmode_deals_summary`, `godmode_deals_search`, `godmode_investors_nearby` and `godmode_buyers_match`
    take the three filters (the two booleans as JSON booleans); every coverage entry carries `auction_counted`. The concise
    deal row omits `bought_auction_kind` and the concise investor list row omits `investor.auction` (the detailed format
    carries both). No resource or prompt was added or removed.
    [The MCP endpoint](https://developers.investorlift.com/mcp/overview)

  * **Data** Phoenix and Houston rebuilt on stage on 2026-09-11 with the auction and REO counts (registry runs 9 and 5;
    `auction_counted` true for both). Built on the branch as 0.5.0 on 2026-09-10 and renumbered at the rebase onto main
    after the short-term rental release (0.5.0) and the MCP release (0.6.0).
    [Coverage and freshness](https://developers.investorlift.com/guides/concepts/coverage)

  * **Changed** API 0.6.2: every problem body's `type` URL now points at the Errors page of the documentation site,
    anchored at the code (`https://developers.investorlift.com/guides/concepts/errors#invalid_cursor`), instead of at
    `GET /docs/problems` on the API host. The `code` you switch on is unchanged, `GET /docs/problems` still serves the
    machine-readable catalogue, and its entries carry the same new URLs.
    [Errors](https://developers.investorlift.com/guides/concepts/errors)

  * **Changed** every operation description in `openapi.json` is now one paragraph, capped at 600 characters and checked
    in CI. The depth moved to a page per endpoint on the documentation site, linked from the description.
    [API reference](https://developers.investorlift.com/api-reference/introduction)

  * **Added** one example error body per code on every problem response in `openapi.json`, so the reference shows a real
    400 and a real 429 beside the 200.
    [API reference](https://developers.investorlift.com/api-reference/introduction)

  * **Docs** the reference is organised objects first: one page per object (Deal, Investor, Property, Listing agent,
    Investorlift listing, Wholesaler, Short-term rental parcel, the response envelope), then the endpoints that return
    them.
    [Objects](https://developers.investorlift.com/api-reference/objects/deal)

  * **Docs** new Quickstart: get a key, make one call, read the answer.
    [Quickstart](https://developers.investorlift.com/guides/quickstart)

  * **Docs** every request on the site is now runnable in curl, Node and Python, generated from `openapi.json` by
    `npm run docs:sync` and checked in CI, so a renamed parameter cannot survive in a guide.
    [Quickstart](https://developers.investorlift.com/guides/quickstart)

  * **Docs** "Calling the API" is split into one page per concept: authentication and scopes, the response envelope,
    pagination, filters, coverage and freshness, rate limits, errors, request ids and versioning. The old URLs redirect.
    [Core concepts](https://developers.investorlift.com/guides/concepts/authentication)

  * **Docs** the Errors page carries one anchored section per code, with the body it produces, what causes it and what to
    do. It is what every error's `type` URL now opens.
    [Errors](https://developers.investorlift.com/guides/concepts/errors)

  * **Docs** pages can be copied as Markdown or opened in Claude, ChatGPT and Cursor from the menu on every page, and the
    site serves its own documentation search server at `/mcp` beside the data server on the API host.
    [The MCP endpoint](https://developers.investorlift.com/mcp/overview)

  * **Docs** five corrections an audit of every reference page against the schemas found: an error body carries eight
    fields, `errors?` included; a wholesaler's `n_none` is the NONE verdict, which is wider than "no closing deed", and
    that verdict has four cases, two of which do have a recorded deed; `chain_certain` sits in the deed block; and an
    agent profile's `n_self_listings` counts HIGH-confidence investor links only.
    [Investorlift listings](https://developers.investorlift.com/api-reference/objects/wholesale-listing)

  * **Docs** a page no longer restates a page size or a cap that the parameter table on the same page already shows, and
    a number the code owns is named where it lives rather than copied.
    [API reference](https://developers.investorlift.com/api-reference/introduction)

  * **Changed** every published error example now carries the path of the operation it sits under. Examples were keyed by
    HTTP status alone, so the 503 shown on a deals call named `/ready` as its `instance` and the 422 on agent search named
    `/v1/deals/pins`; 529 of the 587 published bodies named another route. `not_ready` is no longer offered under any
    route but the readiness probe.
    [API reference](https://developers.investorlift.com/api-reference/introduction)

  * **Changed** the codes listed in each problem response's description are derived from the catalogue rather than typed
    out a second time, so a new code reaches the reference with its example, its section and its status description
    together.
    [Errors](https://developers.investorlift.com/guides/concepts/errors)
</Update>

<Update label="2026-09-10" description="API 0.6.0, 0.6.1" tags={["Added","Changed","Docs"]} rss={{"title":"2026-09-10: API 0.6.0, 0.6.1","description":"6 added, 2 changed, 2 docs"}}>
  * **Added** API 0.6.0: `POST /mcp`, a Model Context Protocol server on the same host, with the same bearer key,
    `X-On-Behalf-Of` rule, rate limits and request line as `/v1` (the 2026-07-28 revision through the official TypeScript
    SDK v2, stateless; `GET` and `DELETE` answer 405, any `Origin` header 403, batched JSON-RPC arrays 400; hidden from
    `/docs` and `openapi.json`). Fourteen read-only tools, four resources, six prompts.
    [The MCP endpoint](https://developers.investorlift.com/mcp/overview)
  * **Added** the scope value `mcp_contact`: contact fields travel over MCP only on single-record tools, only for a key
    carrying it, and only when its `X-On-Behalf-Of` names a person. Metered by a new bucket of 30 per minute per (key,
    user). A key carrying either contact scope must send the header.
    [Keys and headers](https://developers.investorlift.com/mcp/keys)
  * **Added** `recovery`, one plain sentence per problem code, on `GET /docs/problems` and appended to every MCP error
    result.
    [Errors](https://developers.investorlift.com/guides/concepts/errors)
  * **Added** `meta.contact` on every MCP tool result: `not_on_lists`, `redacted_no_scope`, `served` or `none_on_record`.
    [The MCP endpoint](https://developers.investorlift.com/mcp/overview)
  * **Changed** the request line gains `mcp_method`, `tool_name`, `outcome` and `response_format` on `/mcp`, with new
    metrics `mcp_requests_total`, `mcp_request_duration_seconds`, `mcp_contact_served_total` and
    `rate_limit_rejections_total{bucket="mcp_contact"}`. The in-flight slot, the request line and the `http_*` metrics now
    also cover a client that disconnects after its request body was consumed, through the response's `close` event.
  * **Changed** the query part of every `/v1` handler moved into the plan functions of `api/src/v1/plans/`, shared with the
    MCP tools. No wire change, and `openapi.json` is unchanged by the move.
  * **Added** API 0.6.1: `GET /docs` answers 302 to `DOCS_URL` (new setting) instead of serving Scalar, which is removed.
    `GET /openapi.json` and `GET /docs/problems` are unchanged.
  * **Added** the setting `CORS_ORIGINS`: the browser origins allowed on the read routes, none by default. `/mcp` stays
    closed to any browser origin.
  * **Docs** the rendered reference moved to a documentation site built from `docs/site/` and published at
    [https://developers.investorlift.com](https://developers.investorlift.com). New script `npm run docs:sync` writes its generated pages; CI checks them like
    `openapi.json` and validates the site.
    [Introduction](https://developers.investorlift.com)
  * **Docs** `openapi.json` lists production and stage in `servers[]`, hides the four ops probes from the site, names its
    tag groups, writes every path template in inline code, and publishes the recorded examples in the deals-only shape
    with every contact-scope field nulled.
</Update>

<Update label="2026-09-09" description="API 0.5.0, 0.4.0" tags={["Added","Changed"]} rss={{"title":"2026-09-09: API 0.5.0, 0.4.0","description":"8 added, 3 changed"}}>
  * **Added** API 0.5.0: the cities' short-term rental licence and permit rolls. New route `GET /v1/str-parcels`
    (geometry, `str_status` default LICENSED,PENDING, `owner_attributed`, `business_use`, `has_investor`, `operator`,
    `jurisdiction`, sort, cursor, CSV).
    [List short-term rental parcels](https://developers.investorlift.com/api-reference/endpoints/str-parcels)
  * **Added** `short_term_rental` on every Property and on every hold Deal row, `str` on `InvestorProfile`,
    `InvestorListRow` and `BuyerMatch`, and `str_as_of` with `str.jurisdictions` on `meta.coverage[]` and `/health`. All
    null where the tables are not published.
    [The short-term rental parcel](https://developers.investorlift.com/api-reference/objects/str-parcel)
  * **Added** the filters `str_status` on `/v1/deals`, its summary and CSV and on `/v1/investors/{id}/deals`, and
    `str_operator` and `str_holdings_min` in the shared filters. New problem code 422 `str_unavailable`.
    [Filters in plain words](https://developers.investorlift.com/guides/concepts/filters)
  * **Added** `str_business_use` on the same deal lists: hold rows whose licence is a business use attributed to the
    current owner, or the rest with `false`. Measured over LICENSED and PENDING records unless `str_status` says
    otherwise.
  * **Changed** the owner identity rule of `GET /v1/properties/{property_id}` (decision D2): names and mailing are also
    served when the parcel carries a business-use licence attributed to the owner by a date or a name, never by
    assumption. The `not_an_investor_hold` reason and the RENTAL\_SIGNAL holding-kind description are reworded
    accordingly.
    [Read one parcel](https://developers.investorlift.com/api-reference/endpoints/properties-get)
  * **Added** five short-term rental cells to the deal CSVs and two to the investors CSV. None is a contact column.
  * **Changed** `str_status` accepts LICENSED, PENDING and EXPIRED only; NONE stays on every hold row's block but is not
    filterable in this release (400 for the value). No query shape made its predicate over the 868k-row
    `gm_str_coverage` cold-safe on stage: 17.6 s at best against the 10 s statement timeout. A build-time column on
    `gm_deal` is the fix for a later release.
  * **Added** API 0.4.0: every Investorlift listing is served as a wholesale transaction judged by the county deeds. New
    routes `GET /v1/wholesale-listings`, `GET /v1/wholesale-listings/{id}`, `GET /v1/wholesalers/search`,
    `GET /v1/wholesalers/{id}`, `GET /v1/wholesalers/{id}/listings` and `GET /v1/investors/{id}/wholesale-purchases`; new
    id kinds `wl_<32 hex>` and `wsr_<12 hex>`.
    [List Investorlift listings](https://developers.investorlift.com/api-reference/endpoints/wholesale-listings)
  * **Added** `wholesale_listing` on every Deal, `wholesale_purchases` on `InvestorProfile`, `InvestorListRow` and
    `BuyerMatch`, `wholesale_listings[]` on Property, and `wholesale_as_of` on `meta.coverage[]` and `/health`. All null
    where the tables are not published.
    [The Investorlift listing](https://developers.investorlift.com/api-reference/objects/wholesale-listing)
  * **Added** the filters `bought_on_investorlift=true` on `/v1/deals` and `source=investorlift` on
    `/v1/investors/{id}/deals`. New problem code 422 `wholesale_unavailable`.
  * **Changed** `meta.terms` now reads "public records and Investorlift marketplace records".
    [Terms and attribution](https://developers.investorlift.com/guides/terms)
</Update>

<Update label="2026-09-04" tags={["Removed"]} rss={{"title":"2026-09-04","description":"1 removed"}}>
  * **Removed** `candidate_class`, `tier` and `sub_tier` from the full `Deal` object on `/v1/deals`, `/v1/deals/{id}`,
    `/v1/investors/{id}/deals` and the `deals[]` of the two property routes. They were classifier diagnostics marked
    "safe to ignore" in the schema and nothing read them; `kind` carries the meaning. The trimmed `sample_deals[]` on a
    match row and the CSV exports never carried them. Part of the served-column cut, commit c466b50.
    [The Deal object](https://developers.investorlift.com/api-reference/objects/deal)
</Update>


## Related topics

- [Versioning](/guides/concepts/versioning.md)
- [List one lender's loans](/api-reference/endpoints/lenders-loans.md)
- [API reference](/api-reference/introduction.md)
- [Plans and limits](/guides/plans-and-limits.md)
