Browser Client
@webdecoy/client is the client-side half of WebDecoy’s SDK story. It collects behavioral, environmental, and temporal signals in the browser, solves the proof-of-work challenge, and submits to your server: the self-hosted captcha endpoints mounted by the Express, Fastify, or Next.js packages. No third-party captcha script ever watches your visitors.
Prefer zero code? The Detection Script is a one-tag install with the full detection engine. The browser client is for teams who want the widget and signals under their own build.
For the client-and-server architecture and package overview, see the Bot Detection SDK product page.
Installation
Section titled “Installation”npm install @webdecoy/clientThe Captcha Widget
Section titled “The Captcha Widget”import { WebDecoyCaptcha } from '@webdecoy/client';
WebDecoyCaptcha.configure({ serverUrl: 'https://your-server.com'});
// Checkbox widgetWebDecoyCaptcha.render('captcha-box', { siteKey: 'YOUR_SITE_KEY', // any label; your own captcha endpoints receive it callback: (token) => submit(token)});
// Or run a zero-click session in the backgroundWebDecoyCaptcha.invisible({ siteKey: 'YOUR_SITE_KEY' });
// Or score on demand, when you chooseconst result = await WebDecoyCaptcha.execute('YOUR_SITE_KEY', { action: 'login' });Three modes:
| Mode | Behavior |
|---|---|
| Checkbox | Renders the familiar widget in a container element |
| Invisible | Collects signals and solves in the background, with no click |
| On-demand | WebDecoyCaptcha.execute() runs the flow programmatically when you choose |
The token the widget produces goes to your backend, which verifies it with await captcha.verifyToken(token, ip) from the Node SDK.
What It Collects
Section titled “What It Collects”The widget gathers the signals the detection engine scores: behavioral (mouse and interaction patterns), environmental (browser and device characteristics), and temporal (timing consistency). It binds them into the proof-of-work, so a bot can’t skip the collection and still solve the challenge.
Session Clearance
Section titled “Session Clearance”The browser check that session clearance builds on is this same prove-you’re-a-real-browser flow: passing it is how a session earns clearance on enforced routes.
For most sites you don’t need the captcha at all: silent minting earns the token during normal browsing. Either drop in the standalone script with your site key (for clearance, the site key is your publishable organization ID from the Enforcement page):
<script async src="https://cdn.jsdelivr.net/npm/@webdecoy/client/dist/webdecoy.global.js" data-site-key="YOUR_SITE_KEY"></script>or call it programmatically:
import { startClearance } from '@webdecoy/client';
startClearance({ siteKey: 'YOUR_SITE_KEY' });Minting posts the site key plus a device fingerprint to
POST /api/v1/clearance and sets a Secure, SameSite Lax, host-only
wd_clearance cookie. Initial work runs at browser idle. A fresh cookie is reused
and renewal continues before expiry, including when the page starts with an
existing cookie. Use @webdecoy/client 0.18.5 or later for this renewal fix.
Repeated startClearance() calls share one lifecycle on the page. The client
reuses its collected fingerprint and checks the cookie periodically, so renewal
by another tab can postpone its own request. Returning to a visible page checks
clearance again. A mint request times out after 10 seconds; transient failures
retry with backoff from one minute up to five minutes. An explicit server refusal
stops automatic attempts on that page. Token expiry read by the client is only a
scheduling hint; the server and edge validator still decide whether clearance is
valid.
Minting does not solve proof-of-work or block page rendering. After the visitor
interacts, the client can upgrade the token once with an aggregate interaction
summary (pass behavior: false to turn that off). Accepted aggregates remain on
later renewal requests, and the server evaluates each request again.
The client does not intercept or retry your application’s fetch/XHR requests. An API request challenged by the edge returns a JSON 403; the explicit helper below can recover safe reads through your application’s clearance flow. Clearance remains first-party: this cookie does not travel to shared-domain decoys or a different customer subdomain.
API challenge recovery
Section titled “API challenge recovery”With @webdecoy/client 0.19.0 or later and an updated edge validator, opt in
for the API reads your application wants to recover:
import { createClearanceFetch } from '@webdecoy/client';import { earnClearance } from './clearance-flow'; // your application's flow
const fetchProtected = createClearanceFetch({ origin: location.origin, timeoutMs: 10_000, recover: ({ origin, challenge, signal }) => earnClearance({ origin, reason: challenge.reason, signal }), onEvent: event => console.debug(event.type, event.status),});
const controller = new AbortController();const response = await fetchProtected('/api/catalog', { credentials: 'same-origin', signal: controller.signal, // your application's AbortController});// Handle response.ok/status/body just as with native fetch.earnClearance is application code, not an SDK export. It must earn and install
the host’s wd_clearance cookie through your configured issuance/verification
flow and return true only when ready to retry. startClearance() returns
void; awaiting it does not wait for a token. A self-hosted captcha widget token
is not a clearance cookie. A stronger-grade response needs a flow that earns the
route’s required grade; simply replacing a token cannot grant that grade.
The helper retries GET/HEAD once after a recognized recoverable challenge.
Concurrent calls through one adapter share one recovery callback for its
configured origin; that callback receives the first challenge’s reason. Use one
adapter for each origin/recovery flow. The edge evaluates every retry again.
POST/PATCH/PUT/DELETE and other methods pass through once with their original
body. There is no mutation replay opt-in and no XHR interception. The standalone
script exposes the same helpers at window.WebDecoyClearance.
Recovery and the retry share a deadline after the first challenge: 10 seconds by
default, configurable from 1 ms to 60 seconds. Refusal, resolver rejection,
offline state, or deadline expiry returns the original readable challenge
response. Caller cancellation and fetch network errors reject as native fetch
does. One cancelled caller does not cancel other callers waiting for the shared
flow; when none remain, the recovery signal aborts. Your flow must stop network
work and close its UI on that signal. If it ignores cancellation, later calls
remain attached to that stopped attempt until it settles, rather than opening
overlapping recovery dialogs. Browsers without AbortSignal.any() return the
challenge without attempting recovery.
Only version-1 responses with the exact WebDecoy challenge headers qualify.
Ordinary application 403s, unsupported/malformed contracts, revoked sessions,
crawler-policy refusals and agent impersonation do not trigger recovery.
Redirected responses, different origins and credentials: 'omit' also do not.
The helper preserves request options and leaves response bodies/streams unread.
It adds no request headers or CORS permission. Cross-origin calls need
credentials: 'include', the protected host’s cookie, and an application-approved
CORS policy that exposes the four challenge headers; otherwise the browser
cannot identify the response. A cookie set on the page’s host does not clear a
different API host.
onEvent reports challenge-served, recovery-attempted,
recovery-succeeded/recovery-failed, and the retried enforcement-outcome
(status). Attempts are per waiting request; count callback invocations separately
for shared flows. Success means the retry returned an OK response, not a new
trust grade. These local observations send no telemetry automatically and must
remain distinct from the validator’s verdict counts. Observation failures cannot
change the request. Interactive application flows must provide keyboard access,
focus restoration, an announced status and cancellation; the helper renders no UI.
Next Steps
Section titled “Next Steps”- Node SDK: verify widget tokens server-side
- Detection Scripts: the no-code client-side alternative
- Session Clearance: enforcement built on the browser check