# Property workspace: private API and evidence contract

The direct permit audit and weekly monitoring share a readable property workspace. This release adds no new subscription tier, account scopes or public customer-data feed.

## Buyer path

1. Optionally connect an Airbnb listing-owner account at `/connect-host`. The provider requests consent. A redirect alone does not confirm imported listings.
2. In `/permit-audit`, open **Use your connected listings**, load your received listings, select 1–25 in one city/state, then review the unit and choose the supported jurisdiction. Or type addresses manually.
3. Validate scope and pay through Stripe. The order uses a fixed portfolio; later listing changes do not modify it. No card data passes through our forms.
4. Use the private delivery link to read the property workspace, follow-up suggestions, source dates, field-presence details and original packet. Download JSON/CSV or print to PDF.

## Private fulfillment read

`GET https://civicdataforge.pages.dev/api/fulfillment?session_id=YOUR_PRIVATE_CHECKOUT_REFERENCE`

Keep this reference secret; it grants access to one delivery. Use a server-side request, never embed the reference in a public page, repository, analytics event or shared URL. Responses are `no-store`. The customer dashboard removes the reference from the visible URL and uses no analytics.

The existing response fields remain unchanged. When a permit packet is available, `property_workspace` is added. The latest successful monitoring check supplies this view; a failed new check remains in history and does not replace the last successful permit packet. No new paid work is triggered by reading results. Unknown sessions return 404; malformed references return 400.

`property_workspace.schema_version` is `civicdataforge.property-workspace.v1`. It includes:

- `records[]`: stable jurisdiction/address-bound property ID, submitted address, permit decision, observed decision-field presence, municipal coverage, optional property operating record, follow-up suggestions and record receipt hash.
- `records[].property_operations`: the additive `civicdataforge.property-operating-record.v1` object. It joins parcel/building-scoped public facts to the property while keeping current market-feed, owner-ledger and derived-metric requirements explicit. Every fact has a value, presence, evidence state, source reference, method reference and limitation note. The object carries its own deterministic receipt.
- `records[].property_operations.resources`: derived satellite and nearby Street View links when an official parcel record supplies a valid coordinate pair, plus jurisdiction-specific official contact routes for licensing, building permits, parcel assessment and property-tax questions. Map imagery availability, capture time and exact viewpoint remain provider-controlled and are not a current inspection. Each contact includes the date its official route was reviewed.
- `records[].evidence_contract`: the additive `civicdataforge.decision-ready-evidence.v1` contract. It separates observed, derived, conflicted and unknown fields; binds each field to a source and observation time; records the method and limitations for derived fields; preserves explicit unknowns; and includes a deterministic contract receipt.
- `source_registry` and `method_registry`: workspace-level dictionaries referenced by each compact record contract. Keeping shared proof once prevents 25-property deliveries from repeating the same rights and method text.
- `coverage`: explicit included/excluded layers. Chicago adds bounded Cook County parcel, assessment, sale, improvement-characteristic and parcel-universe queries. Orlando adds an exact-address Orange County parcel query with published tax, appraiser-value, sale and structure fields. Other jurisdictions return `NOT_COVERED`; regulations, insurance and satellite layers are not included.
- `workspace_receipt_hash`: SHA-256 of the JSON object excluding this hash, recursively sorted object keys, array order preserved, UTF-8 encoding with no whitespace.

The contract schemas are published at `/schemas/decision-ready-evidence-v1.schema.json` and `/schemas/property-operating-record-v1.schema.json`. Their source-rights posture is deliberately fail-closed: publisher terms and attribution remain controlling, and the record does not assert commercial redistribution rights merely because a source is publicly reachable. The operating record, property record, field contract and workspace hashes are separate so a consumer can verify the smallest object it stores.

## Ephemeral private composition

`POST https://civicdataforge.pages.dev/api/property-compose`

This route enriches an existing paid delivery in memory. It does not mutate the stored fulfillment packet.

```json
{
  "session_id": "YOUR_PRIVATE_CHECKOUT_REFERENCE",
  "market_evidence": [{
    "scope": "PROPERTY_RECORD",
    "property_id": "COPY_FROM_THE_DELIVERED_WORKSPACE",
    "currency": "USD",
    "provider": "Your authorized provider",
    "provider_record_id": "your-record-reference",
    "observed_at": "2026-08-28T18:00:00Z",
    "rights": {"authorization_state": "CALLER_ATTESTED", "scope": "Describe your authorized use"},
    "facts": {"market_value": 417500, "valuation_date": "2026-08-28T00:00:00Z"}
  }],
  "owner_records": [{
    "scope": "PROPERTY_RECORD",
    "property_id": "COPY_FROM_THE_DELIVERED_WORKSPACE",
    "currency": "USD",
    "owner_record_id": "your-private-record-reference",
    "period_start": "2025-08-01T00:00:00Z",
    "period_end": "2026-08-01T00:00:00Z",
    "period_basis": "TRAILING_TWELVE_MONTHS",
    "selected_market_value_path": "facts.authorized_market_value",
    "attestation": {"authority": "OWNER", "attested_at": "2026-08-28T18:30:00Z"},
    "facts": {"gross_operating_income": 60000, "operating_expenses": 21000}
  }]
}
```

The body is limited to 96 KB and requires a successful private checkout session. Every input binds to one exact `property_id` with `PROPERTY_RECORD` scope; unmatched and duplicate records fail closed. Credential-shaped fields, credential-bearing URLs, non-HTTPS source URLs, expired/future clocks, contradictory listing states, incompatible currencies/periods and invalid arithmetic inputs are rejected. Market authorization is caller-attested and remains subject to provider terms; CivicDataForge does not independently verify the license.

The response is `no-store`, `noindex`, marked `persisted:false` and contains a newly receipt-bound ephemeral workspace. The original `GET /api/fulfillment` response remains unchanged. Inputs follow:

- `/schemas/property-market-evidence-input-v1.schema.json`
- `/schemas/property-owner-ledger-input-v1.schema.json`
- `/schemas/property-operating-record-v1.schema.json`

The origin lanes stay separate: official/public values retain `OBSERVED`; normalized market values use `OBSERVED_CALLER_ASSERTED`; private owner values use `OBSERVED_PRIVATE`; arithmetic from private inputs uses `DERIVED_PRIVATE`. An authorized market value is stored as `authorized_market_value`; it never overwrites an official appraiser `market_value`. Owner-reported tax is likewise separate from an official published tax value.

Derived formulas are disclosed: adjusted basis = acquisition + capitalized closing costs + improvements + other adjustments − depreciation; NOI = income − operating expenses; cap rate = NOI ÷ selected value; cash-on-cash = (income − expenses − debt service) ÷ cash invested; break-even occupancy = fixed costs ÷ gross potential income; DSCR = NOI ÷ debt service. Missing or invalid inputs leave the result `UNKNOWN`.

Normal records use `projection: INLINE`. If optional context plus unusually large primary values would exceed the delivery-store budget, the rebuilt workspace uses `projection: COMPACT_PARENT_REFERENCE`: the complete paid values remain unchanged in the hash-bound parent packet, the workspace names the omitted projections, and each field contract retains its value receipt. This is a storage projection, not data loss or a different decision.

New dispatches bind the workspace inside the parent packet hash. Older packets stay immutable; the API may return a separately hashed derived workspace with municipal state `NOT_CHECKED` or `NOT_COVERED`. The derived view does not imply that a new source fetch occurred.

## Meaning, freshness and limits

- Permit IDs, statuses and expiration dates in the original decision are grouped values. They are not paired into invented per-permit objects. A candidate's date is not an expiry finding for the submitted property.
- Presence distinguishes `absent`, `null`, `empty` and `value` **in the observed representation**. Permit decisions may already have flattened original publisher fields, so their presence is not proof of the original publisher key set.
- Chicago uses the official Building Violations dataset, `https://data.cityofchicago.org/resource/22u3-xenr.json`. Only selected public case fields are retained, not owner contacts. One exact normalized building-address query per portfolio is bounded to 201 fetched rows, 200 retained source rows and at most 100 property/case projections across the workspace. Limits produce `BOUNDED`, never a complete negative.
- A building record is not a finding against every unit. The case's publisher modification date remains separate from retrieval time. A new fetch is not a promise of new government data.
- Chicago describes this dataset as historical: it can include liable and not-liable cases, may not reflect current building condition, and is not intended for real-estate transaction clearance. A status published as OPEN is not our determination of a current defect.
- The public property join retains no owner, taxpayer, buyer or seller names. It uses an exact normalized building-address query to resolve a 14-digit parcel PIN, then projects selected assessment, parcel-sale, improvement and current parcel-universe fields. Public parcel facts are building/parcel scope and do not automatically describe a specific unit.
- `assessed_value` is the latest nonempty published Board-certified, Assessor-certified or mailed assessment stage. It is not market value. For a published Cook County residential class, `assessor_implied_market_value` may be derived from the county's stated 10% residential assessment level; the method and limitation remain attached. `market_value` still stays unknown without a licensed AVM or appraisal.
- A published parcel sale is neither an active asking price nor tax basis. Current sale/rental listing status requires an authorized RESO/MLS, rental or PMS feed. Cost basis requires owner acquisition, closing-cost, improvement, depreciation and adjustment records. A tax code is not a tax rate; no current rate or bill is inferred from an outdated table.
- Orlando's official parcel response may include appraiser market, assessed, land, building and taxable values; published total taxes; adjusted sale; living area; beds/baths; year built; zoning, pool, coordinates and a publisher edit clock. The taxes-to-taxable-value ratio is `DERIVED`, not statutory millage. Tax year and payment status remain `UNKNOWN` unless separately sourced. Multiple parcel candidates, source errors and result caps do not become an arbitrary match or complete negative.
- NOI, cap rate, cash-on-cash return and break-even occupancy remain unknown until the owner/PMS/accounting inputs required by each method exist. A result may not silently substitute asking rent for achieved rent or assessed value for a chosen market-value basis.
- Complete comparable municipal snapshots can show changed case fields. Source failures, partial results and unsupported addresses cannot produce a “resolved” or “clear” conclusion. History retains prior packets for inspection; secondary-source failures do not trigger another paid permit run.
- Weekly monitoring alerts are in the private dashboard, not email. A published expiration date is a review suggestion, not an independently confirmed renewal deadline.
- Large optional context is removed with `BOUNDED` / `WORKSPACE_PAYLOAD_LIMIT` before it can overflow the one-off delivery store. If duplicating an unusually large primary record would still exceed the budget, only the original primary packet is stored and the read API derives the permit-only view. The paid primary evidence is never truncated to make room for context.
- CSV exports escape spreadsheet formulas. JSON and reports contain property data; handle them as private files.

## Connection read

`GET /api/host-properties` uses the existing Secure, HttpOnly, 30-day same-browser capability cookie. It accepts no customer ID, email or listing-ID lookup parameter. Deactivated/expired connections cannot retrieve retained listings. It returns at most 250 received active listings; this limit and incomplete addresses are explicit. Browser sessions do not claim email authentication or cross-device recovery. Contact support for ownership-verified assistance.

No partner keys, guest data or payment details are exposed. Selecting addresses does not authorize an extra charge. This is a human-reviewed copy into intake, not a portfolio auto-sync subscription.
