Protocol
Verification flow
The complete request sequence between host, agent and imnotahuman.ai, including registration challenges and token issuance.
Sequence
Host page imnotahuman.ai Agent
│ POST /api/v1/challenges │ │
│ Origin: https://example.com │ │
│ {site_key, action, intent} │ │
│─────────────────────────────▶│ │
│ 201 ChallengePublic │ │
│◀─────────────────────────────│ │
│ (widget polls GET /challenges/:id every 2 s) │
│ │ GET /api/v1/challenges/:id │
│ │◀────────────────────────────────│
│ │ 200 ChallengePublic │
│ │────────────────────────────────▶│
│ │ sign(base) with Ed25519
│ │ POST /api/v1/verify {proof} │
│ │◀────────────────────────────────│
│ │ 200 VerificationResult + token │
│ │────────────────────────────────▶│
│ 200 {status: VERIFIED, result: {..., token}} │
│◀─────────────────────────────│ │
│ (host backend) POST /api/v1/tokens/verify {token, site_key, consume:true}
│─────────────────────────────▶│ │
│ 200 {verified: true, ...} │ │
│◀─────────────────────────────│ │Proof providers
POST /api/v1/verify dispatches to the first provider that recognises the request:
| Provider | Recognised by | Method in result |
|---|---|---|
| Registry (primary) | JSON body with string signature and agent_id | ed25519_challenge |
| Web Bot Auth (experimental) | Signature-Agent, Signature-Input and Signature headers present | web_bot_auth |
If neither matches the response is 400 SIGNATURE_MALFORMED ("No supported proof was found in the request."). Test-mode simulation uses a separate endpoint and reports method simulated.
Consumption and token issuance
After a proof passes, the server consumes the challenge with a conditional update (status must still be PENDING). Exactly one proof can win; a concurrent one receives CHALLENGE_CONSUMED. Then, for purpose: "verification" challenges, a token is issued with jti equal to the challenge id and stored so it can be consumed once. Registration challenges activate the key instead and return token: null.
The result is also attached to the challenge: GET /challenges/:id returns result with the token while it is unconsumed and unexpired, which is how the widget learns the outcome.
Registration flow
- Operator uploads a public key (
POST /agents/:id/keysorPOST /registration/keys). The key ispending. - The server creates a challenge with
purpose: "registration", originhttps://imnotahuman.ai, actionregister_key, intent = the key id, TTL 15 minutes, bound to that key. - The agent signs it with the pending key and POSTs to
/verify. Any other key getsKEY_PENDINGorKEY_UNKNOWN. - The key becomes active; a pending agent becomes active. No token is issued.
Events
Every accepted or rejected proof is recorded as a verification event with result (VERIFIED or REJECTED), failure_reason (an error code), method, mode, origin and, when known, the agent. Site owners see events for their sites and agent owners for their agents via GET /api/v1/events and the dashboard.