Deterministic, source-cited rental property tools: turnover estimates and deposit rules by US state.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Looks up the security deposit rules (deposits only) for one US state, with a covered city's deposit ordinance (Chicago, San Francisco, Berkeley, Los Angeles, Seattle, Portland, Minneapolis, Saint Paul). Inputs: state (two-letter code); optional city. Returns the deposit return deadline and what starts it, itemization and documentation duties, allowed deductions and depreciation, deposit interest, penalties, the deposit cap and recent changes, with citations. Not for: settling a specific move-out (deposit_settlement_settle does that), evictions, unpaid-rent collection, rent increases or rent control, lease renewal or notice periods, legal advice. Deterministic and source-cited: the same input gives the same result.
US state, 2-letter code or name (e.g. 'TX').
Optional city for its overlay (e.g. 'Chicago').
Optional date (YYYY-MM-DD): show the law in force on that day, for a past move-out the day the tenancy ended. Omitted: the current law.
Also return the run record and input/result hashes (an audit trail). Default false.
Optional, with expect_ruleset. True asks us to answer under the ruleset version you named even when the rules have changed since, from that version's signed bundle; the result says so in served_from_bundle. We refuse a version older than 12 months, one a later correction or law change replaced for your state, or one we have no bundle for. Default false: the call fails whenever the rules have changed.
Optional. The ruleset version you built against, as results carry it in provenance.ruleset_version (e.g. 'sd-2026.11.14'), or its sha256: hash. If the rules have changed since, the call fails (nothing computed, nothing charged) and says what is in force now, so you upgrade on your own schedule. Leave it out to answer under the current rules.
Looks up the security deposit rules (deposits only) for one US state, with a covered city's deposit ordinance (Chicago, San Francisco, Berkeley, Los Angeles, Seattle, Portland, Minneapolis, Saint Paul). Inputs: state (two-letter code); optional city. Returns the deposit return deadline and what starts it, itemization and documentation duties, allowed deductions and depreciation, deposit interest, penalties, the deposit cap and recent changes, with citations. Not for: settling a specific move-out (deposit_settlement_settle does that), evictions, unpaid-rent collection, rent increases or rent control, lease renewal or notice periods, legal advice. Deterministic and source-cited: the same input gives the same result.
| Argument | Type | Description |
|---|---|---|
| city | string | Optional city for its overlay (e.g. 'Chicago'). |
| as_of | string | Optional date (YYYY-MM-DD): show the law in force on that day, for a past move-out the day the tenancy ended. Omitted: the current law. |
| audit | boolean | Also return the run record and input/result hashes (an audit trail). Default false. |
| state* | string | US state, 2-letter code or name (e.g. 'TX'). |
| serve_pinned | boolean | Optional, with expect_ruleset. True asks us to answer under the ruleset version you named even when the rules have changed since, from that version's signed bundle; the result says so in served_from_bundle. We refuse a version older than 12 months, one a later correction or law change replaced for your state, or one we have no bundle for. Default false: the call fails whenever the rules have changed. |
| expect_ruleset | string | Optional. The ruleset version you built against, as results carry it in provenance.ruleset_version (e.g. 'sd-2026.11.14'), or its sha256: hash. If the rules have changed since, the call fails (nothing computed, nothing charged) and says what is in force now, so you upgrade on your own schedule. Leave it out to answer under the current rules. |
{
"type": "object",
"examples": [
{
"city": "Chicago",
"state": "IL"
}
],
"required": [
"state"
],
"properties": {
"city": {
"type": "string",
"maxLength": 80,
"description": "Optional city for its overlay (e.g. 'Chicago')."
},
"as_of": {
"type": "string",
"format": "date",
"description": "Optional date (YYYY-MM-DD): show the law in force on that day, for a past move-out the day the tenancy ended. Omitted: the current law."
},
"audit": {
"type": "boolean",
"default": false,
"description": "Also return the run record and input/result hashes (an audit trail). Default false."
},
"state": {
"type": "string",
"description": "US state, 2-letter code or name (e.g. 'TX')."
},
"serve_pinned": {
"type": "boolean",
"default": false,
"description": "Optional, with expect_ruleset. True asks us to answer under the ruleset version you named even when the rules have changed since, from that version's signed bundle; the result says so in served_from_bundle. We refuse a version older than 12 months, one a later correction or law change replaced for your state, or one we have no bundle for. Default false: the call fails whenever the rules have changed."
},
"expect_ruleset": {
"type": "string",
"description": "Optional. The ruleset version you built against, as results carry it in provenance.ruleset_version (e.g. 'sd-2026.11.14'), or its sha256: hash. If the rules have changed since, the call fails (nothing computed, nothing charged) and says what is in force now, so you upgrade on your own schedule. Leave it out to answer under the current rules."
}
}
}Settles a US residential security deposit after move-out under the state's statute (50 states and DC) and a covered city's ordinance. Inputs: state, deposit_usd, vacated_on; optional city, monthly_rent_usd, move_in_on, deductions (category, amount, item age, whether the tenant caused it), the dates that start a state's clock, and as_of. Returns the return deadline and its status, each deduction allowed or not (with depreciation), interest owed, refund due, penalty exposure, a draft itemized statement and a citation for every rule. Not for: legal advice, tenant demand letters, repair cost estimates, pet deposits, rentals outside the US. Deterministic and source-cited: the same input gives the same result.
| Argument | Type | Description |
|---|---|---|
| city | string | City, for a city overlay where covered (e.g. 'Chicago'). Optional. |
| as_of | string | Today's date (YYYY-MM-DD) for the deadline status. Defaults to today (UTC); send it for repeatable results. |
| audit | boolean | Also return the run record and input/result hashes (an audit trail). Default false. |
| state* | string | US state, 2-letter code (e.g. 'CA'); a state name also works. |
| county | string | Tennessee only: the county the unit is in (the landlord-tenant act applies only in counties over 75,000 by the 2010 census, 66-28-102(a)). Taken from the city when it lies in one county. |
The issue-code catalog for rental-unit turnover findings (group.issue codes such as carpet.pet_urine). Inputs, all optional: q (search words or a code), group (e.g. flooring), limit, include_aliases; query and area are older names for q and group. With no arguments it returns every code in short form (code, name, group, default extent, refer-out). With q it returns the best matches with description, aliases, not_when, rooms and examples. Returns the codes, the match count, every group, and general.manual_quote, the code for a condition no other code fits. Not for: cost estimates, legal or security-deposit questions, work other than rental turnover. Deterministic and source-cited: the same input gives the same result.
| Argument | Type | Description |
|---|---|---|
| q | string | Words to search for in the codes, names, aliases, descriptions and examples, e.g. 'pet stain carpet'. Hits come back with full detail. Leave out to list every code in short form. |
| area | string | Older name for group; use group. |
| audit | boolean | Also return the run record and input/result hashes (an audit trail). Default false. |
| group | string | Only codes in this group, e.g. 'flooring' or 'walls' (a part of the unit, not a room). `groups` in the answer lists them all. |
| limit | integer | Most codes to return. Default: 8 with q, every code without. |
| query |
Estimates what it costs to turn over a US rental unit after move-out; the first tool for a turnover estimate. Inputs: a 5-digit ZIP, bedrooms, bathrooms and the issues in plain English (e.g. "stained carpet in both bedrooms"), which may come from the user's photos; optional unit type, size, year built. Returns a priced make-ready estimate: repair tasks by trade with low/expected/high costs for the ZIP, priorities, refer-outs to licensed professionals, assumptions and links. An AI model reads the issues; the answer is a draft listing what it read (reading), and words it cannot place bill nothing. Not for: diagnosing hazards, legal advice on deposits, remodel bids, properties outside the US. The same findings under the same ruleset give the same result; AI never sets a number.
| Argument | Type | Description |
|---|---|---|
| zip* | string | The user's 5-digit US ZIP code. If it is missing or not exactly 5 digits, ask the user; never guess, pad or correct it. |
| audit | boolean | Also return the run record and input/result hashes (an audit trail). Default false. |
| sq_ft | integer | Interior sq ft. |
| carpet | boolean | Is there wall-to-wall carpet? |
| detail | string | summary (default, small) or full (every line). |
| issues | array | What was found in plain English, e.g. ['stained carpet in both bedrooms', 'nail holes everywhere', 'oven is filthy']. Read like pasted notes: an entry may hold several conditions ('carpet stained, nail holes'; one finding each); a room before or after the condition sets the location ('Bedroom 2 carpet torn', 'mold in bathroom'); a written count sets the quantity ('3 blinds broken'); an entry saying there is no work ('no mold', 'leak fixed by tenant') is reported as not billed. All the issues together may hold up to 12,000 characters; send longer notes in more than one request. |
Turns move-out inspection findings for a US rental unit into a priced make-ready scope. For a first estimate use property_turnover_quick_estimate (plain words). Inputs: property (ZIP, bedrooms, bathrooms, optional details) and findings, each an issue code from property_turnover_issue_codes with rooms, status, quantity and the original words (source_text); optional rate_card, decisions, detail. Returns tasks by trade, low/expected/high costs, priorities, vendor scopes, refer-outs, assumptions, unknowns and download links. Not for: diagnosing hazards, legal advice on deposits, remodel bids, properties outside the US. Deterministic and source-cited: the same input gives the same result.
| Argument | Type | Description |
|---|---|---|
| read | string | Deprecated and ignored: scope reads no free text. |
| audit | boolean | Also return the run record and input/result hashes (an audit trail). Default false. |
| detail | string | summary (small): totals, trade scopes, lines tied to findings. full: all. |
| retain | boolean | false: keep none of this request's text. |
| findings | array | Move-out inspection findings, one per observed condition, coded. |
| property* | object | The unit: ZIP, size, type and year built. |
| returned |
| object |
| What the landlord has returned so far and when. With it, timeliness says whether the refund and the itemized statement were on time and how many days late. |
| furnished | boolean | The unit is rented furnished (California before AB 12: 3 months; Kansas: 1.5 months; Delaware: no cap). |
| deductions | array | What you intend to deduct (or, for a tenant, what the landlord listed). An explicit empty list means nothing is deducted (it decides the shorter no-deduction periods, e.g. Montana 10 days, Alaska 14); leave it out when that is not known yet. |
| move_in_on | string | Tenancy start (YYYY-MM-DD). Used for interest and, when an item's age is unknown, for depreciation. |
| vacated_on* | string | Day the tenant moved out and returned possession (YYYY-MM-DD). |
| client_refs | object | Your own reference ids (e.g. unit_id, job_id). Echoed back, never used. |
| deposit_usd* | any | Security deposit held, dollars. |
| lease_end_on | string | Day the lease was scheduled to end (YYYY-MM-DD), when it differs from when the tenancy actually ended (a broken lease). Lost rent and lease-break charges run to it; deadlines do not (they follow tenancy_ended_on, else the move-out, with the scheduled end as a later alternative). |
| serve_pinned | boolean | Optional, with expect_ruleset. True asks us to answer under the ruleset version you named even when the rules have changed since, from that version's signed bundle; the result says so in served_from_bundle. We refuse a version older than 12 months, one a later correction or law change replaced for your state, or one we have no bundle for. Default false: the call fails whenever the rules have changed. |
| tenancy_type | string | written_lease or at_will (Maine 6033(2): a written agreement's period, at most 30 days, or 21 days for a tenancy at will; Massachusetts 15B(4): at will, 30 days after the move-out, a written lease, 30 days after its end). Left out with a move-out before tenancy_ended_on: the earlier (at-will) date leads and the other is listed. |
| documentation | object | What documentation you have; leave a field out when unknown. |
| building_units | integer | Units in the building. Some interest rules depend on it (NY 6+, IL 25+). |
| expect_ruleset | string | Optional. The ruleset version you built against, as results carry it in provenance.ruleset_version (e.g. 'sd-2026.11.14'), or its sha256: hash. If the rules have changed since, the call fails (nothing computed, nothing charged) and says what is in force now, so you upgrade on your own schedule. Leave it out to answer under the current rules. |
| owner_occupied | boolean | The landlord lives in the building (ME, NH, NJ and Chicago exempt small owner-occupied buildings; with building_units). |
| small_landlord | boolean | California AB 12 small landlord (a natural person or an LLC of natural persons owning at most two properties with at most four units; not for a service member): a two-month cap. |
| pet_deposit_usd | any | The part of deposit_usd that is a pet deposit (outside the cap in Missouri, Alabama, North Dakota and Saint Paul; allowed on top of it up to one month in Hawaii and Alaska, half a month in Kansas, a quarter in Nebraska). |
| monthly_rent_usd | any | Monthly rent, for the deposit cap check. |
| prepaid_rent_usd | any | Rent paid in advance beyond the first month, held besides deposit_usd. Counts toward the cap in AK, AZ, CA, CT, MD, MI and NY; earns interest in CT, MA (last month's rent), MD and Chicago; in Minneapolis it lowers the deposit cap to half a month. In UT its balance is returned with the deposit and counted in the refund (57-17-3(2)(b)): send only the unused part (rent already applied to a rental period is not a balance). Elsewhere the refund covers the deposit only and a warning says unused prepaid rent is owed back too. |
| tenancy_ended_on | string | When the tenancy actually ended (YYYY-MM-DD): the lease's end, or an earlier termination or accepted surrender. Deadlines that run from the end of the tenancy use it. For a tenant who left before the lease was due to end, send the scheduled end as lease_end_on (older callers sent it here, which still works: the deadline is then counted from it, with the move-out date as an earlier alternative where the facts point to one). |
| tenant_abandoned | boolean | The tenant abandoned the unit or was evicted (Maryland 8-203(h): the clock runs from the tenant's written demand; Washington 59.18.280: from when the landlord learns of it; Louisiana 9:3251(C): the one-month rule does not apply). Left out: inferred from tenant_gave_proper_notice: false; with a move-out before tenancy_ended_on the earlier abandonment clock leads and the other date is listed. |
| contractor_needed | boolean | Damage exceeds the deposit and needs a third-party contractor, and you said so in writing in time (Virginia, West Virginia: 15 more days). |
| lease_return_days | integer | Return period the lease sets, in days, where the statute lets a lease set one (Colorado up to 60, Idaho up to 30, Maine shorter). |
| lease_term_months | integer | Term of the lease in months (0 for month-to-month). Delaware's cap applies to leases of a year or more, New Mexico's to shorter ones. |
| statement_sent_on | string | Day you sent (or delivered) the itemized statement (YYYY-MM-DD). With it the statement is judged on time or late; documentation.itemized_statement alone cannot tell. |
| demand_received_on | string | Day you received the tenant's demand for the deposit (YYYY-MM-DD; written in Oklahoma, by first-class mail in Maryland 8-203(h)). Arizona, Kansas, Oklahoma, South Carolina and Mississippi start the clock on it. |
| tenant_age_60_plus | boolean | A person 60 or older occupies the unit (New Hampshire 540-A:5, I: the owner-occupied exemption does not reach the unit). tenant_age_62_plus true implies it. |
| tenant_age_62_plus | boolean | The tenant is 62 or older (Connecticut: a one-month cap). |
| new_tenant_rent_usd | any | Monthly rent the new tenant pays (with new_tenant_moved_in_on): the rent received for overlapping days is credited against rent claimed for them. |
| third_party_managed | boolean | A third party manages the unit or collects rent for a fee. |
| claim_notice_sent_on | string | Day you sent the written notice of intent to impose a claim on the deposit (YYYY-MM-DD). Florida (due within 30 days of termination) and the District of Columbia (within 45 days): the balance is due 30 days after it; a late notice does not count. |
| deposit_collected_on | string | Day the deposit was collected (YYYY-MM-DD); defaults to move_in_on. California (AB 12, from 2024-07-01) and Colorado caps depend on it. |
| landlord_units_owned | integer | Rental units the landlord owns in all (Arkansas 5 or fewer, Georgia 10 or fewer, with third_party_managed, may be exempt). |
| interest_paid_through | string | Interest already paid or credited to the tenant up to this day (annual-payment rules: San Francisco, Los Angeles, Berkeley, Connecticut, Massachusetts, ...): interest is counted from it. |
| interest_rate_percent | any | Annual rate the deposit account paid, where the law sets interest at the bank's rate (New Jersey, North Dakota, New Hampshire, New York, Pennsylvania, Portland), or caps it (Massachusetts: 5% or the bank's lesser rate). Without it bank-rate interest is unknown. Illinois 765 ILCS 715 uses IDFPR's published rate instead. |
| new_tenant_moved_in_on | string | Day a new tenant moved in, if the unit was re-rented (YYYY-MM-DD). West Virginia and Wisconsin can end the period earlier; rent claimed for days from it on is not recoverable (re-rent mitigation). |
| rent_ordinance_covered | boolean | The unit is covered by the city's rent ordinance (Los Angeles RSO, Berkeley Rent Ordinance). Their deposit interest is owed only then; unknown: the amount is shown beside the refund, not in it. |
| owner_is_natural_person | boolean | Georgia only (44-7-36): the units are owned by a natural person (false: an LLC, corporation, partnership or trust owns them). The small-landlord exemption reaches only a natural person; not given, the statute is shown with the exemption as the alternative. |
| renter_notice_served_on | string | Utah only (YYYY-MM-DD): day the renter served the 57-17-3(3) notice after the deadline passed. The owner then has five business days to comply (57-17-3(5)); without the notice there is no relief under 57-17-5. |
| landlord_filed_action_on | string | Michigan only (YYYY-MM-DD): day the landlord sued for the damages claimed (554.613: sue or return the balance within 45 days). Not given: no suit assumed. |
| tenant_gave_proper_notice | boolean | A termination notice that meets the statute was given (Alaska: 14 days instead of 30 when nothing is deducted for damage). |
| lease_signed_or_renewed_on | string | Day the current lease was signed or last renewed (YYYY-MM-DD). Georgia (2024-07-01) and Maryland (2024-10-01) caps apply to leases from then; defaults to move_in_on, with a warning that a later renewal may bring the new cap. |
| deposit_in_separate_account | boolean | Tennessee only (66-28-301(a)): the deposit was kept in a separate account used only for deposits. No account and no damage listing means none of it can be kept (66-28-301(c)). |
| nonstandard_provision_signed | boolean | Wisconsin: the tenant signed a separate NONSTANDARD RENTAL PROVISIONS document covering the charge (ATCP 134.06(3)(b)); false makes such charges 'no'. |
| forwarding_address_received_on | string | Day you received the tenant's written forwarding address (YYYY-MM-DD). Several states start or hold the clock on it (Texas, Connecticut, Iowa, Minnesota). |
| tenant_gave_forwarding_address | boolean | False when the tenant never gave a forwarding address in writing (not just 'not yet known'). Illinois, Pennsylvania, Ohio, Delaware, South Carolina and New Hampshire then relieve the landlord of the penalty. Implied true by forwarding_address_received_on. |
{
"type": "object",
"$defs": {
"Returned": {
"type": "object",
"required": [
"what"
],
"properties": {
"on": {
"type": "string",
"format": "date",
"description": "Day it was sent or delivered (YYYY-MM-DD); required unless what is nothing. Statutes count the day the landlord mails or delivers it; a postmark is the best evidence."
},
"what": {
"enum": [
"nothing",
"full_refund",
"partial_refund",
"itemized_statement"
],
"type": "string",
"description": "nothing: nothing returned yet; full_refund: the whole deposit; partial_refund: part of it, with no itemized statement; itemized_statement: an itemized statement of deductions (with any refund the same day)."
},
"method": {
"enum": [
"check",
"electronic",
"other"
],
"type": "string",
"description": "How the money went back: check, electronic (direct deposit or electronic transfer) or other. Some statutes key on it (Michigan 554.609(3): an electronic balance may follow the notice by 10 days; California 1950.5 as amended by AB 414: electronic return when the deposit or rent was paid electronically)."
},
"amount_usd": {
"anyOf": [
{
"type": "number",
"maximum": 10000000,
"minimum": 0
},
{
"type": "string",
"pattern": "^\\$?[0-9][0-9,]*(\\.[0-9]+)?$"
}
],
"description": "Amount refunded, dollars (partial_refund or itemized_statement)."
}
},
"description": "What the landlord has returned so far: lets the result say whether the refund and the\nitemized statement came on time (``timeliness``).",
"additionalProperties": false
},
"Deduction": {
"type": "object",
"required": [
"category",
"amount_usd"
],
"properties": {
"category": {
"enum": [
"unpaid_rent",
"unpaid_utilities",
"cleaning",
"carpet",
"flooring",
"paint",
"blinds_window_coverings",
"appliance",
"fixture_or_hardware",
"wall_or_door_repair",
"key_or_lock",
"late_fees",
"lease_break_fee",
"moving_and_storage",
"reletting_costs",
"trash_and_abandoned_items",
"other_damage",
"other"
],
"type": "string",
"description": "One of: unpaid_rent, unpaid_utilities, cleaning, carpet, flooring, paint, blinds_window_coverings, appliance, fixture_or_hardware, wall_or_door_repair, key_or_lock, late_fees, lease_break_fee, moving_and_storage, reletting_costs (advertising or a broker to re-rent after an early move-out), trash_and_abandoned_items (removing trash or belongings left behind), other_damage, other. Carpet cleaning is cleaning (with cleaning_kind), not carpet."
},
"work_kind": {
"enum": [
"repair",
"replace",
"cleaning"
],
"type": "string",
"description": "repair (restores the item: never depreciated), replace (a new item: prorated by the old one's remaining life where the rule does), or cleaning (e.g. carpet cleaning: judged as a cleaning line)."
},
"amount_usd": {
"anyOf": [
{
"type": "number",
"maximum": 10000000,
"minimum": 0
},
{
"type": "string",
"pattern": "^\\$?[0-9][0-9,]*(\\.[0-9]+)?$"
}
],
"description": "Amount you intend to deduct, in dollars (rounded to cents)."
},
"has_photos": {
"type": "boolean",
"description": "You have photos of this damage."
},
"description": {
"type": "string",
"maxLength": 200,
"description": "What was damaged or owed and the repair or charge (e.g. 'bedroom carpet: pet stains, two areas patched'). Printed under the line on the itemized statement you download (HTML or PDF); never stored, logged or hashed, and not in the result."
},
"has_receipt": {
"type": "boolean",
"description": "You have a receipt, invoice or estimate for this line."
},
"rent_through": {
"type": "string",
"format": "date",
"description": "unpaid_rent only: the last day the rent charged covers. With new_tenant_moved_in_on it decides how much of the rent overlaps the new tenancy (not recoverable: the landlord cannot collect rent twice for the same days)."
},
"utility_kind": {
"enum": [
"water",
"sewer",
"electric",
"gas",
"heat",
"trash",
"internet",
"other"
],
"type": "string",
"description": "unpaid_utilities only: which utility. Some states allow only some (Massachusetts: water); without it such a line is 'depends'."
},
"cleaning_kind": {
"enum": [
"beyond_wear",
"routine",
"unknown"
],
"type": "string",
"description": "cleaning only: beyond_wear (the unit was left dirtier than normal use; cleaning to restore its move-in condition), routine (ordinary cleaning after normal use, which most states treat as normal wear) or unknown. Where the statute does not name cleaning, an unknown kind is 'depends'."
},
"late_payments": {
"type": "integer",
"maximum": 600,
"minimum": 1,
"description": "late_fees only: how many late payments (or months, where the cap is per month) the fee covers. A statutory cap is per late payment; without this, more than one cap is 'depends'."
},
"item_age_years": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"minimum": 0
},
{
"type": "string",
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
}
],
"description": "Age of the item in years at move-out (e.g. 4.5)."
},
"caused_by_tenant": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": true,
"description": "False when the condition is normal wear and tear (never deductible); null when it is disputed or unknown (a tenant checking a landlord's list): the line is then 'depends', with the amount the rule would allow if it is damage."
},
"item_installed_on": {
"type": "string",
"format": "date",
"description": "When the item (carpet, paint, appliance...) was installed or last replaced (YYYY-MM-DD). Give this or item_age_years, not both."
},
"useful_life_years": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"exclusiveMinimum": 0
},
{
"type": "string",
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
}
],
"description": "Override the useful life used for depreciation (years)."
},
"good_faith_estimate": {
"type": "boolean",
"description": "The amount is a good-faith estimate: the repair cannot reasonably be finished within the return period, or the vendor's bills have not arrived (California 1950.5(h)(3)). The receipts then follow within 14 days of the repair or the documents."
}
},
"additionalProperties": false
},
"Documentation": {
"type": "object",
"properties": {
"receipts": {
"type": "boolean",
"description": "You have receipts or invoices for the repairs and cleaning."
},
"move_out_list": {
"type": "boolean",
"description": "You inspected the unit and made the move-out damage list in time (Georgia 44-7-33(b): within 3 business days)."
},
"photos_move_in": {
"type": "boolean",
"description": "Dated move-in photos."
},
"cleaning_notice": {
"type": "boolean",
"description": "You gave the tenant written notice of the cleaning needed and time to do it before charging for cleaning (Montana 70-25-201(3): 24 hours)."
},
"photos_move_out": {
"type": "boolean",
"description": "Dated move-out photos."
},
"itemized_statement": {
"type": "boolean",
"description": "You will send (or sent) an itemized statement."
},
"photos_after_repair": {
"type": "boolean",
"description": "Photos after repairs."
},
"move_in_condition_statement": {
"type": "boolean",
"description": "You gave the tenant a written move-in condition statement or checklist (Montana 70-25-206, Washington 59.18.260; listed as a duty in Georgia 44-7-33(a), Kentucky 383.580(2), Michigan 554.608 and Virginia 55.1-1214)."
}
},
"additionalProperties": false
}
},
"examples": [
{
"as_of": "2026-10-09",
"state": "CA",
"deductions": [
{
"category": "cleaning",
"amount_usd": "150"
},
{
"category": "carpet",
"amount_usd": "1200",
"item_age_years": 5
}
],
"move_in_on": "2023-06-01",
"vacated_on": "2026-09-30",
"deposit_usd": "2000",
"monthly_rent_usd": "2000"
}
],
"required": [
"state",
"deposit_usd",
"vacated_on"
],
"properties": {
"city": {
"type": "string",
"maxLength": 80,
"description": "City, for a city overlay where covered (e.g. 'Chicago'). Optional."
},
"as_of": {
"type": "string",
"format": "date",
"description": "Today's date (YYYY-MM-DD) for the deadline status. Defaults to today (UTC); send it for repeatable results."
},
"audit": {
"type": "boolean",
"default": false,
"description": "Also return the run record and input/result hashes (an audit trail). Default false."
},
"state": {
"type": "string",
"description": "US state, 2-letter code (e.g. 'CA'); a state name also works."
},
"county": {
"type": "string",
"maxLength": 60,
"description": "Tennessee only: the county the unit is in (the landlord-tenant act applies only in counties over 75,000 by the 2010 census, 66-28-102(a)). Taken from the city when it lies in one county."
},
"returned": {
"$ref": "#/$defs/Returned",
"type": "object",
"description": "What the landlord has returned so far and when. With it, timeliness says whether the refund and the itemized statement were on time and how many days late."
},
"furnished": {
"type": "boolean",
"description": "The unit is rented furnished (California before AB 12: 3 months; Kansas: 1.5 months; Delaware: no cap)."
},
"deductions": {
"type": "array",
"items": {
"$ref": "#/$defs/Deduction"
},
"maxItems": 50,
"description": "What you intend to deduct (or, for a tenant, what the landlord listed). An explicit empty list means nothing is deducted (it decides the shorter no-deduction periods, e.g. Montana 10 days, Alaska 14); leave it out when that is not known yet."
},
"move_in_on": {
"type": "string",
"format": "date",
"description": "Tenancy start (YYYY-MM-DD). Used for interest and, when an item's age is unknown, for depreciation."
},
"vacated_on": {
"type": "string",
"format": "date",
"description": "Day the tenant moved out and returned possession (YYYY-MM-DD)."
},
"client_refs": {
"type": "object",
"description": "Your own reference ids (e.g. unit_id, job_id). Echoed back, never used.",
"additionalProperties": {
"type": "string"
}
},
"deposit_usd": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
}
],
"description": "Security deposit held, dollars."
},
"lease_end_on": {
"type": "string",
"format": "date",
"description": "Day the lease was scheduled to end (YYYY-MM-DD), when it differs from when the tenancy actually ended (a broken lease). Lost rent and lease-break charges run to it; deadlines do not (they follow tenancy_ended_on, else the move-out, with the scheduled end as a later alternative)."
},
"serve_pinned": {
"type": "boolean",
"default": false,
"description": "Optional, with expect_ruleset. True asks us to answer under the ruleset version you named even when the rules have changed since, from that version's signed bundle; the result says so in served_from_bundle. We refuse a version older than 12 months, one a later correction or law change replaced for your state, or one we have no bundle for. Default false: the call fails whenever the rules have changed."
},
"tenancy_type": {
"enum": [
"written_lease",
"at_will"
],
"type": "string",
"description": "written_lease or at_will (Maine 6033(2): a written agreement's period, at most 30 days, or 21 days for a tenancy at will; Massachusetts 15B(4): at will, 30 days after the move-out, a written lease, 30 days after its end). Left out with a move-out before tenancy_ended_on: the earlier (at-will) date leads and the other is listed."
},
"documentation": {
"$ref": "#/$defs/Documentation",
"type": "object",
"description": "What documentation you have; leave a field out when unknown."
},
"building_units": {
"type": "integer",
"maximum": 100000,
"minimum": 1,
"description": "Units in the building. Some interest rules depend on it (NY 6+, IL 25+)."
},
"expect_ruleset": {
"type": "string",
"description": "Optional. The ruleset version you built against, as results carry it in provenance.ruleset_version (e.g. 'sd-2026.11.14'), or its sha256: hash. If the rules have changed since, the call fails (nothing computed, nothing charged) and says what is in force now, so you upgrade on your own schedule. Leave it out to answer under the current rules."
},
"owner_occupied": {
"type": "boolean",
"description": "The landlord lives in the building (ME, NH, NJ and Chicago exempt small owner-occupied buildings; with building_units)."
},
"small_landlord": {
"type": "boolean",
"description": "California AB 12 small landlord (a natural person or an LLC of natural persons owning at most two properties with at most four units; not for a service member): a two-month cap."
},
"pet_deposit_usd": {
"anyOf": [
{
"type": "number",
"maximum": 10000000,
"minimum": 0
},
{
"type": "string",
"pattern": "^\\$?[0-9][0-9,]*(\\.[0-9]+)?$"
}
],
"description": "The part of deposit_usd that is a pet deposit (outside the cap in Missouri, Alabama, North Dakota and Saint Paul; allowed on top of it up to one month in Hawaii and Alaska, half a month in Kansas, a quarter in Nebraska)."
},
"monthly_rent_usd": {
"anyOf": [
{
"type": "number",
"maximum": 10000000,
"minimum": 0
},
{
"type": "string",
"pattern": "^\\$?[0-9][0-9,]*(\\.[0-9]+)?$"
}
],
"description": "Monthly rent, for the deposit cap check."
},
"prepaid_rent_usd": {
"anyOf": [
{
"type": "number",
"maximum": 10000000,
"minimum": 0
},
{
"type": "string",
"pattern": "^\\$?[0-9][0-9,]*(\\.[0-9]+)?$"
}
],
"description": "Rent paid in advance beyond the first month, held besides deposit_usd. Counts toward the cap in AK, AZ, CA, CT, MD, MI and NY; earns interest in CT, MA (last month's rent), MD and Chicago; in Minneapolis it lowers the deposit cap to half a month. In UT its balance is returned with the deposit and counted in the refund (57-17-3(2)(b)): send only the unused part (rent already applied to a rental period is not a balance). Elsewhere the refund covers the deposit only and a warning says unused prepaid rent is owed back too."
},
"tenancy_ended_on": {
"type": "string",
"format": "date",
"description": "When the tenancy actually ended (YYYY-MM-DD): the lease's end, or an earlier termination or accepted surrender. Deadlines that run from the end of the tenancy use it. For a tenant who left before the lease was due to end, send the scheduled end as lease_end_on (older callers sent it here, which still works: the deadline is then counted from it, with the move-out date as an earlier alternative where the facts point to one)."
},
"tenant_abandoned": {
"type": "boolean",
"description": "The tenant abandoned the unit or was evicted (Maryland 8-203(h): the clock runs from the tenant's written demand; Washington 59.18.280: from when the landlord learns of it; Louisiana 9:3251(C): the one-month rule does not apply). Left out: inferred from tenant_gave_proper_notice: false; with a move-out before tenancy_ended_on the earlier abandonment clock leads and the other date is listed."
},
"contractor_needed": {
"type": "boolean",
"description": "Damage exceeds the deposit and needs a third-party contractor, and you said so in writing in time (Virginia, West Virginia: 15 more days)."
},
"lease_return_days": {
"type": "integer",
"maximum": 365,
"minimum": 0,
"description": "Return period the lease sets, in days, where the statute lets a lease set one (Colorado up to 60, Idaho up to 30, Maine shorter)."
},
"lease_term_months": {
"type": "integer",
"maximum": 600,
"minimum": 0,
"description": "Term of the lease in months (0 for month-to-month). Delaware's cap applies to leases of a year or more, New Mexico's to shorter ones."
},
"statement_sent_on": {
"type": "string",
"format": "date",
"description": "Day you sent (or delivered) the itemized statement (YYYY-MM-DD). With it the statement is judged on time or late; documentation.itemized_statement alone cannot tell."
},
"demand_received_on": {
"type": "string",
"format": "date",
"description": "Day you received the tenant's demand for the deposit (YYYY-MM-DD; written in Oklahoma, by first-class mail in Maryland 8-203(h)). Arizona, Kansas, Oklahoma, South Carolina and Mississippi start the clock on it."
},
"tenant_age_60_plus": {
"type": "boolean",
"description": "A person 60 or older occupies the unit (New Hampshire 540-A:5, I: the owner-occupied exemption does not reach the unit). tenant_age_62_plus true implies it."
},
"tenant_age_62_plus": {
"type": "boolean",
"description": "The tenant is 62 or older (Connecticut: a one-month cap)."
},
"new_tenant_rent_usd": {
"anyOf": [
{
"type": "number",
"maximum": 10000000,
"minimum": 0
},
{
"type": "string",
"pattern": "^\\$?[0-9][0-9,]*(\\.[0-9]+)?$"
}
],
"description": "Monthly rent the new tenant pays (with new_tenant_moved_in_on): the rent received for overlapping days is credited against rent claimed for them."
},
"third_party_managed": {
"type": "boolean",
"description": "A third party manages the unit or collects rent for a fee."
},
"claim_notice_sent_on": {
"type": "string",
"format": "date",
"description": "Day you sent the written notice of intent to impose a claim on the deposit (YYYY-MM-DD). Florida (due within 30 days of termination) and the District of Columbia (within 45 days): the balance is due 30 days after it; a late notice does not count."
},
"deposit_collected_on": {
"type": "string",
"format": "date",
"description": "Day the deposit was collected (YYYY-MM-DD); defaults to move_in_on. California (AB 12, from 2024-07-01) and Colorado caps depend on it."
},
"landlord_units_owned": {
"type": "integer",
"minimum": 0,
"description": "Rental units the landlord owns in all (Arkansas 5 or fewer, Georgia 10 or fewer, with third_party_managed, may be exempt)."
},
"interest_paid_through": {
"type": "string",
"format": "date",
"description": "Interest already paid or credited to the tenant up to this day (annual-payment rules: San Francisco, Los Angeles, Berkeley, Connecticut, Massachusetts, ...): interest is counted from it."
},
"interest_rate_percent": {
"anyOf": [
{
"type": "number",
"maximum": 25,
"minimum": 0
},
{
"type": "string",
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
}
],
"description": "Annual rate the deposit account paid, where the law sets interest at the bank's rate (New Jersey, North Dakota, New Hampshire, New York, Pennsylvania, Portland), or caps it (Massachusetts: 5% or the bank's lesser rate). Without it bank-rate interest is unknown. Illinois 765 ILCS 715 uses IDFPR's published rate instead."
},
"new_tenant_moved_in_on": {
"type": "string",
"format": "date",
"description": "Day a new tenant moved in, if the unit was re-rented (YYYY-MM-DD). West Virginia and Wisconsin can end the period earlier; rent claimed for days from it on is not recoverable (re-rent mitigation)."
},
"rent_ordinance_covered": {
"type": "boolean",
"description": "The unit is covered by the city's rent ordinance (Los Angeles RSO, Berkeley Rent Ordinance). Their deposit interest is owed only then; unknown: the amount is shown beside the refund, not in it."
},
"owner_is_natural_person": {
"type": "boolean",
"description": "Georgia only (44-7-36): the units are owned by a natural person (false: an LLC, corporation, partnership or trust owns them). The small-landlord exemption reaches only a natural person; not given, the statute is shown with the exemption as the alternative."
},
"renter_notice_served_on": {
"type": "string",
"format": "date",
"description": "Utah only (YYYY-MM-DD): day the renter served the 57-17-3(3) notice after the deadline passed. The owner then has five business days to comply (57-17-3(5)); without the notice there is no relief under 57-17-5."
},
"landlord_filed_action_on": {
"type": "string",
"format": "date",
"description": "Michigan only (YYYY-MM-DD): day the landlord sued for the damages claimed (554.613: sue or return the balance within 45 days). Not given: no suit assumed."
},
"tenant_gave_proper_notice": {
"type": "boolean",
"description": "A termination notice that meets the statute was given (Alaska: 14 days instead of 30 when nothing is deducted for damage)."
},
"lease_signed_or_renewed_on": {
"type": "string",
"format": "date",
"description": "Day the current lease was signed or last renewed (YYYY-MM-DD). Georgia (2024-07-01) and Maryland (2024-10-01) caps apply to leases from then; defaults to move_in_on, with a warning that a later renewal may bring the new cap."
},
"deposit_in_separate_account": {
"type": "boolean",
"description": "Tennessee only (66-28-301(a)): the deposit was kept in a separate account used only for deposits. No account and no damage listing means none of it can be kept (66-28-301(c))."
},
"nonstandard_provision_signed": {
"type": "boolean",
"description": "Wisconsin: the tenant signed a separate NONSTANDARD RENTAL PROVISIONS document covering the charge (ATCP 134.06(3)(b)); false makes such charges 'no'."
},
"forwarding_address_received_on": {
"type": "string",
"format": "date",
"description": "Day you received the tenant's written forwarding address (YYYY-MM-DD). Several states start or hold the clock on it (Texas, Connecticut, Iowa, Minnesota)."
},
"tenant_gave_forwarding_address": {
"type": "boolean",
"description": "False when the tenant never gave a forwarding address in writing (not just 'not yet known'). Illinois, Pennsylvania, Ohio, Delaware, South Carolina and New Hampshire then relieve the landlord of the penalty. Implied true by forwarding_address_received_on."
}
}
}| string |
| Older name for q; use q. |
| client_refs | object | Your own reference ids (e.g. unit_id, job_id). Echoed back, never used. |
| serve_pinned | boolean | Optional, with expect_ruleset. True asks us to answer under the ruleset version you named even when the rules have changed since, from that version's signed bundle; the result says so in served_from_bundle. We refuse a version older than 12 months, one a later correction or law change replaced for your state, or one we have no bundle for. Default false: the call fails whenever the rules have changed. |
| expect_ruleset | string | Optional. The ruleset version you built against, as results carry it in provenance.ruleset_version (e.g. 'sd-2026.11.14'), or its sha256: hash. If the rules have changed since, the call fails (nothing computed, nothing charged) and says what is in force now, so you upgrade on your own schedule. Leave it out to answer under the current rules. |
| include_aliases | boolean | Also return aliases in the short list (search hits always carry them). |
{
"type": "object",
"properties": {
"q": {
"type": "string",
"maxLength": 200,
"description": "Words to search for in the codes, names, aliases, descriptions and examples, e.g. 'pet stain carpet'. Hits come back with full detail. Leave out to list every code in short form."
},
"area": {
"type": "string",
"maxLength": 40,
"description": "Older name for group; use group."
},
"audit": {
"type": "boolean",
"default": false,
"description": "Also return the run record and input/result hashes (an audit trail). Default false."
},
"group": {
"type": "string",
"maxLength": 40,
"description": "Only codes in this group, e.g. 'flooring' or 'walls' (a part of the unit, not a room). `groups` in the answer lists them all."
},
"limit": {
"type": "integer",
"maximum": 500,
"minimum": 1,
"description": "Most codes to return. Default: 8 with q, every code without."
},
"query": {
"type": "string",
"maxLength": 200,
"description": "Older name for q; use q."
},
"client_refs": {
"type": "object",
"description": "Your own reference ids (e.g. unit_id, job_id). Echoed back, never used.",
"additionalProperties": {
"type": "string"
}
},
"serve_pinned": {
"type": "boolean",
"default": false,
"description": "Optional, with expect_ruleset. True asks us to answer under the ruleset version you named even when the rules have changed since, from that version's signed bundle; the result says so in served_from_bundle. We refuse a version older than 12 months, one a later correction or law change replaced for your state, or one we have no bundle for. Default false: the call fails whenever the rules have changed."
},
"expect_ruleset": {
"type": "string",
"description": "Optional. The ruleset version you built against, as results carry it in provenance.ruleset_version (e.g. 'sd-2026.11.14'), or its sha256: hash. If the rules have changed since, the call fails (nothing computed, nothing charged) and says what is in force now, so you upgrade on your own schedule. Leave it out to answer under the current rules."
},
"include_aliases": {
"type": "boolean",
"default": false,
"description": "Also return aliases in the short list (search hits always carry them)."
}
}
}| retain | boolean | Optional (contract 0.5.1). false: keep none of this request's text, so the AI reader's answer for the issues is not stored either. By default the text is kept with contact details and access codes removed, to improve matching; the estimate is the same either way. |
| profile | string | minimum (rent-ready basics) | standard | premium (higher finish). |
| bedrooms* | integer | 0 for a studio. |
| bathrooms* | any | Half baths count as 0.5. |
| unit_type | string | Single-family homes and townhomes get house-sized defaults (floor area, locks). |
| walk_date | string | The walk-through date; dated or aged items are judged against it. Omit if unknown. |
| price_tier | string | bulk_contract (portfolio contract prices) | pm_vendor (default: a property manager buying turns from local vendors) | retail_one_off (an owner hiring one contractor). |
| year_built | integer | Drives lead-paint (pre-1978) handling. |
| client_refs | object | Your own reference ids (e.g. unit_id, job_id). Echoed back, never used. |
| serve_pinned | boolean | Optional, with expect_ruleset. True asks us to answer under the ruleset version you named even when the rules have changed since, from that version's signed bundle; the result says so in served_from_bundle. We refuse a version older than 12 months, one a later correction or law change replaced for your state, or one we have no bundle for. Default false: the call fails whenever the rules have changed. |
| expect_ruleset | string | Optional. The ruleset version you built against, as results carry it in provenance.ruleset_version (e.g. 'sd-2026.11.14'), or its sha256: hash. If the rules have changed since, the call fails (nothing computed, nothing charged) and says what is in force now, so you upgrade on your own schedule. Leave it out to answer under the current rules. |
{
"type": "object",
"$defs": {
"Profile": {
"enum": [
"minimum",
"standard",
"premium"
],
"type": "string"
},
"UnitType": {
"enum": [
"apartment",
"single_family",
"townhome",
"condo",
"duplex",
"manufactured"
],
"type": "string"
},
"PriceTier": {
"enum": [
"bulk_contract",
"pm_vendor",
"retail_one_off"
],
"type": "string",
"description": "Purchasing channel. Library prices are pm_vendor (a PM buying turns from a local vendor)."
}
},
"examples": [
{
"zip": "43215",
"issues": [
"stained carpet in both bedrooms",
"nail holes everywhere",
"oven is filthy"
],
"bedrooms": 2,
"bathrooms": 1
}
],
"required": [
"zip",
"bedrooms",
"bathrooms"
],
"properties": {
"zip": {
"type": "string",
"description": "The user's 5-digit US ZIP code. If it is missing or not exactly 5 digits, ask the user; never guess, pad or correct it."
},
"audit": {
"type": "boolean",
"default": false,
"description": "Also return the run record and input/result hashes (an audit trail). Default false."
},
"sq_ft": {
"type": "integer",
"maximum": 20000,
"minimum": 150,
"description": "Interior sq ft."
},
"carpet": {
"type": "boolean",
"description": "Is there wall-to-wall carpet?"
},
"detail": {
"enum": [
"summary",
"full"
],
"type": "string",
"default": "summary",
"description": "summary (default, small) or full (every line)."
},
"issues": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 100,
"description": "What was found in plain English, e.g. ['stained carpet in both bedrooms', 'nail holes everywhere', 'oven is filthy']. Read like pasted notes: an entry may hold several conditions ('carpet stained, nail holes'; one finding each); a room before or after the condition sets the location ('Bedroom 2 carpet torn', 'mold in bathroom'); a written count sets the quantity ('3 blinds broken'); an entry saying there is no work ('no mold', 'leak fixed by tenant') is reported as not billed. All the issues together may hold up to 12,000 characters; send longer notes in more than one request."
},
"retain": {
"type": "boolean",
"default": true,
"description": "Optional (contract 0.5.1). false: keep none of this request's text, so the AI reader's answer for the issues is not stored either. By default the text is kept with contact details and access codes removed, to improve matching; the estimate is the same either way."
},
"profile": {
"$ref": "#/$defs/Profile",
"type": "string",
"default": "standard",
"description": "minimum (rent-ready basics) | standard | premium (higher finish)."
},
"bedrooms": {
"type": "integer",
"description": "0 for a studio."
},
"bathrooms": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
}
],
"description": "Half baths count as 0.5."
},
"unit_type": {
"$ref": "#/$defs/UnitType",
"type": "string",
"default": "apartment",
"description": "Single-family homes and townhomes get house-sized defaults (floor area, locks)."
},
"walk_date": {
"type": "string",
"format": "date",
"description": "The walk-through date; dated or aged items are judged against it. Omit if unknown."
},
"price_tier": {
"$ref": "#/$defs/PriceTier",
"type": "string",
"default": "pm_vendor",
"description": "bulk_contract (portfolio contract prices) | pm_vendor (default: a property manager buying turns from local vendors) | retail_one_off (an owner hiring one contractor)."
},
"year_built": {
"type": "integer",
"maximum": 2100,
"minimum": 1700,
"description": "Drives lead-paint (pre-1978) handling."
},
"client_refs": {
"type": "object",
"description": "Your own reference ids (e.g. unit_id, job_id). Echoed back, never used.",
"additionalProperties": {
"type": "string"
}
},
"serve_pinned": {
"type": "boolean",
"default": false,
"description": "Optional, with expect_ruleset. True asks us to answer under the ruleset version you named even when the rules have changed since, from that version's signed bundle; the result says so in served_from_bundle. We refuse a version older than 12 months, one a later correction or law change replaced for your state, or one we have no bundle for. Default false: the call fails whenever the rules have changed."
},
"expect_ruleset": {
"type": "string",
"description": "Optional. The ruleset version you built against, as results carry it in provenance.ruleset_version (e.g. 'sd-2026.11.14'), or its sha256: hash. If the rules have changed since, the call fails (nothing computed, nothing charged) and says what is in force now, so you upgrade on your own schedule. Leave it out to answer under the current rules."
}
}
}| tax_rate |
| any |
| Applied to all lines, e.g. 0.07. |
| decisions | array | Approve, decline or defer lines or findings from an earlier scope. |
| rate_card | array | Your own prices by trade or task; they replace the regional library prices. |
| standards | object | Finish profile, price tier and overrides. |
| client_refs | object | Your own reference ids (e.g. unit_id, job_id). Echoed back, never used. |
| constraints | object | Target ready date, whether the unit is occupied, access notes. |
| serve_pinned | boolean | Optional, with expect_ruleset. True asks us to answer under the ruleset version you named even when the rules have changed since, from that version's signed bundle; the result says so in served_from_bundle. We refuse a version older than 12 months, one a later correction or law change replaced for your state, or one we have no bundle for. Default false: the call fails whenever the rules have changed. |
| trade_vendors | object | Vendor (name, contact) to put on each trade's lines. |
| expect_ruleset | string | Optional. The ruleset version you built against, as results carry it in provenance.ruleset_version (e.g. 'sd-2026.11.14'), or its sha256: hash. If the rules have changed since, the call fails (nothing computed, nothing charged) and says what is in force now, so you upgrade on your own schedule. Leave it out to answer under the current rules. |
{
"type": "object",
"$defs": {
"Uom": {
"enum": [
"each",
"sq_ft",
"sq_ft_floor",
"sq_yd",
"linear_ft",
"room",
"unit",
"bedroom",
"bathroom",
"hour",
"cu_yd",
"visit"
],
"type": "string"
},
"Area": {
"enum": [
"whole_unit",
"kitchen",
"living_room",
"dining_room",
"bedroom",
"bathroom",
"hallway",
"entry",
"laundry",
"closet",
"garage",
"patio_balcony",
"exterior",
"yard",
"other"
],
"type": "string"
},
"Rooms": {
"type": "object",
"properties": {
"area": {
"$ref": "#/$defs/Area",
"type": "string",
"default": "whole_unit",
"description": "Left out, from the labels, else the whole unit."
},
"count": {
"type": "integer",
"default": 1,
"maximum": 50,
"minimum": 1,
"description": "At least the number of labels; capped at the unit's count."
},
"labels": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 12,
"description": "The rooms' names as written, e.g. ['Bedroom 2']."
}
},
"description": "Which rooms a finding covers.",
"additionalProperties": false
},
"Trade": {
"enum": [
"hauling",
"handyman",
"drywall",
"painter",
"flooring",
"carpet_cleaning",
"cleaning",
"appliance",
"plumber",
"electrician",
"hvac",
"locksmith",
"pest_control",
"landscaping",
"window_glass",
"environmental",
"structural"
],
"type": "string"
},
"Extent": {
"enum": [
"spot",
"partial",
"full"
],
"type": "string"
},
"Vendor": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"maxLength": 120
},
"contact": {
"type": "string",
"maxLength": 200
}
},
"additionalProperties": false
},
"Finding": {
"type": "object",
"properties": {
"id": {
"type": "string",
"pattern": "^[A-Za-z0-9_.:-]+$",
"maxLength": 40,
"description": "Your id; defaults to f1, f2, ... in order."
},
"code": {
"enum": [
"adhesive.residue",
"asbestos.suspect",
"bath.accessory_missing",
"bath.grab_bar_loose",
"bath.mildew_surface",
"blinds.bent_missing",
"blinds.dirty",
"cabinet.base_water_damage",
"cabinet.damage_widespread",
"cabinet.door_damaged",
"cabinet.hinge_loose",
"cabinet.surface_worn",
"carpet.burn",
"carpet.odor",
"carpet.pet_urine",
"carpet.replace_room",
"carpet.soiled",
"carpet.stained",
"carpet.torn_frayed",
"carpet.water_damaged",
"carpet.worn_matted",
"carpet.wrinkled",
"caulk.sink_counter",
"ceiling.hole",
"ceiling.nail_pops",
"ceiling.popcorn_damaged",
"ceiling.repaint",
"ceiling.sagging",
"ceiling.stain",
"ceiling_fan.broken",
"clean.general",
"clean.heavy_soil",
"clean.windows",
"closet.shelf_rod_missing",
"countertop.chip_burn",
"countertop.replace",
"countertop.substrate_exposed",
"dishwasher.inoperable",
"dishwasher.missing",
"disposal.inoperable",
"door.closet_off_track",
"door.entry_damaged",
"door.entry_sticks",
"door.fire_rated_defect",
"door.interior_hole",
"door.interior_replace",
"door.interior_sticks",
"door.slider_damaged",
"door.stop_missing",
"door.weatherstrip_missing",
"drain.blocked_slow",
"electrical.exposed_wiring",
"electrical.panel_issue",
"exhaust_fan.inoperable",
"exterior.paint_peeling",
"exterior.walkway_hazard",
"faucet.leak",
"fence.pool_gate_latch",
"fireplace.firebox_damaged",
"floor.soft_spot",
"floor.substrate_exposed",
"floor.trip_hazard",
"garage.cleanout",
"garage.door_inoperable",
"garage.safety_reverse",
"general.manual_quote",
"gfci.missing",
"gfci.test_fail",
"hardwood.water_stain",
"hardwood.worn",
"hvac.dryer_duct_unsafe",
"hvac.dryer_vent_lint",
"hvac.filter_dirty",
"hvac.minisplit_dirty",
"hvac.not_heating_cooling",
"hvac.vent_cover_missing",
"key.missing",
"laundry.hookup_leak",
"lead.paint_concern",
"leak.active_water",
"leak.moisture_elevated",
"leak.sewage",
"light.bulb_out",
"light.fixture_damaged",
"lock.deadbolt_broken",
"lock.knob_broken",
"lock.rekey",
"lock.smart_reset",
"lvp.plank_damaged",
"lvp.water_swollen",
"mailbox.lock_change",
"microwave.inoperable",
"microwave.otr_inoperable",
"mirror.damaged",
"mold.visible_large",
"mold.visible_medium",
"mold.visible_small",
"odor.general",
"outlet.burn_marks",
"outlet.dead",
"patio.dirty",
"pest.bedbugs",
"pest.inspection_turn",
"pest.other",
"pest.roaches",
"pest.rodents",
"range.anti_tip_missing",
"range.burner_inoperable",
"range.cooktop_glass_cracked",
"range.dirty",
"range.missing",
"range.oven_not_heating",
"range.parts_missing",
"range.replace",
"range_hood.filter",
"refrigerator.dirty",
"refrigerator.gasket",
"refrigerator.ice_maker",
"refrigerator.missing",
"refrigerator.not_cooling",
"refrigerator.parts_missing",
"remote.garage_missing",
"safety.biohazard",
"safety.co_alarm_inoperable",
"safety.co_alarm_missing",
"safety.combustible_clearance",
"safety.drug_contamination",
"safety.egress_blocked",
"safety.extinguisher_expired",
"safety.fire_damage",
"safety.gas_leak",
"safety.guardrail_stairs",
"safety.hazardous_material",
"safety.hvac_combustion",
"safety.pest_extensive",
"safety.roof_envelope",
"safety.sharp_edge",
"safety.smoke_alarm_expired",
"safety.smoke_alarm_missing",
"safety.structural_concern",
"shower.door_damaged",
"shower.head_leaks",
"shower.rod_missing",
"sink.chipped",
"sink.not_draining",
"switch.broken",
"switch_plate.missing",
"tenant_alteration.restore",
"thermostat.broken",
"tile.cracked",
"tile.grout_dirty",
"tile.grout_missing",
"toilet.clog",
"toilet.cracked",
"toilet.loose_base",
"toilet.running",
"toilet.seat_broken",
"trash.heavy",
"trash.light",
"trim.baseboard_damaged",
"trim.casing_damaged",
"trim.paint_worn",
"tub.caulk_failed",
"tub.chip_stain",
"tub.surround_damaged",
"unit.final_inspection",
"vanity.damaged",
"vct.dull_dirty",
"vinyl.lifting",
"vinyl.torn_gouged",
"walls.crack_hairline",
"walls.crack_structural",
"walls.hole_medium",
"walls.holes_many",
"walls.holes_multiple",
"walls.large_hole",
"walls.nail_holes",
"walls.paint_aged",
"walls.paint_peeling",
"walls.paint_unauthorized_color",
"walls.scuffs",
"walls.smoke_nicotine",
"walls.stain_other",
"walls.stain_water",
"walls.surface_detached",
"walls.texture_damage",
"walls.touchup",
"walls.wallpaper",
"washer_dryer.inoperable",
"water_heater.no_hot_water",
"window.glass_cracked",
"window.lock_broken",
"window.screen_missing",
"window.screen_torn",
"window.seal_failed",
"window.sill_damaged",
"window.stuck",
"yard.debris",
"yard.overgrown"
],
"type": "string",
"maxLength": 80,
"description": "From property_turnover_issue_codes; general.manual_quote if none fits. Send it on every finding."
},
"notes": {
"type": "string",
"maxLength": 1000,
"description": "More words; never billed or coded from."
},
"rooms": {
"$ref": "#/$defs/Rooms",
"type": "object",
"description": "Which rooms: {\"area\": \"bedroom\", \"count\": 3} is all 3 bedrooms; labels name them."
},
"extent": {
"$ref": "#/$defs/Extent",
"type": "string",
"description": "Only when the words say how far; else the code's default."
},
"status": {
"$ref": "#/$defs/FindingStatus",
"type": "string",
"default": "needed",
"description": "Only needed (the default) bills; on a hazard, done/ok/none without evidence stays Possible."
},
"evidence": {
"$ref": "#/$defs/Evidence",
"type": "object",
"description": "The test behind the finding; the only thing that clears a hazard."
},
"location": {
"$ref": "#/$defs/Location",
"type": "object",
"description": "{\"area\": \"bedroom\", \"label\": \"Bedroom 2\"} or \"Bedroom 2\"; older form of rooms."
},
"material": {
"type": "string",
"maxLength": 80,
"description": "The material as written, e.g. '9x9 vinyl tile'."
},
"quantity": {
"$ref": "#/$defs/Quantity",
"type": "object",
"description": "Measured amount (120 sq_ft), only as the words give it."
},
"confirmed": {
"type": "boolean",
"default": false,
"description": "true: the person checked this AI-read finding."
},
"repeat_of": {
"type": "string",
"pattern": "^[A-Za-z0-9_.:-]+$",
"maxLength": 40,
"description": "Id of an earlier finding this repeats in its own rooms (\"same as above\"); it bills."
},
"area_sq_ft": {
"anyOf": [
{
"type": "number",
"maximum": 100000,
"exclusiveMinimum": 0
},
{
"type": "string",
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
}
],
"description": "Measured area in sq ft; wins over a derived size."
},
"extraction": {
"$ref": "#/$defs/Extraction",
"type": "object",
"description": "Set by our AI reader; send it back unchanged."
},
"description": {
"type": "string",
"maxLength": 300,
"description": "Deprecated; send code, with the words in source_text."
},
"source_text": {
"type": "string",
"maxLength": 1000,
"description": "The original words. Scanned for hazards; never billed or coded from."
},
"signature_like": {
"type": "boolean",
"default": false,
"description": "Set by our reader; send it back unchanged."
},
"year_statement": {
"$ref": "#/$defs/YearStatement",
"type": "object",
"description": "A year said in passing (\"built 1972\"); never sets property.year_built."
}
},
"description": "One observed condition, coded from the issue-code catalog.",
"additionalProperties": false
},
"Profile": {
"enum": [
"minimum",
"standard",
"premium"
],
"type": "string"
},
"Decision": {
"type": "object",
"required": [
"action"
],
"properties": {
"note": {
"type": "string",
"maxLength": 300
},
"action": {
"enum": [
"approve",
"decline",
"defer"
],
"type": "string"
},
"line_id": {
"type": "string"
},
"finding_id": {
"type": "string"
}
},
"description": "Owner decision. Re-run the same request with decisions to get the approved scope.",
"additionalProperties": false
},
"Evidence": {
"type": "object",
"required": [
"kind",
"result"
],
"properties": {
"kind": {
"$ref": "#/$defs/EvidenceKind",
"type": "string"
},
"result": {
"$ref": "#/$defs/EvidenceResult",
"type": "string"
},
"material": {
"type": "string",
"maxLength": 80,
"description": "What was tested, e.g. 'kitchen floor tile'."
}
},
"description": "A negative or working result clears: inspector any hazard; lab mold, lead, asbestos, drugs, sewage; meter moisture; self_test or checklist a device.",
"additionalProperties": false
},
"Location": {
"type": "object",
"properties": {
"area": {
"$ref": "#/$defs/Area",
"type": "string",
"default": "whole_unit"
},
"label": {
"type": "string",
"maxLength": 60,
"description": "e.g. 'Bedroom 2'."
}
},
"description": "Where; a plain string works too.",
"additionalProperties": false
},
"Quantity": {
"type": "object",
"required": [
"value",
"uom"
],
"properties": {
"uom": {
"$ref": "#/$defs/Uom",
"type": "string"
},
"value": {
"anyOf": [
{
"type": "number",
"maximum": 100000,
"exclusiveMinimum": 0
},
{
"type": "string",
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
}
]
}
},
"additionalProperties": false
},
"UnitType": {
"enum": [
"apartment",
"single_family",
"townhome",
"condo",
"duplex",
"manufactured"
],
"type": "string"
},
"Occupants": {
"type": "object",
"properties": {
"elderly": {
"type": "boolean",
"description": "Housing for the elderly (62+) or disabled; not just an older tenant."
},
"hud_assisted": {
"type": "boolean"
},
"child_under_6": {
"type": "boolean",
"description": "A child under 6 lives there or is expected to."
}
},
"description": "property.occupants: lead-rule facts about the unit; leave out what is unknown.",
"additionalProperties": false
},
"PriceTier": {
"enum": [
"bulk_contract",
"pm_vendor",
"retail_one_off"
],
"type": "string",
"description": "Purchasing channel. Library prices are pm_vendor (a PM buying turns from a local vendor)."
},
"Standards": {
"type": "object",
"properties": {
"profile": {
"$ref": "#/$defs/Profile",
"type": "string",
"default": "standard",
"description": "minimum (rent-ready basics) | standard | premium (higher finish)."
},
"overrides": {
"$ref": "#/$defs/StandardsOverrides",
"type": "object"
},
"price_tier": {
"$ref": "#/$defs/PriceTier",
"type": "string",
"default": "pm_vendor",
"description": "bulk_contract (portfolio contract prices) | pm_vendor (default: a property manager buying turns from local vendors) | retail_one_off (an owner hiring one contractor)."
}
},
"additionalProperties": false
},
"Extraction": {
"type": "object",
"required": [
"method",
"model",
"confidence"
],
"properties": {
"cell": {
"enum": [
"move_out",
"move_in",
"comment"
],
"type": "string",
"description": "ai_document: the cell read; move_in bills nothing, a hazard there is Possible."
},
"page": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "ai_document: the page read."
},
"model": {
"type": "string",
"pattern": "^[A-Za-z0-9._:() -]+$",
"maxLength": 80,
"description": "The model that read it."
},
"quote": {
"type": "string",
"maxLength": 300,
"description": "ai_document: the row's words, contacts masked."
},
"method": {
"enum": [
"ai",
"ai_document"
],
"type": "string",
"description": "ai: free text; ai_document: an upload's page."
},
"review": {
"$ref": "#/$defs/ExtractionReview",
"type": "object",
"description": "ai_document: the person's review."
},
"confidence": {
"anyOf": [
{
"type": "number",
"maximum": 1,
"minimum": 0
},
{
"type": "string",
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
}
],
"description": "0 to 1."
},
"prompt_version": {
"type": "string",
"pattern": "^[A-Za-z0-9._:-]+$",
"maxLength": 40,
"description": "The reader's prompt version."
}
},
"description": "How our AI reader read a finding; never changes the price.",
"additionalProperties": false
},
"Constraints": {
"type": "object",
"properties": {
"occupied": {
"type": "boolean",
"default": false,
"description": "Work happens while occupied."
},
"access_notes": {
"type": "string",
"maxLength": 500
},
"target_ready_date": {
"type": "string",
"format": "date"
}
},
"additionalProperties": false
},
"EvidenceKind": {
"enum": [
"self_test",
"lab",
"meter",
"checklist",
"inspector"
],
"type": "string"
},
"FindingStatus": {
"enum": [
"needed",
"done",
"ok",
"none"
],
"type": "string"
},
"RateCardEntry": {
"type": "object",
"properties": {
"uom": {
"$ref": "#/$defs/Uom",
"type": "string",
"description": "Required with a trade unit_price; a task in another uom ignores it."
},
"trade": {
"$ref": "#/$defs/Trade",
"type": "string"
},
"task_id": {
"type": "string",
"description": "A task_id from a result line, e.g. 'carpet.clean'."
},
"min_charge": {
"anyOf": [
{
"type": "number",
"minimum": 0
},
{
"type": "string",
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
}
],
"description": "USD per trade visit."
},
"unit_price": {
"anyOf": [
{
"type": "number",
"minimum": 0
},
{
"type": "string",
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
}
],
"description": "USD per uom; on a trade entry, uom 'hour' is its labor rate."
},
"trip_charge": {
"anyOf": [
{
"type": "number",
"minimum": 0
},
{
"type": "string",
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
}
],
"description": "USD per trade visit."
}
},
"description": "Your price for a task_id (from a result line) or a trade, e.g. {\"trade\": \"handyman\", \"unit_price\": 65, \"uom\": \"hour\"}.",
"additionalProperties": false
},
"YearStatement": {
"type": "object",
"required": [
"text"
],
"properties": {
"text": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The words, e.g. 'built in 1972'."
},
"year": {
"type": "integer",
"maximum": 2100,
"minimum": 1700,
"description": "Left out when the words name none ('Victorian')."
}
},
"description": "When the words say it was built.",
"additionalProperties": false
},
"EvidenceResult": {
"enum": [
"negative",
"positive",
"working",
"failed"
],
"type": "string"
},
"FloorCoverings": {
"type": "object",
"properties": {
"carpet_sq_ft": {
"type": "integer",
"maximum": 20000,
"minimum": 0,
"description": "Carpeted area; omit to assume 60% of the floor area."
},
"carpet_present": {
"type": "boolean",
"description": "Is there wall-to-wall carpet? Omit if unknown."
}
},
"description": "What is on the floors. Drives the carpet-cleaning baseline and whole-unit carpet sizes.",
"additionalProperties": false
},
"PropertyProfile": {
"type": "object",
"required": [
"zip",
"bedrooms",
"bathrooms"
],
"properties": {
"zip": {
"type": "string",
"pattern": "^\\d{5}$",
"description": "5-digit US ZIP code (no street address)."
},
"sq_ft": {
"type": "integer",
"maximum": 20000,
"minimum": 150,
"description": "Interior sq ft."
},
"bedrooms": {
"type": "integer",
"maximum": 12,
"minimum": 0,
"description": "0 for a studio."
},
"bathrooms": {
"anyOf": [
{
"type": "number",
"maximum": 12,
"minimum": 0,
"multipleOf": 0.5
},
{
"type": "string",
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
}
],
"description": "Half baths count as 0.5."
},
"occupants": {
"$ref": "#/$defs/Occupants",
"type": "object"
},
"unit_type": {
"$ref": "#/$defs/UnitType",
"type": "string",
"default": "apartment",
"description": "Single-family homes and townhomes get house-sized defaults (floor area, locks)."
},
"walk_date": {
"type": "string",
"format": "date",
"description": "The walk-through date; dated or aged items are judged against it. Omit if unknown."
},
"year_built": {
"type": "integer",
"maximum": 2100,
"minimum": 1700,
"description": "Drives lead-paint (pre-1978) handling."
},
"floor_coverings": {
"$ref": "#/$defs/FloorCoverings",
"type": "object"
}
},
"additionalProperties": false
},
"ExtractionReview": {
"type": "object",
"required": [
"outcome"
],
"properties": {
"flags": {
"type": "array",
"items": {
"enum": [
"low_confidence",
"medium_confidence",
"quote_not_on_page",
"count_unread",
"column_unclear",
"ok_unclear",
"unmatched",
"ai_match"
],
"type": "string"
},
"maxItems": 8,
"description": "Why the page flagged it."
},
"changed": {
"type": "array",
"items": {
"enum": [
"description",
"area",
"label",
"extent",
"quantity"
],
"type": "string"
},
"maxItems": 5,
"description": "edited: the fields changed."
},
"outcome": {
"enum": [
"confirmed",
"accepted",
"edited"
],
"type": "string"
}
},
"description": "A person's review of a finding read from a document.",
"additionalProperties": false
},
"StandardsOverrides": {
"type": "object",
"properties": {
"markup_pct": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"minimum": 0
},
{
"type": "string",
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
}
],
"description": "Management markup applied to every line, %."
},
"always_rekey": {
"type": "boolean"
},
"paint_policy": {
"enum": [
"touch_up",
"full_if_needed",
"always_full"
],
"type": "string"
},
"carpet_policy": {
"enum": [
"clean",
"replace_if_damaged",
"always_replace"
],
"type": "string"
},
"cleaning_policy": {
"enum": [
"standard",
"deep"
],
"type": "string"
}
},
"additionalProperties": false
}
},
"examples": [
{
"findings": [
{
"code": "carpet.pet_urine",
"rooms": "Bedroom 2",
"source_text": "pet urine in BR2 carpet"
},
{
"code": "blinds.bent_missing",
"quantity": {
"uom": "each",
"value": 3
}
}
],
"property": {
"zip": "43215",
"bedrooms": 2,
"bathrooms": 1
}
}
],
"required": [
"property"
],
"properties": {
"read": {
"enum": [
"parser",
"ai"
],
"type": "string",
"default": "parser",
"description": "Deprecated and ignored: scope reads no free text."
},
"audit": {
"type": "boolean",
"default": false,
"description": "Also return the run record and input/result hashes (an audit trail). Default false."
},
"detail": {
"enum": [
"summary",
"full"
],
"type": "string",
"default": "summary",
"description": "summary (small): totals, trade scopes, lines tied to findings. full: all."
},
"retain": {
"type": "boolean",
"default": true,
"description": "false: keep none of this request's text."
},
"findings": {
"type": "array",
"items": {
"$ref": "#/$defs/Finding"
},
"maxItems": 200,
"description": "Move-out inspection findings, one per observed condition, coded."
},
"property": {
"$ref": "#/$defs/PropertyProfile",
"type": "object",
"description": "The unit: ZIP, size, type and year built."
},
"tax_rate": {
"anyOf": [
{
"type": "number",
"maximum": 0.2,
"minimum": 0
},
{
"type": "string",
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
}
],
"description": "Applied to all lines, e.g. 0.07."
},
"decisions": {
"type": "array",
"items": {
"$ref": "#/$defs/Decision"
},
"maxItems": 500,
"description": "Approve, decline or defer lines or findings from an earlier scope."
},
"rate_card": {
"type": "array",
"items": {
"$ref": "#/$defs/RateCardEntry"
},
"maxItems": 300,
"description": "Your own prices by trade or task; they replace the regional library prices."
},
"standards": {
"$ref": "#/$defs/Standards",
"type": "object",
"description": "Finish profile, price tier and overrides."
},
"client_refs": {
"type": "object",
"description": "Your own reference ids (e.g. unit_id, job_id). Echoed back, never used.",
"additionalProperties": {
"type": "string"
}
},
"constraints": {
"$ref": "#/$defs/Constraints",
"type": "object",
"description": "Target ready date, whether the unit is occupied, access notes."
},
"serve_pinned": {
"type": "boolean",
"default": false,
"description": "Optional, with expect_ruleset. True asks us to answer under the ruleset version you named even when the rules have changed since, from that version's signed bundle; the result says so in served_from_bundle. We refuse a version older than 12 months, one a later correction or law change replaced for your state, or one we have no bundle for. Default false: the call fails whenever the rules have changed."
},
"trade_vendors": {
"type": "object",
"description": "Vendor (name, contact) to put on each trade's lines.",
"propertyNames": {
"$ref": "#/$defs/Trade"
},
"additionalProperties": {
"$ref": "#/$defs/Vendor"
}
},
"expect_ruleset": {
"type": "string",
"description": "Optional. The ruleset version you built against, as results carry it in provenance.ruleset_version (e.g. 'sd-2026.11.14'), or its sha256: hash. If the rules have changed since, the call fails (nothing computed, nothing charged) and says what is in force now, so you upgrade on your own schedule. Leave it out to answer under the current rules."
}
}
}