Tektolo

All tools

Property Turnover Scopeexperimentalv0.3.0

A vendor-ready turnover scope and owner estimate in minutes, with every number traced to a source or a stated assumption.

Turn move-out inspection findings into a priced, vendor-ready make-ready scope.

Try it freeNo sign-in, no account.

Who it is for: property managers, landlords, maintenance coordinators, AI agents working for them

Issue codes
189
Repair tasks
164
Refer-out rules
23
Cited sources
191

When to use it

Quick turnover estimate (start here)

Use it when: 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.

Do not use it for: Diagnosing hazards (they come back as refer-outs to a licensed professional); legal advice on deposits; remodel bids; properties outside the US.

  • If the ZIP is missing or not exactly 5 digits, ask the user; never guess or pad it.
  • Relay refer_outs first, then the totals; say the numbers are estimates, not quotes.

Scope a property turnover

Use it when: Turnover findings for a US rental unit when you need control: rooms, measured quantities, the manager's own prices (rate_card) or owner approve/decline decisions. For a plain-English estimate, property_turnover_quick_estimate is easier.

Do not use it for: Diagnosing hazards (they come back as refer-outs to a licensed professional); legal advice on deposits; remodel bids; properties outside the US.

  • If the ZIP is missing or not exactly 5 digits, ask the user; never guess or pad it.
  • Describe each finding in plain words (description). Only send a code returned by property_turnover_issue_codes; unmatched findings come back as manual-quote lines with suggested codes.
  • Give measured quantities when you have them (e.g. 120 sq_ft); otherwise typical sizes are assumed and listed in assumptions.
  • Send property.floor_coverings when you know whether there is carpet, and unit_type single_family for houses; both change the baseline scope.
  • Relay the summary, refer_outs, assumptions and unknowns to the user; costs are estimate ranges, not quotes.
  • Use detail=summary unless the user asks for every line item (summary already lists every line tied to a finding).
  • Rate card examples - {"trade": "handyman", "unit_price": 65, "uom": "hour"} or {"task_id": "carpet.clean", "unit_price": 0.2, "uom": "sq_ft"}; check rate_card_applied and rate_card_ignored before saying a rate was used.
  • For the Excel workbook or owner estimate, give the user links.web_tool_prefilled (opens the free web tool with these findings) or POST the same arguments to links.downloads; do not build a spreadsheet yourself.

Look up turnover issue codes

Use it when: Only while preparing findings for a turnover cost estimate, to find the standard issue code for something observed (e.g. "pet stain on carpet"). Never needed to answer a question; call with no arguments to list the taxonomy areas and the first codes.

Do not use it for: Legal or security-deposit questions; anything outside rental turnover and make-ready work. Not needed before quick_estimate or scope: a plain description works there.

  • Call with no arguments to list the taxonomy areas (in `areas`) and the first codes.
  • Codes are optional for scoping; a plain description also works.

Try it

Run this in a terminal. The response carries the result, its assumptions, unknowns and a result hash.

Scope a property turnover

From examples/one_bed_charlotte_light_turn.json.

curl -sS 'https://tektolo.com/v1/property-turnover/scope' \
  -H 'content-type: application/json' \
  -d '{
  "property": {
    "zip": "28202",
    "unit_type": "apartment",
    "bedrooms": 1,
    "bathrooms": "1",
    "sq_ft": 700,
    "year_built": 2008
  },
  "standards": {
    "profile": "standard"
  },
  "findings": [
    {
      "id": "f1",
      "location": {
        "area": "bedroom",
        "label": "Bedroom"
      },
      "code": "walls.nail_holes",
      "extent": "partial"
    },
    {
      "id": "f2",
      "location": {
        "area": "living_room",
        "label": "Living room"
      },
      "code": "walls.scuffs",
      "extent": "spot"
    },
    {
      "id": "f3",
      "location": {
        "area": "living_room",
        "label": "Living room"
      },
      "code": "carpet.soiled",
      "extent": "partial",
      "quantity": {
        "value": 220,
        "uom": "sq_ft"
      }
    },
    {
      "id": "f4",
      "location": {
        "area": "bedroom",
        "label": "Bedroom"
      },
      "code": "carpet.soiled",
      "extent": "partial",
      "quantity": {
        "value": 140,
        "uom": "sq_ft"
      }
    },
    {
      "id": "f5",
      "location": {
        "area": "kitchen",
        "label": "Kitchen"
      },
      "code": "range.dirty"
    },
    {
      "id": "f6",
      "location": {
        "area": "kitchen",
        "label": "Kitchen"
      },
      "code": "refrigerator.dirty"
    },
    {
      "id": "f7",
      "location": {
        "area": "living_room",
        "label": "Living room"
      },
      "code": "blinds.bent_missing",
      "quantity": {
        "value": 1,
        "uom": "each"
      }
    }
  ],
  "client_refs": {
    "unit_id": "example-1bed-charlotte"
  }
}'

Operations

OperationRESTMCP toolPricingArtifacts (rendered on request)
Quick turnover estimate (start here)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).POST /v1/property-turnover/quick_estimateproperty_turnover_quick_estimateFree tier (rate limited). Free while experimental; rate limited per client.None
Scope a property turnoverTurn move-out / turnover inspection findings for a US rental unit into a priced make-ready scope: standardized tasks by trade, regional low/expected/high costs, must/should/could priorities, vendor scopes, refer-outs to licensed professionals, and every assumption stated.POST /v1/property-turnover/scopeproperty_turnover_scopeFree tier (rate limited). Free while experimental; rate limited per client.POST /v1/property-turnover/scope/render/xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
POST /v1/property-turnover/scope/render/html text/html
POST /v1/property-turnover/scope/render/csv text/csv
POST /v1/property-turnover/scope/render/vendor_csv application/zip
Look up turnover issue codesSearch the standard taxonomy of rental-unit turnover issues (component.issue codes) by keyword or area.POST /v1/property-turnover/issue_codesproperty_turnover_issue_codesFreeNone

Schemas

Quick turnover estimate (start here): input and output schemas

input.json · output.json

Input

{
  "$defs": {
    "PriceTier": {
      "description": "Purchasing channel. Library prices are pm_vendor (a PM buying turns from a local vendor).",
      "enum": [
        "bulk_contract",
        "pm_vendor",
        "retail_one_off"
      ],
      "title": "PriceTier",
      "type": "string"
    },
    "Profile": {
      "enum": [
        "minimum",
        "standard",
        "premium"
      ],
      "title": "Profile",
      "type": "string"
    },
    "UnitType": {
      "enum": [
        "apartment",
        "single_family",
        "townhome",
        "condo",
        "duplex",
        "manufactured"
      ],
      "title": "UnitType",
      "type": "string"
    }
  },
  "additionalProperties": false,
  "description": "Flat, primitive-only input: the easiest way to get an estimate.",
  "properties": {
    "client_refs": {
      "anyOf": [
        {
          "additionalProperties": {
            "type": "string"
          },
          "type": "object"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Your own reference ids (e.g. unit_id, job_id). Echoed back, never used.",
      "title": "Client Refs"
    },
    "zip": {
      "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.",
      "pattern": "^\\d{5}$",
      "title": "Zip",
      "type": "string"
    },
    "bedrooms": {
      "description": "0 for a studio.",
      "maximum": 12,
      "minimum": 0,
      "title": "Bedrooms",
      "type": "integer"
    },
    "bathrooms": {
      "anyOf": [
        {
          "maximum": 12.0,
          "minimum": 0.0,
          "multipleOf": 0.5,
          "type": "number"
        },
        {
          "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$",
          "type": "string"
        }
      ],
      "description": "Half baths count as 0.5.",
      "title": "Bathrooms"
    },
    "issues": {
      "description": "What was found, one plain-English item per entry, e.g. ['stained carpet in both bedrooms', 'nail holes everywhere', 'oven is filthy']. A room at the start sets the location ('Bedroom 2 carpet torn').",
      "items": {
        "type": "string"
      },
      "maxItems": 100,
      "title": "Issues",
      "type": "array"
    },
    "unit_type": {
      "$ref": "#/$defs/UnitType",
      "default": "apartment"
    },
    "sq_ft": {
      "anyOf": [
        {
          "maximum": 20000,
          "minimum": 150,
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Interior sq ft.",
      "title": "Sq Ft"
    },
    "year_built": {
      "anyOf": [
        {
          "maximum": 2100,
          "minimum": 1700,
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Year Built"
    },
    "carpet": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Is there wall-to-wall carpet?",
      "title": "Carpet"
    },
    "profile": {
      "$ref": "#/$defs/Profile",
      "default": "standard"
    },
    "price_tier": {
      "$ref": "#/$defs/PriceTier",
      "default": "pm_vendor"
    },
    "detail": {
      "default": "summary",
      "description": "summary (default, small) or full (every line).",
      "enum": [
        "summary",
        "full"
      ],
      "title": "Detail",
      "type": "string"
    }
  },
  "required": [
    "zip",
    "bedrooms",
    "bathrooms"
  ],
  "title": "QuickEstimateRequest",
  "type": "object"
}

Output

{
  "$defs": {
    "CostRange": {
      "description": "A low / expected / high estimate. Invariant: low <= expected <= high.",
      "properties": {
        "low": {
          "title": "Low",
          "type": "string"
        },
        "expected": {
          "title": "Expected",
          "type": "string"
        },
        "high": {
          "title": "High",
          "type": "string"
        }
      },
      "required": [
        "low",
        "expected",
        "high"
      ],
      "title": "CostRange",
      "type": "object"
    },
    "Extent": {
      "enum": [
        "spot",
        "partial",
        "full"
      ],
      "title": "Extent",
      "type": "string"
    },
    "FindingResolution": {
      "properties": {
        "finding_id": {
          "title": "Finding Id",
          "type": "string"
        },
        "resolution": {
          "enum": [
            "exact",
            "alias",
            "unmapped"
          ],
          "title": "Resolution",
          "type": "string"
        },
        "code": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Code"
        },
        "issue_name": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Readable name of `code`.",
          "title": "Issue Name"
        },
        "text": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "The finding as given (description, else code), for tracing.",
          "title": "Text"
        },
        "location": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Where, as shown on the lines.",
          "title": "Location"
        },
        "match_note": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Set when the match left a fixture word unexplained: check this finding.",
          "title": "Match Note"
        },
        "uncertain": {
          "default": false,
          "description": "The description hedges ('mold?', 'maybe', 'possible'). Refer-outs from uncertain findings are check-first: they add a warning instead of holding phases.",
          "title": "Uncertain",
          "type": "boolean"
        },
        "extent": {
          "anyOf": [
            {
              "$ref": "#/$defs/Extent"
            },
            {
              "type": "null"
            }
          ]
        },
        "suggested_codes": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Suggested Codes",
          "type": "array"
        },
        "suggestions": {
          "description": "Suggested codes with readable names, best first.",
          "items": {
            "$ref": "#/$defs/Suggestion"
          },
          "title": "Suggestions",
          "type": "array"
        },
        "line_ids": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Line Ids",
          "type": "array"
        }
      },
      "required": [
        "finding_id",
        "resolution",
        "code",
        "extent"
      ],
      "title": "FindingResolution",
      "type": "object"
    },
    "Note": {
      "properties": {
        "code": {
          "title": "Code",
          "type": "string"
        },
        "message": {
          "title": "Message",
          "type": "string"
        },
        "finding_ids": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Finding Ids",
          "type": "array"
        },
        "line_ids": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Line Ids",
          "type": "array"
        }
      },
      "required": [
        "code",
        "message"
      ],
      "title": "Note",
      "type": "object"
    },
    "Phase": {
      "description": "Make-ready order. Values sort in sequence order via PHASE_ORDER.",
      "enum": [
        "assess",
        "trash_out",
        "repairs",
        "paint",
        "flooring",
        "appliances",
        "cleaning",
        "punch",
        "rekey"
      ],
      "title": "Phase",
      "type": "string"
    },
    "PriceBasis": {
      "enum": [
        "rate_card",
        "unit_price",
        "labor_derived",
        "placeholder"
      ],
      "title": "PriceBasis",
      "type": "string"
    },
    "PriceTier": {
      "description": "Purchasing channel. Library prices are pm_vendor (a PM buying turns from a local vendor).",
      "enum": [
        "bulk_contract",
        "pm_vendor",
        "retail_one_off"
      ],
      "title": "PriceTier",
      "type": "string"
    },
    "Priority": {
      "enum": [
        "must",
        "should",
        "could"
      ],
      "title": "Priority",
      "type": "string"
    },
    "Profile": {
      "enum": [
        "minimum",
        "standard",
        "premium"
      ],
      "title": "Profile",
      "type": "string"
    },
    "Provenance": {
      "properties": {
        "ruleset_id": {
          "title": "Ruleset Id",
          "type": "string"
        },
        "ruleset_version": {
          "title": "Ruleset Version",
          "type": "string"
        },
        "ruleset_hash": {
          "title": "Ruleset Hash",
          "type": "string"
        },
        "sources": {
          "items": {
            "$ref": "#/$defs/SourceRef"
          },
          "title": "Sources",
          "type": "array"
        }
      },
      "required": [
        "ruleset_id",
        "ruleset_version",
        "ruleset_hash",
        "sources"
      ],
      "title": "Provenance",
      "type": "object"
    },
    "RateCardUse": {
      "properties": {
        "entry": {
          "description": "0-based index into the request's rate_card.",
          "title": "Entry",
          "type": "integer"
        },
        "target": {
          "description": "The entry's task_id or trade.",
          "title": "Target",
          "type": "string"
        },
        "lines": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Lines",
          "type": "array"
        },
        "note": {
          "title": "Note",
          "type": "string"
        }
      },
      "required": [
        "entry",
        "target",
        "note"
      ],
      "title": "RateCardUse",
      "type": "object"
    },
    "ReferOutNote": {
      "properties": {
        "id": {
          "title": "Id",
          "type": "string"
        },
        "name": {
          "title": "Name",
          "type": "string"
        },
        "professional": {
          "title": "Professional",
          "type": "string"
        },
        "reason": {
          "title": "Reason",
          "type": "string"
        },
        "kind": {
          "enum": [
            "assessment",
            "compliance"
          ],
          "title": "Kind",
          "type": "string"
        },
        "finding_ids": {
          "items": {
            "type": "string"
          },
          "title": "Finding Ids",
          "type": "array"
        },
        "blocks_phases": {
          "items": {
            "$ref": "#/$defs/Phase"
          },
          "title": "Blocks Phases",
          "type": "array"
        },
        "sources": {
          "items": {
            "type": "string"
          },
          "title": "Sources",
          "type": "array"
        },
        "unconfirmed": {
          "default": false,
          "description": "Every triggering finding was hedged ('maybe', '?'): check first; no phase is held until the condition is confirmed (see warnings).",
          "title": "Unconfirmed",
          "type": "boolean"
        }
      },
      "required": [
        "id",
        "name",
        "professional",
        "reason",
        "kind",
        "finding_ids",
        "blocks_phases",
        "sources"
      ],
      "title": "ReferOutNote",
      "type": "object"
    },
    "Region": {
      "properties": {
        "zip3": {
          "title": "Zip3",
          "type": "string"
        },
        "basis": {
          "enum": [
            "metro",
            "nonmetro",
            "national"
          ],
          "title": "Basis",
          "type": "string"
        },
        "cbsa": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Cbsa"
        },
        "cbsa_name": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Cbsa Name"
        },
        "vintage": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Vintage"
        }
      },
      "required": [
        "zip3",
        "basis"
      ],
      "title": "Region",
      "type": "object"
    },
    "Responsibility": {
      "enum": [
        "normal_wear",
        "likely_damage",
        "elective",
        "undetermined",
        "mixed"
      ],
      "title": "Responsibility",
      "type": "string"
    },
    "ScopeLine": {
      "properties": {
        "line_id": {
          "title": "Line Id",
          "type": "string"
        },
        "task_id": {
          "title": "Task Id",
          "type": "string"
        },
        "name": {
          "title": "Name",
          "type": "string"
        },
        "trade": {
          "$ref": "#/$defs/Trade"
        },
        "phase": {
          "$ref": "#/$defs/Phase"
        },
        "priority": {
          "$ref": "#/$defs/Priority"
        },
        "location": {
          "title": "Location",
          "type": "string"
        },
        "finding_ids": {
          "items": {
            "type": "string"
          },
          "title": "Finding Ids",
          "type": "array"
        },
        "quantity": {
          "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$",
          "title": "Quantity",
          "type": "string"
        },
        "uom": {
          "$ref": "#/$defs/Uom"
        },
        "quantity_source": {
          "enum": [
            "given",
            "converted",
            "derived",
            "fixed"
          ],
          "title": "Quantity Source",
          "type": "string"
        },
        "price_basis": {
          "$ref": "#/$defs/PriceBasis"
        },
        "unit_price": {
          "$ref": "#/$defs/CostRange"
        },
        "cost": {
          "$ref": "#/$defs/CostRange"
        },
        "confidence": {
          "enum": [
            "sourced",
            "industry_typical",
            "assumed"
          ],
          "title": "Confidence",
          "type": "string"
        },
        "responsibility_hint": {
          "$ref": "#/$defs/Responsibility"
        },
        "responsibility_by_finding": {
          "additionalProperties": {
            "$ref": "#/$defs/Responsibility"
          },
          "description": "Set when the hint is 'mixed': the hint of each finding the line carries.",
          "title": "Responsibility By Finding",
          "type": "object"
        },
        "allowance": {
          "anyOf": [
            {
              "$ref": "#/$defs/CostRange"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Quote-required lines only: a non-binding budget range. NOT included in any total; summed separately in totals.allowances."
        },
        "refer_out": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Refer Out"
        },
        "decision": {
          "anyOf": [
            {
              "enum": [
                "approve",
                "decline",
                "defer"
              ],
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Decision"
        },
        "vendor_note": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Vendor Note"
        },
        "rule_ids": {
          "items": {
            "type": "string"
          },
          "title": "Rule Ids",
          "type": "array"
        }
      },
      "required": [
        "line_id",
        "task_id",
        "name",
        "trade",
        "phase",
        "priority",
        "location",
        "finding_ids",
        "quantity",
        "uom",
        "quantity_source",
        "price_basis",
        "unit_price",
        "cost",
        "confidence",
        "responsibility_hint",
        "rule_ids"
      ],
      "title": "ScopeLine",
      "type": "object"
    },
    "SourceRef": {
      "properties": {
        "id": {
          "title": "Id",
          "type": "string"
        },
        "title": {
          "title": "Title",
          "type": "string"
        },
        "url": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Url"
        }
      },
      "required": [
        "id",
        "title"
      ],
      "title": "SourceRef",
      "type": "object"
    },
    "Suggestion": {
      "properties": {
        "code": {
          "title": "Code",
          "type": "string"
        },
        "name": {
          "title": "Name",
          "type": "string"
        }
      },
      "required": [
        "code",
        "name"
      ],
      "title": "Suggestion",
      "type": "object"
    },
    "Totals": {
      "properties": {
        "note": {
          "default": "must, should and could are work lines only. visit_charges (minimum call-out and trip fees, once per trade) are added on top: subtotal = must + should + could + visit_charges; total = subtotal + tax. allowances are outside the total.",
          "description": "How the totals add up.",
          "title": "Note",
          "type": "string"
        },
        "must": {
          "$ref": "#/$defs/CostRange"
        },
        "should": {
          "$ref": "#/$defs/CostRange"
        },
        "could": {
          "$ref": "#/$defs/CostRange"
        },
        "visit_charges": {
          "$ref": "#/$defs/CostRange"
        },
        "subtotal": {
          "$ref": "#/$defs/CostRange"
        },
        "tax": {
          "$ref": "#/$defs/CostRange"
        },
        "total": {
          "$ref": "#/$defs/CostRange"
        },
        "approved": {
          "anyOf": [
            {
              "$ref": "#/$defs/CostRange"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "allowances": {
          "$ref": "#/$defs/CostRange",
          "description": "Budget allowance for quote-required lines (refer-outs, manual quotes). Shown outside the totals: subtotal and total never include it."
        }
      },
      "required": [
        "must",
        "should",
        "could",
        "visit_charges",
        "subtotal",
        "tax",
        "total"
      ],
      "title": "Totals",
      "type": "object"
    },
    "Trade": {
      "enum": [
        "hauling",
        "handyman",
        "drywall",
        "painter",
        "flooring",
        "carpet_cleaning",
        "cleaning",
        "appliance",
        "plumber",
        "electrician",
        "hvac",
        "locksmith",
        "pest_control",
        "landscaping",
        "window_glass",
        "environmental",
        "structural"
      ],
      "title": "Trade",
      "type": "string"
    },
    "TradeScope": {
      "properties": {
        "trade": {
          "$ref": "#/$defs/Trade"
        },
        "vendor": {
          "anyOf": [
            {
              "$ref": "#/$defs/Vendor"
            },
            {
              "type": "null"
            }
          ]
        },
        "phase": {
          "$ref": "#/$defs/Phase"
        },
        "line_ids": {
          "items": {
            "type": "string"
          },
          "title": "Line Ids",
          "type": "array"
        },
        "lines_cost": {
          "$ref": "#/$defs/CostRange"
        },
        "visit_charges": {
          "$ref": "#/$defs/CostRange"
        },
        "total": {
          "$ref": "#/$defs/CostRange"
        }
      },
      "required": [
        "trade",
        "vendor",
        "phase",
        "line_ids",
        "lines_cost",
        "visit_charges",
        "total"
      ],
      "title": "TradeScope",
      "type": "object"
    },
    "Uom": {
      "enum": [
        "each",
        "sq_ft",
        "sq_ft_floor",
        "sq_yd",
        "linear_ft",
        "room",
        "unit",
        "bedroom",
        "bathroom",
        "hour",
        "cu_yd",
        "visit"
      ],
      "title": "Uom",
      "type": "string"
    },
    "Vendor": {
      "additionalProperties": false,
      "properties": {
        "name": {
          "maxLength": 120,
          "title": "Name",
          "type": "string"
        },
        "contact": {
          "anyOf": [
            {
              "maxLength": 200,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Contact"
        }
      },
      "required": [
        "name"
      ],
      "title": "Vendor",
      "type": "object"
    }
  },
  "description": "Field order puts the answer first (summary, disclaimer, totals, refer-outs, vendor\nscopes), because agents and truncating clients read from the top.",
  "properties": {
    "summary": {
      "title": "Summary",
      "type": "string"
    },
    "disclaimer": {
      "title": "Disclaimer",
      "type": "string"
    },
    "warnings": {
      "items": {
        "$ref": "#/$defs/Note"
      },
      "title": "Warnings",
      "type": "array"
    },
    "totals": {
      "$ref": "#/$defs/Totals"
    },
    "refer_outs": {
      "items": {
        "$ref": "#/$defs/ReferOutNote"
      },
      "title": "Refer Outs",
      "type": "array"
    },
    "trade_scopes": {
      "items": {
        "$ref": "#/$defs/TradeScope"
      },
      "title": "Trade Scopes",
      "type": "array"
    },
    "lines": {
      "items": {
        "$ref": "#/$defs/ScopeLine"
      },
      "title": "Lines",
      "type": "array"
    },
    "lines_omitted": {
      "description": "Lines left out in summary detail.",
      "title": "Lines Omitted",
      "type": "integer"
    },
    "omitted_note": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Summary detail: what the omitted lines are (routine turn tasks) and their cost.",
      "title": "Omitted Note"
    },
    "findings": {
      "items": {
        "$ref": "#/$defs/FindingResolution"
      },
      "title": "Findings",
      "type": "array"
    },
    "assumptions": {
      "items": {
        "$ref": "#/$defs/Note"
      },
      "title": "Assumptions",
      "type": "array"
    },
    "unknowns": {
      "items": {
        "$ref": "#/$defs/Note"
      },
      "title": "Unknowns",
      "type": "array"
    },
    "rate_card_applied": {
      "description": "Rate card entries that changed a price.",
      "items": {
        "$ref": "#/$defs/RateCardUse"
      },
      "title": "Rate Card Applied",
      "type": "array"
    },
    "rate_card_ignored": {
      "description": "Rate card entries that changed nothing, with why.",
      "items": {
        "$ref": "#/$defs/RateCardUse"
      },
      "title": "Rate Card Ignored",
      "type": "array"
    },
    "detail": {
      "enum": [
        "summary",
        "full"
      ],
      "title": "Detail",
      "type": "string"
    },
    "region": {
      "$ref": "#/$defs/Region"
    },
    "profile": {
      "$ref": "#/$defs/Profile"
    },
    "price_tier": {
      "$ref": "#/$defs/PriceTier",
      "default": "pm_vendor"
    },
    "provenance": {
      "$ref": "#/$defs/Provenance"
    }
  },
  "required": [
    "summary",
    "disclaimer",
    "warnings",
    "totals",
    "refer_outs",
    "trade_scopes",
    "lines",
    "lines_omitted",
    "findings",
    "assumptions",
    "unknowns",
    "detail",
    "region",
    "profile",
    "provenance"
  ],
  "title": "ScopeResult",
  "type": "object"
}
Scope a property turnover: input and output schemas

input.json · output.json

Input

{
  "$defs": {
    "Area": {
      "enum": [
        "whole_unit",
        "kitchen",
        "living_room",
        "dining_room",
        "bedroom",
        "bathroom",
        "hallway",
        "entry",
        "laundry",
        "closet",
        "garage",
        "patio_balcony",
        "exterior",
        "yard",
        "other"
      ],
      "title": "Area",
      "type": "string"
    },
    "Constraints": {
      "additionalProperties": false,
      "properties": {
        "target_ready_date": {
          "anyOf": [
            {
              "format": "date",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Target Ready Date"
        },
        "occupied": {
          "default": false,
          "description": "Work happens while occupied.",
          "title": "Occupied",
          "type": "boolean"
        },
        "access_notes": {
          "anyOf": [
            {
              "maxLength": 500,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Access Notes"
        }
      },
      "title": "Constraints",
      "type": "object"
    },
    "Decision": {
      "additionalProperties": false,
      "description": "Owner decision. Re-run the same request with decisions to get the approved scope.",
      "properties": {
        "line_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Line Id"
        },
        "finding_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Finding Id"
        },
        "action": {
          "enum": [
            "approve",
            "decline",
            "defer"
          ],
          "title": "Action",
          "type": "string"
        },
        "note": {
          "anyOf": [
            {
              "maxLength": 300,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Note"
        }
      },
      "required": [
        "action"
      ],
      "title": "Decision",
      "type": "object"
    },
    "Extent": {
      "enum": [
        "spot",
        "partial",
        "full"
      ],
      "title": "Extent",
      "type": "string"
    },
    "Finding": {
      "additionalProperties": false,
      "description": "One observed condition. Give a taxonomy `code` if you know it, else a `description`.",
      "properties": {
        "id": {
          "anyOf": [
            {
              "maxLength": 40,
              "pattern": "^[A-Za-z0-9_.:-]+$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Your id for this finding; defaults to f1, f2, ... in order.",
          "title": "Id"
        },
        "location": {
          "$ref": "#/$defs/Location",
          "description": "Object {\"area\": \"bedroom\", \"label\": \"Bedroom 2\"}, or a plain string such as \"Bedroom 2\" or \"kitchen\" (mapped to an area; other text becomes the label)."
        },
        "code": {
          "anyOf": [
            {
              "maxLength": 80,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional. Only a code returned by property_turnover_issue_codes (e.g. 'walls.nail_holes'); otherwise leave it out and use description.",
          "title": "Code"
        },
        "description": {
          "anyOf": [
            {
              "maxLength": 300,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Plain description if no code, e.g. 'dog urine stains in bedroom carpet'.",
          "title": "Description"
        },
        "extent": {
          "anyOf": [
            {
              "$ref": "#/$defs/Extent"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "spot | partial | full; defaults per issue."
        },
        "quantity": {
          "anyOf": [
            {
              "$ref": "#/$defs/Quantity"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Measured amount, e.g. 120 sq_ft. Derived when omitted."
        },
        "notes": {
          "anyOf": [
            {
              "maxLength": 1000,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Notes"
        }
      },
      "title": "Finding",
      "type": "object"
    },
    "FloorCoverings": {
      "additionalProperties": false,
      "description": "What is on the floors. Drives the carpet-cleaning baseline and whole-unit carpet sizes.",
      "properties": {
        "carpet_present": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Is there wall-to-wall carpet? Omit if unknown: carpet findings then imply carpet; with no carpet findings, whole-unit carpet cleaning is left out and listed as unknown.",
          "title": "Carpet Present"
        },
        "carpet_sq_ft": {
          "anyOf": [
            {
              "maximum": 20000,
              "minimum": 0,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Carpeted floor area. Omit to assume 60% of the unit floor area.",
          "title": "Carpet Sq Ft"
        }
      },
      "title": "FloorCoverings",
      "type": "object"
    },
    "Location": {
      "additionalProperties": false,
      "description": "Where the finding is. Also accepted as a plain string (\"Bedroom 2\", \"kitchen\").",
      "properties": {
        "area": {
          "$ref": "#/$defs/Area",
          "default": "whole_unit"
        },
        "label": {
          "anyOf": [
            {
              "maxLength": 60,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "e.g. 'Bedroom 2'.",
          "title": "Label"
        }
      },
      "title": "Location",
      "type": "object"
    },
    "PriceTier": {
      "description": "Purchasing channel. Library prices are pm_vendor (a PM buying turns from a local vendor).",
      "enum": [
        "bulk_contract",
        "pm_vendor",
        "retail_one_off"
      ],
      "title": "PriceTier",
      "type": "string"
    },
    "Profile": {
      "enum": [
        "minimum",
        "standard",
        "premium"
      ],
      "title": "Profile",
      "type": "string"
    },
    "PropertyProfile": {
      "additionalProperties": false,
      "properties": {
        "zip": {
          "description": "5-digit US ZIP code (no street address).",
          "pattern": "^\\d{5}$",
          "title": "Zip",
          "type": "string"
        },
        "unit_type": {
          "$ref": "#/$defs/UnitType",
          "default": "apartment",
          "description": "Single-family homes and townhomes get house-sized defaults (floor area, locks)."
        },
        "bedrooms": {
          "description": "0 for a studio.",
          "maximum": 12,
          "minimum": 0,
          "title": "Bedrooms",
          "type": "integer"
        },
        "bathrooms": {
          "anyOf": [
            {
              "maximum": 12.0,
              "minimum": 0.0,
              "multipleOf": 0.5,
              "type": "number"
            },
            {
              "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$",
              "type": "string"
            }
          ],
          "description": "Half baths count as 0.5.",
          "title": "Bathrooms"
        },
        "sq_ft": {
          "anyOf": [
            {
              "maximum": 20000,
              "minimum": 150,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Interior sq ft.",
          "title": "Sq Ft"
        },
        "year_built": {
          "anyOf": [
            {
              "maximum": 2100,
              "minimum": 1700,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Drives lead-paint (pre-1978) handling.",
          "title": "Year Built"
        },
        "floor_coverings": {
          "$ref": "#/$defs/FloorCoverings"
        }
      },
      "required": [
        "zip",
        "bedrooms",
        "bathrooms"
      ],
      "title": "PropertyProfile",
      "type": "object"
    },
    "Quantity": {
      "additionalProperties": false,
      "properties": {
        "value": {
          "anyOf": [
            {
              "exclusiveMinimum": 0.0,
              "maximum": 100000.0,
              "type": "number"
            },
            {
              "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$",
              "type": "string"
            }
          ],
          "title": "Value"
        },
        "uom": {
          "$ref": "#/$defs/Uom"
        }
      },
      "required": [
        "value",
        "uom"
      ],
      "title": "Quantity",
      "type": "object"
    },
    "RateCardEntry": {
      "additionalProperties": false,
      "description": "Your own price. Examples: {\"trade\": \"handyman\", \"unit_price\": 65, \"uom\": \"hour\"} (an\nhourly labor rate for that trade); {\"trade\": \"painter\", \"min_charge\": 150} (visit minimum);\n{\"task_id\": \"carpet.clean\", \"unit_price\": 0.2, \"uom\": \"sq_ft\"} (task_id from a result line).\nA task_id entry beats a trade entry; both beat the library. The result lists what was\napplied and what was ignored (rate_card_applied / rate_card_ignored).",
      "properties": {
        "task_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "A task_id from a result line, e.g. 'carpet.clean'.",
          "title": "Task Id"
        },
        "trade": {
          "anyOf": [
            {
              "$ref": "#/$defs/Trade"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "unit_price": {
          "anyOf": [
            {
              "minimum": 0.0,
              "type": "number"
            },
            {
              "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "USD per uom. On a trade entry, uom 'hour' is an hourly labor rate (replaces the labor part of that trade's prices); another uom prices that trade's tasks in it.",
          "title": "Unit Price"
        },
        "uom": {
          "anyOf": [
            {
              "$ref": "#/$defs/Uom"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Unit your price is quoted in. Required with a trade unit_price. If it differs from a task's uom, the entry is not applied to that task (see rate_card_ignored)."
        },
        "min_charge": {
          "anyOf": [
            {
              "minimum": 0.0,
              "type": "number"
            },
            {
              "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "USD per trade visit.",
          "title": "Min Charge"
        },
        "trip_charge": {
          "anyOf": [
            {
              "minimum": 0.0,
              "type": "number"
            },
            {
              "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "USD per trade visit.",
          "title": "Trip Charge"
        }
      },
      "title": "RateCardEntry",
      "type": "object"
    },
    "Standards": {
      "additionalProperties": false,
      "properties": {
        "profile": {
          "$ref": "#/$defs/Profile",
          "default": "standard",
          "description": "minimum (rent-ready basics) | standard | premium (higher finish)."
        },
        "overrides": {
          "$ref": "#/$defs/StandardsOverrides"
        },
        "price_tier": {
          "$ref": "#/$defs/PriceTier",
          "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)."
        }
      },
      "title": "Standards",
      "type": "object"
    },
    "StandardsOverrides": {
      "additionalProperties": false,
      "properties": {
        "paint_policy": {
          "anyOf": [
            {
              "enum": [
                "touch_up",
                "full_if_needed",
                "always_full"
              ],
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Paint Policy"
        },
        "carpet_policy": {
          "anyOf": [
            {
              "enum": [
                "clean",
                "replace_if_damaged",
                "always_replace"
              ],
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Carpet Policy"
        },
        "cleaning_policy": {
          "anyOf": [
            {
              "enum": [
                "standard",
                "deep"
              ],
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Cleaning Policy"
        },
        "always_rekey": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Always Rekey"
        },
        "markup_pct": {
          "anyOf": [
            {
              "maximum": 100.0,
              "minimum": 0.0,
              "type": "number"
            },
            {
              "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Management markup applied to every line, %.",
          "title": "Markup Pct"
        }
      },
      "title": "StandardsOverrides",
      "type": "object"
    },
    "Trade": {
      "enum": [
        "hauling",
        "handyman",
        "drywall",
        "painter",
        "flooring",
        "carpet_cleaning",
        "cleaning",
        "appliance",
        "plumber",
        "electrician",
        "hvac",
        "locksmith",
        "pest_control",
        "landscaping",
        "window_glass",
        "environmental",
        "structural"
      ],
      "title": "Trade",
      "type": "string"
    },
    "UnitType": {
      "enum": [
        "apartment",
        "single_family",
        "townhome",
        "condo",
        "duplex",
        "manufactured"
      ],
      "title": "UnitType",
      "type": "string"
    },
    "Uom": {
      "enum": [
        "each",
        "sq_ft",
        "sq_ft_floor",
        "sq_yd",
        "linear_ft",
        "room",
        "unit",
        "bedroom",
        "bathroom",
        "hour",
        "cu_yd",
        "visit"
      ],
      "title": "Uom",
      "type": "string"
    },
    "Vendor": {
      "additionalProperties": false,
      "properties": {
        "name": {
          "maxLength": 120,
          "title": "Name",
          "type": "string"
        },
        "contact": {
          "anyOf": [
            {
              "maxLength": 200,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Contact"
        }
      },
      "required": [
        "name"
      ],
      "title": "Vendor",
      "type": "object"
    }
  },
  "additionalProperties": false,
  "properties": {
    "client_refs": {
      "anyOf": [
        {
          "additionalProperties": {
            "type": "string"
          },
          "type": "object"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Your own reference ids (e.g. unit_id, job_id). Echoed back, never used.",
      "title": "Client Refs"
    },
    "property": {
      "$ref": "#/$defs/PropertyProfile"
    },
    "findings": {
      "items": {
        "$ref": "#/$defs/Finding"
      },
      "maxItems": 200,
      "title": "Findings",
      "type": "array"
    },
    "standards": {
      "$ref": "#/$defs/Standards"
    },
    "rate_card": {
      "items": {
        "$ref": "#/$defs/RateCardEntry"
      },
      "maxItems": 300,
      "title": "Rate Card",
      "type": "array"
    },
    "trade_vendors": {
      "additionalProperties": {
        "$ref": "#/$defs/Vendor"
      },
      "propertyNames": {
        "$ref": "#/$defs/Trade"
      },
      "title": "Trade Vendors",
      "type": "object"
    },
    "constraints": {
      "$ref": "#/$defs/Constraints"
    },
    "tax_rate": {
      "anyOf": [
        {
          "maximum": 0.2,
          "minimum": 0.0,
          "type": "number"
        },
        {
          "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$",
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Applied to all lines, e.g. 0.07.",
      "title": "Tax Rate"
    },
    "decisions": {
      "items": {
        "$ref": "#/$defs/Decision"
      },
      "maxItems": 500,
      "title": "Decisions",
      "type": "array"
    },
    "detail": {
      "default": "full",
      "description": "summary: totals, trade scopes, every line tied to a finding (capped at 40) plus the largest must-fix/refer-out lines, a one-line note on the routine lines left out, notes, and only findings needing attention (small). full: every line, finding and source.",
      "enum": [
        "summary",
        "full"
      ],
      "title": "Detail",
      "type": "string"
    }
  },
  "required": [
    "property"
  ],
  "title": "ScopeRequest",
  "type": "object"
}

Output

{
  "$defs": {
    "CostRange": {
      "description": "A low / expected / high estimate. Invariant: low <= expected <= high.",
      "properties": {
        "low": {
          "title": "Low",
          "type": "string"
        },
        "expected": {
          "title": "Expected",
          "type": "string"
        },
        "high": {
          "title": "High",
          "type": "string"
        }
      },
      "required": [
        "low",
        "expected",
        "high"
      ],
      "title": "CostRange",
      "type": "object"
    },
    "Extent": {
      "enum": [
        "spot",
        "partial",
        "full"
      ],
      "title": "Extent",
      "type": "string"
    },
    "FindingResolution": {
      "properties": {
        "finding_id": {
          "title": "Finding Id",
          "type": "string"
        },
        "resolution": {
          "enum": [
            "exact",
            "alias",
            "unmapped"
          ],
          "title": "Resolution",
          "type": "string"
        },
        "code": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Code"
        },
        "issue_name": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Readable name of `code`.",
          "title": "Issue Name"
        },
        "text": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "The finding as given (description, else code), for tracing.",
          "title": "Text"
        },
        "location": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Where, as shown on the lines.",
          "title": "Location"
        },
        "match_note": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Set when the match left a fixture word unexplained: check this finding.",
          "title": "Match Note"
        },
        "uncertain": {
          "default": false,
          "description": "The description hedges ('mold?', 'maybe', 'possible'). Refer-outs from uncertain findings are check-first: they add a warning instead of holding phases.",
          "title": "Uncertain",
          "type": "boolean"
        },
        "extent": {
          "anyOf": [
            {
              "$ref": "#/$defs/Extent"
            },
            {
              "type": "null"
            }
          ]
        },
        "suggested_codes": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Suggested Codes",
          "type": "array"
        },
        "suggestions": {
          "description": "Suggested codes with readable names, best first.",
          "items": {
            "$ref": "#/$defs/Suggestion"
          },
          "title": "Suggestions",
          "type": "array"
        },
        "line_ids": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Line Ids",
          "type": "array"
        }
      },
      "required": [
        "finding_id",
        "resolution",
        "code",
        "extent"
      ],
      "title": "FindingResolution",
      "type": "object"
    },
    "Note": {
      "properties": {
        "code": {
          "title": "Code",
          "type": "string"
        },
        "message": {
          "title": "Message",
          "type": "string"
        },
        "finding_ids": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Finding Ids",
          "type": "array"
        },
        "line_ids": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Line Ids",
          "type": "array"
        }
      },
      "required": [
        "code",
        "message"
      ],
      "title": "Note",
      "type": "object"
    },
    "Phase": {
      "description": "Make-ready order. Values sort in sequence order via PHASE_ORDER.",
      "enum": [
        "assess",
        "trash_out",
        "repairs",
        "paint",
        "flooring",
        "appliances",
        "cleaning",
        "punch",
        "rekey"
      ],
      "title": "Phase",
      "type": "string"
    },
    "PriceBasis": {
      "enum": [
        "rate_card",
        "unit_price",
        "labor_derived",
        "placeholder"
      ],
      "title": "PriceBasis",
      "type": "string"
    },
    "PriceTier": {
      "description": "Purchasing channel. Library prices are pm_vendor (a PM buying turns from a local vendor).",
      "enum": [
        "bulk_contract",
        "pm_vendor",
        "retail_one_off"
      ],
      "title": "PriceTier",
      "type": "string"
    },
    "Priority": {
      "enum": [
        "must",
        "should",
        "could"
      ],
      "title": "Priority",
      "type": "string"
    },
    "Profile": {
      "enum": [
        "minimum",
        "standard",
        "premium"
      ],
      "title": "Profile",
      "type": "string"
    },
    "Provenance": {
      "properties": {
        "ruleset_id": {
          "title": "Ruleset Id",
          "type": "string"
        },
        "ruleset_version": {
          "title": "Ruleset Version",
          "type": "string"
        },
        "ruleset_hash": {
          "title": "Ruleset Hash",
          "type": "string"
        },
        "sources": {
          "items": {
            "$ref": "#/$defs/SourceRef"
          },
          "title": "Sources",
          "type": "array"
        }
      },
      "required": [
        "ruleset_id",
        "ruleset_version",
        "ruleset_hash",
        "sources"
      ],
      "title": "Provenance",
      "type": "object"
    },
    "RateCardUse": {
      "properties": {
        "entry": {
          "description": "0-based index into the request's rate_card.",
          "title": "Entry",
          "type": "integer"
        },
        "target": {
          "description": "The entry's task_id or trade.",
          "title": "Target",
          "type": "string"
        },
        "lines": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Lines",
          "type": "array"
        },
        "note": {
          "title": "Note",
          "type": "string"
        }
      },
      "required": [
        "entry",
        "target",
        "note"
      ],
      "title": "RateCardUse",
      "type": "object"
    },
    "ReferOutNote": {
      "properties": {
        "id": {
          "title": "Id",
          "type": "string"
        },
        "name": {
          "title": "Name",
          "type": "string"
        },
        "professional": {
          "title": "Professional",
          "type": "string"
        },
        "reason": {
          "title": "Reason",
          "type": "string"
        },
        "kind": {
          "enum": [
            "assessment",
            "compliance"
          ],
          "title": "Kind",
          "type": "string"
        },
        "finding_ids": {
          "items": {
            "type": "string"
          },
          "title": "Finding Ids",
          "type": "array"
        },
        "blocks_phases": {
          "items": {
            "$ref": "#/$defs/Phase"
          },
          "title": "Blocks Phases",
          "type": "array"
        },
        "sources": {
          "items": {
            "type": "string"
          },
          "title": "Sources",
          "type": "array"
        },
        "unconfirmed": {
          "default": false,
          "description": "Every triggering finding was hedged ('maybe', '?'): check first; no phase is held until the condition is confirmed (see warnings).",
          "title": "Unconfirmed",
          "type": "boolean"
        }
      },
      "required": [
        "id",
        "name",
        "professional",
        "reason",
        "kind",
        "finding_ids",
        "blocks_phases",
        "sources"
      ],
      "title": "ReferOutNote",
      "type": "object"
    },
    "Region": {
      "properties": {
        "zip3": {
          "title": "Zip3",
          "type": "string"
        },
        "basis": {
          "enum": [
            "metro",
            "nonmetro",
            "national"
          ],
          "title": "Basis",
          "type": "string"
        },
        "cbsa": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Cbsa"
        },
        "cbsa_name": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Cbsa Name"
        },
        "vintage": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Vintage"
        }
      },
      "required": [
        "zip3",
        "basis"
      ],
      "title": "Region",
      "type": "object"
    },
    "Responsibility": {
      "enum": [
        "normal_wear",
        "likely_damage",
        "elective",
        "undetermined",
        "mixed"
      ],
      "title": "Responsibility",
      "type": "string"
    },
    "ScopeLine": {
      "properties": {
        "line_id": {
          "title": "Line Id",
          "type": "string"
        },
        "task_id": {
          "title": "Task Id",
          "type": "string"
        },
        "name": {
          "title": "Name",
          "type": "string"
        },
        "trade": {
          "$ref": "#/$defs/Trade"
        },
        "phase": {
          "$ref": "#/$defs/Phase"
        },
        "priority": {
          "$ref": "#/$defs/Priority"
        },
        "location": {
          "title": "Location",
          "type": "string"
        },
        "finding_ids": {
          "items": {
            "type": "string"
          },
          "title": "Finding Ids",
          "type": "array"
        },
        "quantity": {
          "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$",
          "title": "Quantity",
          "type": "string"
        },
        "uom": {
          "$ref": "#/$defs/Uom"
        },
        "quantity_source": {
          "enum": [
            "given",
            "converted",
            "derived",
            "fixed"
          ],
          "title": "Quantity Source",
          "type": "string"
        },
        "price_basis": {
          "$ref": "#/$defs/PriceBasis"
        },
        "unit_price": {
          "$ref": "#/$defs/CostRange"
        },
        "cost": {
          "$ref": "#/$defs/CostRange"
        },
        "confidence": {
          "enum": [
            "sourced",
            "industry_typical",
            "assumed"
          ],
          "title": "Confidence",
          "type": "string"
        },
        "responsibility_hint": {
          "$ref": "#/$defs/Responsibility"
        },
        "responsibility_by_finding": {
          "additionalProperties": {
            "$ref": "#/$defs/Responsibility"
          },
          "description": "Set when the hint is 'mixed': the hint of each finding the line carries.",
          "title": "Responsibility By Finding",
          "type": "object"
        },
        "allowance": {
          "anyOf": [
            {
              "$ref": "#/$defs/CostRange"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Quote-required lines only: a non-binding budget range. NOT included in any total; summed separately in totals.allowances."
        },
        "refer_out": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Refer Out"
        },
        "decision": {
          "anyOf": [
            {
              "enum": [
                "approve",
                "decline",
                "defer"
              ],
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Decision"
        },
        "vendor_note": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Vendor Note"
        },
        "rule_ids": {
          "items": {
            "type": "string"
          },
          "title": "Rule Ids",
          "type": "array"
        }
      },
      "required": [
        "line_id",
        "task_id",
        "name",
        "trade",
        "phase",
        "priority",
        "location",
        "finding_ids",
        "quantity",
        "uom",
        "quantity_source",
        "price_basis",
        "unit_price",
        "cost",
        "confidence",
        "responsibility_hint",
        "rule_ids"
      ],
      "title": "ScopeLine",
      "type": "object"
    },
    "SourceRef": {
      "properties": {
        "id": {
          "title": "Id",
          "type": "string"
        },
        "title": {
          "title": "Title",
          "type": "string"
        },
        "url": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Url"
        }
      },
      "required": [
        "id",
        "title"
      ],
      "title": "SourceRef",
      "type": "object"
    },
    "Suggestion": {
      "properties": {
        "code": {
          "title": "Code",
          "type": "string"
        },
        "name": {
          "title": "Name",
          "type": "string"
        }
      },
      "required": [
        "code",
        "name"
      ],
      "title": "Suggestion",
      "type": "object"
    },
    "Totals": {
      "properties": {
        "note": {
          "default": "must, should and could are work lines only. visit_charges (minimum call-out and trip fees, once per trade) are added on top: subtotal = must + should + could + visit_charges; total = subtotal + tax. allowances are outside the total.",
          "description": "How the totals add up.",
          "title": "Note",
          "type": "string"
        },
        "must": {
          "$ref": "#/$defs/CostRange"
        },
        "should": {
          "$ref": "#/$defs/CostRange"
        },
        "could": {
          "$ref": "#/$defs/CostRange"
        },
        "visit_charges": {
          "$ref": "#/$defs/CostRange"
        },
        "subtotal": {
          "$ref": "#/$defs/CostRange"
        },
        "tax": {
          "$ref": "#/$defs/CostRange"
        },
        "total": {
          "$ref": "#/$defs/CostRange"
        },
        "approved": {
          "anyOf": [
            {
              "$ref": "#/$defs/CostRange"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "allowances": {
          "$ref": "#/$defs/CostRange",
          "description": "Budget allowance for quote-required lines (refer-outs, manual quotes). Shown outside the totals: subtotal and total never include it."
        }
      },
      "required": [
        "must",
        "should",
        "could",
        "visit_charges",
        "subtotal",
        "tax",
        "total"
      ],
      "title": "Totals",
      "type": "object"
    },
    "Trade": {
      "enum": [
        "hauling",
        "handyman",
        "drywall",
        "painter",
        "flooring",
        "carpet_cleaning",
        "cleaning",
        "appliance",
        "plumber",
        "electrician",
        "hvac",
        "locksmith",
        "pest_control",
        "landscaping",
        "window_glass",
        "environmental",
        "structural"
      ],
      "title": "Trade",
      "type": "string"
    },
    "TradeScope": {
      "properties": {
        "trade": {
          "$ref": "#/$defs/Trade"
        },
        "vendor": {
          "anyOf": [
            {
              "$ref": "#/$defs/Vendor"
            },
            {
              "type": "null"
            }
          ]
        },
        "phase": {
          "$ref": "#/$defs/Phase"
        },
        "line_ids": {
          "items": {
            "type": "string"
          },
          "title": "Line Ids",
          "type": "array"
        },
        "lines_cost": {
          "$ref": "#/$defs/CostRange"
        },
        "visit_charges": {
          "$ref": "#/$defs/CostRange"
        },
        "total": {
          "$ref": "#/$defs/CostRange"
        }
      },
      "required": [
        "trade",
        "vendor",
        "phase",
        "line_ids",
        "lines_cost",
        "visit_charges",
        "total"
      ],
      "title": "TradeScope",
      "type": "object"
    },
    "Uom": {
      "enum": [
        "each",
        "sq_ft",
        "sq_ft_floor",
        "sq_yd",
        "linear_ft",
        "room",
        "unit",
        "bedroom",
        "bathroom",
        "hour",
        "cu_yd",
        "visit"
      ],
      "title": "Uom",
      "type": "string"
    },
    "Vendor": {
      "additionalProperties": false,
      "properties": {
        "name": {
          "maxLength": 120,
          "title": "Name",
          "type": "string"
        },
        "contact": {
          "anyOf": [
            {
              "maxLength": 200,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Contact"
        }
      },
      "required": [
        "name"
      ],
      "title": "Vendor",
      "type": "object"
    }
  },
  "description": "Field order puts the answer first (summary, disclaimer, totals, refer-outs, vendor\nscopes), because agents and truncating clients read from the top.",
  "properties": {
    "summary": {
      "title": "Summary",
      "type": "string"
    },
    "disclaimer": {
      "title": "Disclaimer",
      "type": "string"
    },
    "warnings": {
      "items": {
        "$ref": "#/$defs/Note"
      },
      "title": "Warnings",
      "type": "array"
    },
    "totals": {
      "$ref": "#/$defs/Totals"
    },
    "refer_outs": {
      "items": {
        "$ref": "#/$defs/ReferOutNote"
      },
      "title": "Refer Outs",
      "type": "array"
    },
    "trade_scopes": {
      "items": {
        "$ref": "#/$defs/TradeScope"
      },
      "title": "Trade Scopes",
      "type": "array"
    },
    "lines": {
      "items": {
        "$ref": "#/$defs/ScopeLine"
      },
      "title": "Lines",
      "type": "array"
    },
    "lines_omitted": {
      "description": "Lines left out in summary detail.",
      "title": "Lines Omitted",
      "type": "integer"
    },
    "omitted_note": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Summary detail: what the omitted lines are (routine turn tasks) and their cost.",
      "title": "Omitted Note"
    },
    "findings": {
      "items": {
        "$ref": "#/$defs/FindingResolution"
      },
      "title": "Findings",
      "type": "array"
    },
    "assumptions": {
      "items": {
        "$ref": "#/$defs/Note"
      },
      "title": "Assumptions",
      "type": "array"
    },
    "unknowns": {
      "items": {
        "$ref": "#/$defs/Note"
      },
      "title": "Unknowns",
      "type": "array"
    },
    "rate_card_applied": {
      "description": "Rate card entries that changed a price.",
      "items": {
        "$ref": "#/$defs/RateCardUse"
      },
      "title": "Rate Card Applied",
      "type": "array"
    },
    "rate_card_ignored": {
      "description": "Rate card entries that changed nothing, with why.",
      "items": {
        "$ref": "#/$defs/RateCardUse"
      },
      "title": "Rate Card Ignored",
      "type": "array"
    },
    "detail": {
      "enum": [
        "summary",
        "full"
      ],
      "title": "Detail",
      "type": "string"
    },
    "region": {
      "$ref": "#/$defs/Region"
    },
    "profile": {
      "$ref": "#/$defs/Profile"
    },
    "price_tier": {
      "$ref": "#/$defs/PriceTier",
      "default": "pm_vendor"
    },
    "provenance": {
      "$ref": "#/$defs/Provenance"
    }
  },
  "required": [
    "summary",
    "disclaimer",
    "warnings",
    "totals",
    "refer_outs",
    "trade_scopes",
    "lines",
    "lines_omitted",
    "findings",
    "assumptions",
    "unknowns",
    "detail",
    "region",
    "profile",
    "provenance"
  ],
  "title": "ScopeResult",
  "type": "object"
}
Look up turnover issue codes: input and output schemas

input.json · output.json

Input

{
  "additionalProperties": false,
  "properties": {
    "client_refs": {
      "anyOf": [
        {
          "additionalProperties": {
            "type": "string"
          },
          "type": "object"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Your own reference ids (e.g. unit_id, job_id). Echoed back, never used.",
      "title": "Client Refs"
    },
    "query": {
      "anyOf": [
        {
          "maxLength": 200,
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Keywords to match against codes, names and aliases, e.g. 'pet stain carpet'.",
      "title": "Query"
    },
    "area": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Optional taxonomy area filter, e.g. 'flooring' or 'walls' (not a room). Call with no arguments to list every area in `areas`.",
      "title": "Area"
    },
    "limit": {
      "default": 8,
      "maximum": 200,
      "minimum": 1,
      "title": "Limit",
      "type": "integer"
    },
    "include_aliases": {
      "default": false,
      "description": "Also return each code's aliases (larger result).",
      "title": "Include Aliases",
      "type": "boolean"
    }
  },
  "title": "IssueCodesRequest",
  "type": "object"
}

Output

{
  "$defs": {
    "IssueCode": {
      "properties": {
        "code": {
          "description": "Stable code, component.issue, e.g. 'carpet.pet_urine'.",
          "title": "Code",
          "type": "string"
        },
        "name": {
          "title": "Name",
          "type": "string"
        },
        "area": {
          "title": "Area",
          "type": "string"
        },
        "aliases": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Aliases",
          "type": "array"
        }
      },
      "required": [
        "code",
        "name",
        "area"
      ],
      "title": "IssueCode",
      "type": "object"
    }
  },
  "properties": {
    "ruleset_version": {
      "title": "Ruleset Version",
      "type": "string"
    },
    "total_matches": {
      "title": "Total Matches",
      "type": "integer"
    },
    "codes": {
      "items": {
        "$ref": "#/$defs/IssueCode"
      },
      "title": "Codes",
      "type": "array"
    },
    "areas": {
      "description": "Every taxonomy area, for the `area` filter (e.g. 'flooring', 'walls').",
      "items": {
        "type": "string"
      },
      "title": "Areas",
      "type": "array"
    }
  },
  "required": [
    "ruleset_version",
    "total_matches",
    "codes"
  ],
  "title": "IssueCodesResult",
  "type": "object"
}

Accuracy and rules

  • Coverage: US
  • Determinism: strict
  • Contract version: 0.3.0 (experimental)
  • How it works: pipeline, counts, confidence mix and every source.
  • Ruleset: pt-2026.10.2, hash sha256:44f837d84a49ffc28782a6c7b0a3c4156013a4699f13f6322fec3ece055eae83. Every result is stamped with these, so a result can be reproduced exactly.
Sources cited by the ruleset (191)

Limitations

  • Estimate ranges, not quotes. Prices vary by vendor, market and condition.
  • Does not diagnose structural, electrical, gas, mold, asbestos or lead hazards; those are referred to licensed professionals.
  • Responsibility labels (wear vs damage) are hints, not legal determinations; deposit rules vary by state.

Results are estimate ranges for planning, not quotes or professional advice. Read the full disclaimer.

Privacy

  • Estimates: We keep an anonymized record of estimates (ZIP3, unit size, findings and totals; never your notes, address, IP or contact details) to improve accuracy.
  • Retention: 730 days.
  • Used to train models: no
  • Personal data: Inputs need a ZIP code, not an address. Request bodies and free-text notes are never logged or stored. Each estimate leaves one anonymized record: first 3 digits of the ZIP, size and age in coarse buckets, issue codes, totals and a daily-rotating anonymous client fingerprint; no notes, labels, names, vendors, rate-card prices, IP address or user agent. Descriptions of issues the taxonomy could not match are kept with digits, emails and links removed. These records are deleted 730 days after they are written and are not used to train models. Optional feedback (a rating, a note, an email only if typed) is stored separately, with a few estimate identifiers (hashes, versions, region, total) but never the request or notes.

Use it from your AI assistant

Every tool is also an MCP tool. Add this URL as a remote MCP server; the free tier needs no sign-in.

https://tektolo.com/mcp
  • Claude: Settings, Connectors, Add custom connector, then paste the URL above.
  • ChatGPT: enable developer mode, then add a custom MCP server with the URL above (menu names change; see OpenAI's connector docs).
  • Claude Code: claude mcp add --transport http quietops https://tektolo.com/mcp
  • Other MCP clients: streamable HTTP transport, URL as above, no headers. Connection metadata: server card.

Step-by-step setup and REST examples

Request an improvement

Something missing, wrong or confusing in Property Turnover Scope? Tell us.

Don't include personal details about tenants. Add your email only if you'd like a reply.