Skip to content
Proposed specification for review. We are seeking feedback on key architectural choices in the industry consultation.

02 Verifiable Credentials Data Model

Version: 0.1 (Draft) Date: 9 April 2026 Author: Ed Molyneux Status: Draft Parent: 00 — Architecture Overview


This sub-spec defines how PDTF property data is represented as Verifiable Credentials. It specifies the credential types, their structure, the claims representation model, evidence, terms of use, revocation, the securing mechanism (SD-JWT-VC — provisional; see §2.4), and the type metadata / optional JSON-LD context that binds it all together.

Every piece of property data in PDTF 2.0 — from an EPC rating to an ownership assertion to a conveyancer’s mandate — is a signed, independently verifiable credential. This document is the authoritative reference for how those credentials are structured.

Scope:

  • Credential envelope structure (W3C VC 2.0 conformance)
  • PDTF-specific credential types and their credentialSubject shapes
  • Claims representation: sparse objects with MERGE semantics and dependency pruning
  • Evidence model (simplified from OIDC-derived schema)
  • Terms of use (access control metadata)
  • Credential status (revocation via Bitstring Status List)
  • Securing mechanism (SD-JWT-VC — provisional; Data Integrity / eddsa-jcs-2022 retained as superseded fallback)
  • Type metadata (vct) and optional JSON-LD context definition
  • Migration path from current OIDC verified claims

Out of scope:

  • Entity graph structure and field mapping (see 01 — Entity Graph)
  • DID methods and resolution (see 03 — DID Methods)
  • OpenID Federation trust architecture and Trust Mark schema (see 04 — OpenID Federation)
  • State assembly algorithms (see 07 — State Assembly)
  • Bitstring Status List hosting infrastructure (see 14 — Credential Revocation)

PDTF 2.0 credentials conform to the W3C Verifiable Credentials Data Model v2.0 (CR as of 2024). This section specifies which parts of the W3C model we use, which we constrain, and which we omit.

Every PDTF credential MUST include:

PropertyW3C StatusPDTF RequirementNotes
@contextOptionalOptionalOptional JSON-LD semantic overlay only (§10) — not required under SD-JWT-VC
typeRequiredRequiredAlways includes VerifiableCredential + PDTF-specific type
issuerRequiredRequiredDID string (not object form)
validFromOptional in W3CRequired in PDTFISO 8601 datetime — when data was asserted/retrieved
credentialSubjectRequiredRequiredSingle subject (not array). id is always present.
credentialStatusOptional in W3CRequired in PDTFBitstringStatusListEntry — see §8
proof——Under SD-JWT-VC the credential is JWS-secured (§9), not an embedded proof; the proof block appears only in the superseded Data-Integrity representation
PropertyPDTF UsageNotes
idRequiredCredential identifier URI. REQUIRED for deduplication (Q3.3 resolved). Format: urn:pdtf:vc:{uuid}.
validUntilOptionalExpiry datetime. Used for time-limited credentials (e.g. EPC with known expiry).
evidenceUsedSimplified evidence model — see §6
termsOfUseUsedPdtfAccessPolicy — see §7
PropertyReason for Omission
credentialSchemaSchema validation handled by PDTF tooling against entity schemas, not via in-credential schema references. May revisit for interoperability.
refreshServiceNot needed — credentials are re-issued when data changes, not refreshed in-place.
name / descriptionHuman-readable metadata not needed for machine-processed property data.
relatedResourceNot needed in initial implementation.

PDTF secures credentials as SD-JWT-VC (IETF SD-JWT VC) — a JWS-signed credential with selective disclosure and holder key binding — and verifies mdoc (ISO/IEC 18013-5) at the GOV.UK identity boundary. Rationale:

  1. Ecosystem alignment — SD-JWT-VC + mdoc are the formats mandated by eIDAS 2.0 and profiled by OpenID4VC HAIP; the GOV.UK Wallet issues mdoc. Plain JSON-LD Data Integrity is issued by neither.
  2. Selective disclosure / data minimisation — native to SD-JWT-VC (salted-hash disclosures), so a holder presents only the fields a party needs, governed by termsOfUse. (This reverses the earlier assumption that selective disclosure was unnecessary for property data.)
  3. Holder binding — SD-JWT-VC key binding (cnf + Key Binding JWT) gives cryptographic proof of possession at presentation — the same primitive the cross-party identity-reuse flow relies on (03 §10.5).
  4. Legibility — SD-JWT-VC decodes to plain JSON (unlike binary-CBOR mdoc), so credentials stay developer- and inspection-friendly. (AI-agent legibility is driven by the composed entity graph exposed over the API, not the wire format — see §13.4.)

Superseded approach. Earlier drafts used embedded W3C Data Integrity proofs (DataIntegrityProof, eddsa-jcs-2022) over JSON-LD; that is retained as a documented fallback/alternative in §9.1 but is no longer primary. PDTF MAY keep a JSON-LD @context purely as an optional semantic overlay (§10), decoupled from the securing mechanism.

PDTF credentials use a single credentialSubject (not an array). The credentialSubject.id is always present and MUST be a valid DID or URN from the PDTF identifier scheme (see 01 — Entity Graph §5).

{
"credentialSubject": {
"id": "urn:pdtf:uprn:100023456789",
"energyEfficiency": { ... }
}
}

Not:

{
"credentialSubject": [
{ "id": "urn:pdtf:uprn:100023456789", ... },
{ "id": "urn:pdtf:uprn:200034567890", ... }
]
}

If a credential needs to make assertions about multiple entities, issue separate credentials. This keeps the trust chain clean: one issuer, one subject, one set of claims, one proof.


Each entity type in the PDTF entity graph (see 01 — Entity Graph) has a corresponding credential type. The credential type determines the expected shape of credentialSubject and the valid credentialSubject.id identifier format.

Credential TypeEntitySubject ID FormatIssuerDescription
PropertyCredentialPropertyurn:pdtf:uprn:{uprn}Trusted proxy / root issuer / userProperty facts: EPC, flood, build info, legal questions, fixtures, searches
TitleCredentialTitleurn:pdtf:titleNumber:{n} or urn:pdtf:unregisteredTitle:{id}HMLR proxy / root issuerRegister extract, ownership type, leasehold terms, encumbrances
SellerCapacityCredentialSellerCapacityurn:pdtf:capacity:{id}Account providerThe capacity in which a party sells. Implies Seller.
OfferCredentialOfferurn:pdtf:offer:{id}Buyer (Person) or platformBuyer DID, amount, status, conditions. Implies Buyer.
GiftCredentialGifturn:pdtf:gift:{id}Giftor or platformDonor DID, offer, gift terms. Implies Giftor.
RepresentationCredentialRepresentationurn:pdtf:representation:{id}Instructing party (Person/Org)Representative DID, represented party DID, kind of representation
TransactionRoleCredentialTransactionRoleurn:pdtf:role:{id}PlatformParticipant DID, role, where no more specific relationship applies
TransactionCredentialTransactiondid:web:...PlatformTransaction metadata, status, milestones, financial context

Purpose: Asserts facts about the physical property that travel with the property across transactions (the logbook — see 01 §2.1).

Subject ID: urn:pdtf:uprn:{uprn} — the property’s Unique Property Reference Number.

Credential subject shape: Sparse subset of the Property entity schema. A single PropertyCredential covers one or more paths on the Property entity. It does NOT need to contain the full Property schema — only the paths the issuer is asserting.

Key design decision (D4): EPC data, flood risk, searches, and other property facts are represented as PropertyCredentials with paths on the Property entity — not as separate first-class entity types. An EPC is a PropertyCredential with credentialSubject.energyEfficiency, not an “EPCCredential”. This keeps the entity model clean and the credential type set small. Primary issuers (MHCLG, EA) will use the same paths when they adopt the standard.

Example paths and typical issuers:

PathDataTypical Issuer
energyEfficiency.*EPC certificate, recommendationsEPC proxy / MHCLG root issuer
environmentalIssues.flooding.*Flood risk zones, historyEA proxy / EA root issuer
buildInformation.*Build date, type, materialsSeller (UserAttestation)
residentialPropertyFeatures.*Bedrooms, bathrooms, parkingEstate agent / seller
heating.*Heating system type, fuelSeller (UserAttestation)
fixturesAndFittings.*What’s included/excludedSeller (UserAttestation)
councilTax.*Band, amountVOA proxy / VOA root issuer
localSearches.*Local land charges, authority searchesLLC proxy / search provider
searches.*Environmental, drainage searchesSearch provider
disputesAndComplaints.*Boundary disputesSeller (UserAttestation)
alterationsAndChanges.*Planning, building regsSeller + council records
connectivity.*Broadband, mobileOfcom data / seller
address.*Property addressPlatform / OS AddressBase

Minimal PropertyCredential (EPC only) — shown as the SD-JWT-VC issuer-signed payload (decoded; the compact JWS and any selective-disclosure digests are omitted for readability — see §9 for the full envelope). This is a public adapter credential, so it is not holder-bound (no cnf):

{
"iss": "did:web:adapters.propdata.org.uk:epc",
"vct": "urn:pdtf:vct:PropertyCredential",
"sub": "urn:pdtf:uprn:100023456789",
"iat": 1774339200,
"exp": 2088547200,
"status": {
"status_list": {
"idx": 18293,
"uri": "https://adapters.propdata.org.uk/status/epc/list-042"
}
},
"credentialSubject": {
"id": "urn:pdtf:uprn:100023456789",
"energyEfficiency": {
"certificate": {
"certificateNumber": "1234-5678-9012-3456-7890",
"currentEnergyRating": "C",
"currentEnergyEfficiency": 72,
"potentialEnergyRating": "B",
"potentialEnergyEfficiency": 85,
"lodgementDate": "2024-01-15",
"expiryDate": "2034-01-15"
}
}
},
"evidence": [{
"type": "ElectronicRecord",
"source": "get-energy-performance-data.communities.gov.uk",
"retrievedAt": "2026-03-24T09:58:00Z",
"method": "API"
}],
"termsOfUse": [{
"type": "PdtfAccessPolicy",
"confidentiality": "public",
"pii": false
}]
}

The signature is the enclosing JWS (§9), not an embedded proof; revocation is the status (Token Status List) claim, replacing the former credentialStatus.

Seller-attested PropertyCredential (heating):

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://trust.propdata.org.uk/ns/pdtf/v2"
],
"type": ["VerifiableCredential", "PropertyCredential"],
"issuer": "did:web:platform.example.com",
"validFrom": "2026-03-20T14:30:00Z",
"credentialSubject": {
"id": "urn:pdtf:uprn:100023456789",
"heating": {
"heatingSystem": {
"heatingType": "Central heating",
"centralHeatingDetails": {
"fuelType": "Mains gas",
"boilerType": "Combination boiler",
"boilerAge": "5-10 years"
}
}
}
},
"evidence": [{
"type": "UserAttestation",
"source": "did:key:z6MkhSellerAbc123",
"attestedAt": "2026-03-20T14:30:00Z",
"method": "BASPI form completion"
}],
"termsOfUse": [{
"type": "PdtfAccessPolicy",
"confidentiality": "restricted",
"pii": false,
"roleRestrictions": ["sellerConveyancer", "buyerConveyancer", "estateAgent", "buyer"]
}],
"credentialStatus": {
"id": "https://api.platform.example.com/status/property/list-007#4521",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "4521",
"statusListCredential": "https://api.platform.example.com/status/property/list-007"
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:web:platform.example.com#platform-key-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-20T14:30:00Z",
"proofValue": "z3hQ8xNr..."
}
}

Note on issuer for user attestations: When a seller fills in a form, the platform signs the credential on their behalf (custodial key management — see D14 in Architecture Overview). The evidence section records that the attestation came from the seller’s DID. When wallet-held keys are available (future), the seller will sign directly.

Purpose: Asserts facts about the legal title — register data, ownership type, leasehold terms, encumbrances.

Subject ID: urn:pdtf:titleNumber:{number} for registered titles, urn:pdtf:unregisteredTitle:{id} for unregistered titles.

Credential subject shape: Sparse subset of the Title entity schema.

Typical issuer: HMLR proxy adapter (trusted proxy for HMLR data), or HMLR directly as root issuer (future).

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://trust.propdata.org.uk/ns/pdtf/v2"
],
"type": ["VerifiableCredential", "TitleCredential"],
"issuer": "did:web:adapters.propdata.org.uk:hmlr",
"validFrom": "2026-03-24T08:15:00Z",
"credentialSubject": {
"id": "urn:pdtf:titleNumber:AB12345",
"ownershipType": "Freehold",
"title": {
"registerExtract": {
"proprietorship": {
"owners": [
{
"name": "John Smith",
"address": "1 Example Street, London, SW1A 1AA"
}
],
"priceStatedPaid": 350000,
"dateOfRegistration": "2018-06-15"
},
"restrictions": [],
"charges": [
{
"chargee": "Nationwide Building Society",
"dateOfCharge": "2018-06-15"
}
]
}
}
},
"evidence": [{
"type": "ElectronicRecord",
"source": "landregistry.data.gov.uk",
"retrievedAt": "2026-03-24T08:14:00Z",
"method": "OC1 API"
}],
"termsOfUse": [{
"type": "PdtfAccessPolicy",
"confidentiality": "restricted",
"pii": true,
"roleRestrictions": ["sellerConveyancer", "buyerConveyancer"]
}],
"credentialStatus": {
"id": "https://adapters.propdata.org.uk/status/hmlr/list-015#7742",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "7742",
"statusListCredential": "https://adapters.propdata.org.uk/status/hmlr/list-015"
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:web:adapters.propdata.org.uk:hmlr#key-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-24T08:15:00Z",
"proofValue": "zR9kW2pL..."
}
}

Leasehold TitleCredential example (partial):

{
"credentialSubject": {
"id": "urn:pdtf:titleNumber:CD67890",
"ownershipType": "Leasehold",
"leaseholdDetails": {
"originalLeaseLength": 125,
"remainingLeaseLength": 98,
"leaseStartDate": "2001-03-01",
"groundRent": {
"amount": 250,
"frequency": "Annual",
"reviewType": "Fixed"
},
"serviceCharge": {
"annualAmount": 1800,
"managingAgent": "ABC Property Management Ltd"
},
"freeholderName": "Freehold Estates Ltd"
},
"title": {
"registerExtract": { ... }
}
}
}

Purpose: A thin signed assertion of the capacity in which a Person or Organisation sells — legal owner, personal representative for a deceased owner, attorney under a power of attorney, mortgagee in possession, company director, trustee. It embodies the Seller role: the Transaction roster carries no role, so a party is a seller because this credential says so (01 §3.2, D32).

Key design decision (D28): The SellerCapacityCredential does NOT duplicate title register details. Those belong on the TitleCredential. The claim is verified by cross-referencing against Title.registerExtract.proprietorship — claim-vs-evidence separation. The SellerCapacityCredential says “X sells, as legal owner”. The TitleCredential provides the evidence from HMLR that proves it.

Subject ID: urn:pdtf:capacity:{id} — a generated URN for this assertion.

Issuer: The account provider that verified the user’s identity and cross-referenced against the title register. Issued for every seller, whether or not a capacity has been declared yet, so the credential embodying the Seller role is never missing while a form is still being filled in.

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://trust.propdata.org.uk/ns/pdtf/v2"
],
"type": ["VerifiableCredential", "SellerCapacityCredential"],
"issuer": "did:web:platform.example.com",
"validFrom": "2026-03-18T09:00:00Z",
"credentialSubject": {
"id": "urn:pdtf:capacity:own-a1b2c3",
"seller": "did:key:z6MkhSellerAbc123",
"transaction": "did:web:platform.example.com:transactions:tx-789",
"sellersCapacity": { "capacity": "Legal Owner" },
"dateBecameOwnerOrAuthority": "2014-06-01"
},
"evidence": [{
"type": "ElectronicRecord",
"source": "landregistry.data.gov.uk",
"retrievedAt": "2026-03-18T08:55:00Z",
"method": "OC1 proprietorship name match"
}],
"termsOfUse": [{
"type": "PdtfAccessPolicy",
"confidentiality": "restricted",
"pii": true,
"roleRestrictions": ["Seller's Conveyancer", "Buyer's Conveyancer", "Estate Agent"]
}],
"credentialStatus": {
"id": "https://api.platform.example.com/status/capacity/list-001#892",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "892",
"statusListCredential": "https://api.platform.example.com/status/capacity/list-001"
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:web:platform.example.com#platform-key-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-18T09:00:00Z",
"proofValue": "z5tPqR7s..."
}
}

SellerCapacityCredential fields:

FieldTypeRequiredDescription
sellerDID stringRequiredDID of the Person or Organisation selling
transactionDID stringRequireddid:web of the transaction
titleURN stringOptionalurn:pdtf:titleNumber:* or urn:pdtf:unregisteredTitle:*, where a capacity is specific to one of several titles
sellersCapacity.capacityenumOptionalLegal Owner, Personal Representative for a Deceased Owner, Under Power of Attorney, Mortgagee in Possession, Company Director, Company Secretary, Trustee, Assistant, Other
dateBecameOwnerOrAuthorityISO dateOptionalWhen the seller acquired the title or the authority to sell it

How verified: the degree of verification is carried in evidence, not in the subject: UserAttestation (the seller’s own declaration), ElectronicRecord (OC1 proprietorship cross-reference), ProfessionalVerification (conveyancer confirmation). A verifier reads the strongest evidence present.

Why thin? A verifier who wants to confirm the right to sell checks:

  1. The SellerCapacityCredential links Person DID X to the Transaction with a declared capacity
  2. The TitleCredential for each title in Transaction.titlesToBeSold has registerExtract.proprietorship showing the registered owner
  3. The two are consistent — the SellerCapacityCredential’s evidence points back to the register cross-reference
  4. Both credentials are signed and not revoked

This separation means the capacity can be revoked (sale completes, a personal representative is replaced) without affecting the title register data. And title data can be updated (charge removed) without re-issuing the capacity assertion.

Purpose: Records that one party has been instructed by another: “I, the seller, instruct Smith & Co Solicitors as my conveyancer.” It carries the kind of representation as its role — a property of the representation, not a duplicated participant attribute. Acting for a seller tells you the side, not whether the party is the conveyancer, the agent or the surveyor.

Subject ID: urn:pdtf:representation:{id} — a generated URN for this representation.

Issuer: The party granting the authority. In practice, during Phase 1, the platform signs on behalf of the person (custodial keys), so the issuer is did:web:platform.example.com and the evidence records the person’s DID and their explicit instruction.

Key design decision (D33): One credential per (representative, represented party) pair. A conveyancer instructed jointly by two sellers holds two, each presentable or revocable on its own. A separated couple instructing their own conveyancers yields one each. The retainer is with the client, so representedParty is a person or organisation, never an offer — a prospective buyer with no offer yet can still be represented. The representative’s firm is recorded on the Transaction roster, not here, because where someone works does not stop being true when a representation ends; the professional duty, PI insurance and regulatory obligations are resolved through that firm.

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://trust.propdata.org.uk/ns/pdtf/v2"
],
"type": ["VerifiableCredential", "RepresentationCredential"],
"issuer": "did:web:platform.example.com",
"validFrom": "2026-03-15T11:00:00Z",
"credentialSubject": {
"id": "urn:pdtf:representation:rep-d4e5f6",
"representative": "did:key:z6MkhConveyancerDef456",
"representedParty": "did:key:z6MkhSellerAbc123",
"role": "Seller's Conveyancer",
"transaction": "did:web:platform.example.com:transactions:tx-789"
},
"evidence": [{
"type": "UserAttestation",
"source": "did:key:z6MkhSellerAbc123",
"attestedAt": "2026-03-15T10:55:00Z",
"method": "Platform instruction flow"
}],
"termsOfUse": [{
"type": "PdtfAccessPolicy",
"confidentiality": "restricted",
"pii": true,
"roleRestrictions": ["Seller's Conveyancer", "Buyer's Conveyancer"]
}],
"credentialStatus": {
"id": "https://api.platform.example.com/status/representation/list-001#334",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "334",
"statusListCredential": "https://api.platform.example.com/status/representation/list-001"
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:web:platform.example.com#platform-key-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-15T11:00:00Z",
"proofValue": "zK8mN4rJ..."
}
}

RepresentationCredential fields:

FieldTypeRequiredDescription
representativeDID stringRequiredDID of the Person or Organisation who is instructed
representedPartyDID stringRequiredDID of the Person or Organisation who instructed them
roleenumRequiredThe kind of representation: Seller's Conveyancer, Buyer's Conveyancer, Estate Agent, Buyer's Agent, Mortgage Broker, Surveyor (the v3 participant role enum)
transactionDID stringRequireddid:web of the transaction this applies to

Revocation is critical: When a seller changes conveyancer, the old RepresentationCredential MUST be revoked. Without revocation, a former conveyancer could still present a valid credential. Because role lives only here, revocation also removes the “Seller’s Conveyancer” role from the transaction. See §8 for the revocation mechanism.

Purpose: Records that a party is gifting funds towards a purchase, and the terms a conveyancer must resolve before reporting to a lender: whether the gift is repayable, whether the giftor will have a beneficial interest in the property, whether they will occupy it. It embodies the Giftor role.

Subject ID: urn:pdtf:gift:{id} — a generated URN for this gift.

Issuer: The platform on behalf of the giftor.

The giftor’s link to the offer lives here (offerId) rather than on the Offer, so that an OfferCredential always identifies a buyer.

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://trust.propdata.org.uk/ns/pdtf/v2"
],
"type": ["VerifiableCredential", "GiftCredential"],
"issuer": "did:web:platform.example.com",
"validFrom": "2026-03-22T16:00:00Z",
"credentialSubject": {
"id": "urn:pdtf:gift:gf-m4n5o6",
"donor": "did:key:z6MkhParentPqr456",
"transaction": "did:web:platform.example.com:transactions:tx-789",
"offerId": "o1",
"giftDetails": {
"amount": 50000,
"currency": "GBP",
"relationshipToBuyer": "Parent",
"fundsTransferred": "None",
"repayable": false,
"confersBeneficialInterest": false,
"willOccupyProperty": false
}
},
"evidence": [{
"type": "UserAttestation",
"source": "did:key:z6MkhParentPqr456",
"attestedAt": "2026-03-22T15:50:00Z",
"method": "Gift declaration flow"
}],
"termsOfUse": [{
"type": "PdtfAccessPolicy",
"confidentiality": "confidential",
"pii": true,
"roleRestrictions": ["Buyer's Conveyancer", "Lender"]
}],
"credentialStatus": {
"id": "https://api.platform.example.com/status/gift/list-001#156",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "156",
"statusListCredential": "https://api.platform.example.com/status/gift/list-001"
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:web:platform.example.com#platform-key-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-22T16:00:00Z",
"proofValue": "z7bQm3vR..."
}
}

GiftCredential fields:

FieldTypeRequiredDescription
donorDID stringRequiredDID of the Person or Organisation making the gift
transactionDID stringRequiredTransaction scope
offerIdstringOptionalThe offer this gift contributes towards, as keyed in Transaction.offers
giftDetailsobjectOptionalamount, currency, relationshipToBuyer, fundsTransferred (None, Part, All), repayable, confersBeneficialInterest, willOccupyProperty

Purpose: Asserts a party’s role where no more specific relationship credential applies — Lender, Landlord, Tenant, Surveyor, Platform Support — or where the specific relationship is not yet established. Every party carrying a role holds exactly one role-bearing credential; this is the one that guarantees it.

Subject ID: urn:pdtf:role:{id} — a generated URN.

Issuer: The platform.

This is how a lender becomes a party to the transaction (Q4.2 in 00). It asserts participation in a role, not a relationship to another named party. Access is then governed by termsOfUse.roleRestrictions on the credentials the lender requests.

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://trust.propdata.org.uk/ns/pdtf/v2"
],
"type": ["VerifiableCredential", "TransactionRoleCredential"],
"issuer": "did:web:platform.example.com",
"validFrom": "2026-03-22T16:00:00Z",
"credentialSubject": {
"id": "urn:pdtf:role:tr-g7h8i9",
"participant": "did:web:bigbank.co.uk",
"role": "Lender",
"transaction": "did:web:platform.example.com:transactions:tx-789"
},
"termsOfUse": [{
"type": "PdtfAccessPolicy",
"confidentiality": "restricted",
"pii": false,
"roleRestrictions": ["Buyer's Conveyancer", "Seller's Conveyancer", "Estate Agent"]
}],
"credentialStatus": {
"id": "https://api.platform.example.com/status/role/list-001#41",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "41",
"statusListCredential": "https://api.platform.example.com/status/role/list-001"
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:web:platform.example.com#platform-key-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-22T16:00:00Z",
"proofValue": "z9cRn4wS..."
}
}

TransactionRoleCredential fields:

FieldTypeRequiredDescription
participantDID stringRequiredDID of the Person or Organisation holding the role
roleenumRequiredThe v3 participant role enum: Lender, Landlord, Tenant, Surveyor, Platform Support, …
transactionDID stringRequiredTransaction scope

Purpose: Records a buyer’s offer on a transaction. Buyers exist in the transaction only through Offers — this models reality: a buyer doesn’t participate until they make an offer. It embodies the Buyer role, and always identifies exactly one buyer; joint purchasers each hold an OfferCredential with the same offerId.

Subject ID: urn:pdtf:offer:{id} — a generated URN for this offer.

Issuer: The platform on behalf of the buyer. Future: buyer signs directly with wallet-held key.

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://trust.propdata.org.uk/ns/pdtf/v2"
],
"type": ["VerifiableCredential", "OfferCredential"],
"issuer": "did:web:platform.example.com",
"validFrom": "2026-03-20T09:30:00Z",
"credentialSubject": {
"id": "urn:pdtf:offer:off-j1k2l3",
"buyer": "did:key:z6MkhBuyerXyz789",
"transaction": "did:web:platform.example.com:transactions:tx-789",
"offerId": "o1",
"amount": 450000,
"currency": "GBP",
"status": "Accepted",
"conditions": [
"Subject to survey",
"Subject to mortgage"
],
"buyerCircumstances": {
"isFirstTimeBuyer": true,
"chainStatus": "No chain",
"mortgageRequired": true,
"mortgageAgreedInPrinciple": true
}
},
"evidence": [{
"type": "UserAttestation",
"source": "did:key:z6MkhBuyerXyz789",
"attestedAt": "2026-03-20T09:25:00Z",
"method": "Offer submission"
}],
"termsOfUse": [{
"type": "PdtfAccessPolicy",
"confidentiality": "confidential",
"pii": true,
"roleRestrictions": ["Seller's Conveyancer", "Buyer's Conveyancer", "Estate Agent"]
}],
"credentialStatus": {
"id": "https://api.platform.example.com/status/offers/list-001#2041",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "2041",
"statusListCredential": "https://api.platform.example.com/status/offers/list-001"
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:web:platform.example.com#platform-key-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-20T09:30:00Z",
"proofValue": "zW2nP9sK..."
}
}

OfferCredential fields:

FieldTypeRequiredDescription
buyerDID stringRequiredDID of the Person or Organisation making the offer
transactionDID stringRequiredThe transaction this offer is for
offerIdstringOptionalThe key of this offer in Transaction.offers
amountnumberOptionalOffer amount
currencystringOptionalISO 4217 currency code
statusenumOptionalPending, Accepted, Withdrawn, Rejected, Note of Interest
conditionsstring[]OptionalFree-text conditions
inclusionsstring[]OptionalItems included in the offer
exclusionsstring[]OptionalItems excluded from the offer
buyerCircumstancesobjectOptionalFirst-time buyer, chain status, mortgage requirement

Purpose: Records transaction metadata — status, milestones, financial context, chain information. The Transaction is the root of the entity graph.

Subject ID: did:web:{host}:transactions:{id} — the transaction’s own DID.

Issuer: The platform hosting the transaction.

Note: The saleContext fields were previously part of the monolithic v1 ownership object and have been decomposed per Q7.1.

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://trust.propdata.org.uk/ns/pdtf/v2"
],
"type": ["VerifiableCredential", "TransactionCredential"],
"issuer": "did:web:platform.example.com",
"validFrom": "2026-03-10T12:00:00Z",
"credentialSubject": {
"id": "did:web:platform.example.com:transactions:tx-789",
"status": "Active",
"milestones": {
"listed": "2026-03-01T00:00:00Z",
"saleAgreed": "2026-03-20T00:00:00Z"
},
"saleContext": {
"numberOfSellers": 1,
"numberOfNonUkResidentSellers": 0,
"outstandingMortgage": "Yes",
"existingLender": "Nationwide",
"hasHelpToBuyEquityLoan": "No",
"isLimitedCompanySale": "No"
},
"property": "urn:pdtf:uprn:100023456789",
"titlesToBeSold": ["urn:pdtf:titleNumber:AB12345"],
"participants": [
{ "participant": "did:key:z6MkhSellerAbc123", "participantId": "s1" },
{ "participant": "did:key:z6MkhBuyerXyz789", "participantId": "b1" },
{ "participant": "did:key:z6MkhConveyancerDef456", "participantId": "c1", "organisation": "Smith & Co Law", "organisationReference": "SC/2026/118" }
]
},
"termsOfUse": [{
"type": "PdtfAccessPolicy",
"confidentiality": "restricted",
"pii": false,
"roleRestrictions": ["Seller's Conveyancer", "Buyer's Conveyancer", "Estate Agent"]
}],
"credentialStatus": {
"id": "https://api.platform.example.com/status/transactions/list-001#5567",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "5567",
"statusListCredential": "https://api.platform.example.com/status/transactions/list-001"
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:web:platform.example.com#platform-key-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-10T12:00:00Z",
"proofValue": "zL5jH9wQ..."
}
}

The credentialSubject.id field identifies the entity the credential makes assertions about. It MUST be present and MUST use the PDTF identifier scheme:

Credential TypeSubject ID FormatExample
PropertyCredentialurn:pdtf:uprn:{uprn}urn:pdtf:uprn:100023456789
TitleCredentialurn:pdtf:titleNumber:{n}urn:pdtf:titleNumber:AB12345
SellerCapacityCredentialurn:pdtf:capacity:{id}urn:pdtf:capacity:own-a1b2c3
OfferCredentialurn:pdtf:offer:{id}urn:pdtf:offer:off-j1k2l3
GiftCredentialurn:pdtf:gift:{id}urn:pdtf:gift:gf-m4n5o6
RepresentationCredentialurn:pdtf:representation:{id}urn:pdtf:representation:rep-d4e5f6
TransactionRoleCredentialurn:pdtf:role:{id}urn:pdtf:role:tr-g7h8i9
TransactionCredentialdid:web:{host}:transactions:{id}did:web:platform.example.com:transactions:tx-789

A credential’s credentialSubject contains only the paths the issuer is asserting — not the full entity schema. This is the sparse object model.

A PropertyCredential issued by the EPC adapter contains only energyEfficiency:

{
"credentialSubject": {
"id": "urn:pdtf:uprn:100023456789",
"energyEfficiency": {
"certificate": {
"currentEnergyRating": "C",
"currentEnergyEfficiency": 72
}
}
}
}

A PropertyCredential issued by the flood adapter contains only environmentalIssues.flooding:

{
"credentialSubject": {
"id": "urn:pdtf:uprn:100023456789",
"environmentalIssues": {
"flooding": {
"floodZone": "1",
"surfaceWaterRisk": "Low",
"historicalFlooding": "No"
}
}
}
}

State assembly merges these sparse objects to build the complete entity state. See §5 for the merge semantics.

The credentialSubject.id connects the credential to the entity graph:

┌─────────────────────────────┐
│ PropertyCredential (EPC) │
│ credentialSubject.id: │
│ urn:pdtf:uprn:10002345... │──┐
└─────────────────────────────┘ │
│ same entity
┌─────────────────────────────┐ │
│ PropertyCredential (flood) │ │
│ credentialSubject.id: │──┤
│ urn:pdtf:uprn:10002345... │ │
└─────────────────────────────┘ │
│
┌─────────────────────────────┐ │
│ Entity Graph │ │
│ properties: │ │
│ "urn:pdtf:uprn:10002345": │◄─┘
│ { merged state } │
└─────────────────────────────┘

Multiple credentials with the same credentialSubject.id assert different facts about the same entity. State assembly merges them, with later credentials (by validFrom) taking precedence for overlapping paths.


Decision D5 — needs LMS consensus.

The current PDTF v1 system uses pathKey:value pairs with REPLACE semantics:

[
{ "claimPath": "/propertyPack/heating/heatingSystem/heatingType", "claimValue": "Central heating" },
{ "claimPath": "/propertyPack/heating/heatingSystem/centralHeatingDetails/fuelType", "claimValue": "Mains gas" },
{ "claimPath": "/propertyPack/heating/heatingSystem/centralHeatingDetails/boilerType", "claimValue": "Combination boiler" }
]

PDTF 2.0 replaces this with sparse objects using MERGE semantics and dependency pruning.

When assembling state from multiple credentials, claims are merged using deep-merge:

State = {}
For each credential (ordered by validFrom, ascending):
State = deepMerge(State, credential.credentialSubject)

Deep merge rules:

  • Object + Object → recursive merge (keys from both, later wins on conflict)
  • Primitive + Primitive → later value wins
  • Array + Array → later array replaces entirely (arrays are not element-merged)
  • Any + undefined → existing value preserved
  • undefined + Any → new value applied

This means a newer credential can update specific paths without needing to re-state the entire entity. The EPC adapter can issue a new PropertyCredential with just energyEfficiency and it merges cleanly with the seller’s attestation of heating.

MERGE alone isn’t sufficient. Consider this scenario:

Step 1: Seller attests heating system:

{
"credentialSubject": {
"id": "urn:pdtf:uprn:100023456789",
"heating": {
"heatingSystem": {
"heatingType": "Central heating",
"centralHeatingDetails": {
"fuelType": "Mains gas",
"boilerType": "Combination boiler",
"boilerAge": "5-10 years"
}
}
}
}
}

Step 2: Seller updates — the property now has no heating:

{
"credentialSubject": {
"id": "urn:pdtf:uprn:100023456789",
"heating": {
"heatingSystem": {
"heatingType": "None"
}
}
}
}

After MERGE (without pruning):

{
"heating": {
"heatingSystem": {
"heatingType": "None",
"centralHeatingDetails": {
"fuelType": "Mains gas",
"boilerType": "Combination boiler",
"boilerAge": "5-10 years"
}
}
}
}

Problem: heatingType is “None” but centralHeatingDetails still exists from the earlier credential. The state is internally inconsistent.

Dependency pruning resolves this. After MERGE, a pruning pass inspects the schema’s discriminator/conditional logic:

  1. The schema defines centralHeatingDetails as conditional on heatingType being "Central heating" (via JSON Schema oneOf / if-then-else).
  2. Since heatingType is now "None", the pruning pass strips centralHeatingDetails from the assembled state.

After MERGE + Prune:

{
"heating": {
"heatingSystem": {
"heatingType": "None"
}
}
}

The state is now consistent.

Dependency pruning operates on the entity schema:

  1. oneOf discriminators — When a discriminator field changes, prune branches that are no longer valid. The heatingType → centralHeatingDetails case above.

  2. if-then-else conditionals — When the if condition is no longer met, prune fields defined only in the then block.

  3. enum-gated sections — When an enum value changes and a section is only valid for the previous value, prune that section.

  4. Explicit dependencies — The PDTF schema MAY annotate fields with a x-pdtf-dependsOn extension keyword to express dependencies not captured by JSON Schema conditional constructs.

Implementation: The pruning pass is implemented in the reference state assembly library (see 07 — State Assembly). It walks the schema tree, evaluates each conditional, and strips paths that fail their conditions.

An important subtlety: after pruning removes centralHeatingDetails, the original credential that asserted those details is not modified or revoked. The credential is still valid — it accurately records what the seller attested at that point in time. Pruning operates on assembled state, not on individual credentials.

The old credential’s centralHeatingDetails data is simply no longer included in the assembled state because the newer credential’s heatingType: "None" makes it irrelevant per the schema.

If the seller later changes back to heatingType: "Central heating", the old centralHeatingDetails data could theoretically re-emerge from the earlier credential. Whether to allow this or require fresh attestation is an implementation decision for the state assembly layer.

5.6 Design Constraint: Issuers Are Stateless

Section titled “5.6 Design Constraint: Issuers Are Stateless”

Issuers assert what they know at the time of issuance. They have no visibility of the current assembled state and no obligation to know what other credentials exist. This means:

  • An issuer changing heatingType to None does not know that a previous credential asserted centralHeatingDetails
  • Issuers cannot and should not be expected to explicitly clear dependent paths
  • Pruning of schema-dependent paths (e.g., removing centralHeatingDetails when heatingType changes to None) is necessarily a state assembly concern, not an issuance concern
  • The schema’s existing if/then/else and oneOf discriminators define the dependency rules; the assembler applies them

This constraint shapes the merge semantics debate. Section-level REPLACE avoids the pruning question entirely (the issuer replaces the whole subtree). Incremental MERGE requires the assembler to understand schema dependencies and prune accordingly. A hybrid approach — REPLACE for adapter-issued institutional data, MERGE for seller-attested incremental data — may be the pragmatic path, but requires clear rules about which credential types use which strategy.

Why this matters per credential type:

  • Adapter-issued credentials (EPC, title register, searches): Section-level REPLACE works naturally. These issuers are authoritative for the whole subtree and re-issue complete data every time. When the EPC adapter issues a credential, it replaces the entire energyEfficiency branch.
  • Seller-attested credentials (TA6, TA7, fixtures & fittings): Incremental MERGE is necessary because data arrives piecemeal as the seller fills in forms over time. A seller answering the heating section doesn’t re-submit the entire property pack. Finer credential granularity (per-section or per-field) amplifies this need — see Q1.2.

The consensus questions this raises:

  1. Should pruning happen at all? (vs letting contradictory data coexist with the newer credential winning on the discriminator)
  2. If yes, where are the dependency rules defined? (schema-level if/then/else and oneOf discriminators are natural candidates — they already exist in the v3 schema)
  3. What is the assembler’s obligation? (MUST prune? SHOULD prune? MAY flag but retain?)

D5 status: 🟡 Needs consensus

Sparse objects with dependency pruning is a significant departure from the current REPLACE semantics. The benefits are clear (structured data, natural JSON, schema-driven consistency), but the implementation complexity is higher. LMS and other implementers need to agree before this is finalised.

Fallback option: If consensus is not reached, an alternative is to use full-object REPLACE at the section level — each credential replaces the entire top-level path it touches (e.g. a credential asserting heating replaces the entire heating subtree). This is simpler but loses the fine-grained merge capability.


Decision D6: The current OIDC-derived evidence schema is deeply nested and over-specified for actual usage patterns. PDTF 2.0 simplifies evidence to four types that reflect how property data is actually sourced.

The current v1 evidence model inherits from OpenID Connect’s verification.evidence[] structure, which was designed for identity verification (vouching, documents, electronic records). Property data sourcing has different patterns — API fetches, PDF extraction, seller forms, professional checks — and needs a simpler model that captures provenance without the OIDC baggage.

TypeDescriptionKey FieldsWhen Used
ElectronicRecordData retrieved from an authoritative APIsource, retrievedAt, methodEPC API, HMLR OC1, EA flood API, LLC API
DocumentExtractionData extracted from a document (PDF, scan, etc.)source, extractedAt, method, documentHashTitle deeds PDF, search result documents, lease documents
UserAttestationData declared by a user (seller, buyer)source, attestedAt, methodBASPI form, TA6/TA7/TA10, fixtures & fittings form
ProfessionalVerificationData verified by a professional (conveyancer, surveyor)source, verifiedAt, method, professionalRoleConveyancer title review, surveyor inspection

All evidence types share:

FieldTypeRequiredDescription
typestringRequiredOne of: ElectronicRecord, DocumentExtraction, UserAttestation, ProfessionalVerification
sourcestringRequiredOrigin of the evidence — API hostname, person DID, or document reference
methodstringOptionalHow the evidence was obtained (e.g. “API”, “PDF extraction”, “Form completion”)

ElectronicRecord:

FieldTypeRequiredDescription
retrievedAtISO datetimeRequiredWhen the API call was made
apiEndpointstringOptionalSpecific API endpoint called
requestIdstringOptionalAPI request identifier for audit trail
{
"type": "ElectronicRecord",
"source": "get-energy-performance-data.communities.gov.uk",
"retrievedAt": "2026-03-24T09:58:00Z",
"method": "API",
"apiEndpoint": "/api/v4/domestic/certificate/1234-5678-9012-3456-7890"
}

DocumentExtraction:

FieldTypeRequiredDescription
extractedAtISO datetimeRequiredWhen extraction was performed
documentHashstringOptionalSHA-256 hash of the source document
documentTypestringOptionalType of document (e.g. “Title Register”, “Environmental Search Report”)
pageRangestringOptionalPages from which data was extracted
{
"type": "DocumentExtraction",
"source": "HMLR Official Copy (Title Register)",
"extractedAt": "2026-03-24T10:15:00Z",
"method": "PDF extraction — structured data parser",
"documentHash": "sha256:a3f2b8c9d1e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0",
"documentType": "Title Register"
}

UserAttestation:

FieldTypeRequiredDescription
attestedAtISO datetimeRequiredWhen the user made the declaration
formTypestringOptionalThe form used (e.g. “BASPI”, “TA6”, “TA7”)
questionRefstringOptionalSpecific question reference (e.g. “TA6.7.1”)
{
"type": "UserAttestation",
"source": "did:key:z6MkhSellerAbc123",
"attestedAt": "2026-03-20T14:30:00Z",
"method": "BASPI form completion",
"formType": "BASPI",
"questionRef": "BASPI.3.2"
}

ProfessionalVerification:

FieldTypeRequiredDescription
verifiedAtISO datetimeRequiredWhen verification was performed
professionalRolestringRequiredRole of the verifier (e.g. “Conveyancer”, “Surveyor”)
firmDidstringOptionalDID of the professional’s firm
notesstringOptionalVerification notes
{
"type": "ProfessionalVerification",
"source": "did:web:smithandco.law",
"verifiedAt": "2026-03-24T11:00:00Z",
"method": "Title review — proprietorship cross-reference",
"professionalRole": "Conveyancer",
"firmDid": "did:web:smithandco.law"
}

A credential MAY have multiple evidence items. This is common when data has been both fetched from an API and verified by a professional:

{
"evidence": [
{
"type": "ElectronicRecord",
"source": "landregistry.data.gov.uk",
"retrievedAt": "2026-03-24T08:14:00Z",
"method": "OC1 API"
},
{
"type": "ProfessionalVerification",
"source": "did:web:smithandco.law",
"verifiedAt": "2026-03-24T11:00:00Z",
"method": "Conveyancer title review",
"professionalRole": "Conveyancer"
}
]
}

Evidence often refers to a source file — a title register PDF, an EPC certificate, a survey report, a search result document. The evidence model needs to support referencing these files and controlling access to them.

Any evidence type MAY include a sourceDocument field referencing the underlying file:

FieldTypeRequiredDescription
digeststringRequiredContent hash for integrity verification. Format: sha256:{hex}
mediaTypestringRequiredMIME type (e.g. application/pdf, image/jpeg)
sizeintegerOptionalFile size in bytes
namestringOptionalHuman-readable filename
urlstringRequiredRetrieval URL — a pdtf:// URI that resolves via the Transaction DID’s document endpoint
confidentialitystringRequiredAccess level — same values as termsOfUse (see §7): public, transactionParticipants, roleRestricted, partyOnly
authorisedRolesstring[]ConditionalRequired when confidentiality is roleRestricted. Role identifiers from the same table as termsOfUse role restrictions.

Example — title register PDF referenced from a DocumentExtraction evidence item:

{
"type": "DocumentExtraction",
"source": "HMLR Official Copy (Title Register)",
"extractedAt": "2026-03-24T10:15:00Z",
"method": "PDF extraction — structured data parser",
"documentHash": "sha256:a3f2b8c9d1e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0",
"documentType": "Title Register",
"sourceDocument": {
"digest": "sha256:a3f2b8c9d1e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0",
"mediaType": "application/pdf",
"size": 245760,
"name": "Official Copy - Title Register NK123456.pdf",
"url": "pdtf://transactions/abc123/documents/doc-tr-nk123456",
"confidentiality": "roleRestricted",
"authorisedRoles": ["sellerConveyancer", "buyerConveyancer"]
}
}

Note that documentHash on the evidence item and digest on sourceDocument are the same value here — the evidence is about the document, and the document is the source. They don’t have to match (evidence could reference a different file from the one that was hashed at extraction time), but when they do, it’s a strong integrity chain.

Example — survey report with restricted access:

{
"type": "ProfessionalVerification",
"source": "did:web:abcsurveys.co.uk",
"verifiedAt": "2026-03-25T14:00:00Z",
"method": "Level 2 HomeBuyer Report",
"professionalRole": "Surveyor",
"sourceDocument": {
"digest": "sha256:f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8",
"mediaType": "application/pdf",
"size": 4521984,
"name": "HomeBuyer Report - 42 Oak Lane.pdf",
"url": "pdtf://transactions/abc123/documents/doc-survey-hb-001",
"confidentiality": "partyOnly",
"authorisedRoles": []
}
}

A partyOnly survey report is accessible only to the party who commissioned it (typically the buyer). They may choose to share it by upgrading confidentiality or adding authorised roles.

The pdtf:// URL scheme resolves through the Transaction DID document:

  1. Parse the pdtf:// URL to extract the transaction identifier and document path
  2. Resolve the Transaction DID (did:web:platform.example.com:transactions:{txnId})
  3. Find the PdtfDocumentEndpoint service in the DID document:
{
"service": [{
"id": "did:web:platform.example.com:transactions:abc123#documents",
"type": "PdtfDocumentEndpoint",
"serviceEndpoint": "https://platform.example.com/api/transactions/abc123/documents"
}]
}
  1. Present a Verifiable Presentation containing the requester’s participation credential to the service endpoint
  2. The endpoint verifies the VP, checks the requester’s role against the document’s confidentiality and authorisedRoles, and returns the file or a 403
  3. The requester verifies the file’s content against the digest field
GET /api/transactions/abc123/documents/doc-tr-nk123456
Authorization: Bearer <VP-token>
→ 200 OK
Content-Type: application/pdf
PDTF-Digest: sha256:a3f2b8c9d1e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0
<file bytes>

6.6.3 Confidentiality Levels for Documents

Section titled “6.6.3 Confidentiality Levels for Documents”

Document confidentiality uses the same levels as credential termsOfUse (§7), ensuring a single access control model across the framework:

LevelWho Can AccessTypical Documents
publicAnyone with the URLEPC certificates, flood zone maps
transactionParticipantsAny party with a valid participation credentialTitle registers, local authority searches
roleRestrictedParties whose role matches authorisedRolesEnvironmental search reports (conveyancers only), contract drafts
partyOnlyThe data subject / commissioning party onlySurvey reports (buyer), mortgage offers (buyer), identity documents

Documents follow the same encryption model as credentials when synced between platforms (see Architecture Overview D30). The confidentiality level determines the recipient set for per-document encryption keys.

  • Files are referenced, not embedded. A 50MB survey PDF does not belong inside a VC. The credential carries the provenance metadata and integrity hash; the file lives behind an authenticated endpoint.
  • One access control model. Documents use the same confidentiality levels and VP-based authentication as credentials. No parallel auth system.
  • Digest is the anchor. The SHA-256 digest binds the evidence claim to a specific file. A verifier can confirm the file hasn’t been tampered with regardless of where it was fetched from.
  • pdtf:// URIs are portable. They resolve through DID documents, not hardcoded hostnames. If a transaction migrates between platforms, the URIs still resolve — the new platform updates the Transaction DID’s service endpoint.

The current OIDC-derived evidence types map as follows:

v1 Evidence Typev2 Evidence TypeNotes
electronic_recordElectronicRecordDirect mapping. Flatten nested record object.
documentDocumentExtractionFlatten document_details. source from document.issuer.name.
vouchProfessionalVerificationvoucher becomes source (DID). attestation becomes notes.
(user form data — no explicit v1 type)UserAttestationNew. Currently inferred from claim context, not explicitly typed.

Every PDTF credential SHOULD include a termsOfUse entry defining its access policy. The PdtfAccessPolicy type carries the same semantics as the current v1 terms_of_use but in a cleaner structure aligned with W3C VC termsOfUse.

{
"termsOfUse": [{
"type": "PdtfAccessPolicy",
"confidentiality": "restricted",
"pii": true,
"roleRestrictions": ["sellerConveyancer", "buyerConveyancer"]
}]
}
FieldTypeRequiredValuesDescription
typestringRequired"PdtfAccessPolicy"Discriminator
confidentialityenumRequiredpublic, restricted, confidentialAccess tier
piibooleanRequiredtrue, falseWhether the data contains personally identifiable information
roleRestrictionsstring[]ConditionalRole identifiersRequired when confidentiality is restricted or confidential

public — Available without authentication. Anyone can read it.

  • Examples: EPC data, flood risk zones, listed building status, council tax band
  • roleRestrictions SHOULD be omitted or empty
  • pii MUST be false

restricted — Available to authenticated participants with a matching role.

  • Examples: Seller contact details, legal questions, title register extract, offer details
  • roleRestrictions specifies which roles can access
  • pii may be true or false

confidential — Available only to specifically authorised parties.

  • Examples: AML verification results, gift terms, internal financial data
  • roleRestrictions typically limited to direct legal representatives
  • pii is typically true

Role identifiers are the v3 participant role enum. Each is carried by exactly one relationship credential (01 §3.2, D32): implied by SellerCapacity, Offer and Gift, explicit on Representation and TransactionRole.

RoleSourceDescription
SellerSellerCapacityCredentialPerson or organisation selling, in a declared capacity
BuyerOfferCredential (accepted)Person with an accepted offer
Prospective BuyerOfferCredential (pending)Person with an offer not yet accepted
GiftorGiftCredentialPerson gifting funds towards an offer
Seller's ConveyancerRepresentationCredentialInstructed by the seller
Buyer's ConveyancerRepresentationCredentialInstructed by the buyer
Estate AgentRepresentationCredentialInstructed by the seller
Buyer's AgentRepresentationCredentialInstructed by the buyer
Mortgage BrokerRepresentationCredentialInstructed by the buyer
SurveyorRepresentationCredential or TransactionRoleCredentialInstructed surveyor, or surveyor with no instructing party in the graph
LenderTransactionRoleCredentialMortgage lender, party to the transaction
Landlord, Tenant, Platform SupportTransactionRoleCredentialRoles with no instructing party

When a requester queries transaction state (either via the API or through the graph composer), the system applies termsOfUse filtering:

  1. Determine requester’s roles — from their presented credentials (SellerCapacity → Seller, Offer → Buyer, Gift → Giftor, Representation → its role, TransactionRole → its role). Each credential MUST reference the transaction being queried.

  2. Filter credentials — for each credential in the entity graph:

    • If confidentiality is public → include
    • If confidentiality is restricted or confidential:
      • Check if the requester has at least one role listed in roleRestrictions
    • If no match → exclude the credential from the response
  3. Assemble filtered state — compose state only from included credentials.

This means different requesters see different views of the same transaction. A buyer’s conveyancer sees title register details, AML status, and legal questions. A potential buyer who hasn’t yet been accepted sees only public data.

Buyer's Conveyancer requests state
→ Present RepresentationCredential (role: Buyer's Conveyancer)
→ Filter: include all public + restricted/confidential where "Buyer's Conveyancer" ∈ roleRestrictions
→ Result: property data, title register, ownership claims, offer details, legal questions
Estate Agent requests state
→ Present RepresentationCredential (role: Estate Agent)
→ Filter: include all public + restricted where "Estate Agent" ∈ roleRestrictions
→ Result: property data, basic offer info — NOT title register, NOT AML details
Unauthenticated request
→ No credential presented
→ Filter: include only public
→ Result: EPC rating, flood zone, address — nothing sensitive

The pii flag enables additional processing:

  • Logging: PII-flagged data must not appear in application logs.
  • Caching: PII-flagged credentials require shorter cache TTL or no caching.
  • Data subject requests: PII-flagged credentials must be discoverable for GDPR subject access/erasure requests.
  • Export: PII-flagged credentials must be excluded from anonymised datasets.

The pii flag is informational — it doesn’t affect access control (that’s confidentiality + roleRestrictions). It affects how the data is handled after access is granted.


Decision D18: Every PDTF credential MUST include a credentialStatus field pointing to a W3C Bitstring Status List v2 entry. There are no exceptions.

Rationale: In a property transaction, data changes frequently (new EPC, price reduction, change of conveyancer, sale falling through). Without revocation, stale credentials are indistinguishable from current ones. A verifier must be able to check whether a credential is still valid.

Every PDTF credential includes:

{
"credentialStatus": {
"id": "https://adapters.propdata.org.uk/status/epc/list-042#18293",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "18293",
"statusListCredential": "https://adapters.propdata.org.uk/status/epc/list-042"
}
}
FieldTypeDescription
idURIUnique identifier for this status entry (list URL + fragment)
typestringAlways "BitstringStatusListEntry"
statusPurposestringAlways "revocation" for PDTF (not "suspension")
statusListIndexstringBit position in the status list bitstring
statusListCredentialURIURL of the status list credential

Each issuer hosts one or more Bitstring Status List credentials. These are themselves VCs:

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://www.w3.org/ns/credentials/status/v2"
],
"type": ["VerifiableCredential", "BitstringStatusListCredential"],
"issuer": "did:web:adapters.propdata.org.uk:epc",
"validFrom": "2026-03-24T00:00:00Z",
"credentialSubject": {
"id": "https://adapters.propdata.org.uk/status/epc/list-042",
"type": "BitstringStatusList",
"statusPurpose": "revocation",
"encodedList": "H4sIAAAAAAAAA-3BMQEAAADCoPVPbQ..."
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:web:adapters.propdata.org.uk:epc#key-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-24T00:00:00Z",
"proofValue": "zH7kN3pR..."
}
}

The encodedList is a GZIP-compressed, base64url-encoded bitstring. Each bit position corresponds to a statusListIndex. Bit = 0 means “not revoked”; bit = 1 means “revoked”.

1. Issuer decides to revoke (data superseded, error found, mandate withdrawn)
2. Issuer flips bit at statusListIndex in the relevant status list
3. Issuer re-signs the status list credential
4. Verifiers fetch the status list (HTTP GET, cacheable with short TTL)
5. Verifier checks the bit at the credential's statusListIndex
6. If bit = 1 → credential is revoked → exclude from state assembly
ScenarioCredential RevokedTrigger
New EPC issuedOld PropertyCredential (EPC paths)New EPC VC replaces old
Seller changes conveyancerOld RepresentationCredentialSeller instructs new firm
Sale completesSellerCapacityCredential, RepresentationCredential, OfferCredentialTransaction closes
Sale falls throughOfferCredentialOffer withdrawn
Data correctionAny credential with incorrect dataError discovered
Lender withdrawsTransactionRoleCredentialLender no longer party to the sale
Gift cancelledGiftCredentialGiftor withdraws

Status list credentials are hosted at stable URLs by each issuer:

  • Adapters: https://adapters.propdata.org.uk/status/{adapter}/{list-id}
  • Platform: https://api.platform.example.com/status/{entity-type}/{list-id}

Caching: Status lists SHOULD be served with Cache-Control: max-age=300 (5 minutes). Verifiers SHOULD cache status lists and refresh on cache expiry. For time-sensitive revocations (e.g. conveyancer change), the issuer can invalidate the cache by updating the list and notifying known verifiers.

For full hosting infrastructure details, see 14 — Credential Revocation.


PDTF issues credentials as SD-JWT-VC (IETF draft-ietf-oauth-sd-jwt-vc, built on the SD-JWT selective-disclosure mechanism): a JWS over the entity claims, with per-field selective disclosure and holder key binding.

Payload claims:

ClaimDescription
issIssuer identifier (DID or HTTPS origin); signing key discovered via the issuer DID document or JWKS
vctVerifiable Credential Type — identifies the PDTF credential type and resolves to its Type Metadata (§10), e.g. urn:pdtf:vct:PropertyCredential
subEntity identifier — the DID/URN that the claims model calls credentialSubject.id
iat / expIssued-at / optional expiry
cnfHolder public key (confirmation), for key binding
statusIETF Token Status List reference for revocation (replaces W3C BitstringStatusList — the revocation sub-spec 14 needs a matching update, tracked in §13.4)
_sd, _sd_algSelective-disclosure digests and hash algorithm
(entity claims)The property/entity data (e.g. energyEfficiency, registerExtract, or a nested credentialSubject object)
  • Selective disclosure. Issuer-marked claims are replaced in the payload by salted digests (_sd); the plaintext + salt (a Disclosure) travels alongside and can be withheld per verifier, honouring termsOfUse.
  • Key binding. At presentation the holder appends a Key Binding JWT signed over the verifier’s nonce and aud, proving possession and preventing replay. This is the same proof-of-control primitive used by cross-party identity reuse (03 §10.5).
  • Signatures. EdDSA (Ed25519, per D16) or ES256.
  • Verification. Verify the issuer JWS via the iss key; recompute disclosure digests and reconcile against _sd; verify the Key Binding JWT against cnf; check status (Token Status List) and exp; validate vct against the expected type + Type Metadata (§10).

Worked example — a holder-bound SD-JWT-VC (SellerCapacity). Entity claims stay under credentialSubject so the entity’s own status field does not collide with the top-level Token Status List status claim.

Compact serialisation — issuer-signed JWS, then ~-separated Disclosures, then the Key Binding JWT (base64url; truncated):

eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJpc3MiOiJkaWQ6d2Vic...fQ.<sig>
~WyJzNGw3X3NhbHQiLCJkYXRlQmVjYW1lT3duZXJPckF1dGhvcml0eSIsIjIwMTQtMDYtMDEiXQ
~eyJhbGciOiJFZERTQSJ9.eyJhdWQiOiJkaWQ6d2ViOmpvbmVzbGVnYWwuY28udWsi...fQ.<kbSig>

Decoded issuer-signed payload:

{
"iss": "did:web:platform.example.com",
"vct": "urn:pdtf:vct:SellerCapacityCredential",
"sub": "urn:pdtf:capacity:own-1a2b",
"iat": 1774339200,
"cnf": { "jwk": { "kty": "OKP", "crv": "Ed25519", "x": "l8k…holderPubKey" } },
"status": { "status_list": { "idx": 94, "uri": "https://platform.example.com/status/capacity/1" } },
"_sd_alg": "sha-256",
"credentialSubject": {
"id": "urn:pdtf:capacity:own-1a2b",
"seller": "did:key:z6Mkh…abc",
"transaction": "did:web:platform.example.com:transactions:tx-789",
"sellersCapacity": { "capacity": "Legal Owner" },
"_sd": ["9gYy…digestOfDateBecameOwnerOrAuthority"]
}
}

Decoded Disclosure ([salt, claimName, value]) for the selectively-disclosable dateBecameOwnerOrAuthority:

["s4l7…salt", "dateBecameOwnerOrAuthority", "2014-06-01"]

Decoded Key Binding JWT payload (holder proves possession over the verifier’s nonce/audience):

{
"iat": 1774340000,
"aud": "did:web:joneslegal.co.uk",
"nonce": "b3f1c9…",
"sd_hash": "X0pq…hashOfPresentedSDJWT"
}

9.1 Superseded — Data Integrity (eddsa-jcs-2022)

Section titled “9.1 Superseded — Data Integrity (eddsa-jcs-2022)”

(Prior-draft embedded-proof mechanism, retained as a documented alternative; no longer primary.)

All PDTF credentials use the Data Integrity securing mechanism with the eddsa-jcs-2022 cryptosuite.

Why eddsa-jcs-2022:

  • Ed25519 keys (D16) — fast, small signatures, widely supported in the DID ecosystem
  • JCS (JSON Canonicalization Scheme) — deterministic JSON serialisation for signing. No ambiguity about whitespace, key ordering, or Unicode normalisation.
  • W3C standard track — part of the EdDSA Cryptosuite v2022 specification.
{
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:web:adapters.propdata.org.uk:epc#key-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-24T10:00:00Z",
"proofValue": "z4oJ9Bvn..."
}
}
FieldTypeDescription
typestringAlways "DataIntegrityProof"
cryptosuitestringAlways "eddsa-jcs-2022"
verificationMethodDID URLPoints to the issuer’s public key in their DID document. Format: {issuer-did}#{key-id}
proofPurposestringAlways "assertionMethod" for PDTF credentials
createdISO datetimeWhen the proof was generated
proofValuestringMultibase-encoded Ed25519 signature over the JCS-canonicalised document (excluding the proof property itself)

To verify a PDTF credential’s proof:

  1. Extract proof — remove the proof property from the credential document.
  2. Canonicalise — apply JCS (RFC 8785) to the remaining document to produce a deterministic byte representation.
  3. Resolve verification method — resolve the DID in verificationMethod to obtain the public key.
    • For did:key: self-resolving — the key is encoded in the DID itself.
    • For did:web: fetch the DID document from https://{domain}/.well-known/did.json (or the path-based resolution for subpath DIDs).
  4. Extract public key — from the resolved DID document’s verificationMethod array, find the entry matching the fragment (e.g. #key-1).
  5. Verify signature — verify the Ed25519 signature (proofValue, multibase-decoded) against the canonicalised document using the public key.
  6. Check proof purpose — confirm the key is listed under the DID document’s assertionMethod relationship.

When an issuer rotates keys:

  1. New key is added to the DID document’s verificationMethod array (e.g. #key-2).
  2. New credentials reference #key-2 in their verificationMethod.
  3. Old key (#key-1) is retained in the DID document for a transition period (existing credentials still verify).
  4. After all credentials signed with #key-1 are expired or revoked, the old key can be removed.

The DID document is the single source of truth for which keys are valid. Key rotation does not require re-issuing existing credentials.


10.1 PDTF v2 Context (optional semantic overlay)

Section titled “10.1 PDTF v2 Context (optional semantic overlay)”

Every PDTF credential MAY include two @context entries:

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://trust.propdata.org.uk/ns/pdtf/v2"
]
}
  1. W3C VC v2 context (https://www.w3.org/ns/credentials/v2) — defines the base credential vocabulary: VerifiableCredential, issuer, credentialSubject, evidence, termsOfUse, credentialStatus, proof, etc.

  2. PDTF v2 context (https://trust.propdata.org.uk/ns/pdtf/v2) — defines PDTF-specific terms.

The PDTF v2 JSON-LD context defines:

Credential types:

  • PropertyCredential
  • TitleCredential
  • SellerCapacityCredential
  • OfferCredential
  • GiftCredential
  • RepresentationCredential
  • TransactionRoleCredential
  • TransactionCredential

Evidence types:

  • ElectronicRecord
  • DocumentExtraction
  • UserAttestation
  • ProfessionalVerification

Terms of use types:

  • PdtfAccessPolicy

Credential subject properties:

  • All Property entity paths (e.g. energyEfficiency, environmentalIssues, heating, buildInformation)
  • All Title entity paths (e.g. registerExtract, ownership, titleExtents)
  • SellerCapacity entity fields (seller, title, sellersCapacity, dateBecameOwnerOrAuthority)
  • Offer entity fields (buyer, offerId, amount, status, buyerCircumstances)
  • Gift entity fields (donor, offerId, giftDetails)
  • Representation entity fields (representative, representedParty, role)
  • TransactionRole entity fields (participant, role)
  • Transaction entity fields (milestones, saleContext, property, titlesToBeSold, participants)
  • The transaction reference shared by every relationship credential

Evidence properties:

  • source, retrievedAt, attestedAt, extractedAt, verifiedAt
  • method, apiEndpoint, requestId
  • documentHash, documentType, pageRange
  • formType, questionRef
  • professionalRole, firmDid

Access policy properties:

  • confidentiality, pii, roleRestrictions

The context document is hosted at https://trust.propdata.org.uk/ns/pdtf/v2 and MUST be:

  • Immutable for a given version — once published, the v2 context does not change.
  • Versioned — breaking changes result in a new version (e.g. v3).
  • Cached — clients SHOULD cache the context document. It is static content.
  • Available — hosted with high availability (CDN-backed static file).

Minor additions (new optional fields) can be added without version bumps, following JSON-LD’s open-world assumption. Removals or semantic changes require a new version.

{
"@context": {
"@version": 1.1,
"pdtf": "https://trust.propdata.org.uk/ns/pdtf/v2#",
"PropertyCredential": "pdtf:PropertyCredential",
"TitleCredential": "pdtf:TitleCredential",
"SellerCapacityCredential": "pdtf:SellerCapacityCredential",
"OfferCredential": "pdtf:OfferCredential",
"GiftCredential": "pdtf:GiftCredential",
"RepresentationCredential": "pdtf:RepresentationCredential",
"TransactionRoleCredential": "pdtf:TransactionRoleCredential",
"TransactionCredential": "pdtf:TransactionCredential",
"ElectronicRecord": "pdtf:ElectronicRecord",
"DocumentExtraction": "pdtf:DocumentExtraction",
"UserAttestation": "pdtf:UserAttestation",
"ProfessionalVerification": "pdtf:ProfessionalVerification",
"PdtfAccessPolicy": "pdtf:PdtfAccessPolicy",
"confidentiality": "pdtf:confidentiality",
"pii": {"@id": "pdtf:pii", "@type": "http://www.w3.org/2001/XMLSchema#boolean"},
"roleRestrictions": {"@id": "pdtf:roleRestrictions", "@container": "@set"},
"seller": {"@id": "pdtf:seller", "@type": "@id"},
"buyer": {"@id": "pdtf:buyer", "@type": "@id"},
"donor": {"@id": "pdtf:donor", "@type": "@id"},
"representative": {"@id": "pdtf:representative", "@type": "@id"},
"representedParty": {"@id": "pdtf:representedParty", "@type": "@id"},
"participant": {"@id": "pdtf:participant", "@type": "@id"},
"transaction": {"@id": "pdtf:transaction", "@type": "@id"},
"title": {"@id": "pdtf:title", "@type": "@id"},
"property": {"@id": "pdtf:property", "@type": "@id"},
"titlesToBeSold": {"@id": "pdtf:titlesToBeSold", "@container": "@list", "@type": "@id"},
"participants": {"@id": "pdtf:participants", "@container": "@list"},
"offerId": "pdtf:offerId",
"sellersCapacity": "pdtf:sellersCapacity",
"giftDetails": "pdtf:giftDetails",
"dateBecameOwnerOrAuthority": {"@id": "pdtf:dateBecameOwnerOrAuthority", "@type": "http://www.w3.org/2001/XMLSchema#date"},
"verifiedAt": {"@id": "pdtf:verifiedAt", "@type": "http://www.w3.org/2001/XMLSchema#dateTime"},
"status": "pdtf:status",
"role": "pdtf:role",
"scope": {"@id": "pdtf:scope", "@container": "@set"},
"purpose": "pdtf:purpose",
"amount": {"@id": "pdtf:amount", "@type": "http://www.w3.org/2001/XMLSchema#decimal"},
"currency": "pdtf:currency",
"source": "pdtf:source",
"retrievedAt": {"@id": "pdtf:retrievedAt", "@type": "http://www.w3.org/2001/XMLSchema#dateTime"},
"attestedAt": {"@id": "pdtf:attestedAt", "@type": "http://www.w3.org/2001/XMLSchema#dateTime"},
"extractedAt": {"@id": "pdtf:extractedAt", "@type": "http://www.w3.org/2001/XMLSchema#dateTime"},
"method": "pdtf:method",
"documentHash": "pdtf:documentHash",
"documentType": "pdtf:documentType",
"formType": "pdtf:formType",
"questionRef": "pdtf:questionRef",
"professionalRole": "pdtf:professionalRole",
"firmDid": {"@id": "pdtf:firmDid", "@type": "@id"},
"energyEfficiency": "pdtf:energyEfficiency",
"environmentalIssues": "pdtf:environmentalIssues",
"heating": "pdtf:heating",
"buildInformation": "pdtf:buildInformation",
"residentialPropertyFeatures": "pdtf:residentialPropertyFeatures",
"fixturesAndFittings": "pdtf:fixturesAndFittings",
"councilTax": "pdtf:councilTax",
"connectivity": "pdtf:connectivity",
"registerExtract": "pdtf:registerExtract",
"milestones": "pdtf:milestones",
"saleContext": "pdtf:saleContext",
"buyerCircumstances": "pdtf:buyerCircumstances"
}
}

Note: This is an excerpt. The full context will include all Property, Title, and other entity fields. The context is generated from the v4 entity schemas to ensure consistency.


11.1 EPC PropertyCredential (Trusted Proxy Issuer)

Section titled “11.1 EPC PropertyCredential (Trusted Proxy Issuer)”

A complete EPC credential issued by the PDTF EPC adapter (trusted proxy for MHCLG):

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://trust.propdata.org.uk/ns/pdtf/v2"
],
"id": "urn:pdtf:vc:epc-7f3a2b1c-9d4e-5f6a-8b7c-0d1e2f3a4b5c",
"type": ["VerifiableCredential", "PropertyCredential"],
"issuer": "did:web:adapters.propdata.org.uk:epc",
"validFrom": "2026-03-24T10:00:00Z",
"validUntil": "2034-01-15T00:00:00Z",
"credentialSubject": {
"id": "urn:pdtf:uprn:100023456789",
"energyEfficiency": {
"certificate": {
"certificateNumber": "1234-5678-9012-3456-7890",
"currentEnergyRating": "C",
"currentEnergyEfficiency": 72,
"potentialEnergyRating": "B",
"potentialEnergyEfficiency": 85,
"environmentalImpactCurrent": 58,
"environmentalImpactPotential": 74,
"lodgementDate": "2024-01-15",
"expiryDate": "2034-01-15",
"totalFloorArea": 85,
"typeOfAssessment": "RdSAP",
"assessmentDate": "2024-01-10"
},
"recommendations": [
{
"sequence": 1,
"improvement": "Floor insulation (suspended floor)",
"indicativeCost": "£800 - £1,200",
"typicalSaving": "£60/year"
},
{
"sequence": 2,
"improvement": "Solar water heating",
"indicativeCost": "£4,000 - £6,000",
"typicalSaving": "£35/year"
}
]
}
},
"evidence": [{
"type": "ElectronicRecord",
"source": "get-energy-performance-data.communities.gov.uk",
"retrievedAt": "2026-03-24T09:58:00Z",
"method": "API",
"apiEndpoint": "/api/v4/domestic/certificate/1234-5678-9012-3456-7890"
}],
"termsOfUse": [{
"type": "PdtfAccessPolicy",
"confidentiality": "public",
"pii": false
}],
"credentialStatus": {
"id": "https://adapters.propdata.org.uk/status/epc/list-042#18293",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "18293",
"statusListCredential": "https://adapters.propdata.org.uk/status/epc/list-042"
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:web:adapters.propdata.org.uk:epc#key-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-24T10:00:00Z",
"proofValue": "z4oJ9BvnXp8kM2nRqY7tL3wS5vU1xZ6bA9dF0gH3jK4mN7pQ8rT2uW5yB1cE4fI6hL9oR2sV3xZ0bD5gJ7kM8nP1qS4tU6wY9aC2eG3iK5lN8oQ0rT7uW1yB4dF6hI9jL2mO3pR5sV8xZ1bD4gJ6kM9nP2qS0tU7wY3aC5eG8iK1lN4oQ6rT9uW2yB0dF3hI5jL8mO1pR7sV4xZ6bD2gJ9kM0nP5qS3tU8wY1aC7eG4iK6lN9oQ2rT0uW5yB3dF8hI1jL4mO6pR9sV2xZ7bD0gJ5kM3nP8qS1tU6wY4aC9eG2iK0lN7oQ5rT3uW8yB1dF6hI4jL9mO2pR0sV7xZ5bD3gJ8kM1nP6qS4tU9wY2aC0eG7iK3lN5oQ8rT1uW6yB4dF9hI2jL0mO7pR5sV3xZ8bD1gJ6kM4nP9qS2tU0wY7aC5eG3iK8lN1oQ6rT4uW9yB2dF0hI7jL5mO3pR8sV1xZ6bD4gJ9kM2nP0qS7tU5wY3aC8eG1iK6lN4oQ9rT2uW0yB7dF5hI3jL8mO1pR6sV4xZ9bD2gJ0kM7nP5qS3tU8wY1aC6eG4iK9lN2oQ0rT7uW5yB3dF8hI1jL6mO4pR9sV2xZ0bD7gJ5kM3nP8qS1tU6wY4aC9eG2iK0l"
}
}

11.2 SellerCapacityCredential (Thin Claim)

Section titled “11.2 SellerCapacityCredential (Thin Claim)”

A complete ownership credential — thin assertion only, no duplicated title data:

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://trust.propdata.org.uk/ns/pdtf/v2"
],
"id": "urn:pdtf:vc:own-3a2b1c7f-4e9d-6a5f-7c8b-1e0d2f3a4b5c",
"type": ["VerifiableCredential", "SellerCapacityCredential"],
"issuer": "did:web:platform.example.com",
"validFrom": "2026-03-18T09:00:00Z",
"credentialSubject": {
"id": "urn:pdtf:capacity:own-a1b2c3",
"seller": "did:key:z6MkhRqN4v5sW8xZ1bD4gJ6kM9nP2qS0tSellerAbc",
"transaction": "did:web:platform.example.com:transactions:tx-789",
"sellersCapacity": { "capacity": "Legal Owner" },
"dateBecameOwnerOrAuthority": "2014-06-01"
},
"evidence": [
{
"type": "ElectronicRecord",
"source": "landregistry.data.gov.uk",
"retrievedAt": "2026-03-18T08:55:00Z",
"method": "OC1 proprietorship name match"
},
{
"type": "UserAttestation",
"source": "did:key:z6MkhRqN4v5sW8xZ1bD4gJ6kM9nP2qS0tSellerAbc",
"attestedAt": "2026-03-18T08:50:00Z",
"method": "SellerCapacity declaration during onboarding"
}
],
"termsOfUse": [{
"type": "PdtfAccessPolicy",
"confidentiality": "restricted",
"pii": true,
"roleRestrictions": ["sellerConveyancer", "buyerConveyancer", "estateAgent"]
}],
"credentialStatus": {
"id": "https://api.platform.example.com/status/ownership/list-001#892",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "892",
"statusListCredential": "https://api.platform.example.com/status/ownership/list-001"
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:web:platform.example.com#platform-key-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-18T09:00:00Z",
"proofValue": "z5tPqR7sK2mN4vL8xZ1bD4gJ6kM9nP2qS0tU7wY3aC5eG8iK1lN4oQ6rT9uW2yB0dF3hI5jL8mO1pR7sV4xZ6bD2gJ9kM0nP5qS3tU8wY1aC7eG4iK6lN9oQ2rT0uW5yB3dF8hI1jL4mO6pR9sV2xZ7bD0gJ5kM3nP8qS1tU6wY4aC9eG2iK0lN7oQ5rT3uW8yB1dF6hI4jL9mO2pR0sV7xZ5bD3gJ8kM1nP6qS4tU9wY2aC0eG7iK3lN5oQ8rT1uW6yB4dF9hI2jL0mO7pR5sV3xZ8bD1gJ6kM4nP9qS2tU0wY7"
}
}

A complete representation credential — seller instructs a conveyancer, whose firm is on the Transaction roster:

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://trust.propdata.org.uk/ns/pdtf/v2"
],
"id": "urn:pdtf:vc:rep-1c7f3a2b-9d4e-5f6a-8b7c-0d1e2f3a4b5c",
"type": ["VerifiableCredential", "RepresentationCredential"],
"issuer": "did:web:platform.example.com",
"validFrom": "2026-03-15T11:00:00Z",
"credentialSubject": {
"id": "urn:pdtf:representation:rep-d4e5f6",
"representative": "did:key:z6MkjConveyancerT3uW8yB1dF6hI4jL9mO2pR0sV7xDef",
"representedParty": "did:key:z6MkhRqN4v5sW8xZ1bD4gJ6kM9nP2qS0tSellerAbc",
"role": "Seller's Conveyancer",
"transaction": "did:web:platform.example.com:transactions:tx-789"
},
"evidence": [{
"type": "UserAttestation",
"source": "did:key:z6MkhRqN4v5sW8xZ1bD4gJ6kM9nP2qS0tSellerAbc",
"attestedAt": "2026-03-15T10:55:00Z",
"method": "Platform instruction flow — seller selected conveyancer"
}],
"termsOfUse": [{
"type": "PdtfAccessPolicy",
"confidentiality": "restricted",
"pii": true,
"roleRestrictions": ["sellerConveyancer", "buyerConveyancer"]
}],
"credentialStatus": {
"id": "https://api.platform.example.com/status/representation/list-001#334",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "334",
"statusListCredential": "https://api.platform.example.com/status/representation/list-001"
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:web:platform.example.com#platform-key-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-15T11:00:00Z",
"proofValue": "zK8mN4rJP2sL7vX0bD3gJ5kM8nQ1tU4wY6aC9eG2iK0lN7oQ5rT3uW8yB1dF6hI4jL9mO2pR0sV7xZ5bD3gJ8kM1nP6qS4tU9wY2aC0eG7iK3lN5oQ8rT1uW6yB4dF9hI2jL0mO7pR5sV3xZ8bD1gJ6kM4nP9qS2tU0wY7aC5eG3iK8lN1oQ6rT4uW9yB2dF0hI7jL5mO3pR8sV1xZ6bD4gJ9kM2nP0qS7tU5wY3aC8eG1iK6lN4oQ9rT2uW0yB7dF5hI3jL8mO1pR6sV4xZ9bD2gJ0kM7nP5qS3tU8wY1aC6eG4iK9lN2oQ0rT7"
}
}

The current PDTF v1 system uses an OIDC-derived verified claims model. Each claim has a path (claimPath), a value (claimValue), and verification metadata (verification). PDTF 2.0 replaces this with W3C Verifiable Credentials.

This section provides a detailed mapping to guide the migration.

v1 Verified Claim Fieldv2 VC EquivalentNotes
claimPathPosition within credentialSubject objecte.g. /propertyPack/heating/heatingType → credentialSubject.heating.heatingSystem.heatingType
claimValueValue at the corresponding path in credentialSubjectDirect value assignment
verification.trust_frameworkissuer DID + OpenID Federation trust resolutionTrust is cryptographic, not framework-declared
verification.timevalidFromWhen the verification/assertion was made
verification.evidenceevidenceSimplified — see §6
verification.evidence[].type (electronic_record)evidence[].type (ElectronicRecord)PascalCase, simplified fields
verification.evidence[].type (document)evidence[].type (DocumentExtraction)Renamed, flattened
verification.evidence[].type (vouch)evidence[].type (ProfessionalVerification)Renamed, clearer semantics
verification.evidence[].record.source.nameevidence[].sourceFlattened — no nested record.source
verification.evidence[].record.created_atevidence[].retrievedAtRenamed for clarity
verification.evidence[].document.issuer.nameevidence[].sourceFlattened
verification.evidence[].document.document_detailsevidence[].documentType, evidence[].documentHashKey fields extracted, rest dropped
verification.evidence[].voucher.nameevidence[].source (DID)Person/org name → DID reference
verification.evidence[].attestationevidence[].notes (ProfessionalVerification)Free text preserved
terms_of_use.confidentialitytermsOfUse[].confidentialitySame semantics
terms_of_use.piitermsOfUse[].piiSame semantics
terms_of_use.roleRestrictionstermsOfUse[].roleRestrictionsSame semantics, same values
(no equivalent)credentialStatusNew — revocation support
(no equivalent)proofNew — cryptographic signature
(no equivalent)@contextNew — JSON-LD context
verifiedClaim.idid (optional)Credential identifier

The claimPath in v1 uses the v3 schema paths. In v2, paths are relative to the entity’s credentialSubject:

v1 claimPathv2 Entityv2 credentialSubject Path
/propertyPack/energyEfficiency/certificate/currentEnergyRatingPropertyCredentialenergyEfficiency.certificate.currentEnergyRating
/propertyPack/heating/heatingSystem/heatingTypePropertyCredentialheating.heatingSystem.heatingType
/propertyPack/environmentalIssues/flooding/floodZonePropertyCredentialenvironmentalIssues.flooding.floodZone
/propertyPack/buildInformation/buildDatePropertyCredentialbuildInformation.buildDate
/propertyPack/fixturesAndFittings/bathroom/itemsPropertyCredentialfixturesAndFittings.bathroom.items
/propertyPack/address/line1PropertyCredentialaddress.line1
/propertyPack/titlesToBeSold/0/registerExtract/proprietorshipTitleCredentialregisterExtract.proprietorship
/propertyPack/titlesToBeSold/0/titleNumberTitleCredential(part of subject ID)
/propertyPack/ownership/ownershipsToBeTransferred/0/ownershipTypeTitleCredentialownership.ownershipType
/statusTransactionCredentialstatus
/milestones/saleAgreedTransactionCredentialmilestones.saleAgreed
/offers/{id}/amountOfferCredentialamount
/offers/{id}/statusOfferCredentialstatus
/participants/0/name(Person entity — not a VC path)(Person entities are not claimPath-based)

The migration is not a big-bang cutover. It follows the dual state assembly approach (see 00 — Architecture Overview §8):

  1. Phase 1: Continue issuing v1 verified claims. composeStateFromClaims works unchanged.
  2. Phase 2: Begin issuing VCs in parallel. Each adapter produces both a v1 claim and a v2 VC for the same data. composeV3StateFromGraph runs in shadow mode, output compared against composeStateFromClaims.
  3. Phase 3: Once outputs match consistently, switch internal consumers to composeV3StateFromGraph (or composeV4StateFromGraph for new consumers).
  4. Phase 4: Stop issuing v1 verified claims. All data is VC-only.

The migration is per-adapter, not all-at-once. The EPC adapter migrates first (just rebuilt, natural candidate), then HMLR, then others.

In v1, each claimPath:claimValue pair is an independent claim. In v2, related paths are grouped into a single credential’s credentialSubject. The grouping follows the entity paths — all EPC data goes into one PropertyCredential, all HMLR data into one TitleCredential, etc.

v1 (4 separate claims):

[
{ "claimPath": "/propertyPack/energyEfficiency/certificate/currentEnergyRating", "claimValue": "C" },
{ "claimPath": "/propertyPack/energyEfficiency/certificate/currentEnergyEfficiency", "claimValue": 72 },
{ "claimPath": "/propertyPack/energyEfficiency/certificate/lodgementDate", "claimValue": "2024-01-15" },
{ "claimPath": "/propertyPack/energyEfficiency/certificate/certificateNumber", "claimValue": "1234-5678-9012-3456-7890" }
]

v2 (1 credential):

{
"type": ["VerifiableCredential", "PropertyCredential"],
"credentialSubject": {
"id": "urn:pdtf:uprn:100023456789",
"energyEfficiency": {
"certificate": {
"currentEnergyRating": "C",
"currentEnergyEfficiency": 72,
"lodgementDate": "2024-01-15",
"certificateNumber": "1234-5678-9012-3456-7890"
}
}
}
}

This is more natural, more efficient (one signature instead of four), and preserves the relationship between related data points.


  1. D5 consensus: Sparse objects + dependency pruning vs section-level REPLACE. The pruning approach is more elegant but more complex to implement. Do implementers prefer the simpler section-level REPLACE? What’s the minimum viable merge strategy?

  2. Credential granularity for seller forms. When a seller fills in BASPI, should each section (heating, fixtures, legal questions) become a separate PropertyCredential, or should the entire form submission be one credential? Separate credentials allow finer-grained revocation and re-attestation. One credential is simpler to issue.

  3. termsOfUse defaults. Should there be default access policies per credential type, or must every credential explicitly declare its termsOfUse? Defaults reduce boilerplate but add implicit behaviour.

  4. Evidence granularity. Should evidence be per-credential (current design) or per-path within a credential? Per-credential is simpler but means all paths in a credential share the same evidence metadata. Per-path is more precise but adds significant complexity.

  5. Credential ID assignment. Should all credentials have an id field, or should it be optional for privacy reasons? IDs enable deduplication and reference but create correlation vectors.

  1. Multi-credential merge conflicts. When two credentials for the same entity have overlapping paths with conflicting values, which wins? Current design: later validFrom wins. But what about concurrent issuance? Should there be a priority based on issuer trust level (root issuer > trusted proxy > user attestation)?

  2. Credential versioning. When an issuer re-issues a credential for the same data (e.g. new EPC), should it explicitly reference the credential it supersedes? The W3C VC model doesn’t have a built-in “supersedes” mechanism.

  3. Status list sizing. How many credentials per status list? The W3C spec suggests 16KB minimum (131,072 bit positions). Is that sufficient for PDTF’s expected credential volume per issuer?

  4. Context evolution. How do we handle adding new property data paths to the JSON-LD context without breaking existing credentials? JSON-LD’s open-world assumption helps, but tooling may not handle unknown terms gracefully.

  1. Custodial signing for user attestations. In Phase 1, the platform signs on behalf of users. The evidence records the user’s DID. But the proof doesn’t come from the user’s key. Is this semantically honest? Should we use a different proof mechanism (e.g. counter-signature) that makes the custodial relationship explicit?

  2. Adapter credential caching. Should adapters cache issued credentials (return the same VC for repeated requests for the same data) or always issue fresh ones? Caching is efficient but means the adapter stores credentials. Fresh issuance is stateless but means multiple VCs for the same data.

  3. Person/Organisation VCs. This spec deliberately doesn’t define PersonCredential or OrganisationCredential. Person and Organisation data is identity data, not property data. Should there be separate identity credentials, or are the entity records in the graph sufficient?

13.4 Credential Format & Securing Mechanism

Section titled “13.4 Credential Format & Securing Mechanism”

(New — July 2026, following a review of the GOV.UK Wallet and eIDAS 2.0 credential-format commitments.)

  1. Primary credential format: Data Integrity (JSON-LD) vs SD-JWT-VC vs mdoc. §2.4/§9/§10 currently commit to embedded Data Integrity proofs over JSON-LD. The wallet ecosystem PDTF must interoperate with points elsewhere:

    • GOV.UK Wallet mandates mdoc (ISO/IEC 18013-5) for new credentials; JSON-LD JWT-VC is a legacy exception only, and neither SD-JWT-VC nor JSON-LD Data Integrity is otherwise surfaced.
    • eIDAS 2.0 mandates SD-JWT-VC + mdoc; W3C VCDM is optional (EAAs only). OpenID4VC HAIP profiles SD-JWT-VC + mdoc.
    • Selective disclosure — dismissed as “not needed” in §2.4 — is in fact relevant to property data minimisation and is native to SD-JWT-VC/mdoc but not to plain Data Integrity.

    Two layers to decide separately:

    1. Identity ingestion (GOV.UK-verified identity → Person.identityBinding, and the cross-party reuse in 03 §10.5): PDTF must be able to verify mdoc via OpenID4VP (plus One Login OIDC) regardless — this is forced by the wallet.
    2. PDTF’s own property credentials (Property, Title, SellerCapacity, …): free choice. Leading candidate is SD-JWT-VC (JSON, native selective disclosure, EU-aligned, HAIP-profiled), keeping mdoc confined to the identity boundary.

    Provisional decision (adopted in this draft). §2.4/§9/§10 now document SD-JWT-VC as the primary securing mechanism + mdoc at the GOV.UK identity boundary (dual-format, mirroring HAIP/eIDAS); Data Integrity / JSON-LD is demoted to a superseded fallback and an optional semantic overlay. AI-agent legibility is not a discriminator here — agents read the composed entity graph over the API, not raw credentials.

    Still open (why this remains a question):

    • Confirm SD-JWT-VC against the GOV.UK Wallet’s current published format list, and ratify via consultation Q20 before locking in.
    • Revocation: SD-JWT-VC uses the IETF Token Status List (status claim), not W3C BitstringStatusList — sub-spec 14 (revocation) must be updated to match.
    • Example migration: the ~15 inline credential examples in this spec still show the superseded JSON-LD + DataIntegrityProof serialisation and need reissuing as SD-JWT-VC (claims model unchanged).
    • Ripple: references in the reference docs (credential-types, schemas, urn-scheme) and specs 03/04 that assume Data Integrity / @context need reconciling.
    • Decide whether PDTF keeps the optional JSON-LD @context overlay for linked-data / ontology tooling, or drops it entirely.

The following reference implementations support this spec:

LibraryPurposeStatus
@pdtf/vc-builderCreate and sign PDTF VCsPlanned
@pdtf/vc-validatorValidate VC structure, proof, issuer, revocationPlanned
@pdtf/did-resolverResolve did:key and did:web to public keysPlanned
@pdtf/state-assemblerMERGE + prune state from VCsPlanned
@pdtf/contextPDTF v2 JSON-LD context documentPlanned
Input: entity data (sparse object), entity ID, issuer DID, evidence, termsOfUse
→ Construct credentialSubject from entity data + ID
→ Determine credential type from entity type
→ Set @context, type, issuer, validFrom
→ Attach evidence, termsOfUse
→ Allocate status list index from issuer's current status list
→ Attach credentialStatus
→ JCS-canonicalise the document
→ Sign with issuer's Ed25519 private key
→ Attach proof
→ Output: complete signed VC
Input: VC document
→ Validate JSON structure (required fields, types)
→ Validate @context includes VC v2 + PDTF v2
→ Validate type includes VerifiableCredential + PDTF type
→ Validate credentialSubject.id format matches credential type
→ Extract issuer DID
→ Resolve DID → DID document → public key
→ Verify Data Integrity proof (JCS canonical, Ed25519 verify)
→ Check credentialStatus:
→ Fetch BitstringStatusList credential (with caching)
→ Verify status list credential's own proof
→ Check bit at statusListIndex
→ Check validFrom ≤ now ≤ validUntil (if present)
→ Optionally: resolve issuer trust chain to the Trust Anchor and check Trust Mark delegation claim for authorised paths
→ Output: { valid, revoked, expired, issuerTrust, errors[] }

Estimated credential sizes (JSON, uncompressed):

Credential TypeTypical SizeNotes
PropertyCredential (EPC)1.5–2 KBCertificate + recommendations
PropertyCredential (seller form section)0.5–3 KBVaries by section
PropertyCredential (full seller form)8–15 KBAll BASPI sections combined
TitleCredential2–5 KBRegister extract + ownership type
SellerCapacityCredential0.8–1 KBThin — smallest credential type
RepresentationCredential0.8–1 KBThin — similar to SellerCapacity
TransactionRoleCredential0.7–0.9 KBThin — three claims
GiftCredential1–1.5 KBIncludes gift terms
OfferCredential1–2 KBAmount, conditions, buyer circumstances
TransactionCredential1.5–3 KBStatus, milestones, sale context

A typical transaction might have 20–40 credentials totalling 30–80 KB of VC data.

┌───────────┐
│ Created │
│ (issued + │
│ signed) │
└─────┬─────┘
│
┌─────▼─────┐
│ Active │◄──── Verifiers check proof +
│ (valid, │ status list. Bit = 0.
│ not │
│ revoked) │
└─────┬─────┘
│
┌───────────┼───────────┐
│ │ │
┌─────▼─────┐ ┌──▼──┐ ┌─────▼─────┐
│ Superseded │ │Revoked│ │ Expired │
│ (new VC │ │(bit=1│ │ (validUntil│
│ replaces) │ │in SL)│ │ passed) │
└───────────┘ └──────┘ └───────────┘
  • Superseded: A new credential covers the same data. The old credential is revoked (bit flipped) and the new one takes its place.
  • Revoked: Explicitly invalidated (e.g. mandate withdrawn, error found). Bit flipped in status list.
  • Expired: validUntil has passed. No bit flip needed — verifiers check the date.
Sub-specRelationship
01 — Entity GraphDefines the entity schemas that shape credentialSubject. This spec wraps those shapes in VCs.
03 — DID MethodsDefines how issuer DIDs and verificationMethod DIDs resolve. This spec references them.
04 — OpenID FederationDefines which issuers are trusted for which entity:path combos, via Trust Mark delegation claims. This spec’s credentials are validated against those Trust Marks.
07 — State AssemblyImplements the MERGE + prune semantics defined in §5 of this spec.
08 — DE MigrationMaps DE evaluation paths to credential credentialSubject paths.
14 — Credential RevocationDetails the hosting, caching, and operational aspects of BitstringStatusList referenced in §8.
  1. Proof verification is mandatory. Never trust a credential without verifying its proof. Even internal systems must verify — defence in depth.

  2. Status list freshness. A cached status list may not reflect recent revocations. For security-critical checks (e.g. verifying a representation credential before granting data access), use short cache TTL or force-refresh.

  3. Issuer impersonation. did:web resolution depends on DNS. An attacker who compromises a domain could issue fraudulent credentials. Mitigation: OpenID Federation trust resolution provides a second check — the issuer must hold a valid Trust Mark whose delegation.authorised_paths covers the relevant paths, and the Subordinate Entity Statement from the Trust Anchor pins the expected JWKS. Monitor DID document changes.

  4. Credential correlation. Credentials with id fields create correlation vectors — the same credential ID appearing in different contexts links those contexts. For privacy-sensitive credentials, omit the id field.

  5. Key compromise. If an issuer’s private key is compromised, all credentials signed with that key are suspect. The issuer must: rotate keys in the DID document, revoke all credentials signed with the compromised key, re-issue with the new key. See 06 — Key Management for rotation procedures.

  6. PII in evidence. Evidence fields like source may contain a person’s DID, which could be PII. The termsOfUse.pii flag should be set accordingly, and evidence data must be handled with the same access controls as the credential subject data.


Appendix A: Architectural Decisions Referenced

Section titled “Appendix A: Architectural Decisions Referenced”
#DecisionStatusRelevance to This Spec
D3Representation to Organisations, not Persons⚪ Superseded by D33§3.5 — the representative is the instructed party (Person or Organisation); the firm is on the Transaction roster
D32Role lives only on the relationship credential; every credential references the Transaction✅ Confirmed§3.4–3.7a, §7.4 — no role on the roster; nothing nests
D33One Representation per (representative, represented party) pair✅ Confirmed§3.5
D4Property-level VCs, not first-class entity VCs✅ Confirmed§3.2 — EPC is a PropertyCredential, not EPCCredential
D5Sparse objects + dependency pruning🟡 Needs consensus§5 — Claims representation model
D6Simpler evidence model✅ Confirmed§6 — Four evidence types replacing OIDC-derived schema
D7did:key for users, did:web for transactions/adapters✅ Confirmed§9 — verificationMethod DID formats
D14Digital ID wallet binding (future, custodial for now)✅ Confirmed§3.2 note on custodial signing
D16Ed25519 key algorithm✅ Confirmed§9 — eddsa-jcs-2022 cryptosuite
D18Bitstring Status List revocation mandatory✅ Confirmed§8 — credentialStatus required on all VCs
D28SellerCapacity credential is thin (claim-vs-evidence separation)✅ Confirmed§3.4 — SellerCapacityCredential design

Appendix B: Credential Type Quick Reference

Section titled “Appendix B: Credential Type Quick Reference”
PropertyCredential
subject: urn:pdtf:uprn:{uprn}
claims: property facts (EPC, flood, heating, fixtures, searches, ...)
issuer: trusted proxy / root issuer / platform (for user attestations)
TitleCredential
subject: urn:pdtf:titleNumber:{n} | urn:pdtf:unregisteredTitle:{id}
claims: register extract, ownership type, leasehold terms
issuer: HMLR proxy / HMLR root issuer
SellerCapacityCredential ⇒ Seller
subject: urn:pdtf:capacity:{id}
claims: seller, transaction, title?, sellersCapacity, dateBecameOwnerOrAuthority
issuer: account provider (platform)
NOTE: thin — no title details, just the capacity; one per seller
OfferCredential ⇒ Buyer
subject: urn:pdtf:offer:{id}
claims: buyer, transaction, offerId, amount, currency, status, conditions, buyerCircumstances
issuer: platform (on behalf of buyer)
GiftCredential ⇒ Giftor
subject: urn:pdtf:gift:{id}
claims: donor, transaction, offerId, giftDetails
issuer: platform (on behalf of giftor)
RepresentationCredential role: which kind
subject: urn:pdtf:representation:{id}
claims: representative, representedParty, role, transaction
issuer: platform (on behalf of the instructing party)
NOTE: one per (representative, represented party) pair
TransactionRoleCredential role: which role
subject: urn:pdtf:role:{id}
claims: participant, role, transaction
issuer: platform
TransactionCredential
subject: did:web:{host}:transactions:{id}
claims: status, milestones, saleContext, offers, property, titlesToBeSold, participants (roster — no roles)
issuer: platform