ANIMA Documentation
The world's most advanced human verification API. Drop in one script, get cryptographic proof that your users are human.
What is ANIMA?
ANIMA replaces CAPTCHA with a biometric verification system that reads 7 simultaneous biological signals — hand tremor, rhythm entropy, associative latency, mouse dynamics, device motion, scroll behavior, and a fusion score — to determine with 99.9% accuracy whether your user is a human or a bot.
Unlike CAPTCHA, which AI now defeats in under 2ms with 90%+ accuracy, ANIMA's signals cannot be replicated programmatically. The combination of passive background analysis and optional active challenges creates a mathematically infeasible barrier for automated systems.
Key Features
- 7 simultaneous bio signals — no single signal can be spoofed
- Invisible background analysis — most users pre-verified before seeing anything
- Cryptographic JWT tokens — verify server-side with your secret key
- Replay attack prevention — each token is single-use and expires in 5 minutes
- Zero PII collected — GDPR, CCPA, and HIPAA compliant by design
- On-premise option — Enterprise plan supports air-gapped deployment
Architecture Overview
- Your page loads → ANIMA SDK starts passive signal collection silently
- User interacts with ANIMA widget → active signals collected (draw, tap, word)
- SDK sends signals to ANIMA API → API runs 7-signal fusion → returns signed JWT token
- Your form submits token to your server → your server calls ANIMA to verify
- ANIMA confirms: human ✅ or bot ❌ → you allow or block the action
Quick Start
Integrate ANIMA in under 5 minutes with 3 steps.
Step 1 — Add the widget to your page
<!-- Add before </body> --> <script src="https://anima.id/sdk/v3/anima.min.js" data-site-key="anima_pub_YOUR_SITE_KEY"></script> <!-- Place widget inside your form --> <form id="myForm"> <!-- your form fields --> <div class="anima-widget"></div> <input type="hidden" id="anima-token" name="anima-token"> <button type="submit">Submit</button> </form> <script> // ANIMA calls this when human is verified ANIMA.onVerified(token => { document.getElementById('anima-token').value = token; }); </script>
Step 2 — Verify the token on your server
// In your form submission handler const response = await fetch('https://anima.id/api/verify/token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token : req.body['anima-token'], siteKey : 'anima_pub_YOUR_SITE_KEY', secretKey: 'anima_sec_YOUR_SECRET_KEY' }) }); const result = await response.json(); if (!result.human) { return res.status(403).json({ error: 'Bot detected' }); } // Human verified — process the form console.log(`Human verified: ${result.confidence}% confidence`);
Step 3 — You're done! ✅
That's it. Your form is now protected. Every submission that passes contains a cryptographically signed proof that a human filled it out.
GET /api/verify/test on your ANIMA instance to generate a test token and see the full response structure.REST API Reference
All endpoints are available at your ANIMA instance base URL.
POST /api/verify/generate
Called by the ANIMA widget after collecting bio signals. Returns a signed JWT verification token.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| siteKey | string | Yes | Your public site key (anima_pub_...) |
| signals.draw | number | No | Tremor signature score (0-100) |
| signals.tap | number | No | Rhythm entropy score (0-100) |
| signals.word | number | No | Associative latency score (0-100) |
| signals.mouse | number | No | Mouse dynamics score (0-100) |
| signals.device | number | No | Device motion score (0-100) |
Response
{ "success" : true, "token" : "eyJ...", // signed JWT — pass this to your server "status" : "verified", // "verified" | "uncertain" | "failed" "confidence": 91 // 0-100 human confidence score }
POST /api/verify/token
Called by your server to cryptographically validate a token received from a form submission. Always call this server-side — never from the browser.
| Field | Type | Required | Description |
|---|---|---|---|
| token | string | Yes | The JWT token from the form submission |
| siteKey | string | Yes | Your public site key |
| secretKey | string | Yes | Your secret key (server-side only) |
Response
{ "success" : true, "valid" : true, "human" : true, // ← the main field to check "status" : "verified", "confidence": 91, "signals" : { // individual signal scores "draw": 90, "tap": 88, "word": 85, "mouse": 72, "device": 65 }, "issuedAt" : "2026-04-02T15:30:00.000Z", "expiresAt" : "2026-04-02T15:35:00.000Z" } // If invalid / bot: { "success": false, "valid" : false, "reason" : "Token expired" // or "Bot detected", "Invalid signature", etc. }
GET /api/verify/test
Generates a test token with sample signals. Use this to test your server-side verification without going through the widget.
Plans & Limits
All plans include full API access. Limits reset on the 1st of every month.
| Feature | Free | Professional — $99/mo | Enterprise |
|---|---|---|---|
| Verifications / month | 10,000 | 500,000 | Unlimited |
| Bio signals | 3 | 7 (full fusion) | 7 + custom |
| API keys | 2 | Unlimited | Unlimited |
| Invisible analysis | ✗ | ✓ | ✓ |
| Custom branding | ✗ | ✓ | ✓ |
| Webhooks | ✗ | ✓ | ✓ |
| On-premise / air-gap | ✗ | ✗ | ✓ |
| SOC2 Type II | ✗ | ✗ | ✓ |
| SLA guarantee | ✗ | 99.9% | 99.99% |
| Support | Community | Priority email | Dedicated 24/7 |
status: "limit_reached" — your site keeps working but ANIMA falls back to allow-all mode. Upgrade to avoid gaps.Security & Privacy
ANIMA is built privacy-first. We never store personal data, never track across sites, and generate ephemeral tokens that expire in 5 minutes.
What data does ANIMA collect?
Nothing personally identifiable. ANIMA collects and processes:
- Drawing strokes (anonymous geometric data)
- Tap timing intervals (numbers only)
- Word association response time (milliseconds)
- Mouse movement entropy (mathematical score, no coordinates stored)
- Hashed IP address (one-way SHA256 + salt, irreversible)
All analysis happens ephemerally. The resulting data is a single confidence score (0–100) and a signed JWT token. No raw behavioral data is ever persisted.
Token Security
- Tokens are signed with HMAC-SHA256 using your secret key
- Each token has a unique JTI (JWT ID) — prevents replay attacks
- Tokens expire after 5 minutes
- Once verified server-side, a token cannot be used again
Compliance
- GDPR: No personal data processed or stored. No consent banner needed for ANIMA.
- CCPA: No sale or sharing of personal information.
- HIPAA: No PHI collected. BAA available on Enterprise plan.
- WCAG 2.2: ANIMA is fully accessible — no image challenges, no visual puzzles.
How It Works
Seven simultaneous biometric signals — measured in under 400ms — create a human fingerprint that no AI can replicate.
The 7 Signals
| # | Signal | Type | Human Range | Bot Pattern |
|---|---|---|---|---|
| 01 | Tremor Signature | Active | CV 0.25–2.5, 2–8px variance | CV <0.01, perfect strokes |
| 02 | Rhythm Entropy | Active | Inter-tap CV 0.04–0.50 | CV <0.01, machine-precise |
| 03 | Associative Latency | Active | 400–2,800ms response | <50ms (scripted) or >5s (lookup) |
| 04 | Mouse Dynamics | Passive | Curved paths, micro-corrections | Linear/geometric movement |
| 05 | Device Motion | Passive | Natural physiological tremor | Zero movement (desktop bot) |
| 06 | Scroll Behavior | Passive | Momentum, pauses, acceleration | Linear or scripted scroll |
| 07 | Fusion Score™ | Computed | ≥72 = verified | Mathematically infeasible to fake all 7 |
Widget Embed
The ANIMA widget is a self-contained component that handles the entire user-facing verification flow.
Configuration Options
| Option | Default | Description |
|---|---|---|
| data-site-key | required | Your public site key |
| data-theme | light | "light" or "dark" |
| data-lang | en | Language code (en, es, fr, de, zh...) |
| data-mode | auto | "auto" (invisible-first), "widget" (always show), "invisible" (never show) |
Server-Side Verification
Always verify tokens server-side. Never trust the client to self-report verification status.
See the API Reference section for the full endpoint documentation and response schema.
Webhooks
Receive real-time notifications for verification events.
Events
| Event | Description |
|---|---|
| verification.success | Human verified successfully |
| verification.failed | Bot detected |
| limit.approaching | 80% of monthly limit used |
| limit.reached | Monthly limit hit |
Node.js Integration
Full Express.js integration example.
// npm install node-fetch (or use built-in fetch in Node 18+) const express = require('express'); const app = express(); app.use(express.urlencoded({ extended: true })); // Middleware to verify ANIMA token async function verifyAnima(req, res, next) { try { const r = await fetch('https://YOUR_ANIMA_URL/api/verify/token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token : req.body['anima-token'], siteKey : process.env.ANIMA_SITE_KEY, secretKey: process.env.ANIMA_SECRET_KEY }) }); const result = await r.json(); if (!result.human) return res.status(403).json({ error: 'Bot detected' }); req.animaResult = result; next(); } catch(e) { res.status(500).json({ error: 'Verification error' }); } } app.post('/contact', verifyAnima, (req, res) => { // Human confirmed — process the form res.json({ success: true }); });
Python Integration
# pip install requests import requests, os ANIMA_URL = os.environ['ANIMA_URL'] SITE_KEY = os.environ['ANIMA_SITE_KEY'] SEC_KEY = os.environ['ANIMA_SECRET_KEY'] def verify_human(token): if not token: return False r = requests.post(f'{ANIMA_URL}/api/verify/token', json={ 'token' : token, 'siteKey' : SITE_KEY, 'secretKey': SEC_KEY }, timeout=5) return r.json().get('human', False) # Django view from django.http import JsonResponse def contact_view(request): if request.method == 'POST': if not verify_human(request.POST.get('anima-token')): return JsonResponse({'error':'Bot detected'}, status=403) # Process form... return JsonResponse({'success': True})
PHP Integration
// WordPress / PHP integration function anima_verify($token) { $payload = json_encode([ 'token' => $token, 'siteKey' => getenv('ANIMA_SITE_KEY'), 'secretKey' => getenv('ANIMA_SECRET_KEY') ]); $ch = curl_init(getenv('ANIMA_URL') . '/api/verify/token'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $payload, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_TIMEOUT => 5 ]); $result = json_decode(curl_exec($ch), true); curl_close($ch); return $result['human'] ?? false; } // WordPress form hook add_action('init', function() { if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!anima_verify($_POST['anima-token'] ?? '')) { wp_die('Bot detected', 'Forbidden', ['response' => 403]); } } });
React / Next.js Integration
// components/AnimaWidget.jsx import { useEffect, useRef } from 'react'; export default function AnimaWidget({ onVerified }) { const loaded = useRef(false); useEffect(() => { if (loaded.current) return; loaded.current = true; const script = document.createElement('script'); script.src = 'https://YOUR_ANIMA_URL/sdk/v3/anima.min.js'; script.setAttribute('data-site-key', process.env.NEXT_PUBLIC_ANIMA_SITE_KEY); script.onload = () => { window.ANIMA?.onVerified(token => onVerified(token)); }; document.body.appendChild(script); }, []); return <div className="anima-widget" />; } // API route: pages/api/contact.js export default async function handler(req, res) { const r = await fetch(`${process.env.ANIMA_URL}/api/verify/token`, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ token:req.body.animaToken, siteKey:process.env.ANIMA_SITE_KEY, secretKey:process.env.ANIMA_SECRET_KEY }) }); const { human } = await r.json(); if (!human) return res.status(403).json({ error:'Bot detected' }); res.json({ success: true }); }