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.
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.
A check the record lacks the data for comes back unavailable, and refuses
the record just as a failed check does. The most common case is a record with
no detected submission: it has no consent facts, so requireConsent comes
back unavailable.
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.
schemaVersion1requiredMust be the number
1. Omitting it is a400 INVALID_ARGUMENT.recordobjectoptionalRequirements about the record itself.
requireConsentobjectoptionalThe person agreed to a disclosure they were shown, in a way you accept.
leadMatchobjectoptionalRequirement that the phone number on the record matches the lead you were sold.
record
maxAgeSecondsintegeroptionalRefuse 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.
"requireConsent": {
"containing": ["Sunshine Media"],
"mechanisms": ["checkbox", "button_submission"],
"userActuatedCheckbox": true
}containingstring[]optionalOnly 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[]requiredHow 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.userActuatedCheckboxbooleanoptionalA 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
phonestringrequiredRefuse 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+12125551234all 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.
{
"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.
ruleIdstringrequiredThe path of the requirement:
record.maxAgeSeconds,requireConsent, orleadMatch.phone.statusstringrequiredfailedmeans the check ran and the record did not meet it.unavailablemeans the record lacks the data the check needs. Both refuse the record.codestringrequiredA stable machine-readable reason, from the table below.
Codes
| Code | Status | Meaning |
|---|---|---|
RECORD_TOO_OLD | failed | Older than maxAgeSeconds |
DISCLOSURE_DETAILS_UNAVAILABLE | unavailable | requireConsent: no submission was detected, so the record has no consent facts to check |
CONSENT_DISCLOSURE_NOT_FOUND | failed | requireConsent: no disclosure contained any of your containing phrases |
CONSENT_NOT_ACCEPTED | failed | requireConsent: no disclosure in scope was agreed to by a mechanism you listed |
CONSENT_CHECKBOX_NOT_USER_ACTUATED | failed | requireConsent: the checkbox consent in scope was boxes the person never toggled, such as pre-checked boxes |
CONSENT_CHECKBOX_ACTUATION_UNAVAILABLE | unavailable | requireConsent: we did not record whether the person toggled the checkboxes in scope |
LEAD_PHONE_NOT_MATCHED | failed | The record has phone numbers, and yours is not one of them |
LEAD_PHONE_MATCH_UNAVAILABLE | unavailable | No 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:
{
"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.