How MyTapWater data maps onto the Property Data Trust Framework (PDTF), the
Open Property Data Association's open schema for residential property data
exchange in England & Wales.
https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json). Latest release verified v3.5.0 (2025-08-28), schema line v3 stable.
Repo: <https://github.com/Property-Data-Trust-Framework/schemas>.
GET /lookup?uprn=<uprn>&format=pdtf (or ?postcode=…&format=pdtf).PDTF models a property as a transaction whose propertyPack holds property
data keyed by integer uprn. We emit a partial transaction:
{
"$schema": "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
"transactionId": "mytapwater:<uprn|postcode>",
"status": "provider-data",
"propertyPack": {
"uprn": <integer>, // PDTF-native key
"address": { "postcode": "...", "homeNation": "England|Wales" },
"mytapwater": { …provider water dataset… } // namespaced extension
}
}
mytapwater extension (not native PDTF fields)PDTF has no native fields for tap-water quality, a composite water score, lead-pipe
likelihood, or storm-overflow proximity. PDTF v3's transaction schema sets **no
additionalProperties: false and explicitly supports custom overlays /
extensions** for provider-specific data (see src/schemas/v3/overlays/extensions
and exampleCustomOverlay in the schema repo). So provider data lives under a
single clearly-namespaced propertyPack.mytapwater key. This validates against the
base v3 schema (permissive) and is the PDTF-sanctioned pattern — a platform engineer
reads propertyPack.uprn + propertyPack.mytapwater.* with no translation.
Water-relevant *native* PDTF structures exist (con29DW = CON29 Drainage & Water
overlay; searches/drainage-and-water) but they model conveyancing search answers,
not ongoing water-quality metrics — a poor fit for our data. We reference them in
provenance rather than forcing our data into them.
| MyTapWater output | PDTF path | Notes |
|---|---|---|
| UPRN | propertyPack.uprn (integer) | PDTF-native. Resolved from ONSUD (Dec 2025). |
| Postcode | propertyPack.address.postcode | resolved_postcode when input was a UPRN. |
| Nation (England/Wales) | propertyPack.address.homeNation | PDTF uses homeNation. |
| Water company | propertyPack.mytapwater.supply.waterCompany | + supplyZone, dataYear, populationServed. |
| Composite score (0–100) + label | …mytapwater.waterQualityScore.{value,label} | MyTapWater-derived; provenance flags derivation. |
| Water-quality parameters (pH, hardness, chlorine, nitrate, lead, fluoride, THM, copper, iron, nickel, arsenic, pesticides) | …mytapwater.parameters.<param>.{min,mean,max,unit} | Source: DWI / company statutory reports. |
| Lead-pipe likelihood (band + score) | …mytapwater.leadPipeRisk | Postcode-area likelihood, EPC-derived. Explicitly *not* property-level (see honesty note). null where no EPC evidence. |
| Hosepipe / water-use restrictions | …mytapwater.restrictions.{status,scope,effectiveFrom} | Company-level; provenance carries the company's own source_url. |
| Storm-overflow (sewage) proximity | …mytapwater.sewage (reference block) | Full nearest-outfall data served UPRN-keyed at /sewage-lookup?uprn=; needs geocoding so it is referenced, not inlined (keeps format=pdtf fast). |
| Live incidents | …mytapwater.activeIncidents[] | {type,severity,title,since}. |
| PFAS enforcement notices | …mytapwater.pfasNotices[] | Active notices only. |
Every data block carries a provenance object aligned to PDTF's verified-claims
vocabulary (src/schemas/verifiedClaims/pdtf-verified-claims.json, an OIDC-IA-style
model):
"provenance": {
"trust_framework": "uk_open_data",
"source": "Drinking Water Inspectorate / <company> statutory water-quality report",
"data_year": 2025,
"source_url": "https://…", // where the underlying record lives
"resolved_by": "MyTapWater API (mytapwater.co.uk)",
"time": "2026-07-15T…Z"
}
This maps to PDTF verified-claims verification.{trust_framework,time} +
verification.evidence[].source. We use a flattened provenance object rather than
the full nested verification/claims envelope — see deviations.
provenance object per block, not the full PDTF verifiedClaims[] envelope ({ claims, verification: { evidence: [...] } }).
Rationale: our provenance is dataset-level (a regulator/company report + our
derivation), not per-field attestations. A wrapper to full verified-claims is
trivial if an integrator needs it.
trust_framework: "uk_open_data" is a MyTapWater value, not a registered PDTFtrust-framework identifier (PDTF's are aimed at identity/KYC frameworks).
mytapwater extension rather than a formally registered PDTFoverlay id — see above; validates against base v3 but is not an OPDA-approved overlay.
leadPipeRisk is postcode-area, not property-level. PDTF's uprn key implies property granularity; our lead model is a postcode-area screen. The block's basis
field and propertyPack.mytapwater._note state this explicitly so no integrator
mistakes it for a property-level test.
sewage is a reference, not inlined (geocoding cost) — full data at /sewage-lookup?uprn=.
status omitted. PDTF's status is a conveyancing lifecycle enum (For sale, Sold subject to contract, …) that does not apply
to provider data, so we omit it (it is optional). Including a non-enum value
like provider-data is the only thing that fails v3 validation.
The emitted transaction validates against the base v3 pdtf-transaction.json
JSON Schema (permissive: no required fields, no additionalProperties:false;
propertyPack.uprn is integer, which we honour). The mytapwater.* extension is
not described by the base schema — that is expected for a namespaced extension and is
the intended deviation (item 3). A saved, validated example lives at
samples/pdtf-example-<uprn>.json.