Protocol

Challenge format

Every field of the challenge document and its public view, with types, formats and which fields are covered by the signature.

Example

GET /api/v1/challenges/:id
{
  "challenge_id": "ch_mfr4q2xk7tbn3ycz5w6ah2ep4d",
  "nonce": "aW1ub3RhaHVtYW4tZG9jcy1leGFtcGxlLW5vbmNlISE",
  "site_key": "site_live_exampleweb2ab3cd4ef5gh6j",
  "origin": "https://example.com",
  "issued_at": 1789905600,
  "expires_at": 1789905720,
  "action": "checkout",
  "intent": "purchase",
  "mode": "live",
  "status": "PENDING",
  "purpose": "verification",
  "challenge_url": "https://imnotahuman.ai/c/ch_mfr4q2xk7tbn3ycz5w6ah2ep4d",
  "verify_url": "https://imnotahuman.ai/api/v1/verify"
}

Fields

The first nine fields form the ChallengeDocument: the exact set bound into the signature. The rest are metadata.

FieldTypeDescription
challenge_idrequiredstringch_ + 26 base32 characters (a–z, 2–7): 128 bits of server-generated entropy. Unguessable; treat as a capability handle.
noncerequiredstringbase64url of 32 random bytes, generated per challenge. Public; it provides freshness, not secrecy.
site_keyrequiredstringThe site key that created the challenge (site_live_… or site_test_…).
originrequiredstringNormalised Origin header of the creating request, e.g. https://example.com. No path, port only if present in the allowed origin.
issued_atrequiredintegerUnix seconds.
expires_atrequiredintegerUnix seconds; issued_at + ttl (default 120 s).
actionrequiredstringHost-declared action, ≤ 64 characters of [A-Za-z0-9_.:-]. Empty string when unset.
intentrequiredstringHost-declared intent, same constraints. Empty string when unset.
moderequired"live" | "test"Derived from the site key prefix.
statusChallengeStatusPENDING, VERIFIED, REJECTED, EXPIRED (REVOKED and ERROR are reserved).
purpose"verification" | "registration"Registration challenges are bound to one pending key and issue no token.
challenge_urlstringhttps://imnotahuman.ai/c/<challenge_id>: the canonical URL an agent can be pointed at.
verify_urlstringhttps://imnotahuman.ai/api/v1/verify: where to POST the proof.
resultVerificationResultPresent only when status is VERIFIED.

Identifier formats

IdentifierPatternExample
Challengech_[a-z2-7]{26}ch_mfr4q2xk7tbn3ycz5w6ah2ep4d
Agentagent_[a-z0-9]{1,24}_[a-z0-9]{4,8}agent_nova_8f2a
Keykey_[a-z2-7]{16}key_b7n2xq4mfc3rtw6y
Sitesite_[a-z2-7]{16}site_c4n7xq2mfb3ktw6y
Site keysite_(live|test)_[a-z2-7]{20,26}site_live_exampleweb2ab3cd4ef5gh6j
Registration tokenreg_[a-z2-7]{32}reg_…
Eventev_[a-z2-7]{20}ev_…

Discovery on a page

The widget publishes the challenge document in a <script type="application/json" data-imnotahuman-challenge> element inside its host, and sets data-challenge-id and data-challenge-url on the host. An agent operating a browser can read either and then follow the API flow.