Acceptance criteria

Declare what a shared record must prove before you will take it. Send acceptance on POST /v1/shares/:token and a record that does not meet your requirements is refused rather than delivered.

bash
curl -sS -X POST -H "X-API-Key: $EC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "acceptance": {
      "schemaVersion": 1,
      "record": { "maxAgeSeconds": 2592000 },
      "requireConsent": { "mechanisms": ["checkbox"] },
      "leadMatch": { "phone": "(212) 555-1234" }
    }
  }' \
  "https://api-next.expressconsent.com/v1/shares/xYz7Qw..."

What a refusal costs you

Nothing. Evaluation happens before anything is written, so a refused record is not saved, you are not charged, and the token stays usable. You can retry with different requirements, or tell the seller the lead does not meet your standard.

An accepted record is delivered exactly as it would have been without any criteria at all.

Shape

Every object here is strict, including the request body that carries it: an unrecognized or misspelled field is a 400 INVALID_ARGUMENT rather than being ignored. That extends to the acceptance key itself, so a typo fails loudly instead of quietly meaning "no requirements". The error's details.issues[] names the offending path.

The smallest valid object is { "schemaVersion": 1 }, which imposes no requirements and accepts anything.

schemaVersion1required

Must be the number 1. Omitting it is a 400 INVALID_ARGUMENT.

recordobjectoptional

Requirements about the record itself.

requireConsentobjectoptional

The person agreed to a disclosure they were shown, in a way you accept.

leadMatchobjectoptional

Requirement that the phone number on the record matches the lead you were sold.

record

maxAgeSecondsintegeroptional

Refuse a record older than this.

Positive, whole, and at most 157,680,000 (5 years).

requireConsent

Passes when at least one disclosure the person was shown, among those you point it at, was agreed to in a way you accept.

json
"requireConsent": {
  "containing": ["Sunshine Media"],
  "mechanisms": ["checkbox", "button_submission"],
  "userActuatedCheckbox": true
}
containingstring[]optional

Only consider disclosures whose text contains one of these phrases, such as the name your company goes by in the seller's disclosure.

Omit it to consider every disclosure. A disclosure that was not agreed to never fails the record on its own: as long as one disclosure in scope passes, an optional "also email me offers" box left unchecked does not matter.

1 to 10 phrases, each 1 to 5,000 characters after trimming.
mechanismsstring[]required

How the person may have agreed. checkbox: every consent checkbox bound to the disclosure was checked. button_submission: the disclosure had no consent checkbox and the person submitted with a control that was not a refusal. A disclosure where the person pressed "No" never counts.

One or both of checkbox and button_submission.
userActuatedCheckboxbooleanoptional

A checkbox counts only if the person ticked or unticked at least one of the disclosure's checkboxes themselves, rather than leaving boxes the page pre-checked. Consent by submission is unaffected.

Records captured before we recorded this come back unavailable, so turning it on refuses them.

How phrases match

Both the disclosure text and your phrases are normalized before comparison: Unicode-normalized, runs of whitespace collapsed to single spaces, trimmed, and lowercased. So matching is case-insensitive and indifferent to line breaks or indentation in the captured markup.

Most punctuation is left alone. A curly apostrophe does not match a straight one, so prefer phrases without punctuation: Sunshine Media rather than Sunshine Media's.

leadMatch

phonestringrequired

Refuse the record unless this phone number was on the captured page, either in a phone form field or printed in a displayed disclosure.

Any common formatting works. (212) 555-1234, 212-555-1234, and +12125551234 all normalize to the same number.

1 to 40 characters, and must normalize to a 10-digit North American number with an optional leading 1. A value that does not is a 400 INVALID_ARGUMENT, not a rule failure, and it carries no details.issues.

This is the check that catches a mismatched handoff: the lead record says one number and the consent evidence shows another. Send the number the seller gave you and the record is refused unless the person in the evidence is the person you were sold.

Your number is compared as a one-way hash against hashes of the numbers on the record. The number you send is never stored.

Failures

A record that does not meet your requirements comes back as 422 CDR_ACCEPTANCE_FAILED, with every check that did not pass listed.

json
{
  "ok": false,
  "error": {
    "code": "CDR_ACCEPTANCE_FAILED",
    "message": "The CDR did not meet the supplied acceptance requirements.",
    "details": {
      "outcome": "rejected",
      "failures": [
        {
          "ruleId": "requireConsent",
          "status": "failed",
          "code": "CONSENT_NOT_ACCEPTED"
        },
        {
          "ruleId": "leadMatch.phone",
          "status": "failed",
          "code": "LEAD_PHONE_NOT_MATCHED"
        }
      ]
    },
    "requestId": "3f2504e0-4f89-11d3-9a0c-0305e82c3301"
  }
}

Each entry has exactly three fields. A single entry is enough to refuse the whole record.

ruleIdstringrequired

The path of the requirement: record.maxAgeSeconds, requireConsent, or leadMatch.phone.

statusstringrequired

failed means the check ran and the record did not meet it. unavailable means the record lacks the data the check needs. Both refuse the record.

codestringrequired

A stable machine-readable reason, from the table below.

Codes

CodeStatusMeaning
RECORD_TOO_OLDfailedOlder than maxAgeSeconds
DISCLOSURE_DETAILS_UNAVAILABLEunavailablerequireConsent: no submission was detected, so the record has no consent facts to check
CONSENT_DISCLOSURE_NOT_FOUNDfailedrequireConsent: no disclosure contained any of your containing phrases
CONSENT_NOT_ACCEPTEDfailedrequireConsent: no disclosure in scope was agreed to by a mechanism you listed
CONSENT_CHECKBOX_NOT_USER_ACTUATEDfailedrequireConsent: the checkbox consent in scope was boxes the person never toggled, such as pre-checked boxes
CONSENT_CHECKBOX_ACTUATION_UNAVAILABLEunavailablerequireConsent: we did not record whether the person toggled the checkboxes in scope
LEAD_PHONE_NOT_MATCHEDfailedThe record has phone numbers, and yours is not one of them
LEAD_PHONE_MATCH_UNAVAILABLEunavailableNo phone field value or disclosure number on the record to compare against

A reasonable starting policy

For a buyer who calls the numbers on the leads they buy:

json
{
  "schemaVersion": 1,
  "record": { "maxAgeSeconds": 7776000 },
  "requireConsent": { "mechanisms": ["checkbox"] },
  "leadMatch": { "phone": "(212) 555-1234" }
}

That accepts a record only when the person checked the consent box on a disclosure they were shown and then submitted the form, the record is no more than 90 days old, and the phone number in the evidence is the one you were sold. Add "button_submission" to mechanisms if you also accept "by clicking Submit, I agree" pages.

If a seller's records come back DISCLOSURE_DETAILS_UNAVAILABLE, we did not detect a submission on their form. Point them to Tagging a form.