Blog / Method / No.001

Why we are building a filter, not an outbound sender

Anyone can send fifty thousand messages this afternoon for the price of a lunch. Capacity stopped being scarce years ago. Judgement did not.

Mohamad · KnockwireApril 28, 20267 min readMethod

You can buy an outbound stack this afternoon, load fifty thousand contacts into it before dinner, and have every one of those messages sent by tomorrow morning for roughly the cost of a decent lunch. Nothing about that is difficult, expensive, or clever any more. It has not been for about a decade. So when a vendor tells you their product sends at scale, they are describing the commodity part of the job, and charging you for the part that got cheap.

01When sending got cheap, sending stopped being the product

The marginal cost of one more outbound message rounds to zero. That is a real engineering achievement and it belongs to a previous decade. The consequence is the interesting part: once a capability costs nothing, having it stops distinguishing you and starts implicating you. Everyone in your category has the same capability, pointed at the same finite set of people, and the people have noticed.

So the binding constraint moved. It used to sit on capacity: how many messages can you physically get out of the door. It now sits on two things that no amount of infrastructure buys you. The first is permission, in the broad sense: spam filters, provider reputation systems, contact form abuse controls, and twenty-seven national implementations of European marketing law, all of which are getting stricter rather than looser. The second is attention, which is fixed in supply and already spent.

When a constraint moves, the valuable operation moves with it. The valuable question is no longer "who can I reach". It is "who should I not". That is a filtering problem, and filtering is genuinely hard in a way that sending has not been for years. It requires knowing enough about a company to say no with a reason, which means researching candidates you are about to throw away. Most of the work is spent on companies that never receive anything.

We are reasonably well placed to notice this. Knockwire is built by the team behind Crawlbase, which has sold web scraping and proxy infrastructure since 2017 to more than 46,000 paying customers. We know exactly what industrial-scale collection looks like, because it is our other business. That is not a boast. It is the reason we are unimpressed by volume as a selling point.

02Every outbound tool reports what it sent, none reports what it declined

Open any outbound dashboard and look at what it is willing to tell you. Messages sent. Delivery rate. Opens, clicks, replies, meetings booked. Every one of those numbers describes something that happened after a decision to send. The denominator is the list you uploaded. Nothing on the screen describes the decision itself.

This is not an oversight, it is a structural property of how these tools are built. A candidate that gets rejected leaves no row. It has no send event, no open, no reply, no thread. It exits the system as an absence, and absences do not appear in reports. So even where a filter exists, and in most tools something does exist, you cannot see it work. You cannot tell whether it is strict, loose, or effectively switched off.

Which produces a quietly absurd situation. The component that now determines whether the product is any good is the one component nobody instruments. You are asked to trust a filter on the evidence of its output alone, which is like judging a proofreader by reading only the sentences they left in.

There is also an incentive problem worth naming, because it explains why this has not been fixed by accident. Most outbound tooling is priced per seat or per send. Under either model, a refusal is revenue that did not happen. A vendor whose margin improves every time the filter says yes is not the vendor you want tuning the threshold.

If your tool cannot show you what it declined to send, you are not running a filter. You are running a list.

03What the refusal ledger actually is

The refusal ledger is the fix, and it is deliberately unglamorous. Every candidate a source returns is a Read. Every Read that fails a gate is Turned down, and the reason is written at the moment of the decision, in words, by the gate that made it. Those rows are kept and they are published. Not summarised into a pass rate: kept, individually, with the reason attached.

That single change makes the filter measurable. You can read down the refusals and form your own opinion about whether the threshold is where you want it. If we are throwing away companies you would have wanted, the evidence is right there and it is embarrassing for us rather than invisible. If we are letting through companies you would have refused, the ledger shows the gates were too loose. Either way you are auditing a mechanism rather than trusting a claim.

The numbers this produces are not flattering, which is rather the point. Our GitHub organisation source lands roughly one usable candidate in twenty, and about a fifth of what it does find turns out to be a company that no longer trades. The Y Combinator directory carries 6,108 companies of which 1,861 are already dead. A tool that reported only its sends would show none of that. It would show a tidy list of qualified prospects and leave you to guess what it cost to produce them.

A Read becomes Qualified only after clearing every gate, which is when we look for a Door, meaning a usable public contact route. Then a human sees a Draft. A Knock is a message that was actually delivered, and it is the last thing to happen, not the first. The vocabulary is boring on purpose. Most of the industry uses one word, "sent", for six different states, and that ambiguity is where the exaggeration lives.

04Why we deliberately cannot send at volume

A filter that can be switched off is not a filter, so several of our constraints are structural rather than configurable. We will not build a bulk send path, and if a customer asks for one, the answer is no. Not "no for now" while we quietly work on it. The whole argument of this post collapses if the product also happens to have a firehose behind a settings toggle.

  • Nothing is delivered without a named human approving that specific Draft. There is no auto-approve, no approve-all, and no threshold above which review is skipped.
  • We do not solve CAPTCHAs or defeat bot detection. A challenge parks the job for a human to look at. That is the designed behaviour, not a failure mode we intend to engineer around later.
  • Outreach goes out under a real named person at a real address. No personas, no rotating identities, no disposable inboxes.
  • Per-domain rate limits and the tenant suppression list are enforced before anything is delivered, not reported afterwards.
  • Email sending is not built. It is scheduled for Q4 2026 and we mark it as unbuilt everywhere it comes up, including here.

The last one costs us something in a sales conversation and we keep it anyway, because the alternative is to describe a roadmap in the present tense. Today a Knock is delivered through the target's own public contact form, which is a route they chose to publish. That is a narrower channel than an inbox, and narrow is the feature.

None of this is a proof. It is a bet, and it is easy to state so you can hold us to it: a tool that refuses well will be worth more than a tool that sends fast, because the scarce input has moved from capacity to permission and attention, and refusal is the only operation that earns either. If the bet is wrong, the refusal ledger is where you will see it first. We publish it partly so that you find out at the same time we do.

Knockwire reads the internet, throws out the companies that will never buy from you, and knocks on the doors of the ones that will. Run it on your own domain and read your own refusals.

Run it on my siteAll posts