Consent Docs

How a consent check decides

Which of a person's records a consent check reads, which answer wins, and what never counts as yes.

Concept3 min readFor web, backend and mobile developers

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 alsoKnownAs entry.
  • Records those identifiers are linked to, through identifiers on a write or POST /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_confirmation when 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_OFFERED says so.
  • A withdrawal made on a notice campaign link that the job has not yet applied. It counts as withdrawn from the moment of the click, with the note WITHDRAWAL_IN_PROGRESS and consentId: 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.

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_ATTESTED carries attestedByStaffOnly: true. When every entry is like that, the note ATTESTED_BY_STAFF_ONLY is 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_ID or DEVICE_ID can 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:read and consent: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 consentId and proofHash in 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.

↑↓ to move, Enter to open