FR EN

The consent lifecycle

A consent in Agreely moves through five stages, each a distinct artifact. The company issues an offer, the person approves it with a passkey, that answer becomes a receipt, the receipt updates a single enforcement record, and your code reads that record on every use. The whole point is that each stage leaves proof, so the final check is not a bare boolean but the tip of a verifiable chain.

1. Offer

The company issues a signed consent request for a set of catalog cells, delivered to the person as a secure deep link. The offer is signed by the company's DID key and commits to a Merkle root over the exact (category, purpose) cells being requested, so the person approves precisely what was offered and nothing can be swapped in afterward.

2. Passkey approval

The person approves per-cell in the citizen portal with a WebAuthn passkey. The passkey signs a challenge bound to the offer's Merkle root, which is what ties a specific human's authenticator to a specific set of cells at a specific moment. Approval is granular: a person can approve some cells of an offer and decline others.

The passkey signs the root, not the plaintext

The challenge the passkey signs is bound to the offer's Merkle root. That is enough to prove the person approved exactly those cells, and it means the citizen side never has to hold the company's plaintext customer identifier to produce a valid approval.

3. ConsentReceipt

The approval becomes a ConsentReceipt, a W3C Verifiable Credential that carries both the company signature and the citizen's passkey assertion. This is the portable, independently verifiable record of what happened. Anyone holding it can verify the receipt without trusting Agreely.

A manual or offline consent, where a person signs on paper rather than in the portal, instead yields a company-attested receipt. It carries the company signature alone: the company attests that it holds a hand-signed document whose hash matches. It does not carry a passkey assertion, and it does not prove a human signed. See assurance tiers below.

4. Enforcement record

The receipt updates a single authoritative enforcement record, keyed per cell and carrying a consent_ref, a status, and an assurance_tier. This is the row a check reads. It is keyed by the company's own plaintext customer_id, and no citizen DID is ever written here. That separation is what keeps the company side and the citizen side from being trivially joined; see unlinkability.

5. The /v1 check

Your code calls the synchronous /v1 check, a default-deny read of exactly that enforcement status. A withdrawal flips the record to revoked, and the very next call denies, with no chain wait. The chain is never read on this path; the enforcement record is authoritative the instant a grant or withdrawal commits.

Assurance tiers

Two vocabularies describe how a consent was obtained. Map them, do not mix them.

  • The storage enum is full or manual. full means the consent went through passkey approval in the portal; manual means it was collected offline and attested by the company.
  • The public label, on the receipt and in the /v1/check response, is citizen_signed or company_attested. full maps to citizen_signed; manual maps to company_attested.

Both tiers enforce identically

A citizen_signed consent carries the person's passkey assertion. A company_attested consent is the company's attestation that it holds a hand-signed document whose hash matches, and it does not prove a human signed. At check time both enforce exactly the same way: only the tier label differs, so a reader can tell how strong the underlying proof is.

Next