# Consent and TCPA

> Show each result's consent statement as ICON gives it, and send the consumer's decision with the lead.

ICON records, for every lead, the consent statement the consumer saw and their decision. You show the statement; ICON keeps the record.

## What to show

Each result carries its statement in `offer.tcpa_text`. Show it next to the button that submits the lead, **word for word**. Do not shorten or rephrase it.

The statement may contain tokens:

| Token | Becomes | Filled by |
| --- | --- | --- |
| `{{BUTTON}}` | the label of the button the visitor clicks | You: replace it with your submit button's label, and send that label as consent_button_label. |
| `{{PHONE}}` | the phone number they entered | ICON, from the phone number on the lead. |
| `{{OFFER NAME}}` | the school the offer sends the lead to | ICON, with the name of the school the result sends the lead to. |

- `offer.needs_checkbox: true`: show an **unticked** checkbox with the statement; the consumer must tick it. Otherwise clicking the button is the consent.
- `offer.consent_checkboxes`: extra boxes an offer asks for (`field`, `text`, `required`). Show each with its `text`; do not let the consumer submit to that offer until every `required` box is ticked.

## What to send

| Field | When | Value |
| --- | --- | --- |
| `tracking.ip_address` | **Required** on every search and every direct post. A result submit uses the search's unless you send it. | The consumer's own IP address: a real public IPv4 or IPv6 address, the one the consumer used on your page (not your server's). Private, shared and reserved addresses are refused. |
| `tcpa_consent` | Every submit, and on search when you collected consent before it. | The boolean `true` when the consumer agreed. |
| `consent_button_label` | Every submit whose statement had `{{BUTTON}}`. | The label of your submit button (at most 80 characters). |
| `additional_consents` | When the result had `consent_checkboxes`. | One entry per ticked box: `{"field": "<field>", "text": "<text as shown>", "checked": true}`. |
| `xxTrustedFormCertUrl`, `xxTrustedFormToken`, `trusted_form_cert_url` | When you use TrustedForm. | The certificate URL. |
| `universal_leadid`, `universalLeadId`, `jornaya_lead_id` | When you use Jornaya. | The LeadiD token. |

```json
{
  "icon_result_id": "…",
  "tracking": {
    "ip_address": "203.0.113.9"
  },
  "tcpa_consent": true,
  "consent_button_label": "Request information",
  "additional_consents": [
    {
      "field": "sms_opt_in",
      "text": "Text me about my application.",
      "checked": true
    }
  ]
}
```

## The consumer's IP

ICON records the consumer's IP address in the consent record and on the lead. Your call comes from your server, so ICON takes the consumer's IP **only from `tracking.ip_address`** (`ip` is accepted too) and never treats the connection's address as the consumer's:

- A valid consumer IP is recorded as the consumer's; your server's address is kept separately, for audit.
- On a search or a direct post, a missing, private, shared or reserved address is not used: the call is still processed, but the lead and its consent record hold your server's address, marked as a server address. That is weaker consent evidence, so always send the consumer's.
- A result submit uses the IP of its search, unless you send one.
- The examples on this site use documentation addresses (`203.0.113.x`), which ICON refuses: send the real one.

## Sending consent

Send the consent fields with **every submit**: each submit is checked on its own. A new phone number withdraws the consent given for the old one: collect consent again for the new number.

## When consent is missing or wrong

| code | reason | When |
| --- | --- | --- |
| `CONSENT_REQUIRED` | Missing TCPA consent | The offer needs consent and none was sent, or the statement shown does not cover the offer. |
| `CONSENT_INVALID` | Invalid TCPA consent | The consent sent cannot be matched to a statement ICON knows. |

Both have status `invalid`: fix the request and submit again.
