---
title: "Scope a property turnover (paid)"
method: POST
path: "/v1/x402/property-turnover/scope"
tags: ["x402"]
---

# Scope a property turnover (paid)

`POST /v1/x402/property-turnover/scope`

## Request

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

Paid lane (x402 v2, exact scheme, USDC on Base Sepolia testnet, $0.02 per call). Same body as POST /v1/property-turnover/scope, which stays free within its rate limits. Unpaid requests get HTTP 402 with PAYMENT-REQUIRED; only successful results are charged. Not available over MCP. The response adds `payment` (network, transaction, payer).

## Request body

- ScopeRequest
  - `client_refs` object, nullable: Your own reference ids (e.g. unit_id, job_id). Echoed back, never used.
  - `property` PropertyProfile, required
    - `zip` string, required: 5-digit US ZIP code (no street address).
    - `unit_type` 'apartment' | 'single_family' | 'townhome' | 'condo' | 'duplex' | 'manufactured'
    - `bedrooms` integer, required: 0 for a studio.
    - `bathrooms` union, required: Half baths count as 0.5.
      - number
      - string
    - `sq_ft` integer, nullable: Interior sq ft.
    - `year_built` integer, nullable: Drives lead-paint (pre-1978) handling.
    - `floor_coverings` FloorCoverings: What is on the floors. Drives the carpet-cleaning baseline and whole-unit carpet sizes.
      - `carpet_present` boolean, nullable: 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.
      - `carpet_sq_ft` integer, nullable: Carpeted floor area. Omit to assume 60% of the unit floor area.
  - `findings` Finding[]: Move-out inspection findings, one per condition, with location and extent.
    - `id` string, nullable: Your id for this finding; defaults to f1, f2, ... in order.
    - `location` Location: Where the finding is. Also accepted as a plain string ("Bedroom 2", "kitchen").
      - `area` 'whole_unit' | 'kitchen' | 'living_room' | 'dining_room' | 'bedroom' | 'bathroom' | 'hallway' | 'entry' | 'laundry' | 'closet' | 'garage' | 'patio_balcony' | 'exterior' | 'yard' | 'other'
      - `label` string, nullable: e.g. 'Bedroom 2'.
    - `code` string, nullable: Optional. Only a code returned by property_turnover_issue_codes (e.g. 'walls.nail_holes'); otherwise leave it out and use description.
    - `description` string, nullable: Plain description if no code, e.g. 'dog urine stains in bedroom carpet'.
    - `extent` 'spot' | 'partial' | 'full'
    - `quantity` Quantity
      - `value` union, required
        - number
        - string
      - `uom` 'each' | 'sq_ft' | 'sq_ft_floor' | 'sq_yd' | 'linear_ft' | 'room' | 'unit' | 'bedroom' | 'bathroom' | 'hour' | 'cu_yd' | 'visit', required
    - `notes` string, nullable
    - `extraction` Extraction: How a finding was produced, when software read it from free text (contract 0.4.0) or from an uploaded document (``ai_document`` and ``page``, additive). Set by the web tool when its AI reader (Azure OpenAI) turned a person's own words, or a page of their inspection report, into this finding. The engine reports such a finding as ``resolution: "ai"`` with ``needs_review`` and flags its lines; it never changes how the finding is priced.
      - `method` 'ai' | 'ai_document', required: 'ai': read by a language model from free text. 'ai_document': read by a language model from a page of an uploaded document.
      - `model` string, required: The model that read it, e.g. 'gpt-5-nano 2025-08-07'.
      - `confidence` union, required: The reader's confidence, 0 to 1.
        - number
        - string
      - `page` integer, nullable: 'ai_document' only: the page of the upload it was read from (1-based).
      - `quote` string, nullable: 'ai_document' only: the row's words as printed on that page, contact details masked. Evidence for checking the finding; it never changes the price.
      - `review` ExtractionReview: What the person did with a finding the web tool read from an uploaded document, before estimating (contract 0.11.0, additive). ``confirmed``: they pressed "Looks right"; ``accepted``: left as read, on a row the page did not flag; ``edited``: they changed it (``changed`` names what). A reviewed finding keeps its ``extraction`` (where it came from) but is no longer reported as needing review: the person has checked it.
        - `outcome` 'confirmed' | 'accepted' | 'edited', required
        - `flags` string[]: Why the page flagged it, if it did.
        - `changed` string[]: 'edited': the fields the person changed.
    - `confirmed` boolean: true: the person checked this AI-read finding (one with extraction) and it says what they meant. It keeps resolution 'ai' but no longer needs review, and the answer is verified once every AI-read finding is confirmed.
    - `signature_like` boolean: Set by Tektolo's notes reader (reading.findings, the web tool's rows): the words came right after a sign-off and read like a name or signature ('Thanks!' / 'Rotten Egg Smell'), kept only because they name a safety item. A safety item in them is raised unconfirmed (check first). Keep it when you send such a finding back, so every way of sending the same words gives the same answer.
  - `standards` Standards
    - `profile` 'minimum' | 'standard' | 'premium'
    - `overrides` StandardsOverrides
      - `paint_policy` 'touch_up' | 'full_if_needed' | 'always_full', nullable
      - `carpet_policy` 'clean' | 'replace_if_damaged' | 'always_replace', nullable
      - `cleaning_policy` 'standard' | 'deep', nullable
      - `always_rekey` boolean, nullable
      - `markup_pct` union: Management markup applied to every line, %.
        - number
        - string
    - `price_tier` 'bulk_contract' | 'pm_vendor' | 'retail_one_off': Purchasing channel. Library prices are pm_vendor (a PM buying turns from a local vendor).
  - `rate_card` RateCardEntry[]: Your own prices by trade or task; they replace the regional library prices.
    - `task_id` string, nullable: A task_id from a result line, e.g. 'carpet.clean'.
    - `trade` 'hauling' | 'handyman' | 'drywall' | 'painter' | 'flooring' | 'carpet_cleaning' | 'cleaning' | 'appliance' | 'plumber' | 'electrician' | 'hvac' | 'locksmith' | 'pest_control' | 'landscaping' | 'window_glass' | 'environmental' | 'structural'
    - `unit_price` union: 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.
      - number
      - string
    - `uom` 'each' | 'sq_ft' | 'sq_ft_floor' | 'sq_yd' | 'linear_ft' | 'room' | 'unit' | 'bedroom' | 'bathroom' | 'hour' | 'cu_yd' | 'visit'
    - `min_charge` union: USD per trade visit.
      - number
      - string
    - `trip_charge` union: USD per trade visit.
      - number
      - string
  - `trade_vendors` object: Vendor (name, contact) to put on each trade's lines.
  - `constraints` Constraints
    - `target_ready_date` string, date, nullable
    - `occupied` boolean: Work happens while occupied.
    - `access_notes` string, nullable
  - `tax_rate` union: Applied to all lines, e.g. 0.07.
    - number
    - string
  - `decisions` Decision[]: Approve, decline or defer lines or findings from an earlier scope.
    - `line_id` string, nullable
    - `finding_id` string, nullable
    - `action` 'approve' | 'decline' | 'defer', required
    - `note` string, nullable
  - `read` 'parser' | 'ai': parser (default): descriptions are read by the deterministic parser and matcher only; what they cannot place comes back as a manual-quote line with suggested codes. ai: what the parser cannot place is then read by an AI model; the answer is a draft (review.status) and echoes the reading, whose findings you confirm and send back for a verified answer (contract 0.12.0).
  - `retain` boolean: Optional (contract 0.5.0). false: keep none of this request's text. By default the finding descriptions are kept with contact details and access codes removed, to improve matching; the estimate is the same either way.
  - `detail` 'summary' | 'full': 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.

## Response `200`

Successful Response

- PropertyTurnoverScopePaidResponse
  - `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
  - `payment` PaymentInfo, required
    - `protocol` string
    - `x402_version` integer
    - `scheme` string
    - `network` string, required
    - `network_name` string, required
    - `testnet` boolean, required
    - `asset` string, required
    - `amount_usd` string, required
    - `amount_atomic` string, required
    - `transaction` string, required
    - `payer` string, nullable

## Other responses

- `402`: Payment required (x402 v2). The PAYMENT-REQUIRED header carries the base64 payment requirements; retry with a PAYMENT-SIGNATURE header. Also returned for an invalid payment or a failed settlement (nothing charged).
- `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`: Invalid input. Checked before payment; never charged.
- `429`: Rate limited (per payer wallet or per client). Never charged.
- `503`: Payment facilitator unreachable. Never charged.

---

[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)
