From agentkit
Handles 402 Payment Required responses with AgentKit extension: checks for extension, constructs/signs CAIP-122 challenges (SIWE for EVM) with EOA (EIP-191) or SCW (ERC-1271) wallets, sends auth header for free/discounted access.
npx claudepluginhub worldcoin/agentkitThis skill uses the workspace's default tool permissions.
This skill is for AI agents that interact with websites and APIs protected by x402 + AgentKit. It tells you how to prove you are backed by a real human so you can get free or discounted access instead of paying.
Integrates @worldcoin/agentkit into x402 servers or facilitator flows: selects free/free-trial/discount modes, wires EVM chain payments, handles ExactEvmScheme parsing, completes end-to-end setups.
References Ethereum standards including ERC-20, ERC-721, ERC-1155, ERC-4337, ERC-8004 for tokens, NFTs, agents. Details usage, interfaces, contracts, EIP-7702, x402.
Integrates Privy for authentication, embedded/smart wallets (ERC-4337), and agent payments (x402/MPP) in React/Next.js apps and Node.js backends. Use for crypto auth, Tempo chain, wallet policies, or agentic flows.
Share bugs, ideas, or general feedback.
This skill is for AI agents that interact with websites and APIs protected by x402 + AgentKit. It tells you how to prove you are backed by a real human so you can get free or discounted access instead of paying.
When you receive a 402 Payment Required response, do not immediately proceed to payment. First inspect the response for an agentkit extension. If one is present, attempt the AgentKit authentication flow below. Only fall back to payment if:
agentkit extension in the 402 responseIf you can change the agent's HTTP client, use createAgentkitClient from @worldcoin/agentkit and route x402 API calls through agentkit.fetch. This makes the "try AgentKit before payment" rule deterministic instead of relying on the model to remember it.
import { createAgentkitClient } from '@worldcoin/agentkit'
const agentkit = createAgentkitClient({
signer: {
address: wallet.address,
chainId: 'eip155:8453',
type: 'eip191',
signMessage: message => wallet.signMessage(message),
},
})
const response = await agentkit.fetch('https://api.example.com/data')
If agentkit.fetch returns another 402, continue with the normal x402 payment flow.
Your wallet determines how you sign the challenge. There are two types:
A standard wallet where you directly hold the private key (e.g. a raw private key, a mnemonic-derived wallet).
eip191personal_sign (EIP-191) to sign the SIWE messagewallet.signMessage(siweMessage)A wallet where the "account" is a smart contract and signing is done by an underlying owner key. The server verifies your signature on-chain via the contract's isValidSignature method (ERC-1271).
Examples: Coinbase Smart Wallet, Safe, any ERC-4337 account.
eip1271account.signMessage({ message: siweMessage })If you are unsure which type your wallet is: if you created it from a private key or mnemonic, it is an EOA. If you created it through a wallet SDK (Coinbase CDP, Safe SDK, etc.), it is likely an SCW.
When you receive a 402 Payment Required, look for the agentkit extension in the response body. The 402 response contains x402 data with extensions. The agentkit extension looks like:
{
"agentkit": {
"info": {
"domain": "api.example.com",
"uri": "https://api.example.com/data",
"version": "1",
"nonce": "abc123",
"issuedAt": "2025-01-01T00:00:00.000Z",
"statement": "Verify your agent is backed by a real human"
},
"supportedChains": [
{ "chainId": "eip155:8453", "type": "eip191" },
{ "chainId": "eip155:8453", "type": "eip1271" }
],
"schema": { ... }
}
}
Key fields to extract:
info — the challenge data you must signsupportedChains — which chains and signature types the server acceptsmode (if present) — tells you the access policy: free, free-trial, or discountMatch your wallet to one of the supportedChains entries:
| Your wallet | Match chainId | Use type |
|---|---|---|
| EVM EOA | eip155:* | eip191 |
| EVM Smart Contract | eip155:* | eip1271 |
Pick the entry that matches both your chain and wallet type.
Construct a SIWE (EIP-4361) message string from the challenge info fields. The format is a plain text string with this exact structure:
{domain} wants you to sign in with your Ethereum account:
{address}
{statement}
URI: {uri}
Version: {version}
Chain ID: {numericChainId}
Nonce: {nonce}
Issued At: {issuedAt}
Where {numericChainId} is extracted from the CAIP-2 chain ID (e.g. eip155:8453 becomes 8453), and {address} must be EIP-55 checksummed.
If the challenge includes optional fields, append them in this order (only include lines for fields that are present):
Expiration Time: {expirationTime}
Not Before: {notBefore}
Request ID: {requestId}
Resources:
- {resources[0]}
- {resources[1]}
Full example:
api.example.com wants you to sign in with your Ethereum account:
0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
Verify your agent is backed by a real human
URI: https://api.example.com/data
Version: 1
Chain ID: 8453
Nonce: abc123def
Issued At: 2025-01-01T00:00:00.000Z
Expiration Time: 2025-01-01T00:05:00.000Z
Request ID: req-456
Resources:
- https://api.example.com/tos
Important formatting rules:
{statement} lineURI:\n (LF, not CRLF)Then sign the message string:
// EOA — use personal_sign (EIP-191)
const signature = await wallet.signMessage(messageToSign)
// SCW — use the wallet SDK's signMessage
const signature = await smartWallet.signMessage({ message: messageToSign })
Base64-encode a JSON object containing the challenge info fields plus your address, type, and signature, and send it as the agentkit HTTP header:
const header = btoa(JSON.stringify({
...info, // all fields from the challenge
address: walletAddress,
chainId: "eip155:8453", // from the supportedChains entry you picked
type: "eip191", // "eip191" for EOA, "eip1271" for SCW
signature: signature, // hex string from signing
}))
const response = await fetch("https://api.example.com/data", {
headers: {
"agentkit": header,
},
})
If the server grants access (based on the mode), you will receive the resource directly — no payment needed.
If the server responds with another 402 or an error, fall back to the normal x402 payment flow.
The mode field in the AgentKit extension tells you what to expect:
| Mode | What it means |
|---|---|
free | Human-backed agents always get free access |
free-trial | First N requests are free, then you must pay (N is per human, not per agent) |
discount | You get N% off the price (pay the discounted amount via x402) |
For discount mode: send both the agentkit header and the x402 payment header, but pay the discounted price. The server will reconcile the underpayment using your human-backed status.
Your wallet address is not registered. You need to register first using the AgentKit CLI:
npx @worldcoin/agentkit-cli register <your-wallet-address>
This opens a World ID verification flow that ties your wallet to an anonymous human identifier on-chain. Registration only needs to happen once per wallet.
type matches your wallet. Use eip191 for EOA, eip1271 for SCW.chainId in the payload must be CAIP-2 format (eip155:8453), but the SIWE message chainId field must be the numeric chain ID (8453).Your header is not properly base64-encoded. Ensure you are encoding the full JSON string: btoa(JSON.stringify(payload)).
The challenge has expired. Re-fetch the 402 response to get a fresh challenge (new nonce, new issuedAt) and sign again. Challenges expire after 5 minutes by default.
You are using a chain that the server does not support. Check supportedChains in the 402 response and pick a chain/type pair that matches your wallet.