Skip to content

Viewing Detections

The Detections page is your central hub for monitoring all threat events captured by WebDecoy. Every decoy trigger, detection script alert, and endpoint interaction is recorded here.

WebDecoy Detections page showing source, category, agent, IP address, actor, and threat score for each event

The current Detections screen. Source and identity filters stay close to the evidence they narrow.

  1. Click Detections in the sidebar
  2. View the detections table
  3. Use filters and search to find specific events
Column Description
Detected At When the detection occurred
Source What captured it (Decoy Link, Endpoint, Detection Script, SDK, Edge Worker, and so on)
Category The threat classification: attacker, bot, scanner, crawler, scraper or legitimate
Agent The client’s declared identity, where it has one
IP Address Visitor’s IP
Actor The adversary this detection correlated to, by handle
Threat Score The unified 0-100 score
Response What was done about it (logged, blocked, rate limited)
Actions Per-row menu: view details, download JSON, download CSV

The Agent, IP Address and Actor cells each carry a filter control that narrows the list to that value, and the Actor handle itself links straight to that actor’s page.

Source Description
Decoy Link Hidden honeypot link accessed
Endpoint API honeypot triggered
Detection Script Browser-side script detection
Tripwire SDK tripwire path hit
SDK Server-side SDK detection
WordPress WordPress plugin detection
Edge Worker Cloudflare edge sensor
Agent Honeypot agent (SSH, RDP, SMB)
Unknown A source the dashboard has not been taught yet, shown as itself rather than folded into something real

Click any row to open its detail flyout. Rows also respond to Enter and Space for keyboard navigation. The cell-level filter controls (Agent, IP, Actor and the row menu) do not open the row, so clicking an IP still filters by it.

Triage is usually sequential: you find a burst of a dozen hits from one address and read them in order. The flyout carries previous and next controls in its header, along with your position in the list (“6 of 20”).

Two things about that sequence are worth knowing:

  • It is the filtered, sorted set currently on screen, not an unfiltered query. Next-from-row-4 is row 5 of what you are looking at, and it follows your filters, your sort and your burst grouping.
  • It stops at the ends of the loaded page rather than paging further, so the flyout never moves the ground underneath the list. The header states the boundary rather than merely hitting it, and the controls are hidden entirely when there is no sequence, which is the case for a flyout opened from a direct link.

Use filters to narrow down detections to specific criteria.

The filter sidebar holds one expansion panel per filter. Each panel that has something selected shows a count on its header, so an applied filter is never invisible.

Panel What it does
Source One or more detection sources. The list is built from the sources this property has actually seen, so it reflects your install rather than a fixed menu
Action Taken Logged, Blocked in Cloudflare, Rate Limited
HTTP Method GET, POST, PUT, DELETE, PATCH, for endpoint detections
Threat Category Attacker, Bot, Scanner, Crawler, Scraper, Legitimate
Threat Score Minimum and maximum on the unified 0-100 score
Country Multi-select, based on GeoIP, with a count beside each country
Risk Indicators Proxy, VPN, Tor
Date Range Presets for the last 24 hours, 7 days and 30 days, or explicit start and end dates
Sort Sort field and direction (see below)

Selections take effect as you make them, and an Apply Filters button at the foot of the sidebar re-runs the query on demand.

On a phone the sidebar stacks above the table, so it collapses behind a Filters control there, with a count of everything currently applied.

Filtering by Agent, IP address or Actor from a table cell adds a removable chip above the table. Those three live in the URL, so the view is shareable and survives a reload, and dismissing the chip clears the parameter rather than leaving it to reappear on the next load. The Actor chip is accompanied by a link straight to that actor’s page.

Filter settings are not saved between sessions. Clear your filters before navigating away if you want a fresh view.


Group by IP burst collapses consecutive hits from one address into a single expandable row, over a 1, 5 or 15 minute window. The toolbar reports how many groups the current page collapsed into and how many detections they hold in total.

A group header carries the address, the hit count, the time span, the highest unified score in the group, and the sources involved. Expanding it lists the individual detections, and clicking any of them opens the flyout exactly as the flat table does.


There is no free-text search box. Narrowing happens through the filter sidebar, through the per-cell filter controls in the table, and through URL parameters.

The list reads these on load, which is how every drill-down elsewhere in the product lands on the right rows rather than on a filter you have to rebuild:

Parameter Effect
ip Every detection from one address
actor_id Every detection attributed to one actor
ai_scraper_name Every detection from one named agent
agent_impersonation=true Only failed identity claims
agent_verification_method Claims proved a particular way
threat_level MINIMAL, LOW, MEDIUM, HIGH or CRITICAL, translated into a score range
threat_category One of attacker, bot, scanner, crawler, scraper, legitimate
source One detection source
country One country code

ip and actor_id render as removable chips, so what the URL did to your view is visible on screen rather than only in the address bar.


Sort By Description
Detection Time When it happened (default)
Threat Score The unified score
Bot Score (Legacy) The older bot score, on rows that predate unified scoring
  • Descending (default): Highest/newest first
  • Ascending: Lowest/oldest first
  • Default: 20 detections per page
  • Use page navigation at bottom
  • Total count shown above table

The flyout reads top to bottom in the order a decision gets made: what happened, what you can do about it, who did it, and why we scored it the way we did.

┌─────────────────────────────────┐
│ Detection Details ◀ ▶ ✕ │
├─────────────────────────────────┤
│ What happened (the verdict) │
│ What you can do about it │
│ Analysis and other indicators │
│ The adversary behind it │
│ Threat Score Breakdown │
│ Request Details │
│ Network │
│ TLS Fingerprint │
│ Rule Enforcement │
│ ▸ Raw data │
└─────────────────────────────────┘

The flyout opens on a verdict: one claim in plain language, the evidence for it underneath, and the disposition, time and origin on the line below.

The score is deliberately not first. A threat score of 87 is the least self-explanatory thing in the product and means nothing at all to somebody running a WordPress site. Meanwhile the claim that needs no interpretation was missing entirely: this panel would show you GET /.env in a neutral field labelled “Path/Page URL” without ever saying what that meant.

The headline runs from judgements that need no theory about identity to the ones that need the most, and stops at the first match:

  1. Went looking for credentials. The request asked for a file whose only content is secrets.
  2. Attempted [attack]. SQL injection, command injection, cross-site scripting, path traversal and the rest, named in words rather than in snake case.
  3. A honeypot protocol was tried. An SSH login, an RDP or SMB connection, against a service that exists only to be tried.
  4. Took the bait. Followed a decoy link or tripwire path that is invisible to anyone actually reading the page.
  5. Impersonated [agent]. Claimed a published crawler, from an address that does not belong to it.
  6. [Crawler] crawled this site. A named AI crawler or agent.
  7. The threat category as a sentence. Scanned this site, scraped this site, behaved like an attacker.

When nothing matches, the flyout reports what was observed (“Detected by Edge Worker”) rather than inventing a characterisation. An unclassified detection is a real thing that happened; it does not become an attack because the panel wants a headline.

The disposition line states once what was done about the request. It used to appear three times in the panel, in three different vocabularies, which is how a merely-logged detection ended up being reported as blocked.


The action bar sits directly under the verdict, above the fold. This is the screen where you decide “this one is real, block it”, so the controls that complete that thought are next to the claim that prompted it rather than eighteen sections further down.

WebDecoy detects, and arms your WAF. The values you paste into Cloudflare or AWS WAF are offered as one-click copies, in the order they are useful for enforcement:

Value What it enforces
IP address One address
JA4 The TLS fingerprint, which survives IP rotation
ASN A whole network range
Clearance fingerprint The rotation-proof client identity
User agent Trivially forged, so mostly useful for a ticket

Only values actually present are offered. An empty copy button is worse than no button: it tells you that you have the value, and you find out otherwise in your WAF console.

Puts the actor’s clearance fingerprint on the session clearance deny-list, so it cannot mint a clearance token on any IP. Binding to the clearance identity rather than the address is the whole point: it survives the rotation that makes an address block worthless. This is the same control the actor detail page carries, available on the screen that made you want it.

It is withheld for two different reasons, and the panel says which:

  • Shared fingerprint. This actor’s fingerprint is shared by unrelated clients, so a deny would lock out whoever else arrives under it. The Actors page refuses for this reason, and the flyout is not a shortcut around that refusal.
  • No clearance identity. This adversary has never been seen carrying a clearance token, so deny-at-mint has nothing to bind to. Not a refusal: it simply does not apply, and the WAF values above are the route instead.

An operator who came to block something and finds no way to is owed the reason, so both are stated rather than the button silently vanishing.

Records that you disagree with the verdict. See Telling us we got it wrong.

Opens the Response Action editor. Denying one adversary is a one-off; a response action is the standing rule that handles the next one without anybody watching. With zero response actions configured, nothing is ever pushed to a WAF.

The overflow menu holds everything that is not a first-reach action: the remaining WAF values, a link to this detection, all detections from this IP address, and downloads as JSON or CSV. Getting an export used to mean closing the panel, finding the row again and opening its menu, having already decided on the panel that you wanted it.


The flyout carries the actor this detection correlated to, by the same handle the Actors page uses, with the numbers that turn a single detection into a reason to act: how many times this adversary has been seen, from how many addresses, across how many of your sites, and since when.

A count of one is said plainly (“First time we’ve seen this adversary”) rather than padded into the same sentence shape as 442, which would make both unreadable.

Two qualifications appear where they apply:

  • Credential probes across its history. If this adversary has asked for credential files at any point, that is stated here, whatever this particular request asked for.
  • A grouped fingerprint. If the actor’s fingerprint is shared by unrelated clients, the flyout says so, because the counts above then belong to several parties rather than one, and the fingerprint cannot be enforced against without blocking whoever else shares it.

When a detection has no actor, the panel says so and offers the IP address as the pivot instead. No actor means the correlation worker has not claimed the row yet, or the request carried no fingerprint strong enough to tie it to one. That is a limit of the evidence, not a statement that nobody is behind it.

Loading, failed and uncorrelated are three distinct states rather than one. A spinner and an “uncorrelated” notice are opposite claims, and a failed fetch is not a fact about the traffic.


The Threat Score Breakdown answers why this detection scored what it did, from the per-signal attribution the scorer recorded at the time.

Threat score: High 87 / 100
Honeypot path hit +40
Datacenter IP, no rDNS +22 of 25
Header order ≠ claimed UA +15 of 20

Each row is a signal that moved the score, with the points it added, its ceiling, a bar, and the description the scorer wrote for it. Three details are deliberate:

  • The parts do not sum to the whole, and the panel says so. Categories are weighted and capped, so printing 40 + 22 + 15 = 87 would be arithmetic that fails the first time anyone checks it.
  • The ceiling is printed only when the signal did not reach it. Most signals contribute a fixed amount and sit at their own ceiling by definition, so “+40 of 40” is noise on every line. “+10 of 30” says something: this one was dialled down and could have been worse.
  • Bars compare signals to each other, not to an absolute 100. Scaled the other way, the largest contributor drew a half-full bar while a signal worth half as much drew a full one, ranking the picture in the opposite order to the numbers printed beside it.

Signals that scored zero are dropped, because a dozen “+0” rows bury the four that produced the score. The radar chart alongside is where “we looked at this category and saw nothing” belongs.

Confidence is mentioned in prose only when it is low enough to qualify the score.

Other indicators below the breakdown holds the flags derived from detection metadata, which carry no points and therefore appear in no breakdown. Anything the scorer scored belongs to the breakdown and is not repeated there.

A How scoring works link opens the general explainer. See Threat Scoring for the full model.


If a detection is wrong, say so. Not a bot records your disagreement against that detection, and clicking the recorded verdict takes it back, so a misclick costs one click to undo.

This is deliberately inert. Nothing in scoring, enforcement, actor rollup or observation reads what it writes, and that is a decision rather than an omission. Making a label change behaviour immediately would mean one misclick could quietly blind a property, and blinding is invisible from our side, because it looks exactly like a quiet week. Whatever eventually consumes these labels will come with its own way to see and undo the effect.

One label is held per detection and replaced rather than accumulated: the question is what you believe now, not a history of your hesitation.


Field Description
IP Address Client IP
User Agent Browser/bot identifier
Requested The path or page URL the request asked for
Referer Where they came from
HTTP Method GET, POST, etc.
Decoy Which decoy or scanner caught this, where applicable

Requested is computed once, server-side. Every consumer used to derive it independently, and they disagreed on rows where only one of the underlying fields was set.


VPN/Proxy Detection, Geographic Information, IP Reputation and Reverse DNS used to be four separate sections describing the same address from different providers. Between them ASN rendered three times, location three times and the hostname three times, each from whichever provider answered, with nothing saying they were the same fact. They could also disagree, and when they did the panel showed both without comment.

They are now one Network section, with the precedence between providers decided in one place:

Field Source
IP address The detection itself
Hostname Reverse DNS, labelled forward-confirmed or not forward-confirmed
Network MaxMind ASN and organization
Location MaxMind, with timezone
ISP AbuseIPDB, shown only when it differs from the network operator
Usage AbuseIPDB usage type

Below the fields sit tags for the named bot, a datacenter address, and any anonymity findings.

Anonymity is a union, not a precedence. Providers answer slightly different questions about proxies, VPNs and Tor, and they disagree routinely. Over-reporting costs you a second look; missing one costs you the finding.

A hostname now says whether it was forward-confirmed. A PTR record that anyone can set is not evidence on its own; the forward confirmation is what turns a claim into a fact.

Where AbuseIPDB data is present it is summarised in a sentence rather than a table of raw numbers. A clean address reads as having no abuse reports against it, rather than showing “0%”, which looks like a missing value rather than a result.

Score Risk Level Meaning
0-10 Very Low Clean or minimal reports
11-25 Low Some suspicious activity
26-50 Medium Multiple reports
51-75 High Significant abuse history
76-100 Critical Known malicious IP

Common abuse categories include port scanning, brute force, web spam, SQL injection, hacking, fraud and DDoS participation.


Where a handshake was captured, the panel reports the TLS version, the number of ciphers offered, the number of extensions, and the JA3 and JA4 fingerprints.

The counts carry what normal looks like, because that is the whole reason they are interesting: a bare “2” means nothing, while “2, browsers offer 15+” is the finding. When both counts are unusually small the panel states the reading directly, since a handshake that small is what automation libraries produce.


For detections from endpoint decoys, additional information is shown.

Field Description
Request Method POST, PUT, DELETE, PATCH
Content-Type application/json, etc.
Body Size Request body size in bytes
Has Auth Header Authorization header present

If body capture is enabled:

{
"username": "admin",
"password": "test123",
"remember": true
}

Any attack signatures found in the request are listed as chips, coloured by severity: SQL injection, command injection, cross-site scripting, path traversal, XXE, insecure deserialization and the rest.

The same signatures drive the flyout’s headline, so a request carrying them is described as having attempted the attack by name rather than being handed to you as a category and a number.


Shows what automated actions were taken. Enforcement is configured through Response Actions, and detections roll up to a persistent actor whose blocking follows it across IP addresses.

Status Meaning
Pending Rules not yet evaluated
Rules Enforced Rules executed successfully
Enforcement Failed Rule execution failed

Where a rule blocked an address, the address is shown beside the status, along with the reason recorded for it.

Field Description
Enforcement Time When the action was taken
Error Error message if the rule failed

This section states what was done. The verdict at the top of the flyout states the same disposition once, in a sentence, and the two use one shared vocabulary, so a logged detection is never reported here as a blocked one.


Every detection carries a collapsed Raw data section holding the full metadata blob.

It used to render as a fallback, on any detection without AbuseIPDB or cryptographic verification data, which meant most SDK and decoy-link rows printed the entire blob raw underneath the formatted sections that had already rendered its contents. By the same condition it was unavailable on exactly the rows a support conversation would want it. It is now collapsed, and present on everything.


Three exports, at three scopes:

Where What it gives you
Export in the page header Everything matching your current filters, as CSV or JSON
A row’s Actions menu That one detection, as CSV or JSON
The flyout’s More menu The detection you are reading, as CSV or JSON

All of them run through one CSV writer, so a column order your spreadsheet formula depends on cannot drift between them, and headers are paired with their values rather than kept as two lists aligned by hand.

Four columns are worth calling out:

  • requested_path and secret_probe, so the judgement the product exists to make travels with the export rather than leaving you to re-derive it.
  • actor_id, so an exported row joins back to its adversary.
  • ja4, the WAF enforcement key, and the value most likely to travel from a file into a firewall rule.

There is no multi-row selection. To act on a set of detections, filter to them and use the page-level Export, or pivot to the actor and enforce there, which is the unit that survives IP rotation.


A detection returns both of the identities that enforcement can key on:

  • ja4, the TLS fingerprint. This is the column enforcement actually keys on, not the copy that happened to be stored under request metadata. That other value came from a different capture path and worked by coincidence of what enrichment stored; it is still read as a fallback for rows scored before the field was returned.
  • client_fp, the clearance identity this particular request arrived carrying.

The distinction between a detection’s clearance identity and its actor’s matters when they disagree. The actor’s rolled-up value is whatever is minting tokens now; the detection’s is the one that arrived with the request in front of you. They agree most of the time, and when they do not, the detection is the honest answer to what is on screen, so copy actions prefer it.

Neither value grants anything on its own, which is why both are safe to display.