Overview
Services that deal with automated traffic increasingly need to distinguish between “random bot” and “bot acting on behalf of a human”. AgentKit solves this by combining the wallet every x402 agent already has with World ID’s proof-of-personhood and an on-chain agent registry (the AgentBook).- Agents register in the AgentBook smart contract using a World ID proof, tying their wallet address to an anonymous human identifier
- When accessing a protected resource, agents sign a CAIP-122 challenge with their wallet
- The server verifies the signature, looks up the agent’s human identifier in the AgentBook, and applies the configured access policy
- Usage limits are tracked per human, not per agent, allowing for multiple agents to share a single human-backed identity
Access Modes
AgentKit supports three configurable modes that control what happens when a human-backed agent is identified:| Mode | Behavior |
|---|---|
free | Human-backed agents always bypass payment. |
free-trial | Human-backed agents bypass payment the first N times (default: 1). After that, normal payment is required. |
discount | Human-backed agents get an N% discount (optionally, only for the first N times). |
How It Works
- Client requests a protected resource
- Server responds with
402 Payment Required, including theagentkitextension with a CAIP-122 challenge (nonce, domain, supported chains, mode) - Client signs the challenge with their wallet and sends it via the
agentkitHTTP header - Server validates the signature, recovers the wallet address, and looks up the human identifier in the AgentBook
- If the agent is registered and the access mode allows it, access is granted or a discount is applied. Otherwise, the standard payment flow continues.
Server Usage
Hooks (Recommended)
The hooks-based approach handles challenge generation, signature verification, and AgentBook lookups automatically.Mode Examples
In Free access mode, human-backed agents never pay:onVerifyFailure hook on the facilitator recovers it by confirming the agent is human-backed with remaining discount uses, then adjusting the required amount so settlement re-verification passes.
Smart Wallet Support (EIP-1271 / EIP-6492)
To support contract wallets (Safe, Coinbase Smart Wallet, etc.), pass a viem public client’sverifyMessage function:
Custom AgentBook Configuration
createAgentBookVerifier() has a built-in mapping of known AgentBook deployments (currently Base Sepolia). The contract address and RPC endpoint are resolved automatically from the agent’s chainId. You can override the contract address and/or RPC for custom deployments:
Manual Usage (Advanced)
For custom flows, use the low-level functions directly:Multi-Chain Support
Servers can accept authentication from multiple blockchain ecosystems:Supported Chains
EVM (Ethereum, Base, Polygon, etc.)
- Chain ID format:
eip155:*(e.g.,eip155:8453for Base) - Signature type:
eip191 - Signature schemes:
eip191(EOA, default),eip1271(smart contract),eip6492(counterfactual) - Message format: EIP-4361 (SIWE)
Solana
- Chain ID format:
solana:*(e.g.,solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpfor mainnet) - Signature type:
ed25519 - Signature scheme:
siws - Message format: Sign-In With Solana
API Reference
declareAgentkitExtension(options?)
Configures the extension for 402 responses. Most parameters are auto-derived from request context when using agentkitResourceServerExtension.
| Parameter | Type | Description |
|---|---|---|
domain | string | Server’s domain. Auto-derived from request URL. |
resourceUri | string | Full resource URI. Auto-derived from request URL. |
network | string | string[] | CAIP-2 network(s). Auto-derived from accepts[].network. |
statement | string | Human-readable purpose for signing. |
version | string | CAIP-122 version (default: "1"). |
expirationSeconds | number | Challenge TTL in seconds. |
mode | AgentkitMode | Access mode (included in 402 response for clients). |
createAgentkitHooks(options)
Creates hooks for x402HTTPResourceServer and optionally x402ResourceServer.
| Option | Type | Description |
|---|---|---|
agentBook | AgentBookVerifier | AgentBook verifier instance (required). |
mode | AgentkitMode | Access mode (default: { type: "free" }). |
storage | AgentKitStorage | Storage for usage tracking (required for free-trial and discount). |
verifyOptions | AgentkitVerifyOptions | Signature verification options (e.g., smart wallet support). |
onEvent | (event: AgentkitHookEvent) => void | Callback for logging/debugging. |
| Field | Type | Description |
|---|---|---|
requestHook | function | Register with httpServer.onProtectedRequest(). |
verifyFailureHook | function | undefined | Register with facilitator.onVerifyFailure(). Only present for discount mode. |
AgentkitMode
| Mode | Fields | Description |
|---|---|---|
free | { type: "free" } | Always bypass payment for human-backed agents. |
free-trial | { type: "free-trial"; uses?: number } | Bypass payment the first N times (default: 1). |
discount | { type: "discount"; percent: number; uses?: number } | N% discount the first N times. |
createAgentBookVerifier(options?)
Creates a verifier that looks up agent wallet addresses in the AgentBook contract. Contract addresses are resolved from a built-in network→address mapping using the agent’s chainId, unless overridden. Throws if no deployment is known for the given chain and no custom address is configured.
| Option | Type | Description |
|---|---|---|
client | PublicClient | Custom viem public client. Overrides automatic client creation. |
contractAddress | `0x${string}` | Custom contract address. Overrides the built-in network→address mapping. |
rpcUrl | string | Custom RPC URL. Used when creating clients automatically (ignored if client is set). |
lookupHuman(address: string, chainId: string): Promise<string | null>. The chainId is a CAIP-2 identifier (e.g., "eip155:84532") used to resolve the contract address and RPC endpoint. Returns the anonymous human identifier (hex string) or null if the agent is not registered.
AgentKitStorage / InMemoryAgentKitStorage
Storage interface for tracking per-human usage counts.
| Method | Description |
|---|---|
getUsageCount(endpoint, humanId) | Get the usage count for a human on an endpoint. |
incrementUsage(endpoint, humanId) | Increment the usage count. |
hasUsedNonce?(nonce) | Optional: check for replay attacks. |
recordNonce?(nonce) | Optional: record a used nonce. |
InMemoryAgentKitStorage is the reference in-memory implementation. For production, implement AgentKitStorage with a persistent backend.
parseAgentkitHeader(header)
Parses a base64-encoded agentkit header into a structured payload object. Throws if the header is malformed or missing required fields.
validateAgentkitMessage(payload, resourceUri, options?)
Validates message fields including domain binding, URI, timestamps, and nonce.
| Option | Type | Description |
|---|---|---|
maxAge | number | Max age for issuedAt in ms (default: 5 minutes). |
checkNonce | (nonce: string) => boolean | Custom nonce validation function. |
{ valid: boolean; error?: string }.
verifyAgentkitSignature(payload, options?)
Verifies the cryptographic signature and recovers the signer address. Routes to EVM or Solana verification based on the chainId prefix.
| Option | Type | Description |
|---|---|---|
evmVerifier | EVMMessageVerifier | Pass publicClient.verifyMessage for smart wallet support. |
{ valid: boolean; address?: string; error?: string }.
Hook events:
| Event | Fields | Description |
|---|---|---|
agent_verified | resource, address, humanId | Agent is human-backed, access granted. |
agent_not_verified | resource, address | Valid signature but agent not registered in AgentBook. |
validation_failed | resource, error | Signature or message validation failed. |
discount_applied | resource, address, humanId | Discount mode: payment recovered at discounted rate. |
discount_exhausted | resource, address, humanId | Discount mode: no more discounted uses remaining. |
Security Considerations
- Domain binding: The signed message includes the server’s domain, preventing signature reuse across services.
- Nonce uniqueness: A fresh nonce is generated per request to prevent replay attacks.
- Temporal bounds:
issuedAtmust be recent (default: 5 minutes) andexpirationTimemust be in the future. - Chain-specific verification: Signatures are verified using chain-appropriate methods, preventing cross-chain reuse.
- Smart wallet support: Requires RPC calls to the wallet contract. Without a verifier, only EOA signatures are checked.
- On-chain verification: AgentBook lookups happen at request time, so revoked registrations take effect immediately.
- Per-human tracking: Usage limits are tracked by anonymous human identifier, not by wallet address. Multiple agents controlled by one person share a single counter.
Troubleshooting
Signature verification fails
- Verify the client is signing with the correct wallet
- Check the signature scheme matches (EIP-191 for EOA, EIP-1271 for smart wallets)
- Enable
evmVerifierif using smart wallets - Confirm the chain ID is consistent between client and server
Message validation fails
- Check that
issuedAtis recent (within 5 minutes by default) - Verify
expirationTimeis in the future - Ensure the
domainmatches the server’s hostname - Confirm the
uristarts with the server’s origin
AgentBook lookup returns null
- Verify the agent wallet has been registered in the AgentBook with a valid World ID proof
- Check that
createAgentBookVerifier()is configured for the correct chain - Ensure the RPC endpoint is reachable
- Confirm the contract address is correct for the target network