API reference

Verification

Submit a proof for a challenge, verify and consume tokens, and fetch the JWKS.

Base URL https://imnotahuman.ai/api/v1. Request bodies must be JSON with Content-Type: application/json (else 415) and at most 32,000 bytes. Bodies are validated strictly: unknown fields fail with VALIDATION_FAILED. Every response carries X-Request-Id (echoing yours if you send one) and Cache-Control: no-store except JWKS. Errors are { "error": { "code", "message", "details"? } }; see Errors.

Submit a proof

POST/api/v1/verify
curl
curl -X POST https://imnotahuman.ai/api/v1/verify \
  -H 'content-type: application/json' \
  -d '{ "challenge_id": "ch_mfr4q2xk7tbn3ycz5w6ah2ep4d", "agent_id": "agent_nova_8f2a", "key_id": "key_b7n2xq4mfc3rtw6y", "signature": "0IKKR9d3yZ4MIdTuKMNO99q-fF8kC5kp1Vak61hrQb1TEBhlzCDtkZxq7Ho1m5IjetPefqSAbrkDrHnueQhlCw", "created": 1789905607 }'
FieldTypeDescription
challenge_idrequiredstringch_… id of the challenge being proven.
agent_idrequiredstringRegistered agent id.
key_idrequiredstringKey id of the signing key.
signaturerequiredstringbase64url Ed25519 signature over the signature base (86 chars).
createdrequiredintegerUnix seconds used in @signature-params.

Alternatively send RFC 9421 headers and a body of {"challenge_id"}; see Web Bot Auth compatibility.

200 OK
{
  "verified": true,
  "status": "VERIFIED",
  "subject_type": "agent",
  "mode": "live",
  "agent": { "id": "agent_nova_8f2a", "name": "Nova" },
  "verification": {
    "method": "ed25519_challenge",
    "verified_at": "2026-09-20T12:00:07.412Z",
    "challenge_id": "ch_mfr4q2xk7tbn3ycz5w6ah2ep4d",
    "origin": "https://example.com",
    "action": "checkout",
    "intent": "purchase"
  },
  "token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6ImluaC1...",
  "expires_at": "2026-09-20T12:10:07.000Z"
}
ErrorStatusWhen
SIGNATURE_MALFORMED400Body does not match the proof schema, no supported proof found, or signature not decodable
CHALLENGE_NOT_FOUND404Unknown challenge id
CHALLENGE_EXPIRED410Past expires_at
CHALLENGE_CONSUMED409Already VERIFIED
CHALLENGE_NOT_PENDING409REJECTED or otherwise not pending
CHALLENGE_MISMATCH400Body challenge_id differs from the challenge; or Web Bot Auth nonce differs
AGENT_UNKNOWN / AGENT_DISABLED / AGENT_REVOKED404 / 403 / 403Agent state
KEY_UNKNOWN / KEY_REVOKED / KEY_PENDING / KEY_INVALID404 / 403 / 403 / 400Key state
SIGNATURE_EXPIRED400created outside the accepted window
SIGNATURE_INVALID400Ed25519 verification failed
SIGNATURE_AGENT_UNAVAILABLE502Web Bot Auth key directory could not be fetched
RATE_LIMITED429240/min per network, 600/min per agent, 600/min per site, 30/min per challenge (Retry-After: 60), or the 11th attempt (challenge becomes REJECTED)

Verify a token

POST/api/v1/tokens/verify
curl
curl -X POST https://imnotahuman.ai/api/v1/tokens/verify \
  -H 'content-type: application/json' \
  -d '{"token":"<jwt>","site_key":"site_live_exampleweb2ab3cd4ef5gh6j","consume":true,"allow_test":false}'
FieldTypeDescription
tokenrequiredstringThe verification JWT (20–8192 characters).
site_keystringIf set, aud must equal it (TOKEN_SITE_MISMATCH otherwise).
consumebooleanMark the token used. Default false.
allow_testbooleanAccept mode "test" tokens. Default false.
200 OK
{
  "verified": true,
  "status": "VERIFIED",
  "subject_type": "agent",
  "mode": "live",
  "site_key": "site_live_exampleweb2ab3cd4ef5gh6j",
  "agent": { "id": "agent_nova_8f2a", "name": "Nova" },
  "verification": {
    "method": "ed25519_challenge",
    "verified_at": "2026-09-20T12:00:07.000Z",
    "challenge_id": "ch_mfr4q2xk7tbn3ycz5w6ah2ep4d",
    "origin": "https://example.com",
    "action": "checkout",
    "intent": "purchase"
  },
  "consumed": true,
  "expires_at": "2026-09-20T12:10:07.000Z"
}
ErrorStatusWhen
TOKEN_INVALID400Malformed, bad signature, unknown kid, wrong issuer/claims, or no server record
TOKEN_EXPIRED400exp has passed
TOKEN_SITE_MISMATCH400aud differs from site_key
TOKEN_TEST_MODE400Test token without allow_test
TOKEN_ALREADY_USED409Previously consumed
RATE_LIMITED429600/min per network or 600/min per site key

JWKS

GET/api/v1/.well-known/jwks.json
curl
curl https://imnotahuman.ai/.well-known/jwks.json
# same document as /api/v1/.well-known/jwks.json and /api/v1/jwks
200 OK
{
  "keys": [
    {
      "kty": "OKP",
      "crv": "Ed25519",
      "x": "_Gqb_W0_oiqrRCCw8P_X8DSeNSwTiMpA4rUS3OqqBZk",
      "kid": "inh-q2Xk7tBn",
      "use": "sig",
      "alg": "EdDSA"
    }
  ]
}

Cache-Control: public, max-age=300. Keys are OKP/Ed25519 with use: "sig" and alg: "EdDSA". Each address is limited to 300 requests per minute on a single instance; cache the document.