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

# Scope a property turnover (rendered as html, paid)

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

## 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/render/html`
- Auth: none declared

Paid lane (x402 v2, exact scheme, USDC on Base Sepolia testnet, $0.05 per call). Same body as POST /v1/property-turnover/scope/render/html, 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 PAYMENT-RESPONSE and x-payment-* headers carry the transaction.

## 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

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