Skip to content

Product · The per-call evidence file

Every call leaves a file you can hand over

One record per attempt, written while the call happens, whether it connected, reached a machine, or was stopped before it was ever placed. It carries the window and the caps that bound the dial in the contact’s local time, the consent record relied on, whether the AI-disclosure opener played and whether the carrier confirmed it, and which checks were waived under your signed attestation and by whom. A check that did not run never reads as a check that found nothing.

What the file records

Not a summary of the call. The state the gate read, the answer it returned, and the signatures behind anything it did not perform itself, in the order the snapshot writes them.

  1. Where the dial stood

    The rules that bound this call, and the version they were at

    policy_jurisdiction · source_versions · timezone · local_time · quiet_hours

    Every set of rules that applied to this dial, each named with the version it was at when it applied, plus the callee’s own timezone, the local clock the gate computed, and the window that resulted. What the file settles is which of them bound the call, down to the pack and the version.

  2. Whether anyone agreed to be called

    The consent records the decision actually stood on

    purpose · consent_required · consent_held · consent_expired · consent_covers_purpose · consent_record_ids

    One record has to carry both a sufficient class and the requested voice purpose; unrelated grants are never added together to reach a threshold, and a revocation row carrying an old record’s id is never accepted as a replacement grant. The ids on the snapshot are evidence, not permission: the records are re-read at dial time rather than trusted across an expiry or a withdrawal.

  3. What ran

    One object per check that executed

    stages[] · stage · verdict · source · dataset_version · cache_age_days

    Normalization and suppression against your own internal do-not-call list are the floor stages, and they run for every tenant in every mode. Nothing reaches them, not a plan, not an API field, not a support tool, because the floor controls are not members of the waivable set at all, so “waive the disclosure opener” is not a value anyone can construct, express in JSON, or store. Making it unrepresentable is stronger than validating it away.

  4. What was waived, and on whose signature

    One object per stage your attestation elected away

    compliance_elections[] · waived · attestation_ref · attested_by

    Each election names the stage, the attestation reference it stands on, and the officer who signed it. A waiver missing either half is refused when the object is constructed, so it cannot exist in memory, let alone reach a dial. The attestation is your own officer’s signature, not ours.

  5. What happened on the call

    The re-check at launch, the opener, and the opt-out

    dial_time_recheck · disclosure · opt_out

    The gate re-runs as the channel is taken and stamps the result on the passing path too, so a dial that was re-verified at launch is distinguishable from one that only ever cleared queueing. The opener’s text lands with whether it was spoken and whether the carrier confirmed it. If the caller asked to stop, the file records when, what phrase matched, and what became of the suppression write.

Spoken and confirmed are two different facts and the file keeps them apart. Spoken means the opener was handed to the transport. Confirmed means the carrier acknowledged the end of it, and only the second is evidence a person heard it. A call carrying no playback record at all is scored a failure rather than an unknown, because a dimension reporting “no data” would let the gap sit unnoticed forever.

The proof, per call

The file, in full

A synthetic tenant and synthetic ids, on a Texas contact called in the middle of a Monday morning. This is the shape a real attempt carries, key for key.

policy_snapshot att_01JB7Q4ZK9M2P6R3T5V7Y9AB2C

Synthetic data

{
  "policy_jurisdiction": "TX",
  "source_versions": {
    "US": "2026.07.1",
    "TX": "2026.07.1"
  },
  "purpose": "marketing",
  "consent_required": "pewc",
  "consent_held": "pewc",
  "consent_expired": false,
  "consent_covers_purpose": true,
  "consent_record_ids": [
    "cons_01JB6M4T7Q2K9P3R5V8Y1D4C60"
  ],
  "self_test_consent_authorized": false,
  "timezone": "America/Chicago",
  "local_time": "2026-08-17T10:22:41-05:00",
  "quiet_hours": [
    "09:00:00",
    "21:00:00"
  ],
  "attempts_in_window": 0,
  "frequency_cap": null,
  "verified_own_number": false,
  "telephony_mode": "byo",
  "stages": [
    {
      "stage": "normalize",
      "verdict": "pass",
      "source": "internal",
      "dataset_version": null,
      "cache_age_days": null
    },
    {
      "stage": "internal_dnc",
      "verdict": "pass",
      "source": "internal_dnc",
      "dataset_version": null,
      "cache_age_days": null
    }
  ],
  "compliance_elections": [
    {
      "waived": "federal_dnc",
      "attestation_ref": "agr_01JB5K3R9T7Q2M6P4V8Y1C0D2E",
      "attested_by": "Dana Reyes, VP Operations"
    },
    {
      "waived": "line_type",
      "attestation_ref": "agr_01JB5K3R9T7Q2M6P4V8Y1C0D2E",
      "attested_by": "Dana Reyes, VP Operations"
    },
    {
      "waived": "litigator",
      "attestation_ref": "agr_01JB5K3R9T7Q2M6P4V8Y1C0D2E",
      "attested_by": "Dana Reyes, VP Operations"
    },
    {
      "waived": "rnd",
      "attestation_ref": "agr_01JB5K3R9T7Q2M6P4V8Y1C0D2E",
      "attested_by": "Dana Reyes, VP Operations"
    },
    {
      "waived": "state_dnc",
      "attestation_ref": "agr_01JB5K3R9T7Q2M6P4V8Y1C0D2E",
      "attested_by": "Dana Reyes, VP Operations"
    },
    {
      "waived": "wireless_ported",
      "attestation_ref": "agr_01JB5K3R9T7Q2M6P4V8Y1C0D2E",
      "attested_by": "Dana Reyes, VP Operations"
    }
  ],
  "waived_jurisdiction_overlay": false,
  "dial_time_recheck": {
    "at": "2026-08-17T15:22:41+00:00",
    "dialable": true,
    "policy_refusals": [],
    "blocked_reasons": [],
    "risk_flags": []
  },
  "disclosure": {
    "text": "Hi, this is Ada calling on behalf of Whitcomb Freight, and I'm an AI assistant",
    "spoken": true,
    "confirmed": true,
    "started_at_ms": null,
    "finished_at_ms": null,
    "error": null
  }
}

An election is the shape a waiver is written in: the stage, the document it stands on, and the officer who signed it, because a stage nobody performed must never render as a stage that came back clean. The pack versions and the window are named with the versions they were at, so the file says which values bound the dial without saying anyone here judged them sufficient. Two millisecond playback offsets the real object can carry are left null here, since no timing figure belongs on a page like this one.

Take it with you

Download the sample (JSON)

A file we authored to show the shape. It is not an export the product generated, and no customer data of any kind is in it.

The part nobody else publishes

Waived is a signature, not a silence

Your file never goes quiet about a check nobody ran. Each one lands as a named election: the stage, the document it stands on, and the officer who signed it. A reviewer can follow that to a person, which is more than a green check has ever given anybody.

The screening you run upstream is what those elections stand on. Every production customer signs one attestation naming that arrangement before the first production dial, and its reference and its signer are written into every attempt afterwards, so the file answers who screened this number and on which document. A stage that turns up with no election behind it is a defect on our side, never a waiver on yours, and the platform refuses to write a half-signed one: an election missing its reference or its signer is rejected when the object is built.

Ran

The check executed. Its answer sits in `stages[]` with the verdict, the source it came from, the dataset version behind it and how old that answer was.

Waived

Your attestation elected it away. The record carries the officer who signed that and the document it stands on, as its own object under its own key.

Not applicable

There was nothing to check. A caller-initiated conversation has no outbound window to evaluate, so the record carries a verdict of not applicable with “no outbound dial” as the reason, in its own words.

Different shapes, under different keys. None of them can be mistaken for another by anyone reading the file, which is the entire point of writing it this way.

The calls it refuses are evidence too

A refusal is written with the same care as a connection, because the dial you did not place is the one you will be asked about. A number on your own suppression list is refused at the batch run and refused again when the channel is taken, and both refusals land on the record with the reason that produced them.

The file keeps two vocabularies apart on purpose. One says the number could not be called at all. The other says the number was fine and this moment was not: outside the window, over the cap, or short of the consent class that purpose requires. Collapse those two and the file can no longer answer the first question anyone asks about a call: was this number callable at all, or just not right now?

contact_attempt

Synthetic data

attempt
att_01JB7Q4ZK9M2P6R3T5V7Y9C1D3
result
REFUSED, never placed
blocked_reasons
["dnc_internal"]
policy_refusals
["internal_dnc"]
disposition
dnc_hit
dial_time_recheck
dialable: false
record
written · exportable

Suppression is re-checked at dial time, not only at import, and no plan, API field or support tool reaches it.

How the file gets out

The snapshot is a column on the attempt row, not a report you have to request. It leaves the platform three ways: the attempt rows and their snapshots as a CSV export, the screening results of a run as their own export kind (scrub_results), and the per-contact verdict live from the API before anything is dialed. Extractions are submitted as a job, and a completed job provides a signed download during its retention window.

The pre-dial verdict is computed over that contact’s own record: your suppression list, the consent you hold, and the window and caps in force at that moment. It answers one question, whether this dial clears the gate right now, and it hands back the inputs it read alongside the answer, so a refusal arrives with the reason that produced it rather than as a number you have to go and investigate.

The shape it arrives in

The record sits on every attempt and the export carries it out with the rows intact. Ask for a matter and you get the attempt rows and their snapshots, which is the form your own counsel or your own tooling can load and search.

What holds it still

The audit tables are append-only at the database itself: no role holds UPDATE or DELETE on them, so a correction is a new row pointing at the row it corrects, and the application tier could not rewrite history even by bug. CSV export jobs let your team retrieve the selected attempt records and snapshots without changing that source history.

The trust close

A record of what happened, not an opinion about what was allowed

The window, the caps, the consent standard and the recording-consent class are enforced on every dial, and the file names the ones that bound this call and dates the rule pack they came from. That distinction, what bound the call rather than whether it was enough, is what keeps the file usable in an answer years later.

What your jurisdiction requires of your program is yours to determine with your own advisors. Our half is enforcement and evidence: the dials the platform can detect as ones that should not go out are stopped before the carrier is asked to originate anything, and every dial, placed or refused, stays reconstructible years later from the record alone. That is the claim still standing on the day somebody asks you to prove it.

Every production dial rides your own Twilio subaccount, on your numbers, to the contacts on your own records. The choice of who to call is yours, and so is the screening behind it. Our half is enforcement and evidence. The rest of the posture, including the floor no plan disables, is on the Trust Center.

What getting started actually looks like

Your own Twilio account is the step that takes real time, and it is the reason your minutes cost what your carrier charges you. See pricing for the plans themselves.

1 · Build it in the sandbox

The whole dial path runs against simulated telephony, every gate live and failing closed, and a record written the way production writes it, before any number rings anywhere.

2 · Sign, and say who screens

A signed production order, your own Twilio subaccount, and the attestation that records your screening arrangement. Its reference and its signer ride into every evidence file from the first dial onward.

3 · Take the records out

Pull the attempt rows and their snapshots by CSV export, or read them from the API. Your stack stays the system of record.

Questions people ask first

Is the evidence file something I download from a dashboard?

It is a column on the attempt row, written as the call happens, so nothing has to be assembled after the fact. It leaves through the exports API as CSV: submit an export job for the selected attempts, and a completed job provides a signed download during its retention window. What lands is the rows as the record wrote them, which is the form your own counsel or your own tooling can load and search.

What does the file say about a check that nobody ran?

It names the check as waived, with the attestation reference it stands on and the officer who signed it. That is a different key and a structurally different object from a check that ran, so a stage nobody performed can never render as a stage that came back clean. A stage simply absent, with no election recorded, is a defect on our side rather than a waiver on yours.

Who signs the attestation, and when does it start showing up?

Your own compliance officer signs it, once, before the first production dial, and it names the screening arrangement you already run. From that point its reference and its signer are written onto your compliance profile and into every attempt the platform makes, so any call you are ever asked about can name the document behind it. Both halves are required in code: an election carrying a reference and no signer is rejected when the object is constructed. The database holds the other end, refusing any profile that carries waived stages with no attestation reference on it.

Which checks does the platform itself perform on a dial?

Normalization, suppression against your own internal do-not-call list, and the dialability gate that applies the calling window, the caps and the consent standard in force. Suppression is re-checked at dial time and not only at import. Each check that runs is written into the stages array with its verdict and its source.

What does the file settle, and what stays with me?

It settles what happened: which values bound the dial, which checks ran, which your attestation covered and who signed for them, and how the call ended. What your jurisdiction requires of your program is yours to determine with your own advisors. The file’s job is to make every attempt, placed or refused, reconstructible from the record alone, years after anyone remembers the call.

The record writes itself, on every call, on every plan

Open a sandbox account and watch one get written, with nothing pointed at anyone.