Reference
The consent receipt: ISO/IEC TS 27560 in practice
A consent receipt is the record a person keeps of a permission they gave. This page describes the receipt LifeCare.ID® issues for every grant over health data: its shape, the proof that approves it, the ledger it lands in, and how it is revoked.
What a consent receipt is
ISO/IEC TS 27560:2023, Privacy technologies. Consent record information structure, defines the fields a record of consent should carry so that the person, the organisation and an auditor can all read the same thing: who consented, to whom, for which categories of data, for what purpose, from when until when, and how to withdraw. The technical specification grew out of the Kantara Initiative's Consent Receipt work and is the shape the W3C Data Privacy Vocabulary community publishes a schema for.
A consent receipt is the copy of that record that the person holds. The word "receipt" is deliberate: like a till receipt, it is proof of a transaction, issued at the moment of the transaction, kept by the party who paid. In health data the transaction is a permission, and the person is the party who should never be left without a record of it.
One grant, two artifacts
Every grant on LifeCare.ID produces two artifacts from one event, and they are written together so they cannot disagree.
- A plain-language ledger line for the person, in the form "ProForta can read your cgm, daily data until 2026-12-03. Purpose: weekly health synthesis." This is what the consent panel shows.
- A machine-readable receipt shaped to ISO/IEC TS 27560:2023, for the app that requested access, for the resource holder that will serve it, and for anyone auditing either.
The same is true of revocation: one ledger line for the person, one updated receipt with revoked: true and a revocation time for the machines.
The receipt shape
| Field | Meaning |
|---|---|
| consent_id | A URN unique to this grant, urn:lifecare:consent:<id>. Tokens issued under the grant reference it; revocation names it. |
| schema | iso-27560:2023. The information structure the receipt follows. |
| subject_did | The person, as a W3C decentralized identifier under their home node's domain: did:web:<domain>:u:<id>. The key that controls it lives in the person's device. |
| grantee | Who may access: a display name and, when the requester has one, its own DID or client identifier. |
| data_categories | The scopes granted, from a fixed vocabulary of categories: cgm, daily, labs, body_composition, recovery, activity, insights. Categories, never values. |
| purpose | Two forms of the same purpose: a W3C Data Privacy Vocabulary term in dpv and the sentence the person read in text. |
| granted_at, expires_at | The window. Grants are time-boxed, one to 365 days, 90 by default. |
| revoked, revoked_at | Whether the person has withdrawn the grant, and when. |
| proof | A reference to the verified device proof that approved this grant, or null when none was presented. A non-null proof was checked before it was stored. |
| revocation_endpoint | The public registry a resource holder or validator checks: the person's revocation list under their DID. |
A sample receipt
The receipt returned when an app is granted read access to a person's glucose and daily wearable categories for a weekly synthesis. Identifiers are illustrative.
{
"consent_id": "urn:lifecare:consent:9b1e4c0f2a7d4e8b8c11d3f5a6b7c8d9",
"schema": "iso-27560:2023",
"subject_did": "did:web:lifecare.id:u:8f2c1a9e0b7d6c54",
"grantee": { "name": "ProForta", "id": "proforta-web" },
"data_categories": ["cgm", "daily"],
"purpose": {
"dpv": "dpv:PersonalisedRecommendation",
"text": "weekly health synthesis"
},
"granted_at": "2026-09-04T09:12:31Z",
"expires_at": "2026-12-03T09:12:31Z",
"revoked": false,
"revoked_at": null,
"proof": {
"type": "webauthn",
"credential_id": "…",
"challenge": "base64url(sha256(canonical grant payload))"
},
"revocation_endpoint": "https://lifecare.id/id/v1/revocation/8f2c1a9e0b7d6c54"
}
Nothing in it is a measurement. The receipt names categories, a purpose, a window and two identifiers. A full copy of every receipt ever issued would tell an attacker who trusted whom for what, and not one glucose value.
Purpose binding with DPV
A grant is bound to a purpose, and access under the grant is checked against it. Two purposes are compared on their Data Privacy Vocabulary term when both carry one: dpv:PersonalisedRecommendation on the grant and on the request match, whatever the surrounding wording. A DPV term on both sides that does not match is a mismatch, whatever the wording. Grants that predate DPV coverage carry text only, and the comparison falls back to the text. The DPV term is what makes purpose limitation something a machine can enforce rather than something a policy promises.
The proof: a passkey approves this grant, not the idea of a grant
The person's key is a passkey in the device's secure element, and a passkey does not sign arbitrary bytes. It signs a WebAuthn assertion over authenticator data and a challenge. LifeCare.ID uses that property: the grant is bound into the assertion as the challenge.
challenge = base64url( sha256( canonical grant payload ) )
canonical grant payload = deterministic JSON, sorted keys, compact separators, over
{
"duration_days": 90,
"grantee": { "id": "proforta-web", "name": "ProForta" },
"purpose": { "dpv": "dpv:PersonalisedRecommendation", "text": "weekly health synthesis" },
"scopes": ["cgm", "daily"],
"subject_did": "did:web:lifecare.id:u:8f2c1a9e0b7d6c54"
}
One function produces those bytes on the device and on the server, so the two digests cannot drift. Change any approved term, a scope, a day, a word of the purpose, and the challenge changes and the assertion no longer verifies. The proof covers this grant and no other. When a proof is presented it is always verified before the grant is stored; when the service is configured to require one, a grant without it is refused.
The hash-chained ledger
Every consent event lands in the person's ledger: a grant, a token issued under it, an access served under it, a revocation, a change in where their data is held. The ledger is per person, append-only and tamper-evident. Each entry's hash covers its own canonical body and the hash of the entry before it, from a fixed genesis value, so rewriting history anywhere invalidates everything after it.
entry_hash = sha256( prev_hash + canonical({ subject_did, at, event_type, description, payload }) )
prev_hash of the first entry = "000…0" (64 zeros)
The same construction, in one module, is shared by the consent ledger and the resource registry that maps what data exists about a person, who holds it and where. Two copies of a hash construction would be two chains that can silently diverge; one copy means one verifier checks both. The person can read the ledger in their app, export it, and verify the chain.
Revocation
Revocation is one tap in the consent panel or one call to the API. Three things happen in order: the grant is marked revoked with a time; every access token issued under it is invalidated, in under half a second; and a plain-language line is appended to the ledger, "You revoked ProForta's access to your cgm, daily data.", with the number of tokens killed in its payload. The receipt is returned again with revoked: true.
Resource holders do not have to be told. They validate every token by introspection against the authorization server, and a revoked grant's tokens fail closed on the next call. The public revocation registry under the person's DID is there for holders and validators that keep their own copy.
What a receipt never contains
No health value, no measurement, no file. The receipt carries categories, a purpose, a window, identifiers, a proof reference and an endpoint. This follows from the invariant the whole service is built on: for any data value held by a resource holder and delivered to a requester, the transfer is direct, and LifeCare.ID participates only in the set {DID, public key, token, scope, consent receipt, revocation status}. A full compromise of the service yields receipts like the sample above, and nothing a receipt describes.
Issuing a receipt from your app
Apps integrate over OAuth 2.0 with PKCE to federate the person in, then request a grant on the server-to-server channel. The receipt comes back in the response.
POST /v1/consent/grant
{
"subject_did": "did:web:lifecare.id:u:8f2c1a9e0b7d6c54",
"grantee": { "name": "ProForta", "id": "proforta-web" },
"scopes": ["cgm", "daily"],
"purpose": { "dpv": "dpv:PersonalisedRecommendation", "text": "weekly health synthesis" },
"duration_days": 90,
"proof": { …the person's WebAuthn assertion over the canonical grant digest… }
}
→ { "consent_id": "urn:lifecare:consent:…", "receipt": { …as above… } }
Two gates guard the endpoint, each verified whenever presented and each enforceable by configuration: who is calling (a holder signature or confidential-client credentials) and who approved it (the device proof over the grant digest). The endpoint, the scope vocabulary and the receipt shape are part of the published API contract, generated from the code and tested against drift.
Standards and further reading
- ISO/IEC TS 27560:2023, Privacy technologies. Consent record information structure.
- W3C Data Privacy Vocabulary (DPV), the purpose and processing terms used in
purpose.dpv, and its ISO 27560 schema. - Kantara Consent Receipt Specification v1.1, the lineage of the ISO structure, and the Kantara User-Managed Access 2.0 grant pattern the tokens follow.
- W3C Decentralized Identifiers and Web Authentication, the identifier and the key behind
subject_didandproof. - The patient-held authorization server: the tokens issued against this receipt, and Verify a node and how its attestation works: the other half of the trust model. Consent decides who may touch the data; attestation lets you check what a node says it runs.
- The paper: design and early operation, the preprint that puts the receipt, the tokens, the enforcement and the attestation in one document, with the design rationale and the limits.
Terminology. ISO/IEC TS 27560 speaks of a consent record; the copy the person holds is a consent receipt. LifeCare.ID issues one record per grant and hands the same object to every party, so the two words name the same thing here. A W3C Verifiable Credential wrapping of the receipt is on the roadmap and is not what ships today.