Start from the uncomfortable part. Every fraud signal the industry has built is a proxy for "is a human here", and a capable AI agent genuinely has the things those proxies measure. It drives a real browser, so the fingerprint is real. It arrives on a residential connection, so the address is clean. It controls a real mailbox, so it receives the verification code. It is not defeating your checks; it is satisfying them. So detection is an arms race you lose slowly, and the useful question is a different one.
"Is this automated" has become a bad question, because a growing share of legitimate traffic is somebody's assistant doing what they explicitly asked it to do. Refusing that refuses a customer who is standing right there. The question that still has an answer is: on whose authority is this acting, and can they be held to it?
Web Bot Auth is the emerging standard for this — RFC 9421 HTTP Message Signatures, with an IETF working group chartered in early 2026 and Cloudflare, Google, Amazon and OpenAI behind it. The agent signs its request with a key its operator publishes at a well-known URL, and you check the signature. This is the one check in fraud detection with no false-positive rate, because it is arithmetic rather than judgement. Increasingly agents do sign, because operators want their traffic accepted.
curl -X POST "https://www.layercall.com/v1/agent/authorize" \ -H "X-Api-Key: YOUR_KEY" -H "Content-Type: application/json" \ -d '{ "method": "POST", "url": "https://yoursite.com/signup", "headers": { "signature": "…", "signature-input": "…", "signature-agent": "…" } }'
An operator's Signature Agent Card declares a trigger with exactly two values: fetcher, meaning a person initiated this request, and crawler, meaning nobody did. That distinction is invisible to every classical signal — both are "a bot", and both may be perfectly signed — and it is the whole game. A customer's assistant filling in a signup because they asked it to is a customer. An autonomous crawler POSTing to the same endpoint on nobody's behalf is not, and never was.
const r = await score({ ip, email, device_id, agent: { method, url, headers } }); // proven is true for exactly one thing: a signature that verified. if (r.actor.proven && r.actor.type === "verified_agent") { if (r.actor.trigger === "fetcher") return allow(); // a person asked for this if (req.method !== "GET") return reject(); // nobody asked for this }
A verified signature is proof. Headless detection and automation markers are inference, and inference a capable adversary patches in an afternoon. Keep those apart in your own code, because the moment they are the same field you will start treating a guess like a certainty — and acting automatically on a guess is how you refuse real customers. Branch on a single boolean that means "this was settled cryptographically", not on a vocabulary.
Signing does not make an agent harmless. Someone can tell their assistant to open fifty trial accounts and it will honestly declare itself user-initiated every single time, because it is telling the truth. The card cannot see that and does not claim to. What gives it away is the shape: one device across twelve addresses, fifty signups from one /24 inside an hour. Score the pattern, not just the request.
Every check here has a cost in real customers. These are the ones that actually bite — several because we shipped them ourselves.
Paste any IP, email, phone or domain into the playground — no signup. Or get a free API key for 1,000 lookups a month, no card.