# Call Clearance API

Base URL: `https://api.callclearance.com`. All agency routes take `Authorization: Bearer cc_live_...`. Bodies are JSON. Phone numbers are US, in any common format; responses return E.164.

Every check, consent, and opt-out is written to a hash-chained audit log. A check counts against the plan's bundle whether it answers allow or block; consent, opt-outs, records and exports are free. Which checks can run today, and from what data, is on the [coverage page](https://callclearance.com/coverage).

## Getting a key

Sign up at `https://api.callclearance.com/signup`. Your dashboard shows the key once; we keep only a fingerprint, and you can make a new one there any time. Every account starts on the Free plan: 500 checks a month for one business, no card. Paid plans are priced by the businesses covered, with checks bundled and a per-check overage past the bundle, drawn from a prepaid balance:

| Plan | Monthly | Businesses | Checks included | Then |
| --- | --- | --- | --- | --- |
| Free | $0 | 1 | 500 | checks pause until the month turns |
| Starter | $49 | 1 | 5,000 | $0.01 each |
| Agency | $199 | 10, then $15 each | 25,000 | $0.005 each |
| Scale | $599 | 50, then $10 each | 150,000 | $0.004 each |

Months are calendar months, UTC. Free also runs 50 checks a day (`402 daily_cap`), is one account per company domain, and does not include FTC registration filing. Every plan keeps records forever and exports them for nothing. A lapsed subscription drops the account to Free; nothing is deleted. A check past what the plan allows answers `402` with `error` of `allowance_used` (Free) or `insufficient_credit` (paid, balance empty); adding a business past the count answers `402 plan_limit`. `GET /account` shows the plan, checks used this month and businesses covered.

## Check a number before you dial

```
POST /v1/check
{ "client_id": "cl_8f2a", "to": "+14105550123", "channel": "voice" }
```

Optional fields:

- `require_consent` (default `true` for both channels). The FCC treats AI-generated voices as artificial voices, so an outbound AI call needs prior express consent and a marketing text needs prior express written consent. Set `false` only for calls where you have determined consent is not required (for example some non-telemarketing or business-to-business calls); the Call Record then says the consent check was skipped at your request, never that it passed. Before Oct 7, 2026 the default for `voice` was `false`.
- `consent_overrides_dnc` (default `false`). When `true`, a consent record on file lets a National or state DNC hit through. Consent you captured for this business and this channel can support that; an established business relationship or a lead vendor's claim is not a consent record. The override is printed on the Call Record. Whether it applies to your calls is a question for your lawyer.

Response, 200:

```
{
  "decision": "block",
  "reason": "national_dnc",
  "retryable": false,
  "checked": [
    { "name": "optout", "result": "pass" },
    { "name": "litigator", "result": "pass" },
    { "name": "national_dnc", "result": "hit", "detail": "ftc_san_12345 loaded 2026-09-01T00:00:00Z" }
  ],
  "evidence_id": "ev_10422",
  "to": "+14105550123",
  "client_id": "cl_8f2a"
}
```

Reasons: `opted_out`, `litigator`, `national_dnc`, `state_dnc`, `no_consent`, `outside_calling_hours` (retryable, with `retry_after_seconds` and a `Retry-After` header), `invalid_number`.

A check result of `skipped` means the check could not run and says why (no registry data loaded for that area code, no state list, no timezone). Skipped is never treated as pass.

Checks run in this order and stop at the first hit: opt-out, litigator, national DNC, state DNC, consent, calling hours.

**Calling hours** start from the federal 8:00–21:00 in the called party's local time. They are then narrowed by the state's own rule where it is stricter, for voice calls and texts alike. The state is taken from the area code. A state that spans two timezones must be open in both. The `detail` names the rule and its citation.

Limit: the called party's location is inferred from the area code. Ported mobile numbers can live in another state. If you know the person's state, keep that in mind; passing it to the check is not supported yet.

| State | Window (local) | Also | Source |
| --- | --- | --- | --- |
| AL | Mon–Sat 8:00–20:00 | no Sundays or legal holidays | [Ala. Admin. Code r. 770-X-5-.17](https://www.law.cornell.edu/regulations/alabama/Ala-Admin-Code-r-770-X-5-.17) |
| LA | Mon–Sat 8:00–20:00 | no Sundays or legal holidays | [LPSC General Order R-29617; La. R.S. 45:811](https://lpsc.louisiana.gov/docs/DNC/DNCGeneralOrder.pdf) |
| CT | 9:00–20:00 | | [Conn. Gen. Stat. § 42-288a(c)](https://law.justia.com/codes/connecticut/title-42/chapter-743m/section-42-288a/) |
| FL | 8:00–20:00 | also caps calls at 3 per 24 hours (not enforced yet; see coverage) | [Fla. Stat. § 501.616(6)](https://www.flsenate.gov/Laws/Statutes/2025/501.616) |
| MD | 8:00–20:00 | also caps calls at 3 per 24 hours (not enforced yet) | [Md. Com. Law § 14-4502(c)](https://mgaleg.maryland.gov/mgawebsite/Laws/StatuteText?article=gcl&section=14-4502) |
| OK | 8:00–20:00 | also caps calls at 3 per 24 hours (not enforced yet) | [15 O.S. § 775C.4](https://www.mintz.com/insights-center/viewpoints/2776/2022-06-28-tcpa-litigation-update-oklahoma-latest-state-enact-mini) (secondary source; statute text to be linked) |
| OR | 8:00–20:00 | also caps calls at 3 per 24 hours (not enforced yet) | [ORS 646.563(1)(b), HB 3865 (2025)](https://olis.oregonlegislature.gov/liz/2025R1/Downloads/MeasureDocument/HB3865/Enrolled) |
| MA | 8:00–20:00 | | [M.G.L. c. 159C § 3; 201 CMR 12.02](https://malegislature.gov/Laws/GeneralLaws/PartI/TitleXXII/Chapter159C/Section3) |
| WA | 8:00–20:00 | | [RCW 80.36.390(8)](https://app.leg.wa.gov/rcw/default.aspx?cite=80.36.390) |
| WY | 8:00–20:00 | | [Wyo. Stat. § 40-12-302(d)](https://wyoleg.gov/statutes/compress/title40.pdf) |
| KY | 10:00–21:00 | | [KRS 367.46955(16)](https://codes.findlaw.com/ky/title-xxix-commerce-and-trade/ky-rev-st-sect-367-46955/) |
| MN | 9:00–21:00 | | [Minn. Stat. § 325E.30](https://www.revisor.mn.gov/statutes/cite/325E.30) |
| NM | 9:00–21:00 | | [NMSA § 57-12-22](https://codes.findlaw.com/nm/chapter-57-trade-practices-and-regulations/nm-st-sect-57-12-22/) |
| MS | Mon–Sat 9:00–20:00 | no Sundays | [Miss. Code § 77-3-723](https://codes.findlaw.com/ms/title-77-public-utilities-and-carriers/ms-code-sect-77-3-723/) |
| NV | 9:00–20:00 | | [NRS 598.0918(3)](https://www.leg.state.nv.us/NRS/NRS-598.html) |
| PA | 8:00–21:00, no legal holidays; **from Oct 18, 2026** Mon–Sat 9:00–19:00 | no Sundays or legal holidays | [73 P.S. § 2245(a)](https://www.palegis.us/legislation/bills/2025/sb992); [Act 47 of 2026 (SB 992)](https://www.palegis.us/legislation/bills/text/HTM/2025/0/SB0992/PN1649) |
| RI | Mon–Fri 9:00–18:00, Sat 10:00–17:00 | no Sundays or legal holidays | [R.I. Gen. Laws § 5-61-3.6](https://webserver.rilegislature.gov/Statutes/TITLE5/5-61/5-61-3.6.htm) |
| SD | Mon–Sat 9:00–21:00 | no Sundays | [SDCL 37-30A-3(2)](https://sdlegislature.gov/api/Statutes/37-30A-3.html) |
| TX | Mon–Sat 9:00–21:00, Sun 12:00–21:00 | | [Tex. Bus. & Com. Code § 301.051(b)(2)](https://texas.public.law/statutes/tex._bus._and_com._code_section_301.051) |
| UT | Mon–Sat 8:00–21:00 | no Sundays or Utah legal holidays | [Utah Code § 13-25a-103(3)](https://le.utah.gov/xcode/Title13/Chapter25A/C13-25a-S103_2022050420220504.html) |
| CA | 9:00–21:00 | voice calls only (artificial-voice rules) | [Cal. Pub. Util. Code § 2872(c)](https://california.public.law/codes/public_utilities_code_section_2872) |
| IL | 9:00–21:00 | voice calls only (artificial-voice rules) | [815 ILCS 305/15(a)](https://law.justia.com/codes/illinois/chapter-815/act-815-ilcs-305/) |
| IN | 9:00–20:00 | voice calls only | [Ind. Code § 24-5-14-8](https://law.justia.com/codes/indiana/title-24/article-5/chapter-14/section-24-5-14-8/) |
| ME | Mon–Fri 9:00–17:00 | voice calls only | [10 M.R.S. § 1498(3)](https://legislature.maine.gov/statutes/10/title10sec1498.html) |

These rules apply even when consent is on file. Several states (MD, AL, LA, NV, OR) have no consent exception, so this is the safe default. Where a statute bans legal holidays without listing them, we block federal holidays on both the actual and the observed date. Rules were last reviewed Sept 29, 2026 against the sources linked above; they have not yet been reviewed by a lawyer. The per-24-hour frequency caps in FL, MD, OK and OR are not enforced yet (see [coverage](https://callclearance.com/coverage)). This is tooling, not legal advice.

402 means the account has no credit. 404 means the client id is not on your account.

## Record consent

```
POST /v1/consent
{
  "client_id": "cl_8f2a",
  "phone": "+14105550123",
  "channel": "sms",                 // voice | sms | any
  "source": "web_form",             // free text: web_form, sms_reply, verbal, written
  "captured_at": "2026-09-10T14:22:00Z",
  "evidence_ref": "https://forms.example.com/submissions/88213",
  "evidence": "{...raw form payload or transcript excerpt...}",
  "scope": "solar quote follow-up"
}
```

`evidence` is hashed (SHA-256) and the hash is stored; the body is not. Keep the original yourself. `evidence_ref` is stored as given.

Revoke for one client: `POST /v1/consent/revoke` with `client_id`, `phone`, `reason`.

## Capture consent at the source

Record consent where it happens instead of reporting it afterwards. Make a publishable capture key for a client (safe to put in a web page; it can only record consent for that client, from the sites you list):

```
POST /v1/capture-keys
{ "client_id": "cl_3f9a2c", "origins": ["https://harborsolar.com", "*.harborsolar.com"], "wording": "By checking this box, I agree that…" }
```

The response includes `snippet` and `hosted_page`. You can also do this from the dashboard under Consent capture.

**On the client's own form**, paste the snippet before `</body>`:

```html
<script src="https://api.callclearance.com/c.js" data-key="cc_pub_…" async></script>
```

When a form with a ticked consent checkbox and a phone number is submitted, the snippet records: the exact checkbox wording as the person saw it, the page URL and title, the time, the IP and browser we saw, and a copy of the form with the answers removed. It never blocks or changes the form. It finds the checkbox whose label mentions consent and calling or texting; mark it explicitly with `data-cc-consent` (and the phone field with `data-cc-phone`) if the form is unusual. For forms submitted by JavaScript, call `CallClearance.recordConsent({ phone, wording, form })`.

**No site to edit?** Send people to the hosted page (`/consent/cc_pub_…`). It shows the client's name and the wording you set; if you set none, a sample is used. Have the client's counsel approve the wording.

Everything captured is stored, and its SHA-256 goes into the consent record and the audit chain. `GET /v1/consents/:id/evidence` returns it with `chain_hash_matches`. The Call Record shows how, where and from what browser consent was given, next to the wording. What this can't do: prove a human, not a script, ticked the box. Origin checks, rate limits and the recorded IP make fabricated entries visible, not impossible.

## Record an opt-out

```
POST /v1/optout
{ "phone": "+14105550123", "source": "sms_stop", "client_id": "cl_8f2a" }
```

Opt-outs are account-wide: the number is blocked for every client on your account, and any live consent under any client is revoked. `client_id` records where it was heard.

## Opt-outs from every channel (the revocation ledger)

Since April 2025 the FCC requires honoring a revocation made by any reasonable means: a STOP text, "take me off your list" on a call, a reply to an email. Send us the texts and call transcripts and we find the request, apply it at once for every client and every channel, and keep what was said, when, and how long it took to apply.

**Webhook addresses (no code).** Make one in the dashboard, or:

```
POST /v1/revocation-hooks
{ "kind": "sms" }            // or "call"; optional "client_id"
→ { "hook": { "url": "https://api.callclearance.com/hooks/sms/cc_hook_…", ... } }
```

- **Texts:** paste the `sms` address as the inbound message webhook. Twilio (Phone Numbers → Messaging → "A message comes in", HTTP POST; we answer with empty TwiML so nothing is sent back), Telnyx (`message.received`), or any sender posting JSON `{ "from": "+1…", "body": "…" }`. STOP, QUIT, END, REVOKE, OPT OUT, CANCEL and UNSUBSCRIBE are applied, and so are phrases like "stop texting me".
- **Calls:** paste the `call` address as Retell's agent webhook (we read `call_ended`; `call_analyzed` for the same call is ignored as a duplicate) or as Vapi's Server URL (`end-of-call-report`). We scan only what the person said, never the agent's lines, and date the request from Retell's word timings or Vapi's message times.

The address is the credential: keep it private, and turn it off from the dashboard if it leaks.

**From your own code:**

```
POST /v1/revocations
{ "phone": "+14105550123", "channel": "voice",
  "transcript": [ { "role": "agent", "content": "…" }, { "role": "user", "content": "please stop calling me" } ],
  "said_at": "2026-09-29T14:02:11Z", "ref": "call_8f2a", "client_id": "cl_8f2a" }
→ 201 { "revoked": true, "status": "applied", "phrase": "stop calling", "latency_ms": 412, "record_id": "ev_1042", ... }
```

`channel` is `sms`, `voice`, `email`, `crm`, `web` or `api`. Send `text` (a message, or a transcript with `Agent:`/`User:` labels), `transcript` (string or array of turns), or `"explicit": true` with a `label` for an opt-out you already know about. `ref` makes retries safe: the same source and ref are recorded once.

**Unclear messages** ("not interested", "leave me alone", "wrong number", a bare "stop" on a call) come back `"status": "flagged"` and are not applied. Decide in the dashboard or with `POST /v1/revocations/:id/apply` or `/dismiss`. Applying keeps the original time, so the record shows the review delay honestly. `GET /v1/revocations?status=flagged` lists them.

Each applied revocation's SHA-256 goes into the audit chain. The Call Record for a blocked check shows what the person said, when, how long it took to apply, and every attempt blocked since. `POST /v1/optout` and GoHighLevel's Record opt-out also land in the ledger.

## Evidence

```
GET /v1/evidence/+14105550123?client_id=cl_8f2a          JSON
GET /v1/evidence/+14105550123.pdf?client_id=cl_8f2a      PDF
GET /v1/evidence/+14105550123.html?client_id=cl_8f2a&record=ev_10422   Call Record (one printable page)
```

Returns every consent record, the opt-out if any, and every audit event for that number under that client, each with its chain hashes, plus whether the full chain verifies.

The `.html` form is the Call Record: one page for a person to read, led by the check named in `record` (default: the latest check). It opens with the answer (checked before dialing, allowed or blocked, when, for which client), then every check with its detail and list date (a check that could not run reads Skipped, never Passed), the consent the check relied on with the `scope` wording you recorded, the number's full history, and the record's chain hashes. Print it to PDF from any browser.

`GET /v1/audit/verify` re-walks the whole log and reports the first broken row, if any.

## Daily fingerprints (public, no key)

```
GET /fingerprints                                  page listing every day
GET /fingerprints.json                             the same, as data
GET /fingerprints/callclearance-2026-09-28.txt     that day's statement
GET /fingerprints/callclearance-2026-09-28.txt.ots its OpenTimestamps proof
```

After each UTC day closes, the last row hash of the audit chain is written into a short statement (day, last record id, row count, head hash), and the statement's SHA-256 is timestamped with public OpenTimestamps calendars, which commit it to Bitcoin. Check one with `ots verify callclearance-YYYY-MM-DD.txt.ots` next to the .txt. The Call Record for any check cites the fingerprint of its day.


## Proof files and the open verifier

```
GET /v1/records/ev_1042/proof
→ { "format": "callclearance-proof/1", "record": {...}, "tree": {...}, "statement": {...} }
```

A proof file lets anyone, such as your lawyer, an insurer or an auditor, check a Call Record without trusting us. It holds the record's exact stored fields, a Merkle path (RFC 6962) from the record to its day's root, the day's published statement that names that root, and the OpenTimestamps proof on the statement. It reveals no other record and no phone number.

Check one at [api.callclearance.com/verify](https://api.callclearance.com/verify). The check runs in the browser and nothing is uploaded. Or run the verifier yourself: `node callclearance-verify.mjs proof.json --online`. It needs Node 18+, has no dependencies, is MIT-licensed, and can be downloaded at `/verify.mjs`. It recomputes:

1. the record's fingerprint from its fields;
2. the path to the day's root;
3. that the day's statement names that root, covers the record and hashes to its stated SHA-256;
4. that the OpenTimestamps proof is for that statement, walking it to the Bitcoin block it lands in;
5. with `--online`, that the published statement is byte-for-byte the same;
6. with `--online`, that the block really has that Merkle root, asked of mempool.space and blockstream.info.

We upgrade each day's proof to its Bitcoin attestation automatically, usually a few hours after the day closes, so a proof file needs no further steps.

Proofs exist once the record's UTC day closes and is fingerprinted, just after midnight UTC. Before that the endpoint answers `409 day_not_closed`. Days fingerprinted before Sept 29, 2026 answer `409 predates_proofs`; those records are covered by the day's chain head. The one thing a file can't prove is which number a record is about, because numbers are stored as keyed hashes. That link is attested by our records custodian.

## Clients

```
GET  /v1/clients
POST /v1/clients            { "name": "Sunny Solar LLC", "state": "MD" }
PATCH /v1/clients/:id       { "san": "1234567", "san_status": "active" }
```

Each client is a separate seller under the Telemarketing Sales Rule and needs its own FTC Subscription Account Number for national registry access. We handle the registration; the FTC's fee is paid by the seller — the first five area codes are free, then $85 per area code per year, capped at $23,425 for a seller taking every area code in the country (these are the rates from October 1, 2026; before that date it was $82 and $22,626).

Registry data is scoped to the subscription it was downloaded under. The FTC does not allow one subscription's data to be reused for another seller, so a client is only ever checked against its own. Until `san_status` is `active` with data loaded under that SAN, national DNC checks report `skipped` and say why. Skipped is never treated as pass.

## Retell and Vapi: connect once, dial through the check

Retell and Vapi have no hook that runs before they dial and can stop a call; a tool or webhook inside the agent runs after the phone has already rung. So the check sits in front of the dial request instead: link your platform account once, then send calls to `POST /v1/call`. On allow we ask your platform to place the call. On block nothing is dialed.

Connect on the dashboard, or by API:

- `POST /v1/connections {"platform": "retell" | "vapi", "api_key": "..."}` checks the key with the platform and returns your agents and numbers. The key is stored encrypted and never returned.
- `PATCH /v1/connections/:id {"default_from": "...", "default_agent": "..."}` picks what calls go out on. Retell: `from` is a number you own on Retell in E.164; the agent is optional and defaults to that number's outbound agent. Vapi: `from` is a `phoneNumberId` and the agent is an `assistantId`; both are required.
- `GET /v1/connections` lists them. `DELETE /v1/connections/:id` disconnects and deletes the key.

Then, wherever you start calls today:

```
curl -s https://api.callclearance.com/v1/call \
  -H "Authorization: Bearer $CC_KEY" -H 'content-type: application/json' \
  -d '{"client_id":"cl_8f2a","to":"+14105550123","variables":{"first_name":"Pat"}}'
```

- Allow: `{"placed": true, "platform": "retell", "platform_call_id": "...", "decision": "allow", "checked": [...], "evidence_id": "ev_..."}`
- Block: `{"placed": false, "decision": "block", "reason": "national_dnc", ...}`, and no request reaches the platform.
- Platform refused after an allow: HTTP 502, `"placed": false`, with the platform's message.

Optional fields: `connection_id` (defaults to your most recent connection), `agent`, `from`, `metadata` (Retell; we add `call_clearance_evidence_id`), `variables` (dynamic variables for the agent), `consent_overrides_dnc`. Each call is one billed check, and the platform's call id is written to the audit chain next to it.

## GoHighLevel workflow actions

Three actions for GoHighLevel workflows. HighLevel sends `{ data: {...}, extras: { locationId, contactId, workflowId } }`; your key goes in `X-Api-Key` (or `Authorization: Bearer`).

`POST /v1/integrations/ghl/check` takes `data.phone` (normally `{{contact.phone}}`), optional `business_name` (`{{location.name}}`), `state`, `channel` (`voice` or `sms`), `require_consent`, `consent_overrides_dnc`, `client_id`. It always answers 200 with a branch:

```json
{ "result": "blocked", "allowed": false, "reason": "national_dnc",
  "reason_text": "Number is on the National Do Not Call list.",
  "record_id": "ev_1042", "client_id": "cl_3f9a2c", "phone": "+14105550101",
  "checked": "opt_out: pass, litigator: pass, national_dnc: hit", "branchId": "blocked" }
```

`result` is `allowed`, `blocked`, or `later` (outside calling hours; `retry_after_minutes` says how long to wait). Anything unreadable, a missing phone, or an empty balance goes down `blocked`, never `allowed`.

Each GoHighLevel sub-account becomes one client the first time it sends an action, named from `business_name`. Pass `client_id` once to link a sub-account to a client you already have.

`POST /v1/integrations/ghl/consent` takes `phone`, `consent_text` (the exact checkbox wording), optional `page_url`, `channel`, `captured_at`, `source`. `POST /v1/integrations/ghl/optout` takes `phone` and optional `source`; the opt-out covers every client on your account.

## Bland, ElevenLabs, Twilio, your own dialer

Call `POST /v1/check` right before you place the call and place it only on `allow`. Five lines:

```
curl -s https://api.callclearance.com/v1/check \
  -H "Authorization: Bearer $CC_KEY" -H 'content-type: application/json' \
  -d '{"client_id":"cl_8f2a","to":"+14105550123","channel":"voice"}' \
  | jq -e '.decision == "allow"' && place_call.sh +14105550123
```

## Legacy Retell adapter

`POST /v1/adapters/retell` still answers `{"allow": true|false, "reason": "..."}` for anyone who wired it up as a Retell custom function. It runs inside the call, after the phone has rung, so it does not stop a call from being placed. Use `/v1/call` for that.

## Account

`GET /account` shows balance and price. `POST /account/topup {"amount_dollars": 100}` returns a Stripe Checkout URL.

## Errors

`{ "error": "<code>", "message": "<plain English>" }` with 400, 401, 402, 403, 404, or 500.
