File DA-021 View as Markdown
Verdict
The verdict is what await doubleagent.ready() gives you: who is on the page (human, bot or AI agent), how sure Double Agent is, and what it suggests you do. This page explains each field, the da_* properties your tools receive, and which field to act on.
Nothing here is enforced. Double Agent labels the visit; your code decides what to do.
A verdict
{
"sessionId": "k2f9x0a1b2c3d4e5f6g7",
"class": "agent",
"probability": { "human": 0.04, "bot": 0.03, "agent": 0.93 },
"confidence": 0.81,
"evidence": "sufficient",
"agent": { "family": "openai", "id": "openai.chatgpt-agent", "operator": "openai", "controller": "playwright.automation", "verified": false, "method": "marker" },
"reasons": [{ "code": "rhythm.think_then_act", "weight": 1.1 }],
"scores": { "automation": 0.96, "environment": 0.42, "driving": 0.88 },
"recommendation": "tag",
"behavior": { "version": 1, "label": "neutral", "risk": 0, "source": "browser", "scope": "observation", "reasons": [] },
"authorization": { "decision": "unknown" },
"profile": "ecommerce",
"action": "checkout",
"stage": "provisional",
"model": "doubleagent-2026.10.1",
"ts": 1791000000000
}
| Field | What it means |
|---|---|
class | human, bot or agent. The most likely of the three |
probability | The chance of each class. They add up to 1 |
confidence | 0 to 1: how much evidence the verdict rests on. A low-confidence human means "no sign of automation yet", not "proven human" |
evidence | insufficient when there was a hint of automation but not enough to call it. Read it as "unverified" |
agent | Which agent, when one is found: id (catalog id), operator (who runs it), controller (the automation tool driving the browser), family (a coarse label), verified and method (what proved it) |
reasons | The evidence, strongest first. Prefixes below |
scores | Sub-scores from 0 to 1. A clean environment with machine-driven input usually means an agent; a broken environment usually means a bot |
recommendation | allow, tag, challenge, step_up or deny, for this site profile and action. rate_limit is reserved: no profile returns it today |
behavior, authorization, integrity | How the visitor behaved, whether your policy allows it, and whether the report can be trusted. See Conduct |
profile, action | The site profile and the action the verdict was made for, such as checkout |
stage | provisional in the browser, final once the server has checked it |
model | The detection model version |
judge | The model that gave a second opinion, when the Handler ran. Absent today |
token | The signed token, after check() or getToken(). Verify it on your server with @doubleagent-so/node |
Families: claude, openai, perplexity, google, browser_use, browserbase, skyvern, manus, amazon, unknown.
agent.verified is true only when the server checked a Web Bot Auth signature from a known operator, or a published IP list. A user agent or a DOM marker in the browser is a claim, never proof. See Verified agents.
Which field to act on
- To filter reports, use
class. - To decide on a risky action (signup, login, checkout), use
recommendation. It already weighsprobability
against your site profile.
- For anything that matters, call
check()orgetToken()and verify the token on your server. A browser verdict can
be faked by a visitor; a signed token cannot.
Reason prefixes
| Prefix | Evidence |
|---|---|
auto. global. marker. | Automation and agent-browser traces in the page |
env. | The browser environment does not add up |
drive. | How input is driven (synthetic events, remote control) |
rhythm. | Pauses that look like a language model thinking |
bio. human. | Pointer and typing patterns |
edge. ua. net. | Request headers and network, checked on the server |
verified. | Identity proven on the server. See Verified agents |
evidence. | Not enough evidence to call it |
judge. | The Handler, when it runs |
The engine's full list of codes is in Agent Detector.
Conduct
Class says what the visitor is. Conduct says how it behaved and whether your rules allow it. They are separate: a verified agent can still break your rules, and a bot can be one you allow.
| Field | Values | What sets it |
|---|---|---|
behavior.label | friendly, neutral, rogue | friendly: a verified agent your policy allows for this action, with nothing suspicious. rogue: a rule it broke, such as a denied action, too many failed logins or a trap it touched. neutral: everything else |
behavior.risk | 0 to 100 | How serious the worst finding is. A rule severity, not a probability |
behavior.reasons | Codes such as policy.action_denied, abuse.repeated_auth_failure, identity.invalid_signature | What was found |
authorization.decision | allowed, denied, unknown | Your permission rules for this agent and action. allowed needs a verified identity |
integrity | trust 0 to 1, quarantined, reasons | Whether the browser's report looks genuine. Server verdicts only |
In the browser, behavior is always neutral and authorization is unknown. The server fills them in, using what your backend reports through evaluate() and the rules you set in Sites › your site › Settings › Agent behaviour policy. In Monitor mode (the default) conduct is only a label. In Enforce mode a rogue or denied result changes recommendation to deny, and a friendly one changes it to allow. See The portal.
Properties and events
Every tool Double Agent sends to (GA4, GTM, Meta, Mixpanel and the rest) gets the same nine fields:
| Property | Values |
|---|---|
da_class | human, bot, agent |
da_agent | Agent family, or none |
da_score | 0 to 100: the chance it is not human |
da_bucket | 0-20, 20-50, 50-80, 80-100 |
da_rec | The recommendation |
da_verified | true only when the server verified the agent |
da_behavior | friendly, neutral, rogue |
da_behavior_risk | 0 to 100 |
da_authorization | allowed, denied, unknown |
Events: da_classified once per session, when the first verdict lands. da_final once, when the Handler sends a final verdict. The Handler is off today, so da_final and doubleagent.on('final', fn) don't fire yet. A server check through check(), getToken() or a protected form updates the verdict and fires doubleagent.on('verdict', fn).
See Browser API.
Default policy
- Every tool gets the
da_*fields, as long as the visitor consented to that tool's category. - Ad conversions (Meta, TikTok, Google Ads) are held back when the class is
botwith a bot probability of 0.8 or
more. Change it with block.adConversions: bot (default), bot+agent or none. See Meta Pixel.
- Agents are tagged, never blocked.
Next: Browser API for check() and tokens, or Site profiles to see how the recommendation is picked.