URL: /products/voight

---
title: Voight
description: An invisible CAPTCHA replacement that tells bots and AI agents from humans with proof-of-work, behavioral, and fingerprint signals — no puzzles, no clicks
---

Voight is the CAPTCHA replacement humans never see. It tells bots and AI agents apart from people using proof-of-work, behavioral, and fingerprint signals — with zero clicks, puzzles, or traffic lights for real users.

reCAPTCHA, hCaptcha, and Turnstile tax every real user with image grids and checkboxes to stop a minority of bots, hurting conversion and accessibility. Voight stays invisible to humans while still building a record of who is actually a bot. Drop the `/check/challenge` widget where your CAPTCHA used to sit, issue a challenge with `POST /v1/challenge`, and verify on submit with `POST /v1/challenge/verify` — real users see nothing.

<Warning>
  Voight is in **beta** and **capture-only** today. It observes, scores, and labels traffic to train the bot/human verdict — and **every `verify` currently passes** (`passed` is always `true`). Wire it in now to start building your own labeled corpus and to be ready to flip on enforcement once the verdict finishes calibrating. Until then, keep an existing gate if you need hard blocking.
</Warning>

## When to use it

Use Voight anywhere you would reach for a CAPTCHA — signup, login, contact, checkout — but do not want to interrupt real people. It binds to your `form_id` and origin like a reCAPTCHA site key, but never shows a checkbox or an image grid. Because it is capture-only in beta, today it is a way to **build a labeled bot/human corpus from your own traffic** without touching conversion, and to have enforcement wired up the day the verdict ships.

## How it works

<Steps>
  <Step title="Issue a challenge">
    Call `POST /v1/challenge` to mint a single-use nonce, a binding token, and an ALTCHA-style proof-of-work. The embeddable `/check/challenge` widget solves the proof-of-work in the background and captures interaction telemetry — no checkbox, no image grid, nothing the user has to do.
  </Step>

  <Step title="Verify on submit">
    On form submit, call `POST /v1/challenge/verify` with the solved proof-of-work, the token, and the nonce. The nonce is single-use, so a replayed or forged challenge is rejected. The captured pointer, scroll, and timing trajectory is recorded for the bot/human model.
  </Step>

  <Step title="Read the capture summary">
    `verify` returns a compact summary — `pow_valid`, `token_valid`, solve time, interaction counts, and a `challenge_id`. In beta this is capture-only: every request passes while the verdict calibrates, so you wire it in now and switch on enforcement when the model is ready.
  </Step>
</Steps>

## Quickstart

Issue a challenge, let the widget solve the proof-of-work and capture telemetry, then verify on submit. The widget and the issue call need only your publishable key; verify is a server-side call with your API key.

### 1. Issue a challenge

```bash
curl -X POST https://api.formshield.dev/v1/challenge \
  -H "Authorization: Bearer $FORMSHIELD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "form_id": "signup" }'
```

You get back a single-use `nonce`, a binding `token`, and an ALTCHA-style proof-of-work for the browser to solve. The `/check/challenge` widget does this for you and solves the proof-of-work in the background.

### 2. Verify on submit

On form submit, send the solved proof-of-work, the token, the nonce, and the captured `voight` telemetry to `POST /v1/challenge/verify`.

```bash
curl -X POST https://api.formshield.dev/v1/challenge/verify \
  -H "Authorization: Bearer $FORMSHIELD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "form_id": "signup",
    "token": "fs2.eyJ...",
    "nonce": "n_8f3a91c2",
    "pow_solution": { "challenge": "a1b2c3...", "salt": "d4e5f6", "number": 18244, "signature": "9c7e...", "took": 62 },
    "voight": { "t0": 0, "ts": 1840, "tgt": [220, 140, 18], "ptr": [[12,40,210,0]], "scr": [], "key": [], "pow": { "d": 18244, "ms": 62 } }
  }'
```

Response:

```json
{
  "version": "1",
  "data": {
    "passed": true,
    "pow_valid": true,
    "token_valid": true,
    "solve_ms": 62,
    "duration_ms": 1840,
    "n_pointer": 1,
    "n_scroll": 0,
    "target_hit": true,
    "challenge_id": "vgt_3f9a2c71b840"
  },
  "error": null,
  "metadata": { "credits": 1 }
}
```

<Note>
  In beta, `passed` is always `true` — Voight is capture-only and the verdict is still calibrating. Use `pow_valid`, `token_valid`, and the interaction counts to inspect the capture; do not gate enforcement on `passed` yet.
</Note>

## The signals

Voight fuses several independent signals so no single tell decides the verdict. Each is one input to the bot/human model, never a standalone human/bot gate.

<CardGroup cols={2}>
  <Card title="Proof-of-work (ALTCHA core)" icon="hammer">
    The browser brute-forces an HMAC-bound SHA-256 challenge that the server re-derives statelessly. It imposes a real compute cost on bulk automation and is one tamper-resistant signal, never a standalone human/bot gate.
  </Card>
  <Card title="Behavioral trajectory" icon="route">
    The widget records timestamped pointer movement, scroll, and interaction timing as the user reaches the target. Trajectory shape feeds a bot/human model — generative cursors are why this is one fusion signal, not the whole verdict.
  </Card>
  <Card title="Single-use nonce binding" icon="fingerprint">
    Each challenge is HMAC-bound to a fresh nonce and your form/origin. `verify` consumes the nonce once, so a replayed token reports `token_valid=false`. This is a corpus-poisoning tax and a tell against scripted reuse.
  </Card>
  <Card title="Fingerprint and network context" icon="globe">
    Every `verify` also captures IP, ASN, country, user-agent, and referrer alongside the trajectory, giving the label-later pipeline independent signals to cross-check rather than trusting any single tell.
  </Card>
</CardGroup>

## Verify fields

The `verify` response is a compact capture summary. Each `verify` costs 1 credit; issuing a challenge is free.

<ResponseField name="passed" type="boolean">
  The verdict. **Capture-only in beta — always `true`.** Do not gate enforcement on this field yet.
</ResponseField>

<ResponseField name="pow_valid" type="boolean">
  Whether the submitted proof-of-work re-derives correctly server-side. A valid solution means the browser paid the compute cost.
</ResponseField>

<ResponseField name="token_valid" type="boolean">
  Whether the token and nonce are intact and unused. A replayed or forged challenge reports `false` — the nonce is single-use.
</ResponseField>

<ResponseField name="solve_ms" type="number">
  Milliseconds the browser took to solve the proof-of-work.
</ResponseField>

<ResponseField name="duration_ms" type="number">
  Total time from challenge issue to submit, derived from the captured trajectory.
</ResponseField>

<ResponseField name="n_pointer" type="number">
  Count of captured pointer-movement samples.
</ResponseField>

<ResponseField name="n_scroll" type="number">
  Count of captured scroll samples.
</ResponseField>

<ResponseField name="target_hit" type="boolean">
  Whether the interaction reached the widget's target region.
</ResponseField>

<ResponseField name="challenge_id" type="string">
  Identifier for the stored capture, prefixed `vgt_`. Correlates this verify with its observation.
</ResponseField>

## Endpoints

<ParamField path="POST /v1/challenge" type="endpoint">
  Issue a challenge: mints a single-use nonce, a binding token, and an ALTCHA-style proof-of-work for the browser to solve. Issuing is free.
</ParamField>

<ParamField path="POST /v1/challenge/verify" type="endpoint">
  Verify on submit with the solved proof-of-work, the token, the nonce, and the captured `voight` telemetry. Returns the capture summary. Costs 1 credit.
</ParamField>

<ParamField path="/check/challenge" type="widget">
  The embeddable widget. Drop it where your CAPTCHA used to sit; it solves the proof-of-work in the background and captures interaction telemetry — no checkbox, no image grid, nothing the user has to do.
</ParamField>

## Common questions

<ParamField path="How do I replace reCAPTCHA or Turnstile with an invisible CAPTCHA replacement?" type="question">
  Drop in the `/check/challenge` widget where your CAPTCHA used to sit, then call `POST /v1/challenge` to issue and `POST /v1/challenge/verify` on submit. There is no checkbox or image grid — the widget solves a background proof-of-work and captures interaction signals, so real users see nothing. It binds to your `form_id` and origin like a reCAPTCHA site key, but never interrupts a human.
</ParamField>

<ParamField path="Does Voight block bots today?" type="question">
  Not yet. Voight is in beta and capture-only: it observes, scores, and labels traffic to train the bot/human verdict, and every `verify` currently passes (`passed` is always `true`). Wire it in now to start building your own labeled corpus and to be ready to flip on enforcement once the verdict finishes calibrating. Until then, keep an existing gate if you need hard blocking.
</ParamField>

<ParamField path="What does a verify cost and what do I get back?" type="question">
  Each `POST /v1/challenge/verify` costs 1 credit. You get a compact JSON summary — `pow_valid`, `token_valid`, `solve_ms`, `duration_ms`, pointer and scroll counts, `target_hit`, and a `challenge_id` — plus the captured telemetry is recorded to your corpus. Issuing a challenge is free; you are billed per verify.
</ParamField>

## Next steps

<CardGroup cols={2}>
  <Card title="Server reporting" icon="server" href="/guides/server-reporting">
    Capture AI crawlers and bots that never run JavaScript.
  </Card>
  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    The response envelope, authentication, and error codes.
  </Card>
</CardGroup>
