{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/propraven-com/json-schema/propraven-com-parcel-dossier-schema.json",
  "title": "ParcelDossier",
  "description": "The paid, provenance-first parcel dossier returned by GET /parcels/{id}/report. Every populated field is delivered as `{name, value}`; the receipts (source, as_of, confidence, coverage) come as the opt-in `provenance` map (`include_provenance=true`), keyed by field name. The deeds/comps/permits sub-tables carry real rows; the GeoJSON boundary is a separately-priced add-on omitted from the base payload. Nulls/empties are dropped (see meta.field_count), not returned as null; columns a `sections`/`fields` projection leaves out are named in `meta.projection.omitted_fields`.",
  "x-generated": "2026-10-31",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/propraven-com-openapi.json#/components/schemas/ParcelDossier",
  "type": "object",
  "properties": {
    "parcel": {
      "type": "object",
      "description": "Parcel identity.",
      "properties": {
        "canonical_id": {
          "type": "string",
          "description": "state_fips:county_fips:parcel_id."
        },
        "id": {
          "type": [
            "string",
            "null"
          ],
          "description": "PropRaven row UUID when the serving row carried one."
        },
        "state_fips": {
          "type": "string"
        },
        "county_fips": {
          "type": "string",
          "description": "3-digit within-state county code (the parcels_serving convention)."
        },
        "county_fips_3": {
          "type": "string",
          "description": "Same as county_fips, named for its width."
        },
        "county_fips_5": {
          "type": "string"
        },
        "parcel_id": {
          "type": "string"
        },
        "state": {
          "type": [
            "string",
            "null"
          ]
        },
        "address": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "fields": {
      "type": "array",
      "description": "Every populated field the delivery kept, as {name, value}. The receipt properties below are present only on the pure assembler's unprojected output; on a delivered dossier they live in the `provenance` map when `include_provenance=true` was requested.",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "value": {
            "description": "The field value (any JSON type)."
          },
          "source": {
            "type": "string",
            "description": "Field-level source label from the sealed catalog."
          },
          "as_of": {
            "type": [
              "string",
              "null"
            ]
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "0..1 FIELD-LEVEL coverage (this parcel's state, else national); null when uncatalogued. Not a per-cell probability -- see confidence_basis."
          },
          "confidence_basis": {
            "type": "string"
          },
          "national_coverage": {
            "type": [
              "number",
              "null"
            ]
          },
          "state_coverage": {
            "type": [
              "number",
              "null"
            ]
          },
          "tier": {
            "type": [
              "string",
              "null"
            ],
            "description": "prime|strong|good|partial|sparse|trace, re-derived from the applicable (state, else national) coverage."
          },
          "grain": {
            "type": [
              "string",
              "null"
            ]
          },
          "section": {
            "type": [
              "string",
              "null"
            ]
          },
          "flags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "catalogued": {
            "type": "boolean",
            "description": "False when the column is absent from the sealed catalog (source unknown)."
          }
        }
      }
    },
    "sections": {
      "type": "object",
      "description": "Real sub-tables from the serving relations.",
      "properties": {
        "deeds": {
          "$ref": "#/$defs/DossierSubSection"
        },
        "comps": {
          "$ref": "#/$defs/DossierSubSection"
        },
        "permits": {
          "$ref": "#/$defs/DossierSubSection"
        }
      }
    },
    "boundary": {
      "type": "object",
      "description": "The GeoJSON boundary add-on. In the base dossier included=false and geometry is absent; a phase-2 add-on purchase populates geometry.",
      "properties": {
        "code": {
          "type": "string"
        },
        "price": {
          "$ref": "#/$defs/Money"
        },
        "included": {
          "type": "boolean"
        },
        "purchasable": {
          "type": "boolean"
        },
        "available": {
          "type": [
            "boolean",
            "null"
          ]
        },
        "relation": {
          "type": "string"
        },
        "note": {
          "type": "string"
        },
        "geometry": {
          "description": "GeoJSON geometry, present only for an entitled add-on purchase."
        }
      }
    },
    "meta": {
      "type": "object",
      "description": "Dossier-level provenance and pricing.",
      "properties": {
        "catalog_version": {
          "type": "string"
        },
        "catalog_seal": {
          "type": "string"
        },
        "contract_version": {
          "type": "number"
        },
        "snapshot_as_of": {
          "type": [
            "string",
            "null"
          ]
        },
        "generated_at": {
          "type": "string"
        },
        "price": {
          "allOf": [
            {
              "$ref": "#/$defs/Money"
            }
          ],
          "description": "The dossier product's contract price. The amount actually charged is the value-tiered per-parcel quote settled via x402 (or billed on a paid subscription) -- see the 402 accepts and /storefront/availability?parcel_id=."
        },
        "currency": {
          "type": "string"
        },
        "field_count": {
          "type": "number",
          "description": "Populated fields actually emitted."
        },
        "catalog_sellable_fields": {
          "type": "number"
        },
        "catalog_total_columns": {
          "type": "number"
        },
        "sections_included": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "geometry_included": {
          "type": "boolean"
        },
        "confidence_basis": {
          "type": "string"
        },
        "provenance_note": {
          "type": "string"
        },
        "license": {
          "type": "string",
          "format": "uri",
          "description": "The data license that governs this dossier's data (the Data license section of the Terms of Use)."
        },
        "entitlement": {
          "type": "object",
          "properties": {
            "gated": {
              "type": "boolean"
            },
            "method": {
              "type": "string"
            },
            "note": {
              "type": "string"
            }
          }
        }
      }
    },
    "people_fields": {
      "$ref": "#/$defs/PeopleFieldsWithheld",
      "description": "Legacy marker: since 2026-10-09 every paid dossier carries its people fields, so this is absent; it would mean the people fields in this dossier were withheld."
    }
  },
  "$defs": {
    "DossierSubSection": {
      "type": "object",
      "description": "One real sub-table (deeds, comps, or permits) in a dossier, with the exact serving relation it came from and that relation's own watermark (each is NOT pinned to the parcels epoch).",
      "properties": {
        "relation": {
          "type": "string",
          "description": "The serving relation the rows came from."
        },
        "as_of": {
          "type": [
            "string",
            "null"
          ],
          "description": "Watermark for this relation."
        },
        "status": {
          "type": "string",
          "enum": [
            "ok",
            "empty"
          ]
        },
        "row_count": {
          "type": "integer"
        },
        "note": {
          "type": "string"
        },
        "rows": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "similarity_basis": {
          "type": "string",
          "description": "comps: present when rows exist; each comps row carries `similarity_score` (0–100) and `similarity_components`, computed at read time exactly as GET /parcels/{id}/comps computes them. They do not affect the dossier price."
        },
        "similarity_method": {
          "type": "string",
          "description": "comps: how `similarity_score` is computed."
        }
      }
    },
    "Money": {
      "type": "object",
      "description": "A money amount as a decimal string plus its currency code.",
      "required": [
        "amount",
        "currency"
      ],
      "properties": {
        "amount": {
          "type": "string"
        },
        "currency": {
          "type": "string"
        }
      },
      "additionalProperties": false
    },
    "PeopleFieldsWithheld": {
      "type": "object",
      "description": "Additive marker on a record (or list) whose people fields were withheld because the caller has no PropRaven account. People fields are owner names, owner mailing / owner-address columns, entity principals, recorded-document party names and addresses (grantor/grantee, buyer/seller, prior/new owner), permit applicant names and the resolved `owner_contact` block. Withheld keys are kept and set to null (lists of people records become []), so the record's shape does not change. Send an API key (`Authorization: Bearer pz_...`) or call from a signed-in session; on the paid products a settled x402 payment or a prepaid credit debit also delivers people data.",
      "required": [
        "status",
        "code",
        "reason",
        "note"
      ],
      "properties": {
        "status": {
          "type": "string",
          "enum": [
            "withheld"
          ]
        },
        "code": {
          "type": "string",
          "enum": [
            "account_required"
          ]
        },
        "reason": {
          "type": "string",
          "enum": [
            "people_data_requires_account"
          ]
        },
        "note": {
          "type": "string"
        }
      }
    }
  }
}