---
title: "Quick turnover estimate (start here)"
method: POST
path: "/v1/property-turnover/quick_estimate"
tags: ["property-turnover"]
---

# Quick turnover estimate (start here)

`POST /v1/property-turnover/quick_estimate`

## Request

- Base URL: `https://tektolo.com` (the document declares no server; this is the origin it was published from)
- URL: `https://tektolo.com/v1/property-turnover/quick_estimate`
- Auth: none declared

The easiest way to estimate a US rental turnover: a ZIP, bedrooms, bathrooms and a list of plain-English issues in, a priced make-ready estimate out (same engine and result as property_turnover_scope, short summary by default).
When to use: Start here when the user describes a move-out or turnover in words ("carpet stained in both bedrooms, oven filthy") and wants a cost estimate or what to tell vendors. Use property_turnover_scope instead for rate cards, owner decisions, measured quantities or per-finding control.
Not for: Diagnosing hazards (they come back as refer-outs to a licensed professional); legal advice on deposits; remodel bids; properties outside the US.
Tip: If the ZIP is missing or not exactly 5 digits, ask the user; never guess or pad it.
Tip: Relay refer_outs first, then the totals; say the numbers are estimates, not quotes.
Tip: When review.status is draft, show the user what was read (reading.findings) before you quote a number, and ask them to confirm or correct it. For a verified answer, send the confirmed findings to property_turnover_scope with confirmed true on each. A review.note means the AI reader was unavailable and the text was read by the matcher alone.
Tip: From photos, list only what is visible, with its room ("stained carpet in bedroom 2"). Hedge doubtful ones ("possible water stain?") and ask the user about what photos cannot show: working appliances, odors, pests, sizes. Never state that a tenant caused damage because of a photo.
The same findings under the same ruleset return the same result_hash. An AI model may read notes into findings, marked for review (review, reading); it never sets a number. Capability property-turnover v0.14.0 (experimental).

## Headers

- `Tektolo-Ruleset` string, nullable: 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.
- `Tektolo-Ruleset-Mode` string, nullable: Optional, with Tektolo-Ruleset. 'serve' asks us to answer under the ruleset version you named even when the rules have changed since, from that version's signed bundle. The answer says so (run.served_from_bundle and the Tektolo-Ruleset-Served header). We refuse (412, with a reason) a version older than 12 months, one a later correction or law change replaced for your state, or one we have no bundle for. Leave it out to be refused whenever the rules have changed.

## Request body

- QuickEstimateRequest: Flat, primitive-only input: the easiest way to get an estimate.
  - `client_refs` object, nullable: Your own reference ids (e.g. unit_id, job_id). Echoed back, never used.
  - `zip` string, required: 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.
  - `bedrooms` integer, required: 0 for a studio.
  - `bathrooms` union, required: Half baths count as 0.5.
    - number
    - string
  - `issues` string[]: 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.
  - `unit_type` 'apartment' | 'single_family' | 'townhome' | 'condo' | 'duplex' | 'manufactured'
  - `sq_ft` integer, nullable: Interior sq ft.
  - `year_built` integer, nullable: Drives lead-paint (pre-1978) handling.
  - `carpet` boolean, nullable: Is there wall-to-wall carpet?
  - `profile` 'minimum' | 'standard' | 'premium'
  - `price_tier` 'bulk_contract' | 'pm_vendor' | 'retail_one_off': Purchasing channel. Library prices are pm_vendor (a PM buying turns from a local vendor).
  - `detail` 'summary' | 'full': summary (default, small) or full (every line).
  - `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.

## Response `200`

Successful Response

- PropertyTurnoverQuickEstimateResponse
  - `run` RunInfo, required
    - `id` string, required
    - `capability` string, required
    - `operation` string, required
    - `contract_version` string, required
    - `engine_version` string, required
    - `ruleset_version` string, nullable, required
    - `ruleset_hash` string, nullable, required
    - `received_at` string, date-time, required
    - `duration_ms` integer, required
  - `client_refs` object
  - `input_hash` string, required: Hash of the question the rules answered: the request, or when free text was read by AI, the request with the read findings (reading.findings) in its place.
  - `request_hash` string, nullable: Hash of the request as sent; equal to input_hash when nothing was read by AI.
  - `result_hash` string, required
  - `review` Review: How far the answer can be relied on.
    - `status` 'draft' | 'verified': draft: some findings were read by AI and nobody has confirmed them; show the person reading.findings before quoting a number. verified: no finding rests on an unconfirmed AI reading.
    - `ai_read` integer: Findings in this answer read by AI and not yet confirmed.
    - `reading_id` string, nullable: The reading's id, when one was made.
    - `note` string, nullable: Set when an AI reading was asked for but not made (capped or unavailable).
  - `reading` Reading: The findings software read from free text or a document for this answer. Send ``findings`` (edited if need be, each one checked marked ``confirmed: true``) to the rules operation to get a verified answer.
    - `id` string, required: rd_ and 24 hex characters; the same reading has the same id.
    - `kind` 'notes' | 'document', required
    - `model` string, nullable: The model that read it.
    - `prompt_version` string, nullable
    - `counts` ReadingCounts, required
      - `total` integer, required: Findings in the reading.
      - `ai` integer, required: Findings the AI model read (they carry extraction).
      - `parser` integer, required: Findings the deterministic parser and matcher placed, or sent coded.
      - `unmatched` integer, required: Findings nothing could match (manual-quote lines).
    - `findings` object[], required: Every finding the rules answered, in the shape the rules operation accepts.
    - `notes` ReadingNote[]: What the reading noticed while reading the text.
      - `kind` 'warning' | 'assumption', required
      - `code` string, required
      - `message` string, required
      - `finding_ids` string[]
  - `signature` ResultSignature: Tektolo's signature over where this result came from (docs/verify.md).
    - `jws` string, required: Compact JWS (RFC 7515), alg EdDSA (Ed25519, RFC 8037), typ tektolo-provenance+jws. Check it against the keys at /.well-known/tektolo-signing-keys.json on the site you called, never against a key a document points to.
    - `payload` ResultSignaturePayload, required: The signed statement (the JWS payload, decoded). Canonical JSON: sorted keys, compact.
      - `v` integer, required: Payload format version (1).
      - `kid` string, required: Id of the key that signed it (see the signing keys file).
      - `capability` string, required
      - `operation` string, required
      - `ruleset_version` string, nullable, required
      - `ruleset_hash` string, nullable, required
      - `engine_version` string, required
      - `result_hash` string, required: The envelope's result_hash.
      - `issued_at` string, required: When the result was computed (UTC, RFC 3339).
    - `verify_code` string, required: A short code that proves the result came from Tektolo without carrying its figures. Safe to print on a document; the payload above is not meant to be shared.
    - `verify_url` string, required: Where anyone can check the code.
  - `result` ScopeResult, required: Field order puts the answer first (summary, disclaimer, totals, refer-outs, vendor scopes), because agents and truncating clients read from the top.
    - `summary` string, required
    - `disclaimer` string, required
    - `warnings` TektoloPropertyTurnoverModelsNote[], required
      - `code` string, required
      - `message` string, required
      - `finding_ids` string[]
      - `line_ids` string[]
    - `totals` TektoloPropertyTurnoverModelsTotals, required
      - `note` string: How the totals add up.
      - `must` CostRange, required: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
      - `should` CostRange, required: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
      - `could` CostRange, required: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
      - `visit_charges` CostRange, required: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
      - `subtotal` CostRange, required: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
      - `tax` CostRange, required: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
      - `total` CostRange, required: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
      - `approved` CostRange: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
      - `to_confirm` CostRange: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
      - `allowances` CostRange: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
    - `refer_outs` ReferOutNote[], required
      - `id` string, required
      - `name` string, required
      - `professional` string, required
      - `reason` string, required
      - `kind` 'assessment' | 'compliance', required
      - `finding_ids` string[], required
      - `blocks_phases` Phase[], required
      - `sources` string[], required
      - `unconfirmed` boolean: No finding confirms it: every finding that raised it was hedged ('maybe', '?'), named the hazard only in its words (description, notes or location label) rather than through its issue code, or stated an older year built than the request. Check first; no phase is held until the condition is confirmed (see warnings).
    - `trade_scopes` TradeScope[], required
      - `trade` 'hauling' | 'handyman' | 'drywall' | 'painter' | 'flooring' | 'carpet_cleaning' | 'cleaning' | 'appliance' | 'plumber' | 'electrician' | 'hvac' | 'locksmith' | 'pest_control' | 'landscaping' | 'window_glass' | 'environmental' | 'structural', required
      - `vendor` Vendor, required
        - `name` string, required
        - `contact` string, nullable
      - `phase` 'assess' | 'trash_out' | 'repairs' | 'paint' | 'flooring' | 'appliances' | 'cleaning' | 'punch' | 'rekey', required: Make-ready order. Values sort in sequence order via PHASE_ORDER.
      - `line_ids` string[], required
      - `lines_cost` CostRange, required: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
      - `visit_charges` CostRange, required: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
      - `total` CostRange, required: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
    - `lines` ScopeLine[], required
      - `line_id` string, required
      - `task_id` string, required
      - `name` string, required
      - `trade` 'hauling' | 'handyman' | 'drywall' | 'painter' | 'flooring' | 'carpet_cleaning' | 'cleaning' | 'appliance' | 'plumber' | 'electrician' | 'hvac' | 'locksmith' | 'pest_control' | 'landscaping' | 'window_glass' | 'environmental' | 'structural', required
      - `phase` 'assess' | 'trash_out' | 'repairs' | 'paint' | 'flooring' | 'appliances' | 'cleaning' | 'punch' | 'rekey', required: Make-ready order. Values sort in sequence order via PHASE_ORDER.
      - `priority` 'must' | 'should' | 'could', required
      - `location` string, required
      - `finding_ids` string[], required
      - `quantity` string, required
      - `uom` 'each' | 'sq_ft' | 'sq_ft_floor' | 'sq_yd' | 'linear_ft' | 'room' | 'unit' | 'bedroom' | 'bathroom' | 'hour' | 'cu_yd' | 'visit', required
      - `quantity_source` 'given' | 'converted' | 'derived' | 'fixed', required
      - `price_basis` 'rate_card' | 'unit_price' | 'labor_derived' | 'placeholder', required
      - `unit_price` CostRange, required: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
      - `cost` CostRange, required: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
      - `confidence` 'sourced' | 'industry_typical' | 'assumed', required
      - `responsibility_hint` 'normal_wear' | 'likely_damage' | 'elective' | 'undetermined' | 'mixed', required
      - `responsibility_by_finding` object: Set when the hint is 'mixed': the hint of each finding the line carries.
      - `allowance` CostRange: A low / expected / high estimate. Invariant: low <= expected <= high.
        - `low` string, required
        - `expected` string, required
        - `high` string, required
      - `refer_out` string, nullable
      - `decision` 'approve' | 'decline' | 'defer', nullable
      - `vendor_note` string, nullable
      - `rule_ids` string[], required
      - `to_confirm` boolean: Every finding behind this line is uncertain, or it is a check-first assessment from the safety net: priced, but not in must/should/could, subtotal or total (see totals.to_confirm).
      - `needs_review` boolean: A finding behind this line was read by AI from free text: check it.
    - `lines_omitted` integer, required: Lines left out in summary detail.
    - `omitted_note` string, nullable: Summary detail: what the omitted lines are (routine turn tasks) and their cost.
    - `findings` FindingResolution[], required
      - `finding_id` string, required
      - `resolution` 'exact' | 'alias' | 'ai' | 'unmapped' | 'not_billed', required: exact: a taxonomy code was given. alias: the description matched an alias. ai: an AI reader chose the code from free text (see confidence, model; needs_review). unmapped: nothing matched (a manual-quote line). not_billed: the description says there is no work ('no mold', 'faucet not leaking', 'fixed by tenant'), or names no condition at all (a heading, a date, a signature, a year built); no lines, no refer-out; the issue it names is in suggestions and the reason in not_billed_reason.
      - `not_billed_reason` 'none' | 'ok' | 'done' | 'no_condition', nullable: resolution 'not_billed' only. none: says there is none ('no mold', 'mold: none'); ok: says it works or is fine; done: says it was already fixed or done; no_condition: names no condition, only a heading, small talk, a signature or the year built ('Unit 4B - Move-out inspection', 'Built 1972'), see match_note (contract 0.13.0, additive).
      - `code` string, nullable, required
      - `issue_name` string, nullable: Readable name of `code`.
      - `text` string, nullable: The finding as given (description, else code), for tracing.
      - `location` string, nullable: Where, as shown on the lines.
      - `match_note` string, nullable: Set when the match left a fixture word unexplained: check this finding.
      - `uncertain` boolean: The description hedges ('mold?', 'maybe', 'possible'). Refer-outs from uncertain findings are check-first: they add a warning instead of holding phases.
      - `extent` 'spot' | 'partial' | 'full', required
      - `suggested_codes` string[]
      - `suggestions` Suggestion[]: Suggested codes with readable names, best first.
        - `code` string, required
        - `name` string, required
      - `line_ids` string[]
      - `needs_review` boolean: True when software read this finding from free text (resolution 'ai'): check it.
      - `confidence` string, nullable: resolution 'ai' only: the AI reader's confidence, 0 to 1.
      - `model` string, nullable: resolution 'ai' only: the model used.
      - `confirmed` boolean: Read by AI and confirmed by the person (sent with confirmed: true).
    - `assumptions` TektoloPropertyTurnoverModelsNote[], required
      - `code` string, required
      - `message` string, required
      - `finding_ids` string[]
      - `line_ids` string[]
    - `unknowns` TektoloPropertyTurnoverModelsNote[], required
      - `code` string, required
      - `message` string, required
      - `finding_ids` string[]
      - `line_ids` string[]
    - `rate_card_applied` RateCardUse[]: Rate card entries that changed a price.
      - `entry` integer, required: 0-based index into the request's rate_card.
      - `target` string, required: The entry's task_id or trade.
      - `lines` string[]
      - `note` string, required
    - `rate_card_ignored` RateCardUse[]: Rate card entries that changed nothing, with why.
      - `entry` integer, required: 0-based index into the request's rate_card.
      - `target` string, required: The entry's task_id or trade.
      - `lines` string[]
      - `note` string, required
    - `detail` 'summary' | 'full', required
    - `region` Region, required
      - `zip3` string, required
      - `basis` 'metro' | 'nonmetro' | 'national', required
      - `cbsa` string, nullable
      - `cbsa_name` string, nullable
      - `vintage` string, nullable
    - `profile` 'minimum' | 'standard' | 'premium', required
    - `price_tier` 'bulk_contract' | 'pm_vendor' | 'retail_one_off': Purchasing channel. Library prices are pm_vendor (a PM buying turns from a local vendor).
    - `provenance` TektoloPropertyTurnoverModelsProvenance, required
      - `ruleset_id` string, required
      - `ruleset_version` string, required
      - `ruleset_hash` string, required
      - `sources` SourceRef[], required
        - `id` string, required
        - `title` string, required
        - `url` string, nullable

## Other responses

- `412`: The ruleset named in the Tektolo-Ruleset header is not the one in force, and the call did not ask to be answered under it (Tektolo-Ruleset-Mode: serve) or that version can't be served (the body's reason says why). Nothing was computed or charged; the body names both versions.
- `422`: Validation Error

---

[API](https://skmtc.dev/tektolo/apis/tektolo-capabilities-api.md) · [All operations](https://skmtc.dev/tektolo/apis/tektolo-capabilities-api/llms.txt) · [OpenAPI document](https://skmtc.dev/tektolo/apis/tektolo-capabilities-api/revisions/6e667aa7b6f7?raw)
