Website integration

Callbacks

onVerified, onChange, the imnotahuman:verified and imnotahuman:change DOM events, and the exact payloads they carry.

onVerified

onVerified
const off = window.imnotahuman.onVerified((result) => {
  // result.verified === true
  fetch("/api/agent-session", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ token: result.token }),
  });
});
// later: off();
VerifiedResult
{
  "verified": true,
  "type": "agent",
  "agent_id": "agent_nova_8f2a",
  "agent_name": "Nova",
  "verification_method": "ed25519_challenge",
  "mode": "live",
  "challenge_id": "ch_mfr4q2xk7tbn3ycz5w6ah2ep4d",
  "token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6ImluaC1...",
  "issued_at": "2026-09-20T12:00:07.412Z",
  "expires_at": "2026-09-20T12:10:07.000Z"
}

verification_method is ed25519_challenge for registered agents, web_bot_auth for the experimental HTTP Message Signatures path, and simulated in test mode. token is null only if the token was already consumed or expired by the time the widget polled.

onChange

onChange
window.imnotahuman.onChange(({ status, challenge, result, error }) => {
  // status: "LOADING" | "PENDING" | "VERIFIED" | "REJECTED" | "EXPIRED" | "ERROR"
  // challenge: the challenge document or null
  // result: VerifiedResult when status === "VERIFIED", else null
  // error: an API error code such as "ORIGIN_UNAUTHORIZED" when status === "ERROR"
  document.body.dataset.agentCheck = status;
});

The listener runs immediately with the current snapshot, then on every transition.

DOM events

Both callbacks have DOM equivalents dispatched on the host element with bubbles: true, so you can listen at the document level without a reference to the widget:

CustomEvent
document.addEventListener("imnotahuman:verified", (event) => {
  const result = (event as CustomEvent).detail; // VerifiedResult
});

document.addEventListener("imnotahuman:change", (event) => {
  const { status, error } = (event as CustomEvent).detail;
});
EventdetailFires
imnotahuman:verifiedVerifiedResultOnce per verified challenge
imnotahuman:change{ status, challenge, result, error }On every status change

Multiple widgets

window.imnotahuman.onVerified and onChange subscribe to every widget on the page, including ones mounted later. getChallenge() and getResult() read the first widget only; use the instance returned by render() or the widgets array for others.

Security note

Callbacks are UI signals, not authorization

Anything a callback tells you can be forged by a script on the page. Use callbacks to update the UI and to forward the token; make decisions only after server verification.