Home /how-it-works/dnc-compliance

How does InsertLead handle DNC and STOP requests?

Six layered defenses keep opted-out phone numbers from ever receiving another message: a 47-phrase deterministic STOP filter, a Claude semantic-STOP classifier as a second opinion, a per-user DNC table for audit, a global cross-tenant DNC table that protects the platform's sender reputation, manual CRM "DNC" flagging, and the standard Twilio-side STOP keyword auto-handling. Once any of these flags a number, every outbound across the platform skips it. There is no path back from a global DNC entry.

On this page
  1. Why DNC matters legally and economically
  2. The six layers, in order
  3. Global DNC across tenants (and why)
  4. What counts as an opt-out
  5. Per-user DNC table for audit

Why DNC matters legally and economically

Two reasons we built this hard:

Legal: the TCPA (Telephone Consumer Protection Act) lets recipients sue for $500-$1,500 per text sent after they asked you to stop. State telemarketing laws layer on top. Failing to honor a STOP request is one of the highest-friction TCPA violations because the recipient already told you "stop" in writing — the case is open-and-shut.

Economic: carriers (T-Mobile, AT&T, Verizon) score sender numbers on the rate at which recipients flag messages as spam or report them via STOP. A high opt-out rate that you keep texting through is a direct path to "spam likely" labeling on every future message, then to outright delivery blocks, then to A2P 10DLC campaign suspension. One bad campaign can cripple a number for months.

The combination means: every wholesaler should treat opt-outs as a fire-and-forget rule. We do that automatically.

The six layers, in order

Layer 1: Deterministic 47-phrase pre-filter.

Every inbound message gets matched against 47 substring patterns covering every common opt-out phrase: "stop," "unsubscribe," "remove me," "take me off your list," "do not contact," "leave me alone," "not interested" (and variants), and so on. Match on any one and the lead is opted out, no AI call made. This layer alone catches probably 85-90% of opt-outs.

Layer 2: Claude semantic-STOP classifier (second-opinion).

Even if the deterministic filter doesn't match, we ask Claude a single yes/no question on the inbound: "Is this message asking the sender to stop contacting them, in any phrasing?" Claude catches the cases the substring filter misses — "I really don't want to deal with this," "please don't text me anymore today," etc. If Claude says yes, the lead opts out.

Layer 3: Twilio-side STOP keyword auto-handling.

Twilio itself auto-handles "STOP" / "UNSUBSCRIBE" / "QUIT" / "CANCEL" / "END" at the carrier level — the recipient never even sees a reply, and Twilio refuses future outbound to that number from your account. This is the carrier-mandated baseline; we run it on top of layers 1+2 because it covers the case where Twilio caught the STOP before our webhook fired.

Layer 4: Manual CRM DNC flag.

In the CRM, you can flag any lead "DNC" with a click. The flag is binding: that lead never gets another message from your account, AI or otherwise. Manual flagging adds the lead to layer 5 (your per-user DNC table) and layer 6 (the global table).

Layer 5: Per-user DNC table for audit.

Every opt-out (regardless of which layer caught it) gets a row in your account's dnc_entries table: phone, when, which layer triggered, the original message text. This is your audit trail. If a recipient ever sues you under TCPA, you can prove the opt-out was honored at the millisecond.

Layer 6: Global DNC table (the platform-wide one).

Once a number opts out anywhere on InsertLead, it goes into a single platform-wide global_dnc table. Every outbound message from every tenant checks this table first. Read the next section for why we do this.

Global DNC across tenants (and why)

This is the one place we deliberately break the multi-tenant isolation rule that governs the rest of the system. Normally, your leads are yours; another InsertLead user can't see them or affect them. The global DNC is the documented exception.

Suppose Linda opts out of John's wholesaling campaign by texting "STOP." A week later, Sarah (a different InsertLead user) imports a list that happens to include Linda. Without a global DNC, Sarah's account would happily text Linda — even though Linda has already explicitly told the platform she doesn't want to be contacted.

The carriers don't see the difference between Sarah's number and John's number; they just see "InsertLead-shaped traffic that ignores opt-outs." That makes the carriers more aggressive on every InsertLead user. So the global DNC is partly a courtesy to recipients and partly self-defense for the platform.

Mechanics: any STOP keyword, opt-out phrase match, AI-detected opt-out, or manual CRM DNC by ANY tenant adds the phone to global_dnc. Every send across every tenant checks it first. Once globally listed, a phone stays listed — clearing a per-user DNC does NOT remove it from the global table. We never offer a "remove from global DNC" UI.

What counts as an opt-out

Concretely, any of these in an inbound message:

  • "STOP," "Stop," "stop," and case variants — layer 1 substring match.
  • "Unsubscribe," "Quit," "Cancel," "End" — layer 1 + Twilio-side.
  • "Remove me," "Take me off," "Do not text," "Don't contact me" — layer 1 substring match.
  • "I'm not interested," "Not interested," "Leave me alone," "Wrong number" — layer 1 + Claude semantic.
  • Anything where Claude says "yes, this is asking to stop" even if the substring filter missed.
  • Any manual CRM DNC click by you.

Borderline cases — "maybe later," "not right now" — are NOT opt-outs. The AI continues the conversation but tags the lead as soft-paused so it doesn't pester them.

Per-user DNC table for audit (provenance)

A nuance: the global DNC table holds just the phone number. The per-user DNC table holds who first reported the opt-out, when, what message triggered it, and which layer caught it. This provenance matters legally — if you're ever audited or sued, you can produce a row that says "Lead X opted out at 2:14 PM on March 5, 2026 with the message 'please stop texting me,' and our system stopped sending to that number at 2:14:01 PM."


Ready to try it? Request beta access