Both Halves of Bot Defense

Client & Server SDKs

A browser SDK that collects signals, runs the captcha, and earns session clearance — and server SDKs for Node.js, Go, and PHP that verify results, set scraper tripwires, and enforce where bots can't tamper.

Installation
// server
npm install @webdecoy/node

// browser
npm install @webdecoy/client

// framework middleware
npm install @webdecoy/express

Two SDKs, One Loop

WebDecoy integration has two halves that work together: the client detects and proves, the server verifies and enforces.

Client Side — in the browser

@webdecoy/client collects behavioral and device signals, runs the self-hosted captcha widget, and proves the session is a real browser — the check that earns session clearance.

Prefer no code at all? The Detection Script is a one-tag client-side install with the full detection engine.

Server Side — in your backend

@webdecoy/node (plus Go and PHP) verifies tokens where bots can't tamper, adds JA4 TLS fingerprinting, sets deterministic tripwires, and makes the allow, challenge, or block call.

Client-side signals are powerful; server-side verification makes them trustworthy. You'll usually run both.

What the Server SDK Does

The Node.js SDK provides server-side utilities for working with WebDecoy detections.

Verify Detections

Server-side verification of bot scanner results to prevent client-side tampering.

Query Detections

Access your detection history and analytics data through the API.

Signed Webhooks

Every webhook is HMAC-SHA256 signed, with event-type and delivery-ID headers for routing and idempotency.

New in v0.4.0

Tripwires: Deterministic Scraper Detection

Stealth scrapers that spoof a real browser fingerprint slip past identity checks. A tripwire catches them by behavior instead: plant a hidden honeytoken link, and any request to its secret path is a deterministic bot hit. Zero false positives, no API key, no network call.

Deterministic, Not Probabilistic

A tripwire hit is a fact, not a score: the client requested a URL that exists nowhere a human could find it. Un-spoofable by a better fingerprint.

Zero False Positives

The honeytoken() link is off-screen, aria-hidden, and nofollow. Real users never see it and search engines never follow it, so only scrapers trip the wire.

Runs Fully In-Process

The tripwire rule and honeytoken generator ship in the open-source @webdecoy/node package. No API key, no account, no outbound call. Local path matching only.

Set a scraper trap (@webdecoy/express)
import { webdecoy } from '@webdecoy/express';
import { tripwire, honeytoken } from '@webdecoy/node';

// 1. Generate a hidden decoy link + its secret path
const trap = honeytoken();

// 2. One line blocks any hit on the trap (auto 403)
app.use(webdecoy({
  rules: [tripwire({ paths: [trap.path] })],
  skipPaths: ['/health', '/static']
}));

// 3. Inject the invisible link so scrapers find it
app.get('/', (req, res) => {
  res.send(`<!doctype html><html><body>
    <h1>Welcome</h1>
    ${trap.linkHtml}
  </body></html>`);
});

A tripwire hit is ground truth that session clearance (below) enforces on directly: the SDK forwards the session's clearance token with the violation, and the client is denied new clearance on every IP it rotates to — the same durable lockout a hosted decoy triggers.

Read the deep dive: Tripwires: Catch the Scrapers Fingerprinting Misses

Agent Identity

Verify AI Agents in Your Own Middleware

@webdecoy/node verifies Web Bot Auth (RFC 9421) signatures locally — no API round trip, no network on the warm path, under 5ms at p95. A user agent claiming to be GPTBot either proves it or gets caught.

Next.js middleware.ts
import { WebDecoy, webBotAuth } from '@webdecoy/node';

const wd = new WebDecoy({ apiKey: process.env.WEBDECOY_API_KEY });

export async function middleware(request) {
  const { agent } = await wd.detectBot(request);

  // 'verified' | 'claimed' | 'impersonation' | 'none'
  switch (agent.verdict) {
    case 'impersonation':
      // Claimed a signing agent's identity and failed verification
      return new Response('Forbidden', { status: 403 });

    case 'verified':
      // agent.name → 'ChatGPT-User', agent.category → 'agent-browser'
      return allowVerifiedCrawler(agent);
  }
}

// Or drop it in as a rule — DENY on impersonation by default
app.use(webdecoy({ rules: [webBotAuth()] }));

No Round Trip

Key directories are fetched and cached ahead of time, so verification is a local Ed25519 check. Warm path does zero network I/O.

Curated Directories Only

A Signature-Agent header is attacker-controlled. The SDK resolves keys only from a curated list, so it can never be pointed at an arbitrary host.

Node + Vercel Edge

Full verification runs inside the Edge runtime, not just Node. Zero dependencies, so there is nothing to shim.

The verification engine is open source: github.com/WebDecoy/web-bot-auth — an Apache-2.0 Go implementation with zero dependencies, cross-validated against Cloudflare's reference vectors and live deployment.

Learn more: Agent Identity & Trust Layer

The Enforcement Direction

Session Clearance: The Decoy Issues the Verdict

No fingerprint or IP rule can stop a real browser automated from a residential IP without blocking the human beside it. So WebDecoy enforcement asks a different question: does this session carry valid clearance? Real users prove themselves once and pass everywhere. A client that trips a decoy is denied new clearance — on every IP it rotates to.

Step 1 · Prove

The browser passes a check

The client-side SDK verifies it's talking to a real browser — signals, headless checks, proof-of-work — with zero friction for real users.

Step 2 · Clear

The session earns clearance

A signed clearance rides with the session itself — the client, not the IP address. Rotating IPs resets nothing.

Step 3 · Enforce

Protected routes ask one question

Valid clearance? Pass. Missing on a sensitive route — login, checkout, your scraped endpoints? Challenge. Passing the challenge lets a real user self-heal.

Trip a decoy, and the story ends differently: the client's active clearance is revoked within about a minute, and new clearance is refused — so a fresh IP no longer buys a clean slate.

Clearance is graded, not binary

A clearance token no longer just means "hasn't tripped a decoy." It carries a trust level — clean, human-likely, or attested-human — plus the evidence that earned it: browser-integrity checks at the interstitial, interaction cadence that reads as a real hand, or a Turnstile verdict you already collect.

Evidence only ever raises a grade and a high threat score caps it, so a session that produces none is treated exactly as it was before. Per-route minimum trust levels are enforced today by the edge validator; SDK-side minimum-trust enforcement is in progress.

Rotation-Proof by Design

Clearance binds to the client, not the address. A residential-proxy bot cycling through thousands of home IPs carries its verdict with it to every one of them.

Zero Collateral

A real user who shares a JA4 fingerprint — or even an IP — with an attacker simply proves their own session and moves on. No blanket fingerprint block, no innocent users caught in it.

Ground Truth, Not Guesswork

Behavioral vendors estimate whether traffic is a bot. A decoy or tripwire hit is certainty — no one clicks a honeypot by accident — and only that ground truth revokes clearance. Heuristic scores never touch the deny-list.

Fingerprint and IP rules don't go away — they stay on as the cheap first layer for datacenter and scripted bots, where a fingerprint block is safe. Read the deep dive: Closed-Loop Bot Enforcement in Your Cloudflare & AWS WAF

New in v0.3.0

Self-Hosted Captcha and In-Process Detection

The SDK now scores requests inside your own process. The detection engine and a proof-of-work captcha run on your server with no third-party call for scoring. Only optional IP enrichment uses the WebDecoy API.

In-Process Detection Engine

DetectionEngine scores around 40 signals across vision-AI, headless, automation, behavioral, fingerprint, JA3/JA4, and keystroke-cadence detectors into an allow, challenge, or block verdict. The JA4 fingerprint is also the correlation key for WebDecoy's cross-IP actor model. No remote call.

Proof-of-Work Captcha

HMAC-signed SHA-256 challenges with difficulty scaling, replay protection, and the signals bound into the work. Issues single-use, IP-bound session tokens. A self-hosted alternative to reCAPTCHA, hCaptcha, and Turnstile.

Browser Widget

The new @webdecoy/client package collects the signals, solves the proof-of-work, and submits to your server. Checkbox, invisible, and on-demand modes.

Browser (@webdecoy/client)
import { WebDecoyCaptcha } from '@webdecoy/client';

WebDecoyCaptcha.configure({
  serverUrl: 'https://your-server.com'
});

// Checkbox widget
WebDecoyCaptcha.render('captcha-box', {
  siteKey: 'pk_live_...',
  callback: (token) => submit(token)
});

// Or protect a form invisibly
WebDecoyCaptcha.invisible({ siteKey: 'pk_live_...' });
Server (@webdecoy/express)
import express from 'express';
import { webdecoyCaptcha } from '@webdecoy/express';
import { Captcha } from '@webdecoy/node';

const app = express();
app.use(express.json());

// Serves /__webdecoy/challenge, /verify,
// /score, and /token/verify
app.use(webdecoyCaptcha({
  secret: process.env.WEBDECOY_SECRET
}));

// Verify the token on a protected route
const captcha = new Captcha({
  secret: process.env.WEBDECOY_SECRET
});

app.post('/login', (req, res) => {
  const { valid } = captcha.verifyToken(
    req.body.webdecoy_token,
    req.ip
  );
  if (!valid) {
    return res.status(403).json({ error: 'captcha failed' });
  }
  // proceed with login
});

Read the deep dive: Self-Hosted Captcha for Node.js (SDK v0.3.0)

Basic Usage

Quick Start

Initialize the SDK with your API key and start verifying bot scanner results from your backend.

API Key Authentication

Use your API key from the WebDecoy dashboard

Async/Await Support

Modern Promise-based API

TypeScript Types

Full type definitions included

Example Usage
import { WebDecoy } from '@webdecoy/node';

const webdecoy = new WebDecoy({
  apiKey: process.env.WEBDECOY_API_KEY
});

// Check a request before processing it
app.post('/api/submit', async (req, res) => {
  const { allowed, detection } = await webdecoy.protect({
    method: req.method,
    path: req.path,
    ip: req.ip,
    user_agent: req.get('user-agent'),
    headers: req.headers
  });

  if (!allowed) {
    return res.status(403).json({
      error: 'Bot detected',
      threat: detection.threat_level
    });
  }

  // Process legitimate request
});
Webhook Validation
import crypto from 'crypto';

app.post('/webhooks/webdecoy',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['x-webdecoy-signature'];

    const expected = 'sha256=' + crypto
      .createHmac('sha256', process.env.WEBHOOK_SECRET)
      .update(req.body)
      .digest('hex');

    const valid = signature && crypto.timingSafeEqual(
      Buffer.from(signature), Buffer.from(expected)
    );
    if (!valid) {
      return res.status(401).send('Invalid signature');
    }

    // Route by event type; dedupe by delivery ID
    const event = req.headers['x-webdecoy-event'];
    const delivery = req.headers['x-webdecoy-delivery'];

    res.status(200).send('OK');
  }
);
Security

Webhook Validation

Every webhook WebDecoy sends is signed with HMAC-SHA256 over the raw payload. Verifying takes a few lines of Node's built-in crypto — no extra dependency.

HMAC-SHA256 signature in X-WebDecoy-Signature
Event type and unique delivery ID headers for routing and idempotency
Constant-time comparison via crypto.timingSafeEqual

Framework-Specific Packages

Install the core SDK or use our framework-specific packages with built-in middleware.

Core SDK

npm install @webdecoy/node

Works with any framework

Browser Client

npm install @webdecoy/client

Captcha widget

Express

npm install @webdecoy/express

Middleware included

Fastify

npm install @webdecoy/fastify

Plugin included

Next.js

npm install @webdecoy/nextjs

Middleware wrapper

Express.js

Express Middleware

The Express package provides middleware that automatically analyzes every request and attaches detection results.

Global or per-route middleware
Configurable block threshold
Path exclusions for static assets
Detection results on req.webdecoy
Express Middleware
import express from 'express';
import { webdecoy } from '@webdecoy/express';

const app = express();

// Apply globally — blocked requests get a 403
app.use(webdecoy({
  threshold: 70,
  skipPaths: ['/health', '/static'],
  onBlocked: (req, res, detection) => {
    res.status(403).json({ error: 'Blocked' });
  }
}));

// Detection results attached to req.webdecoy
app.post('/api/login', (req, res) => {
  const { decision, confidence, threat_level } = req.webdecoy;

  if (decision === 'challenge') {
    return res.status(429).json({
      error: 'Please complete verification'
    });
  }

  // Process login...
});
Fastify Plugin
import Fastify from 'fastify';
import webdecoyPlugin from '@webdecoy/fastify';

const fastify = Fastify();

// Register the plugin
await fastify.register(webdecoyPlugin, {
  threshold: 70,
  skipPaths: ['/health']
});

fastify.post('/api/checkout', async (req, reply) => {
  // Detection attached to the request
  const { decision, confidence, bot_detected } = req.webdecoy;

  if (decision === 'block') {
    return reply.code(403).send({
      error: 'Request blocked'
    });
  }

  if (decision === 'challenge') {
    return reply.code(429).send({
      error: 'Verification required'
    });
  }

  // Process checkout...
});
Fastify

Fastify Plugin

Register WebDecoy as a Fastify plugin. Detection data is automatically attached to every request object.

Native Fastify plugin architecture
Async/await throughout
High-performance request handling
Full TypeScript support
Next.js

Next.js Middleware

Protect your Next.js app at the edge with middleware that runs before every request.

Edge Runtime compatible
App Router and Pages Router support
Route-level protection decorators
Server Actions protection
middleware.ts
import { withWebDecoy } from '@webdecoy/nextjs';
import { NextResponse } from 'next/server';

export default withWebDecoy({
  threshold: 70,

  // Customize what blocked requests receive
  onBlocked: (request, detection) => {
    return NextResponse.json(
      {
        error: 'Blocked',
        threat: detection.threat_level
      },
      { status: 403 }
    );
  }
});

export const config = {
  matcher: ['/api/:path*', '/checkout/:path*']
};

Detection Response

Every check returns a clear decision plus the analysis behind it.

Detection Object
{
  "decision": "block",
  "confidence": 92,
  "threat_level": "HIGH",
  "bot_detected": true,
  "bot_type": "headless_browser",
  "detection_id": "det_8f3c2a91",
  "rule_enforced": true
}

Threat Levels

MINIMAL (0-29): Likely human
LOW (30-49): Some signals
MEDIUM (50-69): Suspicious
HIGH (70-89): Likely bot
CRITICAL (90-100): Confirmed bot

The Decision Field

allow Pass the request through
challenge Ask for verification — e.g. the @webdecoy/client captcha
block Reject the request (middleware default: 403)

Why Use Server-Side Bot Detection

Client-side detection is powerful, but server-side verification makes it tamper-proof.

Tamper-Proof Verification

Attackers cannot modify bot scores or bypass detection by manipulating client-side JavaScript. Server verification is the final authority.

TLS Fingerprinting

The SDK adds JA3/JA4 TLS fingerprint analysis that can only be done server-side, catching bots that spoof browser user agents. The JA4 identity correlates an actor across every IP it rotates through — the cheap first enforcement layer for datacenter bots, with session clearance handling the hard cases.

Real-Time Webhooks

Receive instant notifications when bots are detected. Trigger automated responses in your backend without polling.

Available SDKs

WebDecoy provides official SDKs for multiple languages and platforms.

Browser

Client-side signals, captcha widget, and session clearance. Checkbox, invisible, and on-demand modes.

npm install @webdecoy/client Or use the Detection Script

Node.js

Express, Next.js, NestJS, Fastify. Full TypeScript support.

npm install @webdecoy/node View on GitHub

Go

High-performance Go SDK for net/http handlers.

View Go documentation

PHP / WordPress

WordPress plugin and PHP SDK for custom integrations.

View PHP documentation

Frequently Asked Questions

Common questions about the WebDecoy Node.js SDK.

Does WebDecoy have a client-side SDK, or only server-side?

Both, and they are designed to work together. The client side is @webdecoy/client — a browser SDK that collects behavioral and device signals, runs the self-hosted captcha widget, and earns session clearance — or the no-code Detection Script, a single tag with the full detection engine. The server side is @webdecoy/node (plus Go and PHP SDKs), which verifies results where bots cannot tamper with them and makes the allow, challenge, or block decision.

Can the SDK verify AI agents like GPTBot cryptographically?

Yes. @webdecoy/node verifies Web Bot Auth (RFC 9421) signatures locally in your middleware via detectBot(), or as a drop-in webBotAuth() rule. Key directories are cached ahead of time so the warm path does no network I/O and verification runs under 5ms at p95, on both Node and the Vercel Edge runtime. The verdict is verified, claimed, impersonation, or none — the same taxonomy the detection pipeline and the edge validator use.

What does an "impersonation" verdict mean?

It means the request claimed an identity that could be checked and failed the check — a signing agent's user agent with an invalid or missing signature, or a crawler arriving from outside its operator's published IP ranges. That is not a heuristic guess; it is a request caught in a lie, so the webBotAuth() rule denies it by default. A legitimate crawler that simply does not sign yet degrades to "claimed" and is never accused.

Why do I need server-side verification with the SDK?

Client-side bot detection can be tampered with by sophisticated attackers. The SDK allows you to verify detection tokens server-side, ensuring that bot scores have not been modified. This two-layer approach combines client-side signals with tamper-proof server verification.

Which Node.js frameworks are supported?

The SDK works with any Node.js framework including Express, Fastify, Next.js, NestJS, Koa, and Hapi. It provides simple middleware integration patterns and works with both JavaScript and TypeScript projects with full type definitions included.

How do I validate incoming webhooks from WebDecoy?

Every webhook is signed with HMAC-SHA256 over the raw request body, sent as sha256=<hex> in the X-WebDecoy-Signature header. Recompute the HMAC with your webhook secret and compare with a constant-time check (crypto.timingSafeEqual in Node). Each delivery also carries X-WebDecoy-Event for routing and a unique X-WebDecoy-Delivery ID you can use for idempotency.

What data does the detection response include?

Every check returns a decision (allow, challenge, or block), a confidence score (0-100), a threat level from MINIMAL to CRITICAL, whether a bot was detected and its type, a detection ID for cross-referencing the dashboard, and whether a response rule was enforced. You can use this data to make access control decisions in your application.

Are there SDKs for other languages besides Node.js?

Yes. WebDecoy also provides SDKs for Go and PHP (with WordPress integration). All SDKs support the same core functionality: token verification, webhook validation, and API access. See our documentation for Go and PHP integration guides.

Does the SDK send my traffic to a third party for scoring?

Not in v0.3.0. The detection engine and proof-of-work captcha run in your own process, so the allow, challenge, or block decision is made locally with no remote call. The only optional outbound request is IP enrichment (VPN, proxy, Tor, abuse score, geo) against the WebDecoy API, which you can disable.

Can WebDecoy replace reCAPTCHA, hCaptcha, or Turnstile?

Yes. v0.3.0 ships a self-hosted captcha: an HMAC-signed proof-of-work challenge plus the @webdecoy/client browser widget. You hold the signing secret and the widget posts to your own domain, so there is no third-party captcha script watching your visitors. It runs as a checkbox, invisibly, or on demand.

What is session clearance and how does it defeat IP rotation?

Session clearance flips enforcement from blocklisting bad fingerprints to allowlisting proven sessions. A real browser passes WebDecoy’s client-side checks and its session carries signed clearance that protected routes verify. Because clearance travels with the client rather than the IP address, rotating IPs resets nothing — and a client that trips a decoy is refused new clearance on any IP.

How do tripwires and session clearance work together?

A tripwire hit is deterministic proof of a bot — no one follows a hidden honeytoken link by accident. The SDK forwards the session's clearance token with the violation, and the client is denied clearance durably, on every IP — identical to tripping a hosted decoy. By design, only deception signals drive that deny-list: heuristic rules like rate limits and filters never carry the token, so enforcement stays proof-based rather than score-based.

Ready to integrate bot detection?

Install the browser and server SDKs and close the loop from detection to enforcement in minutes.

View npm Package