> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dncscrub.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Compliance for AI Voice Agents

> One API call tells your AI dialer whether it may place this call right now — consent, DNC, wireless, holidays and calling hours, resolved to a single flag

AI voice agents are the most tightly regulated way to place an outbound call.
The FCC ruled in February 2024 that an AI-generated voice is an *artificial
voice* under the TCPA. That puts every AI marketing call under 47 U.S.C.
§227(b): **prior express written consent from the person called, for wireless
and residential landlines alike, with no existing-business-relationship
exemption.** Get it wrong and the exposure is $500–$1,500 per call, class-wide.

DNCScrub already holds the three things an AI dialer needs to answer "may I
place this call right now?": the consent you captured, every DNC list, and the
destination's calling hours. With `version=8` the Scrub API returns that answer
as one field, `IsCallAllowedAI`.

<CardGroup cols={3}>
  <Card title="Store consent" icon="file-signature" href="/api-reference/scrub/ebr-list">
    Save express written consent as a Permission EBR the moment you capture it
  </Card>

  <Card title="Gate every call" icon="shield-check" href="#gate-every-call">
    Scrub before dialing; place the call only when `IsCallAllowedAI` is `1`
  </Card>

  <Card title="Honor opt-outs" icon="ban" href="/api-reference/scrub/internal-dnc">
    Add to Internal DNC when the consumer says stop — the next scrub returns `0`
  </Card>
</CardGroup>

## What `IsCallAllowedAI` checks

`IsCallAllowedAI` is `1` only when **every** row below is satisfied. The flag is
computed per number at scrub time.

| Check                             | Condition for `1`                                                                                                           | Source of truth                                                                                           |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Express written consent           | A **Permission (`P`) EBR** is on file for this number and campaign (`EBRType` is `P`)                                       | Consent you stored via the [EBR and Consent API](/api-reference/scrub/ebr-list)                           |
| Consent still valid               | `ResultCode` is `E`, `O`, `G` or `H` — the consent is current and not superseded by a later opt-out                         | EBR expiry rules, Internal DNC precedence                                                                 |
| Not on a DNC list without consent | Implied by the above — a number on the National/State DNC with valid consent returns `O`; without consent returns `D` → `0` | National DNC, state DNC lists, Internal DNC, litigator lists                                              |
| Wireless-prohibited states        | `ResultCode` is not `F` or `V`                                                                                              | State law (WY, NJ, TX, LA, AZ)                                                                            |
| Holidays and emergencies          | `DoNotCallToday` is `0`                                                                                                     | State restricted holidays, declared states of emergency                                                   |
| Calling hours                     | The destination's current local time is inside `CallingWindow`                                                              | State and federal calling windows, DST, and — when you pass a postal code — the contact's actual location |

Anything else returns `0`. In particular:

* A clean number with **no** consent on file (`ResultCode` `C`) is `0`. Clean is
  not consent.
* A Sale (`S`) or Inquiry (`I`) EBR is `0`. An EBR is a DNC exemption, not
  consent for an artificial voice.
* Wireless and VoIP numbers are `1` when a Permission EBR is on file. Express
  written consent covers §227(b) for wireless.

<Note>
  `IsCallAllowedAI` is the strictest of the three `version=8` flags. If your
  agent is `1` here it is also `1` for `IsCallAllowedATDS` and
  `IsCallAllowedNonATDS`. See [Is the call
  allowed?](/api-reference/scrub/output-guide#is-the-call-allowed) for how the
  three relate.
</Note>

## The loop

<Steps>
  <Step title="Capture consent and store it as a Permission EBR">
    When the consumer opts in (web form, checkbox with the required
    disclosures, signed agreement), write it to DNCScrub immediately. Set
    `dateOfLastContact` to the date consent was given and keep your evidence
    reference in `referenceNum`.

    ```bash theme={null}
    curl --request POST 'https://www.dncscrub.com/app/main/rpc/ebr' \
      --header 'loginId: YOUR_API_KEY' \
      --header 'Content-Type: application/json' \
      --data '{
        "ebrList": [{
          "phoneNumber": "5039367181",
          "type": "P",
          "dateOfLastContact": "2026-08-15",
          "referenceNum": "consent-form-84213"
        }]
      }'
    ```

    You can also do this in the same request as the scrub — see
    [Scrub + Add EBR](/api-reference/scrub/scrub-ebr).
  </Step>

  <Step title="Gate every call" id="gate-every-call">
    Immediately before the agent dials, scrub the number with `version=8`.
    Pass the contact's postal code as the third pipe field so calling hours
    follow where the person actually is, not where their area code was
    assigned.

    ```bash theme={null}
    curl 'https://www.dncscrub.com/app/main/rpc/scrub?phoneList=5039367181|lead-8841|10001&version=8&output=json' \
      --header 'loginId: YOUR_API_KEY'
    ```

    Place the call only if `IsCallAllowedAI` is `"1"`.
  </Step>

  <Step title="Honor opt-outs in the conversation">
    When the consumer asks not to be called again, add the number to your
    [Internal DNC](/api-reference/scrub/internal-dnc). Because the Internal DNC
    entry is newer than the consent, the next scrub returns `IsCallAllowedAI`
    `0` — no code change on your side. See [Opt-outs and
    opt-ins](/api-reference/scrub/opt-outs-and-opt-ins) for the precedence
    rules.
  </Step>
</Steps>

## Example

A consumer who gave express written consent on 2026-08-15, scrubbed from an AI
agent at 2 PM Eastern with their postal code:

<CodeGroup>
  ```python Python theme={null}
  import requests

  def may_ai_call(phone: str, lead_id: str, postal_code: str) -> bool:
      r = requests.get(
          "https://www.dncscrub.com/app/main/rpc/scrub",
          params={
              "phoneList": f"{phone}|{lead_id}|{postal_code}",
              "version": "8",
              "output": "json",
          },
          headers={"loginId": "YOUR_API_KEY"},
          timeout=10,
      )
      r.raise_for_status()
      result = r.json()[0]
      return result["IsCallAllowedAI"] == "1"

  if may_ai_call("5039367181", "lead-8841", "10001"):
      start_ai_call("5039367181")
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch(
    `https://www.dncscrub.com/app/main/rpc/scrub?phoneList=5039367181|lead-8841|10001&version=8&output=json`,
    { headers: { loginId: "YOUR_API_KEY" } }
  );
  const [result] = await res.json();

  if (result.IsCallAllowedAI === "1") {
    await startAiCall("5039367181");
  } else {
    // Inspect result.ResultCode, result.EBRType, result.DoNotCallToday,
    // result.CallingWindow to see which condition failed.
  }
  ```
</CodeGroup>

<ResponseExample>
  ```json Consent on file theme={null}
  [
    {
      "Phone": "5039367181",
      "ResultCode": "G",
      "Reserved": "lead-8841",
      "Reason": ";;;W",
      "RegionAbbrev": "OR",
      "Country": "US",
      "Locale": "Portland",
      "CarrierInfo": "5820;WIRELESS;\"Verizon Wireless:Verizon Wireless\"",
      "NewReassignedAreaCode": "",
      "TZCode": "35",
      "CallingWindow": "8:00-21:00;8:00-21:00;8:00-21:00",
      "UTCOffset": "-240",
      "DoNotCallToday": "0",
      "CallingTimeRestrictions": "4",
      "EBRType": "P",
      "IsWirelessOrVoIP": "1",
      "LineType": "Wireless",
      "EBRExpiresOn": "",
      "WirelessPortDate": "0",
      "VoIPDate": "",
      "PostalCode": "10001",
      "TZSource": "postalCode",
      "IsCallAllowedNonATDS": "1",
      "IsCallAllowedATDS": "1",
      "IsCallAllowedAI": "1"
    }
  ]
  ```

  ```json No consent on file theme={null}
  [
    {
      "Phone": "5039367181",
      "ResultCode": "W",
      "Reserved": "lead-8841",
      "Reason": ";;;W",
      "RegionAbbrev": "OR",
      "Country": "US",
      "Locale": "Portland",
      "CarrierInfo": "5820;WIRELESS;\"Verizon Wireless:Verizon Wireless\"",
      "NewReassignedAreaCode": "",
      "TZCode": "35",
      "CallingWindow": "8:00-21:00;8:00-21:00;8:00-21:00",
      "UTCOffset": "-240",
      "DoNotCallToday": "0",
      "CallingTimeRestrictions": "4",
      "EBRType": "",
      "IsWirelessOrVoIP": "1",
      "LineType": "Wireless",
      "EBRExpiresOn": "",
      "WirelessPortDate": "0",
      "VoIPDate": "",
      "PostalCode": "10001",
      "TZSource": "postalCode",
      "IsCallAllowedNonATDS": "1",
      "IsCallAllowedATDS": "0",
      "IsCallAllowedAI": "0"
    }
  ]
  ```
</ResponseExample>

Same number, same time. A live agent could dial it; an AI agent may not until
consent is on file.

## Building an agent framework or LLM tool?

* **Gate in the dialer loop, not in the model.** Call the REST endpoint
  directly from the code that places the call, immediately before dialing. Do
  not route the check through an LLM tool call — that adds seconds of latency
  and puts a non-deterministic step in front of a compliance decision.
* **Deterministic, not advisory.** `IsCallAllowedAI` is computed from rules and
  data, never from a model. Treat it as a hard gate in code, not as context for
  the agent to reason about.
* **Cheap to call.** The flags add no database work; a single-number scrub is
  one round trip. Scrub at dial time, every time — consent, DNC status and
  calling hours all change.
* **Log the response.** Store the full scrub row with the call record. It is
  your evidence that consent existed and hours were respected when the call was
  placed.

## What this flag does not cover

<AccordionGroup>
  <Accordion title="Disclosing that the caller is an AI">
    Several states (California, Utah, Colorado and others) and pending FCC rules
    require that an artificial or AI caller identify itself and, in some cases,
    disclose that the voice is synthetic. That concerns what the call *says*,
    which the scrub cannot see. Build the disclosure into your agent's opening.
  </Accordion>

  <Accordion title="In-call opt-out mechanism">
    FCC rules require prerecorded and artificial-voice telemarketing calls to
    offer an automated opt-out (for example, "press 9 or say stop") that
    immediately ends the call and records the request. Wire that request to the
    [Internal DNC](/api-reference/scrub/internal-dnc) API.
  </Accordion>

  <Accordion title="Consent scope and one-to-one rules">
    DNCScrub stores the fact that consent exists for a number and campaign. It
    does not verify that the consent language named your brand, covered AI or
    prerecorded calls, or satisfies a particular state's disclosure
    requirements. Review your consent capture with counsel; store it as
    Permission only when it meets the standard for the consumer's jurisdiction.
  </Accordion>

  <Accordion title="Informational and non-marketing AI calls">
    Appointment reminders, fraud alerts and similar informational calls have
    different consent standards (prior express consent, not written). The flag
    assumes a marketing call; a `0` does not by itself mean an informational
    call is prohibited. Use `ResultCode`, `LineType` and `CallingWindow` and
    apply your own policy.
  </Accordion>

  <Accordion title="Call recording and two-party consent states">
    Recording or transcribing the call is governed by state wiretap law, not
    the TCPA, and is outside the scrub.
  </Accordion>
</AccordionGroup>

<Warning>
  `IsCallAllowedAI` encodes DNC status, stored consent, line type and calling
  hours — the data DNCScrub holds. It is not legal advice and does not replace
  review of your consent process and call scripts by counsel.
</Warning>
