Half our email clicks were security scanners. How to tell a bot click from a person.
Corporate security gateways open every link in a message within a second of delivery, with a browser's user agent, and a click tracker records each one as a reader. Of 1,766 clicked messages, 884 were scanned rather than read. The signature that gives them away, why you cannot judge it from the first hit, and what an agent acting on a click should see.
A security scanner click is a corporate mail gateway fetching the links in a message to check them before a person sees it. Your click tracker cannot tell the gateway from the recipient, so it counts the scan as a click. On our platform, in the 60 days to 17 Sep 2026, that was half of everything reported as a click: of the 1,766 messages that recorded one, 884 carried the signature of a scanner rather than a reader.
This post is what the signature is, why it cannot be seen from the first hit, and why that changes when a click is allowed to count at all.
Where the clicks came from
Gateways at many companies open every link and every image in an incoming message, to scan it before delivery to the inbox. The obvious defence is the user agent: drop anything that calls itself a bot, a crawler, a previewer, or sends no user agent at all. We already did that, and we still do. It is not enough, because many gateways fetch with an ordinary browser's user agent. From the tracking endpoint's side, that request looks exactly like a reader.
Two places showed it plainly.
A transactional message with two links. 625 of the scanned messages were one tenant's transactional emails, each carrying two links. Real people click one of them, if any. These had both clicked, within two seconds of each other.
A newsletter. In the first newsletter from one of Common Ninja's five products, 126 of the 154 messages that recorded a click were scanned, and the scanners walked 8 to 10 links each. The warm-up post mentioned this in passing: a click-to-open ratio no human audience produces. On the same day, 118 of the 127 link walkers had also fetched the open pixel, because a gateway loads the images while it walks the links.
Almost all of the bursts arrived within ten minutes of delivery. The clicks that did not match came one link at a time, spread over hours, which is what reading looks like.
The signature is a burst
A person cannot open two different links from one email inside two seconds. A scanner does it every time. So the rule is short:
- Two clicks on different links of the same message, within 2 seconds of each other, are both a scanner's.
- An open within 10 seconds of such a burst is the scanner's too.
Two details keep it honest.
The same link twice is not a burst. Microsoft Safe Links fetches the link a person just clicked, right before their browser does. That is one person's click arriving twice, and treating it as a scan would discard real clicks from every inbox behind Safe Links.
An open alone cannot be judged. A gateway that fetches images but never follows links looks exactly like a reader opening the message, and it is counted as one. We would rather say that than claim open rates are clean.
A burst cannot be judged from its first hit
This is the part that shapes the design. When the first click of a burst arrives, the second has not happened yet. At that moment it is indistinguishable from a person's click, and anything done in response to it is done on a guess.
Until 17 Sep, the tracking routes did everything the moment a hit arrived: stamped the message's clicked_at, updated the contact, bumped the daily counter, and queued the click webhook. Which is to say they did all of it before the evidence existed.
Now the click and open routes send their redirect or pixel first, because a person may be waiting on it, and then record the hit as pending, with its user agent. A worker, the engagement settler, picks each one up once its window has passed: about 5 seconds for a click (the 2 second burst window plus slack for the other request still being in flight) and about 15 for an open (the burst window, the 10 second margin, and the same slack). Only then is it classified.
Count after deciding, never reverse
The alternative is to count immediately and subtract later when the burst shows up. It is simpler to write, and it is wrong, because some of what a click causes cannot be taken back.
A webhook cannot be unsent. Once a click event has gone to your endpoint, your code has already acted on it. A follow-up "sorry, that click was a firewall" event only helps if every consumer handles it, and most never will.
So a person's click is counted when the settler decides it is a person's: it stamps the message and the contact, increments the counter, and fires the webhook. A scanner's hit is kept, marked as automated, and does none of those things. Every read of engagement filters on that decision: opened_at and clicked_at on the email, its events, the contact's last_opened_at and last_clicked_at, campaign figures, the clicked-links report, A/B results and metrics. A person who clicks after the scanner has been through, a minute later, counts normally.
The cost is the settle delay. A click webhook arrives about 5 seconds after the click, and an open about 15. For anything a person does with an email, that is nothing, and it buys a webhook that never has to be retracted.
Why this matters more when an agent reads the clicks
A marketer looking at a campaign report with inflated clicks draws a slightly wrong conclusion. An agent acting on a click webhook does something.
Consider the obvious agent workflow: when someone clicks the pricing link, send a personal follow-up. With scanner clicks counted, that agent writes to every recipient behind a corporate gateway, a few seconds after delivery, about a page they never looked at. The trigger was a firewall.
The same inflated number flows into everything built on engagement. A segment of people who clicked in the last 90 days fills up with addresses nobody read from, and mailing an unengaged audience is how a sending domain ends up in the spam folder. An A/B test is decided by the scanners. None of those mistakes is visible from inside the report, which is what makes them expensive.
Engagement data an agent acts on has to be the reader's, not the network's.
What you see as a caller
clickandopenwebhooks fire a few seconds after the event (about 5 and 15 seconds), and never for a scanner's hit.clicked_at,opened_at, the email'sevents, contact engagement fields, campaign figures and A/B results count people only.- The first open of a message is still the only one reported.
- Events recorded before 17 Sep 2026 were not classified.
- Tracking needs the sending domain's
link.hostname to be live, as before.
The rules and the full list are in the webhooks guide under automated clicks and opens, and the campaigns guide covers what opens and clicks can and cannot tell you.
Half of the clicks were never people. The fix was not a better list of user agents. It was waiting two seconds to find out, and deciding before counting.
