Migrate to verifiable consent

What changed

Previously you told the SDK what the user agreed to by passing disclosures: { key: boolean } to captureCDR(). Those booleans were client-asserted, so ExpressConsent stored them verbatim without verifying them.

Now you tag the disclosure, the consent checkbox, and the submit button in your HTML. ExpressConsent detects the submission and derives the facts server-side from the captured DOM, producing a per-disclosure consentMechanism. Nothing about consent is taken on the client’s word, so the evidence is stronger.

Step 1: Swap your HTML tags

Replace the legacy text/checkbox attributes with the disclosure model. The old data-ec-consent-text becomes data-ec-disclosure, the checkbox gets data-ec-consent-checkbox, and the submit button gets data-ec-submit.

Before

html
<!-- Legacy: text + checkbox paired by a matching label value. -->
<p data-ec-consent-text="tcpa">
  By submitting, you agree to be contacted at the number provided.
</p>
<input type="checkbox" data-ec-consent-checkbox="tcpa" />

<!-- The submit button was untagged. -->
<button type="submit">Submit</button>

After

html
<!-- New: the disclosure language. -->
<p data-ec-disclosure="tcpa">
  By submitting, you agree to be contacted at the number provided.
</p>

<!-- The consent checkbox, bound to the disclosure by id. -->
<input type="checkbox" data-ec-consent-checkbox data-ec-consent-for="tcpa" />

<!-- Tag the submit buttons. The one the user presses gets outlined in the evidence. -->
<button type="submit" data-ec-submit>Submit</button>

The full tag set:

  • data-ec-disclosure="<id>": the disclosure language. Its text is captured so the record is searchable. (Replaces data-ec-consent-text.)
  • data-ec-consent-checkbox: the consent checkbox.
  • data-ec-consent-for="<id>": which disclosure a checkbox governs. (If there are multiple.)
  • data-ec-submit: the button the user submits with. ExpressConsent outlines this control in the rendered evidence. This tag is new.

Step 2: Remove legacy captureCDR() parameters

Once the page is tagged, drop disclosures and triggerEvent from your captureCDR() call. They are no longer needed:

Before

javascript
await window.ExpressConsent.captureCDR({
  // Legacy: client-asserted agreement booleans.
  disclosures: {
    tcpa: tcpaCheckbox.checked,
  },
  // Legacy: hint about which event triggered the capture.
  triggerEvent: event,
  custom: { phoneNumber: phone },
});

After

javascript
await window.ExpressConsent.captureCDR({
  // No disclosures, no triggerEvent. ExpressConsent derives the facts
  // from the tagged DOM + the detected submission.
  custom: { phoneNumber: phone },
});

Step 3: Update the payload you read

If you consume webhooks or the API, switch from the legacy fields to detectedDisclosureDetails. Both can appear on the same CDR during a rollout, so prefer the derived object when present.

Field mapping

  • consentLanguage[].language detectedDisclosureDetails.disclosures[].text
  • disclosures[].key detectedDisclosureDetails.disclosures[].key
  • disclosures[].agreed (client-asserted boolean) → detectedDisclosureDetails.disclosures[].consentMechanism (server-derived: checkbox / button_submission / none_detected)

Before

javascript
// Legacy fields you may have read:
const agreed = event.disclosures?.[0]?.agreed;       // client-asserted boolean
const language = event.consentLanguage?.[0]?.language; // auto-detected text

After

javascript
// New: server-derived facts.
const agreed = detectedDisclosureDetails.disclosures?.[0]?.consentMechanism;
const language = detectedDisclosureDetails.disclosures?.[0]?.text;

The same detectedDisclosureDetails object appears on the webhook payload, the CDR API responses, and share pages. Treat a missing consentMechanism on a historical CDR by falling back to the legacy fields.

Related docs