Tellshield
Bot and AI-agent detection, without a CAPTCHA

Person or software?
Now you know why.

Tellshield scores every signup, login, or checkout from 0 to 1 and lists the exact reasons, each with its points. Visitors see nothing. Your server makes the decision.

independent detectors
6
independent detectors
token lifetime
120 s
token lifetime
puzzles shown
0
puzzles shown
risk_score
0.115
recommended_action: allow
0.30.60.8
reasons
  • Starting point-15
  • Natural keystroke rhythm
    beh.natural_keystrokes
    -10
  • Naturally curved pointer paths
    beh.curved_paths
    -10
  • Pointer micro-corrections present
    beh.micro_corrections
    -6
  • Consistent browser environment
    env.consistent
    -5
  • Residential network
    net.consumer
    -5

risk = sigmoid((-15 − 36) / 25) = 0.115

How it works

Three steps. The score never reaches the browser.

  1. 01
    In the browser

    The script records timing

    Pointer paths, click timing, key timing, and browser properties. Never what was typed.

  2. 02
    Before the sensitive action

    You call verify

    The browser gets a single-use token bound to the action. It expires in 2 minutes.

  3. 03
    On your server

    You redeem the token

    With your secret key. You get the score, a confidence, every reason, and a recommended action.

page.html
Browser
<script src="https://tellshield.vercel.app/tellshield.js" data-site-key="site_..." data-api-url="https://tellshield-api.fly.dev"></script>
<script type="module">
  const { token } = await window.Tellshield.client.verify({ action: "signup" });
</script>
api/signup.ts
Server
// Token from the page that loads https://tellshield.vercel.app/tellshield.js
const response = await fetch("https://tellshield-api.fly.dev/v1/token/verify", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.TELLSHIELD_SECRET_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ token, action: "signup" }),
});
const result = await response.json();
if (!result.valid || result.recommended_action === "block") return reject();
if (result.recommended_action === "challenge") return stepUp();
return createAccount();
The score

Points in, a number out, nothing hidden.

Six detectors check browser consistency, automation traces, behavior, device, network, and session speed. Signs of software add points. Signs of a person subtract them. The total goes through a sigmoid to give a 0–1 risk.

00.30.60.81
< 0.30
allow

Let it through.

0.30 – 0.60
monitor

Let it through, but flag it.

0.60 – 0.80
challenge

Ask for more proof.

≥ 0.80
block

Stop the action.

Add your own rules, like network.type = datacenter → rate_limit. Rules can only make the result stricter. No rule can turn a block into an allow.

Privacy

We measure how, never what.

Collected
  • Pointer, click, scroll, and key timing
    Not which key. Raw events deleted after 30 min.
  • Browser properties
    User agent, screen, timezone, languages, automation traces. 7 days.
  • IP address, hashed
    Plus country, network provider, and type, like residential or datacenter.
  • Scores and reasons
    With summary behavior stats. 90 days.
Never collected
  • Field values, passwords, typed text
    The script never reads input contents.
  • Clipboard contents
    Only that a paste happened.
  • Canvas, audio, or font fingerprints
    No hidden device ID.
  • Cross-site identifiers
    Nothing follows a person from one site to another.

Add it to one form. See the reasons for every score.

Create an account, copy your site key, and paste in the two snippets above. Your secret key is shown only once and stays on your server.