Most floors do not have a suppression list. They have four or five partial ones: a do-not-call flag in the dialer, a spreadsheet a manager keeps, a status in the CRM, whatever the last vendor returned, and somebody’s memory. Each of those is real. None of them is consulted when a new file arrives on a Tuesday, which is the only moment that counts.
This is a build specification for the version that works: one file, four sources, a reason code on every row, and a test that proves it ran.
Why it has to be one file
The argument for consolidation is not tidiness. It is that suppression fails at the seams. A household suppressed in the dialer is not suppressed in the SMS tool. A number suppressed under one campaign is not suppressed under the next. A person who asked a knocker to stop coming by is not suppressed anywhere at all, because the field app and the phone system have never spoken.
Every one of those failures is invisible until the moment it produces a complaint, and complaints are the currency that regulators and carriers actually act on. One file, read by everything that initiates contact, is the only structure where the seam does not exist.
The schema
A suppression list is a small table. The temptation is to make it a column of phone numbers, and that is the version that fails within a year.
| Field | Why it is there |
|---|---|
key_type | Phone, email, address, parcel, or person. You will suppress on all five and they behave differently |
key_value | Normalised at write time, not at read time. A number stored three ways matches nothing |
reason | The single most important column. Decides whether this row can ever expire and who is allowed to remove it |
source | Which rep, system, vendor feed or inbound call created it. This is what you produce when somebody asks how you knew |
created_at | Retention obligations attach to dates. So does any expiry logic |
expires_at | Null for anything permanent, which should be most of it. Present only where the reason permits |
note | Free text. Rarely queried, occasionally the only thing that explains a row six months later |
Two rules about this table that are worth stating as rules. It is append-only — corrections are new rows, not edits, so the history survives. And it is never joined destructively: suppression filters a load, it does not delete records from your database. You want to be able to answer “why was this household not called” a year from now.
The four sources
1. Do-not-contact requests
Someone told you to stop. This is the population with actual legal weight behind it, it belongs to your organisation rather than to any list you bought, and it never expires. The operational failure here is almost never the recording — it is the latency. A request logged on Thursday and batched into suppression the following Monday leaves a four-day window in which a second campaign can call the same person. Same day, every day, no exceptions.
This obligation is independent of federal registry scrubbing and is not satisfied by it. DNC compliance for outbound solar covers how the two interact and what the record-keeping expectations look like.
2. Litigator and complaint flags
Vendor-supplied, refreshed on the vendor’s cadence, and worth carrying as its own reason code so you can tell how much of your suppression depends on a subscription you might cancel. What is a litigator list covers what these products are and what a hit actually means. Write the flag into your file rather than relying on the vendor to re-flag the same household next time — you paid for the answer, keep it.
3. Prior contact
The commercial half of the list, and the one that saves the most money. Households you dialled last quarter and got nowhere with, addresses a rep already knocked, records you bought twice from two vendors. None of this is a compliance matter; all of it is waste.
Prior-contact entries are the ones that genuinely should expire, because a household that was not interested eighteen months ago is a reasonable conversation today. Set the window deliberately, write it down, and make it a property of the reason code rather than a thing someone remembers.
4. Customers
Current and former. Suppressed by default, so that a second rep does not pitch a system to somebody who bought one from you last spring — which is embarrassing in a way that costs referrals. Held as a suppression entry with an override, not as an exclusion, because you will deliberately campaign to this population for service, maintenance and upgrades and you want that to be an explicit act.
Choosing the key is the whole design
Everything above is easy. This part is where suppression lists quietly stop working, because the identifier you suppress on determines what actually gets caught.
| Key | Catches | Misses |
|---|---|---|
| Phone number | The exact number that was dialled | The same person on their other number, which a fresh append will happily supply |
| Email address | The exact mailbox | Everyone who has more than one, which is most people |
| Normalised address | The household, across contact points | The person after they move — and catches the new occupant, who never asked for anything |
| Parcel identifier | The property, immune to how the address happens to be spelled | Same limitation as address, more reliably matched |
| Resolved person | The individual across numbers, emails and moves | Nothing, in principle — but it depends on identity resolution being right, which is its own problem |
The practical answer is to suppress on more than one key at once. A do-not-call request should write both the number and the household. A prior-contact entry should be keyed on the property, because that is what you will buy again. And you should understand that address-keyed suppression suppresses the address, not the person, which is occasionally wrong in both directions — an argument for carrying the parcel rather than the address string, as the property data glossary explains.
Where it runs in the pipeline
Before enrichment, always. The order is standardise, deduplicate, suppress, enrich, scrub, load — and teams that enrich first pay to append phone numbers to thousands of records they were never allowed to call. List hygiene for call centers covers the full sequence and the cadence around it. Suppression also has to run at the import step, because a file that enters the CRM outside the pipeline has bypassed everything — data hygiene for CRM imports covers closing that door.
The test
Write down five records you know are suppressed, for five different reasons. Put them in a test file. Run the file through the real pipeline — the one your loads actually use, not a manual approximation — and confirm that none of the five reaches the dialer.
Run it monthly, and after every change to a dialer, an importer, a vendor feed or a field app. It takes twenty minutes and it is the only evidence you will ever have that the file in the middle of your compliance stack is connected to anything.
Disclosure: Scout Data is our product and this is a page about a discipline that mostly happens on your side of the line. We sell homeowner data, and the parts of this we can help with are narrow: List Builder deduplicates against what you have already been delivered and applies federal do-not-call scrubbing before a list reaches you. The internal do-not-call list, the reason codes and the test above stay yours regardless of who supplies the records.
Frequently asked questions
What should go on a suppression list?
Four populations, in one file: anyone who has asked you to stop contacting them, anyone flagged by a litigator or complaint product, anyone you have already contacted and decided not to contact again, and your own current and former customers. They have different reasons and different expiry behaviour, which is why every row needs a reason code rather than just an identifier.
Should the suppression list live in the dialer or the CRM?
Neither, if you can help it. It should live somewhere both of them read from and neither of them owns. A suppression file stored inside a platform is a file you lose when you change platforms, and the entries you lose are the ones with the longest memories — the do-not-call requests from three years ago that no purchased list will ever warn you about.
Can a record ever come off a suppression list?
Do-not-call requests, never. Complaint and litigator entries, not in practice. Prior-contact and customer entries can expire, because they exist for commercial reasons rather than because somebody asked to be left alone. That is precisely why the reason code matters: without it you either expire things that must never expire, or you freeze everything forever and slowly suppress your entire market.
How do you test that a suppression list is working?
With a negative control. Take a handful of records you know are suppressed, put them into a test load, and confirm they do not survive the pipeline. Every floor believes its suppression works; the ones that have checked are a much smaller group. Run it after every change to a dialer, an importer or a vendor feed.