API Reference

Everything you need to
verify humans.

One endpoint. Behavioral signals. A confidence score. No friction for your users — and no false positives.

API Operationalv1Try live demo →
https://humaverify.com
⚡ 2 lines to integrate
Step 1 — Add the snippet to your HTML (enriches behavioral signals)
<script src="https://humaverify.com/huma.js"></script>
Step 2 — Call verify at any critical moment (signup, login, checkout)
const result = await Huma.verify('user_123', 'huma_live_...');
if (result.human && result.confidence > 0.8) { // ✅ allow }

The snippet silently collects behavioral signals (mouse, keyboard, scroll) and sends them with each verify call for a richer score.

All requests must include your API key in the Authorization header as a Bearer token.

Authorization: Bearer huma_live_••••••••••••••••

Get your key from your dashboard →

POST/api/v1/verifyVerify a user

Analyzes a user's behavioral signals and returns a human confidence score. Call at critical moments — signups, logins, form submissions, high-value actions.

Request body

ParameterTypeRequiredDescription
userIdstringYesYour internal user identifier. Not stored — used only for behavioral analysis.

Response fields

FieldTypeDescription
humanbooleanTrue if the user is likely human (confidence ≥ 0.5).
confidencenumberScore from 0.0 to 1.0. Higher = more human-like behavior.
tokenstringUnique verification token. Store for audit purposes.
pii_storedbooleanAlways false. useHUMA never stores personal information.
agentobjectPro & Enterprise. AI-agent detection: { is_agent, agent_confidence (0–1), signals[] }. Parallel to the human score — present only when behavioral signals are sent. Omitted on lower plans.

Example

REQUEST
curl -X POST https://humaverify.com/api/v1/verify \
  -H "Authorization: Bearer huma_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"userId": "user_abc123"}'
RESPONSE 200 OK
{
  "human": true,
  "confidence": 0.92,
  "token": "h_verified_Kd3mP8luYMC2Mi1x",
  "pii_stored": false,
  "agent": {                       // Pro & Enterprise
    "is_agent": false,
    "agent_confidence": 0,
    "signals": []
  }
}

On Pro & Enterprise, every verification returns an agent block alongside the human score. It flags AI agents driving a real browser — Playwright, Puppeteer, Selenium, and extension / computer-use agents — even when the session looks human. It only appears when behavioral signals are sent.

One response, a three-way decision: check human first, then agent.is_agent. The action is always yours.

const { human, confidence, agent } = await res.json();

if (!human) {
  // Not human — block or challenge (bot / automated abuse)
  return reject();
}

if (agent?.is_agent) {
  // A real browser driven by an AI agent (Pro & Enterprise).
  // Your call: allow the automation you want, or block / step-up the rest.
  return challenge();
}

// Trusted human — let them through
return allow();

Detection is high-confidence, not absolute — a perfectly throttled agent that mimics human input is an arms race. Use agent_confidence to tune how aggressively you act.

POST/api/v1/sessionHeartbeat — continuous monitoring

Monitor an active user session for bot behavior changes and account takeovers. Call every 30 seconds after a successful verify(). Returns a live score and flags anomalies when behavior drops significantly from the user's verified baseline.

Use Huma.startSession() in your frontend — it handles the 30s interval automatically. Use this endpoint directly only for custom server-side monitoring.

Request body

ParameterTypeRequiredDescription
userIdstringYesSame userId used in verify().
sessionTokenstringYesThe token returned by verify() — used to fetch the behavioral baseline.
sessionDataobjectNoBehavioral signals from the current window (same shape as verify sessionData).

Response fields

FieldTypeDescription
scorenumber 0–100Live session score for this window.
confidencenumber 0–1Confidence as a decimal.
baselinenumber 0–100Score from the original verify — used as comparison reference.
deltanumberScore drop from baseline. Negative = degraded behavior.
anomalybooleanTrue when delta > 28 points or score < 30 (absolute bot threshold).
actionstringallow | flag | block
notesstring[]Signal labels explaining the score (e.g. organic_mouse, bot_typing).

Frontend usage

// After a successful verify():
const result = await Huma.verify('user_123', 'huma_live_...');

if (result.human) {
  Huma.startSession('user_123', 'huma_live_...', result.token, {
    intervalMs: 30000, // every 30 seconds
    onAnomaly: function(r) {
      if (r.action === 'block') {
        window.location.href = '/logout'; // account takeover detected
      }
    }
  });
}

React hook

import { useHumaSession } from 'usehuma/react';

const { anomaly, lastResult } = useHumaSession({
  apiKey: 'huma_live_...',
  userId: 'user_123',
  token: verifyResult.token,
  onAnomaly: (r) => router.push('/logout'),
});

Receive real-time HTTP POST notifications when a bot is detected or a human is verified. Signatures use HMAC-SHA256 — the same pattern as Stripe. Max 5 webhooks per API key.

GET/api/v1/webhooksList your webhooks
Returns all registered webhook endpoints for the authenticated API key.
POST/api/v1/webhooksRegister a webhook
ParameterTypeRequiredDescription
urlstringYesYour HTTPS endpoint. Must start with https://
eventsstring[]NoEvents to receive: bot.detected, human.verified. Defaults to both.

⚑ The secret is shown only once. Save it immediately.

RESPONSE
{
  "id": "wh_abc123...",
  "secret": "huma_whsec_abc123...",  // shown once
  "url": "https://yourdomain.com/webhooks/huma",
  "events": ["bot.detected", "human.verified"],
  "active": true
}
SIGNATURE VERIFICATION

Each webhook includes a Huma-Signature header. Verify it to confirm the request came from useHUMA.

HEADER FORMAT
Huma-Signature: t=1716840000,v1=abc123def456...
import crypto from 'crypto';

function verifyWebhook(rawBody, signature, secret) {
  const [tPart, vPart] = signature.split(',');
  const timestamp = tPart.replace('t=', '');
  const received  = vPart.replace('v1=', '');

  const payload  = `${timestamp}.${rawBody}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(received)
  );
}
WEBHOOK PAYLOAD
bot.detected / human.verified
{
  "event": "bot.detected",
  "user_id": "user_123",
  "human": false,
  "confidence": 0.18,
  "score": 18,
  "token": "h_verified_Kd3mP8luYMC2Mi1x",
  "timestamp": "2026-05-25T21:00:00.000Z"
}

When confidence is borderline (30–70%), present a visual challenge instead of hard-blocking. If passed, the user receives a verification token equivalent to a standard verify. Max 3 attempts per challenge. Challenges expire in 5 minutes.

POST/api/v1/challenge/createGenerate a challenge
ParameterTypeRequiredDescription
userIdstringYesYour internal user identifier.
RESPONSE
{
  "challenge_id": "ch_abc123...",
  "type": "click_targets",        // or "pattern"
  "payload": {
    "instruction": "Click the 🎯",
    "targets": [
      { "id": "t0", "x": 20, "y": 25, "label": "🌿" },
      { "id": "t1", "x": 65, "y": 25, "label": "🎯" },
      ...
    ]
  },
  "expires_in_seconds": 300
}
POST/api/v1/challenge/verifySubmit the user's answer
ParameterTypeRequiredDescription
challenge_idstringYesID from challenge/create.
answerstringYesFor click_targets: target id (e.g. "t1"). For pattern: dot sequence (e.g. "1-5-9").
PASSED
{ "passed": true, "token": "h_challenge_abc..." }
FAILED
{ "passed": false, "message": "Incorrect answer", "attempts_remaining": 2 }
REACT COMPONENT
import { HumaChallenge } from 'usehuma/react';

// After verify() returns borderline confidence:
const ch = await fetch('/api/v1/challenge/create', {
  method: 'POST',
  headers: { Authorization: 'Bearer huma_live_...' },
  body: JSON.stringify({ userId }),
}).then(r => r.json());

return (
  <HumaChallenge
    challengeId={ch.challenge_id}
    type={ch.type}
    payload={ch.payload}
    apiKey="huma_live_..."
    onPassed={(token) => continueLogin(token)}
    onBlocked={() => router.push('/blocked')}
  />
);
StatusCodeMeaning
200Verification successful.
401MISSING_AUTHNo Authorization header provided.
401INVALID_API_KEY_FORMATKey doesn't match format (huma_live_...).
401INVALID_API_KEYKey not found in database. Check your dashboard.
400Missing or invalid userId in request body.
402TRIAL_EXPIREDFree trial has expired. Upgrade at humaverify.com/#pricing.
429MONTHLY_LIMIT_EXCEEDEDMonthly plan limit reached. Upgrade your plan.
429BURST_LIMIT_EXCEEDEDToo many requests per minute. Check Retry-After header.
429USER_FLOOD_DETECTEDSame userId verified too many times per minute (max 10/min).
503Service temporarily unavailable. Retry with exponential backoff.

The usehuma package ships CJS + ESM + TypeScript definitions and works in any Node.js, Next.js, or edge runtime. It also exports React hooks and the HumaGate component.

Install

npm install usehuma

Browser — collect & verify in one call

import { verify } from "usehuma";

// Collects behavioral signals from the DOM and verifies in one call.
// Call this client-side at a critical moment (login, signup, checkout).
const result = await verify({
  apiKey: "huma_live_...",
  userId: "user_123",
});

if (result.human && result.confidence > 0.7) {
  // ✅ Real human — send result.token to your server for audit
} else {
  // 🚫 Bot — block or trigger step-up challenge
}

Server-side verify (Next.js API route)

import { verifyWithSignals } from "usehuma";

// Browser: collect signals and forward them.
// const signals = Huma.debug()?.features;
// fetch('/api/check', { body: JSON.stringify({ userId, signals }) });

// In your API route (server-side):
const result = await verifyWithSignals({
  apiKey:   process.env.HUMA_API_KEY!,   // huma_live_... (keep secret)
  userId:   req.body.userId,
  signals:  req.body.signals,            // forwarded from browser
});

if (result.human) { /* allow */ }

React hooks

import { useHuma } from "usehuma/react";

function SignupForm() {
  const { verify, collecting } = useHuma({
    apiKey: "huma_live_...",
    userId: currentUser.id,
  });

  async function handleSubmit() {
    const result = await verify();
    if (result.human) {
      // proceed
    }
  }

  return <button onClick={handleSubmit}>Sign up</button>;
}

HumaGate — drop-in protection

import { HumaGate } from "usehuma/react";

// Wraps any form — silently verifies on submit.
// Shows HumaChallenge automatically if confidence is uncertain.
<HumaGate
  apiKey="huma_live_..."
  userId={user.id}
  onPass={(result) => submitForm(result.token)}
  onBlock={() => showBotError()}
>
  <MyForm />
</HumaGate>

Session monitoring hook

import { useHumaSession } from "usehuma/react";

// Call after successful verify() to keep watching mid-session.
useHumaSession({
  apiKey:  "huma_live_...",
  userId:  user.id,
  interval: 45_000,                   // heartbeat every 45s
  onAnomaly: (action) => {
    if (action === "block") logout();  // account takeover detected
    if (action === "flag")  reVerify();
  },
});

Server helpers — usehuma/next v1.2+

Verify on the server, where your secret key stays secret. Works in Server Actions, Route Handlers and any Node/Edge runtime — no dependency on Next.js itself.

// Bot-check a form inside a Server Action
"use server";
import { humaFormCheck } from "usehuma/next";

export async function signUp(formData: FormData) {
  const check = await humaFormCheck(formData); // uses HUMA_API_KEY env
  if (!check.human) redirect("/signup?error=bot_detected");
  // ...create the account
}
// Protect a Route Handler
import { withHumaProtection } from "usehuma/next";

export const POST = withHumaProtection(async (req, huma) => {
  const body = await req.json();   // handler still gets the body
  return Response.json({ ok: true, confidence: huma.confidence });
});

Submissions with no behavioral signals (scripted direct POSTs) are rejected outright; API outages fail open so real users are never blocked. Full walkthrough: Next.js guide →

TypeScript types

import type { VerifyResult, SessionResult } from "usehuma";

// VerifyResult
// { human: boolean; confidence: number; score: number;
//   token: string; pii_stored: false }

// SessionResult
// { score: number; delta: number; anomaly: boolean;
//   action: "allow" | "flag" | "block"; notes: string[] }

Ready to integrate?

Get your API key in seconds. 14-day free trial. No credit card required.

GET YOUR API KEY →