captureCDR()

Capture a snapshot of the page, upload it, and get back a cdrId confirming the evidence is stored.

Usage

Call captureCDR() from your form’s submit handler and await it before the page navigates. Awaiting keeps the page alive during capture, confirms the evidence was stored, and returns the cdrId and shareUrl.

javascript
form.addEventListener("submit", async (event) => {
  event.preventDefault();

  // Read form values synchronously BEFORE the first await.
  const phone = form.phone.value;

  try {
    // captureCDR() must be the first await — calling anything async before it
    // breaks the link to the user-triggered submission.
    const { cdrId, shareUrl } = await window.ExpressConsent.captureCDR({
      autoShare: true,
      custom: { phoneNumber: phone },
    });

    // Pass shareUrl to the lead buyer so they can access the evidence.
    await saveLead({ phone, cdrId, shareUrl });
  } catch (err) {
    // Log it and let the submit proceed — never block submission on a capture failure.
    console.error("Capture failed:", err);
  }

  form.submit();
});

Tag the consent parts of your page and each CDR captures exactly what the user did: which disclosure was shown, whether the consent checkbox was checked, and the button they submitted with. This makes the evidence stronger, and it lets a lead buyer confirm a lead meets their requirements, such as a disclosure that names their company.

The tags

  • data-ec-disclosure="<id>": the disclosure language the user is agreeing to. The text is captured so the record is searchable. The id is optional when the page has a single disclosure.
  • data-ec-submit: the button the user submits with. ExpressConsent outlines this exact control in the rendered evidence, so the record shows precisely how the user submitted.
  • data-ec-consent-checkbox: a consent checkbox. ExpressConsent records whether it was checked when the user submitted. Put it on a native <input type="checkbox">, on a custom widget with role="checkbox" or role="switch", or on a container that wraps exactly one such control.
  • data-ec-consent-for="<id>": which disclosure a checkbox governs. Optional when the page has exactly one disclosure and one checkbox; required when there is more than one.
html
<!-- The disclosure language the user is agreeing to. -->
<p data-ec-disclosure="tcpa">
  By submitting, you agree to be contacted at the number provided,
  including by automated technology.
</p>

<!-- A consent checkbox bound to that disclosure (optional). -->
<input type="checkbox" data-ec-consent-checkbox data-ec-consent-for="tcpa" />

<!-- The button the user submits with. -->
<button type="submit" data-ec-submit>Submit</button>

If you want to gate the capture on whether the checkbox is checked, you can read its state synchronously before calling captureCDR():

javascript
const consentChecked = document.querySelector("[data-ec-consent-checkbox]").checked;
if (consentChecked) {
  // captureCDR() still must be the first await.
  await window.ExpressConsent.captureCDR();
}

That is entirely your call. ExpressConsent derives the checkbox state independently server-side regardless; this just lets you decide whether to capture at all.

Multiple disclosures

When there is more than one disclosure or checkbox, bind each checkbox to its disclosure with data-ec-consent-for.

html
<!-- With more than one disclosure, bind each checkbox explicitly. -->
<p data-ec-disclosure="sms">By checking this box, you agree to receive SMS messages...</p>
<input type="checkbox" data-ec-consent-checkbox data-ec-consent-for="sms" />

<p data-ec-disclosure="email">By checking this box, you agree to receive marketing email...</p>
<input type="checkbox" data-ec-consent-checkbox data-ec-consent-for="email" />

<button type="submit" data-ec-submit>Submit</button>

What ExpressConsent reports: the consent mechanism

For each disclosure, ExpressConsent derives a consentMechanism at the moment of the detected submission. It is one of three values, so a lead buyer can filter on exactly how consent was expressed:

  • checkbox: a consent checkbox bound to the disclosure was checked (affirmative opt-in).
  • button_submission: the disclosure had no consent checkbox, so consent was expressed by submitting the form (the disclosure was shown and the user submitted with an affirmative control).
  • none_detected: no affirmative consent was detected. A bound checkbox was unchecked or unreadable, a consent checkbox on the page did not bind to a disclosure, or the user submitted with a negative control (e.g. a “No” / “Decline” button) on a disclosure that had no checkbox.

You never declare the mechanism. ExpressConsent reads it from the captured evidence and surfaces it as consentMechanism on each disclosure in the API and webhook.

Masking

Masking redacts sensitive data (SSN, CVV, passwords) from the evidence before it leaves the browser. Masked data is gone permanently; you cannot recover it, even as the org owner.

Mask a single input

html
<label>
  SSN
  <input name="ssn" data-expressconsent-mask />
</label>

Mask an entire section

The attribute is inherited by descendants. Place it on a container to redact everything inside.

html
<section data-expressconsent-mask>
  <h3>Payment details</h3>
  <input name="cardNumber" />
  <input name="cvv" />
  <!-- Everything inside is redacted. -->
</section>

Inputs

All options are optional. Call captureCDR() with no arguments and you still get a valid CDR.

custom

Arbitrary metadata stored with the CDR. Every key becomes a queryable field: you can filter CDRs by any single key/value pair using the API’s metadataKey/metadataValue params. Include anything you might want to look a CDR up by later: phone numbers, email addresses, campaign IDs, form names. Must be JSON-serializable. Max 16 KB.

autoShare

Generate a share URL for a lead buyer during the upload, with no separate API call. Pass true for the default 30-day expiry, or an options object for a custom one:

  • autoShare: true: 30-day expiry (default)
  • autoShare: { expiresInMs: 604800000 }: custom expiry (max 2 years)
javascript
const { cdrId, shareUrl } = await window.ExpressConsent.captureCDR({
  autoShare: true,
});

When enabled, the result includes shareUrl, shareToken, and shareExpiresAt. See Sharing with lead buyers.

timeoutMs

Optional request timeout in milliseconds. Overrides the SDK’s default adaptive timeout budget.

Return values

Always present:

  • cdrId: the evidence ID. Store this with your lead record.
  • packageData: session grouping info, containing packageId (the session-level package). Relevant only if you build package-aware workflows.

Present when autoShare is enabled:

  • shareUrl: the full absolute URL the lead buyer opens (with their API key) to receive the evidence.
  • shareToken: the raw share token (the last segment of the URL).
  • shareExpiresAt: token expiry as a Unix timestamp in milliseconds.

Error behavior

captureCDR() throws when it cannot guarantee the evidence was persisted:

  • SDK not loaded (missing script tag or data-ec-cid)
  • Network failure or timeout
  • CAPTURE_DISABLED: capture has been administratively disabled for this organization. Contact ExpressConsent support if you believe this is an error.

Wrap the call in a try/catch, log the error, and let the form submit proceed. Do not block submission on a capture failure.

Next