How a consent check decides
Which of a person's records a consent check reads, which answer wins, and what never counts as yes.
A consent check reads every record held for the person and answers from the newest answer for the purpose. It errs towards no: anything it cannot be sure of is not allowed.
Which records count#
- Records recorded under any identifier in the request: the subject and each
alsoKnownAsentry. - Records those identifiers are linked to, through
identifierson a write orPOST /subjects/link. - Records of every property in the project, because a purpose belongs to the project. A refusal given on one site applies on the others.
- Erased records. An erasure applies to every purpose.
- A test key reads test records only, and a live key live records only.
A key tied to one property still sees refusals and withdrawals given on another property. Those basedOn entries keep their state, decidedAt and matchedBy, with consentId and proofHash set to null.
A phone number written another way, such as 09846011223 for +919846011223, is a different identifier. So is an email address under a different type.
The newest answer wins#
The date of an answer is when the person last changed it for this purpose. Saving a form again without changing the purpose does not move that date. A backdated answer, sent with collectedAt, counts from when it was given.
The newest of a refusal, a withdrawal or an erasure wins over every older grant, whichever record holds it. A grant wins only when it is newer than all of them. When two answers have the same time, the more restrictive one wins: erased, then withdrawn, then refused, then expired.
A grant that has lapsed does not hide an older grant that is still in force. validUntil is the expiry of the record behind the grant.
What never counts as yes#
- An email double opt-in the person has not confirmed. It answers
pending_confirmationwhen nothing confirmed speaks for the purpose. - A grant past its expiry, even before the hourly job marks the record
EXPIRED. - A record of a person erased after their newest answer.
- A partner the form version did not offer when the person answered. A partner added to a purpose later was never shown to them. The note
PARTNER_NOT_OFFEREDsays so. - A withdrawal made on a notice campaign link that the job has not yet applied. It counts as
withdrawnfrom the moment of the click, with the noteWITHDRAWAL_IN_PROGRESSandconsentId: null. It counts when the check names the identifier the campaign listed the person under, or finds a record recorded under or linked to it.
Purposes not based on consent#
When a purpose declares a basis other than consent, sets a section 7 clause, or is always on, the check answers allowed: false and reason: not_consent_based. recorded still says what the records hold, for information. The product does not decide whether section 7 applies to a person.
What the answer does not prove#
- Staff attestation. A grant whose current answer rests only on captures with the method
AGENT_ATTESTEDcarriesattestedByStaffOnly: true. When every entry is like that, the noteATTESTED_BY_STAFF_ONLYis added. If you need the person's own confirmation, treat it as not allowed. A later save on the web or an OTP confirmation of the same answer clears it. - Self-asserted identifiers. An answer under a
COOKIE_ID,SESSION_IDorDEVICE_IDcan come from a browser or an app with the publishable key. It proves nothing about who the person is. - Copied keys. Every secret key also holds
consent:readandconsent:write, so a copied check key can read and write records too. Keep one key for checks only and revoke any key you suspect was copied.
What the check does not do#
- It keeps no cache. Each call reads the records as they are.
- It writes no log entry for each check. Keep
consentIdandproofHashin your own processing log. Only a key that runs out of its check budget is recorded in the audit log, once a minute. - It checks one person and one purpose a call.
- It does not stop processing. Your systems call it and act on the answer.