The Problem: Who Just Called Your API?
AI agents are calling other agents' APIs now. No browser session. No OAuth consent screen. No CAPTCHA. An HTTP request arrives at your endpoint from an agent you have never seen before, carrying a wallet address and asking for data.
How do you know it is legitimate?
Protocols like x402 solve the payment question. An agent proves it can pay by signing a USDC transaction on Base. But payment alone does not prove identity. A fresh wallet with $5 in it can pay for your API just fine. That tells you nothing about who is behind it, how long they have been operating, or whether other agents trust them.
You need a trust layer. And you can add one to an Express API in 3 lines. Let me show you.
The 3 Lines
Here is a standard Express endpoint with no verification:
import express from 'express';
const app = express();
app.get('/api/data', (req, res) => {
res.json({ data: 'anyone can access this' });
});
app.listen(3000);
Now, the protected version:
import express from 'express';
import { requireStamp } from 'agentstamp-verify/express'; // Line 1
const app = express();
app.use('/api', requireStamp({ minTier: 'bronze' })); // Line 2
app.get('/api/data', (req, res) => {
// Line 3: req.agent is now available with verified identity
res.json({ data: 'protected', agent: req.agent });
});
app.listen(3000);
That is it. Every request to /api/* now requires a valid AgentStamp.
Line 1 imports the Express middleware from agentstamp-verify/express. The SDK ships separate entry points for Express, Hono, and a framework-agnostic core client.
Line 2 mounts the middleware on your route prefix. The minTier option sets the minimum stamp tier required. Agents without a valid stamp get a structured 403 response with an error code, the requirements they failed, and a link to register.
Line 3 is not really a line you write -- it is what you get. After verification, req.agent contains the full verified identity: wallet address, stamp tier, endorsement count, agent metadata, and registration status. Your handler just reads it.
If you are using Hono instead of Express:
import { Hono } from 'hono';
import { requireStamp } from 'agentstamp-verify/hono';
const app = new Hono();
app.use('/api/*', requireStamp({ minTier: 'silver' }));
app.get('/api/data', (c) => {
const agent = c.get('agent');
return c.json({ data: 'protected', agent });
});
Same concept. The Hono middleware sets the agent on the context via c.set('agent', ...) instead of mutating the request object.
Under the Hood
Three lines of setup, but there is real machinery underneath.
Wallet Extraction
The middleware needs to know which wallet is making the request. The default extractor checks these sources in order:
-
x-wallet-addressheader (the standard way) -
x-erc8004-agent-idheader (ERC-8004 on-chain agent ID, resolved via bridge) -
x-paymentheader (base64 JSON decoded to extractpayerorwallet) -
walletquery parameter -
wallet_addressorwalletin request body
This ordering means agents using the x402 payment protocol work out of the box -- their payment headers contain the wallet address, and the middleware extracts it automatically. You can also supply a custom walletExtractor function if your agents identify themselves differently.
Caching
Every verification hits the AgentStamp registry API. To avoid hammering it on every request, the SDK uses an LRU cache with a 300-second TTL and a maximum of 1,000 entries. The cache is per-middleware-instance, so each call to requireStamp() gets its own isolated cache. Set cacheTTL: 0 to disable caching entirely, or pass a custom TTL in seconds.
Tier Hierarchy
Stamps come in three tiers: bronze, silver, and gold. The tier check is hierarchical -- a gold stamp passes a minTier: 'bronze' gate, but a bronze stamp fails a minTier: 'silver' gate. Internally, tiers are compared as numeric values (bronze: 1, silver: 2, gold: 3).
Structured Error Responses
When verification fails, the middleware returns a JSON body with a machine-readable error code, the requirements the agent failed to meet, and links to register:
{
"error": "Valid AgentStamp required. Minimum tier: gold",
"code": "INSUFFICIENT_TIER",
"required": { "minTier": "gold", "minEndorsements": 0, "registered": false },
"register": "https://agentstamp.org",
"docs": "https://agentstamp.org/docs"
}
Error codes include NO_WALLET, NO_STAMP, STAMP_EXPIRED, STAMP_REVOKED, INSUFFICIENT_TIER, INSUFFICIENT_ENDORSEMENTS, AGENT_INACTIVE, and SERVICE_UNAVAILABLE. Each maps to either a 403 or 503 HTTP status.
Fail-Open vs Fail-Closed
By default, the middleware fails closed: if the AgentStamp API is unreachable, requests are blocked with a 503. For non-critical endpoints where availability matters more than strict verification, set failOpen: true. The request goes through, but req.agent is set to a frozen sentinel object with verified: false so your handler can degrade gracefully.
Trust Scores: Beyond Binary Verification
A stamp tells you an agent is registered. But how much should you trust it? AgentStamp computes a 0-100 reputation score from six factors:
| Factor | Max Points | What It Measures |
|---|---|---|
| Stamp tier | 30 | bronze (10), silver (20), gold (30) |
| Endorsements | 30 | Each endorsement adds 5 points, capped at 30 |
| Uptime | 20 | Heartbeat consistency, decayed by recency |
| Momentum | 15 | Early activity in first 30 days |
| Wishes | 5 | Community contributions (wishes granted) |
| Wallet verified | 5 | On-chain wallet ownership proof |
Scores map to labels: new (0-25), emerging (26-50), established (51-75), elite (76-100).
The uptime score decays based on heartbeat recency. An agent that stops sending heartbeats loses uptime points progressively: full credit within 3 days, 75% within 7 days, 50% within 14 days, 25% within 30 days. Beyond 30 days of silence, the uptime component is zeroed out.
Why does this matter for trust? Because every stamp costs real USDC. Minting a bronze stamp, endorsing other agents, granting wishes -- these are all on-chain transactions with real cost. A Sybil attacker would need to spend actual money across multiple wallets, build endorsement history, maintain heartbeats, and wait for momentum to accrue. The economic cost of faking a high reputation score is the defense mechanism.
Use tiered access to give more-trusted agents access to better resources:
// Bronze: basic data
app.get('/api/basic', requireStamp({ minTier: 'bronze' }), handler);
// Silver: enhanced access
app.get('/api/standard', requireStamp({ minTier: 'silver' }), handler);
// Gold + endorsements: premium data with community trust
app.get('/api/premium', requireStamp({
minTier: 'gold',
minEndorsements: 3,
requireRegistered: true,
}), handler);
The x402 Power Combo: Trust + Payment on One Endpoint
The most interesting pattern combines identity verification with micropayments. An agent must both be trusted and pay for access.
import express from 'express';
import { paymentMiddleware } from '@x402/express';
import { requireStamp } from 'agentstamp-verify/express';
const app = express();
const paidRoutes = {
'GET /api/data': { price: '$0.001', network: 'solana:mainnet' },
};
// Order matters: requireStamp first, then paymentMiddleware
app.use('/api',
requireStamp({ minTier: 'bronze', x402: true }),
paymentMiddleware(paidRoutes, resourceServer)
);
app.get('/api/data', (req, res) => {
res.json({ message: 'Verified and paid', agent: req.agent });
});
The x402: true flag makes the two middlewares cooperate. When a request carries a payment header, AgentStamp steps aside and lets x402 handle it. When a request carries only a wallet header with no payment, AgentStamp verifies the stamp. This covers x402's two-step flow: first a discovery request gets the 402 payment requirements, then the payment request settles, and identity-only requests are still gated by the stamp.
Get Started
Install the SDK:
npm install agentstamp-verify
Register your agent at agentstamp.org to get a wallet address and stamp.
For programmatic access, the AgentStamp MCP server at agentstamp.org/mcp exposes tools for stamp verification, agent search, endorsement management, and trust checks -- useful if you are building agents that need to verify other agents inside tool-calling workflows.
Your API already validates JWTs for humans. It is time to validate identity for agents too.
AgentStamp is an open identity registry for AI agents. The SDK is MIT-licensed and available on npm.
Top comments (0)