Consent

Consent says which channels a contact agreed to hear from you on. It's tracked separately for calls and ringless voicemail, texts, and email. Use this page to check a contact's consent before you pick a channel, to record an opt-in your own form collected, and to see how opt-outs such as a STOP reply reach you. To stop all contact with a number or email whatever its consent, use the do-not-contact list. When a send fails on consent, its outcome code says why.

Routes

Method Route Scope What it does
POST /contact/public/contacts/{contact_id}/consent consent:write Record an opt-in or an opt-out
GET /contact/public/contacts/{contact_id}/consent consent:read List a contact's consent records
PUT /contact/public/contacts/{contact_id}/consent/{consent_id}/revoke consent:write Revoke a contact's consent

Get a contact returns a flag per channel. Read them before you choose how to reach someone.

Channel The contact agreed when The contact opted out when Record an opt-in with
Calls and ringless voicemail has_tcpa_consent is true tcpa_opted_out_at is set tcpa_optin
Texts has_sms_consent is true sms_opted_out_at is set sms_optin
Email has_email_consent is true email_opted_out_at is set, or email_dnc is true email_optin

Also check dnc. When it's true, one of the contact's phone numbers or their email was added to your do-not-contact list, and sends to a listed number or email are blocked. Check numbers or emails shows which.

Voice consent doesn't cover texts. A contact with has_tcpa_consent: true and has_sms_consent: false can get a ringless voicemail but not a text.

Use the flags to fall back to another channel. If a ringless voicemail fails because the mailbox is full, text the contact only if they agreed to texts:

function canText(contact) {
  return contact.has_sms_consent === true && !contact.sms_opted_out_at && !contact.dnc;
}

function canEmail(contact) {
  return contact.has_email_consent === true && !contact.email_opted_out_at && !contact.email_dnc;
}

If your account has Require TCPA Consent (PEWC) turned on in the app's consent settings, calls, ringless voicemails and texts to a contact without consent for that channel are blocked. Campaigns report them with outcome 6011. Other consent outcomes, such as 6005 for a revoked opt-in and 5014 to 5017 for email, are listed on Outcomes.

How opt-outs reach you

A STOP reply. When a contact texts STOP to one of your numbers, or taps an opt-out suggestion in an RCS message:

  • Their number goes on your do-not-contact list with method: "sms" and reason: "STOP keyword". You can't remove an entry the contact added themselves.
  • A tcpa_optout consent record is saved with consent_method: "sms".
  • The contact's has_sms_consent and has_tcpa_consent become false, and sms_opted_out_at and tcpa_opted_out_at are set. A STOP stops calls and ringless voicemails as well as texts.
  • You get a contact.msg.opt_out webhook with the phone_number, the text they sent in sms_body, and opted_out_at.
  • If Universal Opt-Out (Revoke All) is on in your consent settings, the contact's email consent is revoked too.

An email unsubscribe. When a contact uses the unsubscribe link or header in one of your emails, their email goes on your do-not-contact list, email_dnc becomes true, email_issue_reason says why and email_opted_out_at is set. No webhook is sent, so read the contact's flags before you email them.

A revoke in the app or through this API. You get a contact.consent.revoked webhook. See Revoke a contact's consent for what changes.

Saves a consent record for the contact and updates the contact's flags straight from it. Use it when your own form, call script or CRM collected the opt-in. Send the phone number or email the contact actually agreed on, plus the wording they saw, so the record holds up as proof.

Field Type Required Description
consent_type string Yes One of the types below.
phone_number string One of phone_number or email The number the contact agreed on, in E.164 format.
email string One of phone_number or email The email the contact agreed on.
consent_text string No The exact wording the contact agreed to. We store it with a SHA-256 hash.
consent_method string No How you collected it, such as web_form or phone. Default api.
consent_status string No granted (default) or revoked. Send revoked with an opt-out type.
consent_version string No Version of your consent wording. Default 1.0.0.
ip_address string No The contact's IP address when they agreed. Defaults to the IP of your API call, so send the contact's.
user_agent string No The contact's browser. Defaults to your API call's User-Agent.
trustedform_cert_url string No TrustedForm certificate URL.
trustedform_token string No TrustedForm token.
jornaya_lead_id string No Jornaya LeadiD.
consent_type What it records
tcpa_optin Agreed to calls and ringless voicemail. Sets has_tcpa_consent.
sms_optin Agreed to texts. Sets has_sms_consent.
sms_optin_confirmed Confirmed a texting opt-in, for example by replying to a double opt-in request.
email_optin Agreed to email. Sets has_email_consent.
esign Agreed to sign electronically. Sets has_esign_consent.
web_tracking Agreed to website activity tracking.
tcpa_optout Opted out of calls and ringless voicemail.
sms_optout Opted out of texts. Also clears a confirmed texting opt-in.
email_optout Opted out of email. Sets email_dnc with email_issue_reason: "unsubscribed".

Recording email_optin clears an earlier unsubscribe: email_dnc goes back to false and the email comes off your do-not-contact list. Record it only when the contact has opted in again.

An opt-out type changes the contact's flags for that one channel. It doesn't add anything to the do-not-contact list, and it doesn't send a consent webhook.

curl -X POST "https://api-v2.dropcowboy.com/contact/public/contacts/756c1b7d-c27d-4faf-9365-bb83f0cbed66/consent" \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "consent_type": "sms_optin",
    "phone_number": "+13125550142",
    "consent_method": "web_form",
    "consent_text": "I agree to receive recurring texts from Example Co at the number above. Msg & data rates may apply. Reply STOP to opt out.",
    "ip_address": "203.0.113.24",
    "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)"
  }'

The response is 201 with the new record in consent.

{
  "data": {
    "consent_id": "566bfa04-c530-4b01-afbd-e99f3564cbae",
    "consent": {
      "consent_id": "566bfa04-c530-4b01-afbd-e99f3564cbae",
      "contact_id": "756c1b7d-c27d-4faf-9365-bb83f0cbed66",
      "phone_number": "+13125550142",
      "email": null,
      "consent_type": "sms_optin",
      "consent_status": "granted",
      "consent_version": "1.0.0",
      "consent_method": "web_form",
      "consent_text": "I agree to receive recurring texts from Example Co at the number above. Msg & data rates may apply. Reply STOP to opt out.",
      "consent_text_hash": "sha256:5d0c4f0e9c1b3a7f2e8d6c4b2a0f9e7d5c3b1a9f8e7d6c5b4a3f2e1d0c9b8a7f",
      "ip_address": "203.0.113.24",
      "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)",
      "third_party": {
        "trustedform_cert_url": null,
        "trustedform_token": null,
        "jornaya_lead_id": null,
        "custom_verification_id": null,
        "custom_verification_url": null
      },
      "has_screenshot": false,
      "granted_at": 1790870333466,
      "revoked_at": null,
      "created_at": 1790870333466
    }
  },
  "meta": { "request_id": "35f86685-859f-4a8d-926a-45ec7fba7ddc" }
}

A grant sends a contact.consent.granted webhook.

Returns every consent record for the contact, newest first, including revoked ones. Use it as the audit trail behind the contact's flags. A revoked record keeps its granted_at and has consent_status: "revoked" and a revoked_at.

curl "https://api-v2.dropcowboy.com/contact/public/contacts/756c1b7d-c27d-4faf-9365-bb83f0cbed66/consent" \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
{
  "data": [
    {
      "consent_id": "631a390c-5d38-49b3-aaac-ca8f7201057e",
      "contact_id": "756c1b7d-c27d-4faf-9365-bb83f0cbed66",
      "phone_number": "+13125550142",
      "email": null,
      "consent_type": "tcpa_optin",
      "consent_status": "granted",
      "consent_method": "web_form",
      "consent_text": "By clicking Submit, I agree that Example Co may call me at the number above, including with prerecorded messages.",
      "ip_address": "203.0.113.24",
      "granted_at": 1790783933466,
      "revoked_at": null,
      "created_at": 1790783933466
    }
  ],
  "meta": { "request_id": "23cf132a-adc9-4b91-b241-0199d62bc2e5" }
}

Each record has the same fields as the one Record consent returns. The sample is shortened.

Use this when a contact tells you to stop. What changes depends on the record you name.

Revoking an email consent (email or email_optin) stops email only:

  • Saves a new email_optout record for the contact.
  • Adds the contact's email to your do-not-contact list and clears their email consent.
  • If Universal Opt-Out (Revoke All) or Sync Email Opt-Out to TCPA is on, the contact's phone consent is revoked too, as below.

Revoking any other record stops calls and texts:

  • Saves a new tcpa_optout record for the contact.
  • Marks the contact's granted tcpa_optin records as revoked. If the record you revoke is esign, every granted consent the contact has is revoked.
  • Adds every phone number on the contact to your do-not-contact list, and sets dnc: true, has_tcpa_consent: false and has_sms_consent: false.
  • If the record you revoke is web_tracking, deletes the contact's website activity.
  • If Universal Opt-Out (Revoke All) or Sync TCPA Opt-Out to Email is on, revokes the contact's email consent too.
  • Sends a contact.consent.revoked webhook.

Both send a contact.consent.revoked webhook whose original_consent_id is the record you named. To withdraw texts only, record sms_optout instead.

Field Type Required Description
consent_method string No How the contact asked, such as phone or email. Default api.
ip_address string No Defaults to the IP of your API call.
user_agent string No Defaults to your API call's User-Agent.
curl -X PUT "https://api-v2.dropcowboy.com/contact/public/contacts/756c1b7d-c27d-4faf-9365-bb83f0cbed66/consent/631a390c-5d38-49b3-aaac-ca8f7201057e/revoke" \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "consent_method": "phone" }'

The response is the new opt-out record.

{
  "data": {
    "consent_id": "ff356a80-6548-4144-9726-dcfdf02a0275",
    "consent": {
      "consent_id": "ff356a80-6548-4144-9726-dcfdf02a0275",
      "contact_id": "756c1b7d-c27d-4faf-9365-bb83f0cbed66",
      "phone_number": "+13125550142",
      "email": null,
      "consent_type": "tcpa_optout",
      "consent_status": "revoked",
      "consent_method": "phone",
      "granted_at": null,
      "revoked_at": 1790870396012,
      "created_at": 1790870396012
    }
  },
  "meta": { "request_id": "a4658481-13ce-4e5c-8392-c1f6d36bb15e" }
}

Errors

On these routes, the type URL in an error doesn't tell errors apart. Branch on status.

Status When What to do
400 consent_type is missing or not one of the types above. Send a type from Record consent.
400 Recording without phone_number or email. Send the identifier the contact agreed on.
400 Revoking a consent_id that doesn't exist (At least one identifier required). Get the id from List a contact's consent records.

For everything else, see Responses, errors and limits.


While Drop Cowboy provides tools to support compliance efforts, customers remain solely responsible for obtaining proper consent, maintaining opt-out lists, and complying with all federal and state telemarketing regulations. Consult with your legal counsel to ensure your specific use case and consent mechanisms comply with applicable laws.

Code samples

The requests from this page, ready to copy. Set DC_KEY and DC_SECRET to your API key pair first.

Check consent before you send
function canText(contact) {
  return contact.has_sms_consent === true && !contact.sms_opted_out_at && !contact.dnc;
}

function canEmail(contact) {
  return contact.has_email_consent === true && !contact.email_opted_out_at && !contact.email_dnc;
}
Record consent
curl -X POST "https://api-v2.dropcowboy.com/contact/public/contacts/756c1b7d-c27d-4faf-9365-bb83f0cbed66/consent" \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "consent_type": "sms_optin",
    "phone_number": "+13125550142",
    "consent_method": "web_form",
    "consent_text": "I agree to receive recurring texts from Example Co at the number above. Msg & data rates may apply. Reply STOP to opt out.",
    "ip_address": "203.0.113.24",
    "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)"
  }'
List a contact's consent records
curl "https://api-v2.dropcowboy.com/contact/public/contacts/756c1b7d-c27d-4faf-9365-bb83f0cbed66/consent" \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
Revoke a contact's consent
curl -X PUT "https://api-v2.dropcowboy.com/contact/public/contacts/756c1b7d-c27d-4faf-9365-bb83f0cbed66/consent/631a390c-5d38-49b3-aaac-ca8f7201057e/revoke" \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "consent_method": "phone" }'