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

# TCPA Consent-Record Defense Runbook

> Assemble a tenant-owned evidence pack for a disputed inbound consent with the consent record, write history, audit trail, suppression ledger, and opt-in language.

# TCPA Consent-Record Defense Runbook

When a recipient disputes consent, the strongest response is a dated chain of
records rather than a single screenshot. This runbook shows how to assemble the
tenant-owned evidence of an inbound opt-in: the consent row, the event that
recorded it, the tenant audit entry, the suppression state, and the language the
recipient saw.

<Warning>
  This page describes Orbit's platform controls. It is **not legal
  advice.** Which obligations apply to you — TCPA, state mini-TCPA
  statutes, CTIA messaging principles, carrier 10DLC rules — depends on
  your traffic and recipients. Confirm with qualified counsel.
</Warning>

Keep the original API responses, request timestamps, export parameters, and
hash-verification results together. Do not edit an export in place. If you need
to redact a copy for a recipient, preserve the unredacted tenant copy and record
who made the redacted copy and why.

***

## 1. When the burden lands on you

A TCPA complaint or demand letter often makes the consent record the
dispositive evidence. Start this runbook when the claim says that:

* the recipient never opted in, or the opt-in was not express or written;
* consent was obtained for a different sender, product, purpose, or channel;
* the number was reassigned, the form was completed by somebody else, or the
  recipient could not have seen the notice at the asserted time;
* the sender continued after a revocation, or treated an SMS revocation as
  unrelated to voice; or
* the campaign's registration and opt-in language do not match the traffic that
  reached the recipient.

The current state alone does not answer those claims. A current
`opted_in` lookup can hide an earlier revoke and re-grant; an active suppression
row can prove a later no-send control but not the original grant. Pull the
history and the bounded exports so the timeline remains visible.

This is a control-usage runbook for tenant operators. Preserve the demand
letter, identify the disputed address and UTC window, and involve counsel before
responding to a legal request.

***

## 2. Build the evidentiary chain

Assemble the links below in chronological order. Each link answers a different
question; do not substitute one surface for another.

| Link | What it establishes | Orbit surface |
| - | - | - |
| **Consent grant row** | Which address and channel were granted, when, from what source, and with what capture context. | [`GET /compliance/consent/lookup`](/compliance/consent-management#looking-up-consent) and [`GET /compliance/consent/history`](/compliance/consent-management#consent-history--reading-the-grantrevoke-trail). The record can include `granted_at`, `source`, `consent_text_version`, `consent_proof_url`, `ip_address`, and the available user-agent or metadata captured by your surface. |
| **Consent-write event history** | That the write reached the Consent API at a particular time and returned record IDs. | Preserve the original HTTP request and `201` response from `POST /compliance/consent`, including its request timestamp and `consent_record_ids`. The write and its export are also recorded in the tenant audit trail. |
| **Tenant audit entry** | The append-only event context around the write and each evidence export. | [Audit log export](/compliance/audit-export). Queue a UTC-bounded export and verify its chain; keep the request filters, `rows_checked`, `chain_valid`, hashes, and daily roots with the bundle. |
| **Suppression ledger at the dispute time** | Whether the address was fenced, revoked, or not present in the send gate's durable ledger at the relevant time. | [Consent and suppression export](/compliance/consent-suppression-export), especially `GET /compliance/suppression-list/export` with `status=all`. Keep the `suppressed_at`, `revoked_at`, `reason`, `source`, and `channel`/`all` scope. |

The consent history is the timeline witness: it shows grants, revocations, and
later re-permission without rewriting the original event. The suppression
ledger is the send-control witness: it shows the address fence and its scope.
The audit export is the integrity witness for the actions that Orbit recorded.

### Capture context and the opt-in vocabulary

`source`, `purpose`, `consent_text_version`, and `consent_proof_url` are part of
the consent record when your capture flow supplied them. `ip_address` is
available for web-captured consent. User-agent and other capture details are
available only when your own form or integration sent them in the supported
proof or metadata fields; do not infer a missing value. A missing field is a
limitation to disclose, not a reason to reconstruct the record.

Keep the exact opt-in text and versioned notice from the form, landing page, or
preference flow that called the API. The consent row's `consent_text_version`
should point your reviewer to that tenant-owned artifact.

***

## 3. Worked example: subpoena for `+1555XXXXXXX`

Assume a subpoena identifies `+1555XXXXXXX` and asks for records from
`2026-04-01` through `2026-04-30` UTC. Replace the placeholder with the full
E.164 number in your private shell; do not place a redacted number in an API
request.

Set the common values first:

```bash theme={null}
export ORBIT_API_KEY='dv_live_sk_...'
export NUMBER='+15555550123'
export FROM='2026-04-01'
export TO='2026-04-30'
mkdir -p "tcpa-evidence-$NUMBER"
```

### Step 1: Read the current record and complete history

Start with the point lookup, then pull the full cross-channel history. Save the
raw responses before interpreting them. The lookup is a current-state view; the
history is the timeline needed for discovery.

```bash theme={null}
curl --fail-with-body --get \
  'https://api.orbit.devotel.io/api/v1/compliance/consent/lookup' \
  --data-urlencode "identifier=$NUMBER" \
  --data-urlencode 'channel=sms' \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -o "tcpa-evidence-$NUMBER/01-consent-lookup.json"

curl --fail-with-body --get \
  'https://api.orbit.devotel.io/api/v1/compliance/consent/history' \
  --data-urlencode "identifier=$NUMBER" \
  --data-urlencode 'limit=100' \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -o "tcpa-evidence-$NUMBER/02-consent-history-page-1.json"
```

If `next_cursor` is non-null, request another history page with the same
identifier and `cursor`, and retain every page. Do not treat `unknown` as proof
that an earlier row never existed.

### Step 2: Export the consent rows for the address and window

The export is scoped by `contact_id`, not by phone identifier. Read
`contact_id` from the lookup response, then export all channels and states for
the bounded `created_at` window. If the lookup has no `contact_id`, export the
window with the narrowest available channel filter and document that limitation
for counsel; never guess a contact ID.

```bash theme={null}
CONTACT_ID=$(jq -r '.contact_id // .data.contact_id // empty' \
  "tcpa-evidence-$NUMBER/01-consent-lookup.json")

curl --fail-with-body --get \
  'https://api.orbit.devotel.io/api/v1/compliance/consent/export' \
  --data-urlencode 'format=json' \
  --data-urlencode 'state=all' \
  --data-urlencode 'channel=sms' \
  --data-urlencode "contact_id=$CONTACT_ID" \
  --data-urlencode "from=$FROM" \
  --data-urlencode "to=$TO" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -o "tcpa-evidence-$NUMBER/03-consent-export.json"
```

Check `truncated` and `count` in the JSON envelope. If `truncated` is `true`,
pull consecutive narrower windows until each response is complete. The export
is bounded by `created_at`; the history response remains the source for the
full grant/revoke sequence.

### Step 3: Export the tenant audit window and verify it

Queue the audit export for the subpoena window. Include the surrounding UTC
days if the request asks for the event that created the grant or the later
suppression change. Poll until the job is `complete`, download inside the
signed-URL lifetime, and preserve the job response alongside the bundle.

```bash theme={null}
AUDIT_JOB=$(curl --fail-with-body --get \
  'https://api.orbit.devotel.io/api/v1/compliance/audit-export' \
  --data-urlencode "from=$FROM" --data-urlencode "to=$TO" \
  --data-urlencode 'format=json' \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  | tee "tcpa-evidence-$NUMBER/04-audit-queue.json" \
  | jq -r '.data.id')

curl --fail-with-body \
  "https://api.orbit.devotel.io/api/v1/compliance/audit-export/$AUDIT_JOB" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -o "tcpa-evidence-$NUMBER/05-audit-status.json"
```

Poll the status response until `complete`, download its `download_url`, and
then verify the same job:

```bash theme={null}
curl --fail-with-body \
  "https://api.orbit.devotel.io/api/v1/compliance/audit-export/$AUDIT_JOB/verify" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -o "tcpa-evidence-$NUMBER/06-audit-chain-verification.json"
```

Keep the audit rows whose event details identify the consent write, consent
export, suppression export, or related inbound event. A valid chain proves the
export's integrity; it does not prove that the underlying consent was legally
valid.

### Step 4: Project the suppression ledger for the address

The suppression export is tenant-wide and supports channel, status, and time
filters rather than an address parameter. Request the full relevant ledger
slice, preserve the raw file, and project the matching address locally. Use
`status=all` so a later revocation does not hide the state that existed in the
requested window.

```bash theme={null}
curl --fail-with-body --get \
  'https://api.orbit.devotel.io/api/v1/compliance/suppression-list/export' \
  --data-urlencode 'format=json' \
  --data-urlencode 'channel=all' \
  --data-urlencode 'status=all' \
  --data-urlencode "from=$FROM" \
  --data-urlencode "to=$TO" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -o "tcpa-evidence-$NUMBER/07-suppression-export.json"

jq --arg number "$NUMBER" \
  '[.items[]? | select(.address == $number)]' \
  "tcpa-evidence-$NUMBER/07-suppression-export.json" \
  > "tcpa-evidence-$NUMBER/08-suppression-$NUMBER.json"
```

Check `truncated` before relying on the projection. If it is `true`, repeat the
export over smaller consecutive windows and combine the matching rows. Record
the export filters and the time you ran the projection; a point-in-time
historical state cannot be recreated if the tenant did not retain the relevant
export.

### Step 5: Package and label the chain

Put these artifacts in one tenant-controlled evidence store:

1. the consent lookup and every consent-history page;
2. the consent export and its filters/truncation result;
3. the audit job, downloaded bundle, and chain-verification response;
4. the raw suppression export and the address projection;
5. the original HTTP request/response metadata and the exact opt-in text
   identified by `consent_text_version`; and
6. a manifest with the disputed number, UTC window, retrieval timestamps,
   operator, and any missing fields or redactions.

Hash the files in your own evidence store after download and restrict access to
people handling the demand. The files are tenant-owned evidence; Orbit does not
send a legal response for you.

***

## 4. Carrier-side corroboration

The consent chain answers how your tenant captured permission. Add the carrier
context that shows what traffic the permission covered:

* **10DLC sender and campaign record:** the brand and campaign compliance
  profiles live under [10DLC brand and campaign compliance profiles](/compliance/10dlc-brand-campaign-profiles).
  Pull the campaign profile and preserve its status, approved brand reference,
  use case, message-flow description, sample messages, and HELP/STOP responses.
  The campaign profile's message-flow and consent-language fields are the
  tenant-owned record of what you submitted for the 10DLC campaign.
* **Sender registration:** where the traffic used a market-specific sender ID,
  preserve the tenant's registration record from [Sender-ID Registration](/compliance/sender-id-registration),
  including the sender, country, status, document references, and provider
  registration reference.
* **Opt-in vocabulary:** preserve the exact form, prompt, checkbox copy, and
  versioned notice used by your capture surface. For a double opt-in flow,
  include the prompt and recipient confirmation as well as the confirmed
  consent row; a pending handshake is not a consent grant. The
  [`consent_text_version`](/compliance/consent-management#recording-consent)
  field is the join from the ledger to your stored copy.

These records corroborate sender identity and campaign language. They do not
replace the consent row, its event history, or the suppression ledger.

***

## 5. Limitations and tenant-owned posture

* **This is not legal advice.** Counsel decides what TCPA or state-law
  standard applies, what to produce, and whether the assembled records satisfy
  it.
* **Consent rows are tenant data.** Access is limited to owner/admin surfaces,
  and exports contain recipient identifiers. Redact only a working copy; keep
  the original, the redaction log, and the reason for each redaction.
* **No retroactive evidence repair.** Do not edit a consent row, invent a
  missing IP address or user-agent, or re-record a grant to make an old event
  look newer. A later write is a new event and does not repair the historical
  chain.
* **Exports have limits.** Consent and suppression exports cap at 50,000 rows
  and report truncation. Audit download URLs expire after seven days. Narrow
  windows, retain the raw responses, and queue a fresh export when a URL expires.
* **A current state is not historical proof.** Use `/history`, bounded exports,
  and the audit chain together. A suppression row proves the ledger state and
  scope recorded by Orbit; it does not prove the recipient's original consent.
* **Tenant-owned controls stay tenant-owned.** Orbit provides the API, ledger,
  audit trail, and carrier-profile surfaces. You choose the capture flow, keep
  the opt-in copy and proof artifacts, configure suppression, and retain the
  evidence pack.

## Related

* [Consent Management & Receipts](/compliance/consent-management) — Consent
  API fields, lookup, history, and proof-of-record export.
* [Export Consent & Suppression Records](/compliance/consent-suppression-export)
  — export schemas, filters, truncation, and suppression projections.
* [Audit log export and chain verification](/compliance/audit-export) — queue,
  download, and verify the tenant audit bundle.
* [TCPA Evidence Pack in the Binder](/compliance/tcpa-evidence-binder) —
  outbound posture evidence for quiet hours, DNC, and send gates.
* [10DLC brand and campaign compliance profiles](/compliance/10dlc-brand-campaign-profiles)
  — sender registration and campaign language records.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.