Skip to main content
AC
Sales & RevOps5.7 KBMIT licensed

do-not-contact

Original, written for TechTide client work

Suppression gate consulted before any outbound touch: merges disqualified leads, manual exclusions, bounces, and imported exclusion lists into one do-not-contact list with a live check. Use before promoting discovered leads, drafting outreach, sending email or LinkedIn requests, or resurfacing old leads. A hit drops the candidate; a missing or stale list halts the run. Not for batch send approval itself: use a send-gate skill. Not for post-acceptance deal work: use deal-to-invoice.

  • not
  • contact

SKILL.md

Do Not Contact

One suppression list, consulted by every outreach surface, so no agent or manual batch ever touches a person or company the owner has excluded. Partial coverage is the failure mode this closes: a list that guards only one pipeline stage guards nothing.

Files

All under $WORKDIR/pipeline/:

  • Unified list (build artifact, never hand-edit): exclusions/do-not-contact.jsonl
  • Build metadata (build time, source counts): exclusions/do-not-contact.meta.json

Sources merged on every rebuild:

SourceContributes
exclusions/disqualified-leads.jsonlCompany-scope and person-scope disqualifications
manual-email-suppression.csvBounced and manually suppressed emails
manual-lead-exclusions.csvFriends, peers, bad leads, wrong profiles
imports/buyer-exclusions-*.csvVendor, recruiter, and competitor exclusions
outreach-log.jsonlRecords with status do_not_contact, bounced, excluded_bad_lead

The Gate Contract

Maintain a rebuild script and a check script in the pipeline repo with this exact contract:

  • Rebuild: full idempotent rebuild of the unified list from all sources, writing the meta file with a timestamp.
  • Check: takes one candidate (email, LinkedIn URL, name, company) or a batch JSONL file. Prints a JSON verdict with checked_at. Exit codes: 0 clear, 1 suppressed hit, 2 hard stop (list missing, unreadable, or older than the configured max age).

Matching rules the check must implement: email exact-match after lowercasing. LinkedIn URL match after stripping scheme, www., query strings, and trailing slashes. Company exact-match on normalized name against company-scope entries. Name-plus-company as the fallback person match when no email or URL exists. Any single identifier hit suppresses the candidate. Near-miss company aliases are not caught: review new discovery batches for aliases by hand.

Operating Rules

  1. Every outbound loop runs the live check twice: immediately before drafting and again immediately before send approval. A cached result is not a live check.
  2. Log the checked_at timestamp into the loop's run record.
  3. A hit means drop the candidate and record the reason. Never soft-suppress, never queue for later, never "reach out anyway just this once".
  4. Exit code 2 halts the entire run: rebuild, re-check, log it as a gate incident.
  5. New exclusions go into a source file (people into manual-lead-exclusions.csv, companies into exclusions/disqualified-leads.jsonl), then rebuild. Never append to the unified file directly; the next rebuild would erase it.
  6. Rebuild at the start of any day that includes a send batch, so yesterday's bounces and opt-outs take effect today.

Verification

Run the check against a known-suppressed identifier from a source file. Expect exit code 1 and a verdict naming the source. Then run it against an obviously clear synthetic identifier. Expect exit code 0 with a fresh checked_at. If either result differs, the gate is broken: halt all outbound until the rebuild and check behave to contract.

Good vs Bad

Judgment call: a great-fit lead turns up in a new discovery batch, and the check returns a hit from an old bounce.

Bad: "The bounce was a year ago and they changed companies, I'll include them." The gate just became advisory, which means it no longer exists.

Good: Drop the candidate, record the hit reason, and surface it to the owner: "suppressed by 2024 bounce, flag if you want the source entry reviewed." Only a human edit to a source file plus a rebuild can clear someone.

Footguns

  • Hand-editing the unified list. The next rebuild silently reverts your edit. Fix: edit sources, rebuild, verify the entry appears.
  • Checking at discovery but not at send. Days pass between the two; yesterday's opt-out slips through. Fix: the check runs twice, draft-time and send-time, no exceptions.
  • Treating exit code 2 as a skip. A missing list reads like "no hits" to sloppy code. Fix: hard stop semantics enforced by the calling loop, logged as an incident.
  • Company aliases. "Acme Inc" on the list does not catch "Acme Corporation". Fix: manual alias review on every new discovery batch, add aliases to the source file.

Red Flags

Stop if you catch yourself thinking or saying:

  • "The check passed this morning, that still counts"
  • "It's just a follow-up, not a new touch"
  • "They're clearly a good lead, the entry must be stale"
  • "I'll add them back to the queue and check later"
  • "The list is missing, so nobody's suppressed"

Every touch gets a live check, and only the owner clears an entry. There is no candidate valuable enough to bypass this gate; if one seems to be, that is the strongest sign the gate is doing its job.

Completion Checklist

  • [ ] List rebuilt today if a send batch runs today
  • [ ] Live check run before drafting AND before send approval
  • [ ] checked_at logged in the run record
  • [ ] All hits dropped with reasons recorded, zero soft-suppressions
  • [ ] Any exit code 2 halted the run and was logged as an incident

Any box unchecked: not done. Fix or say so.

More in Sales & RevOps

All skills