Verifying agents in a service
On this page
This guide adds Self Agent ID verification to an API. Afterwards, only registered, human-backed agents with a fresh proof reach your protected routes. Verification reads the registry on-chain, with no API key and no call to Self.
1. Install the SDK
npm install @selfxyz/agent-sdk
Not on Node.js? Implement the check yourself from the signing scheme.
2. Create a verifier
import { SelfAgentVerifier } from '@selfxyz/agent-sdk';
const verifier = SelfAgentVerifier.create()
.network('mainnet') // default; use 'testnet' for agents registered on Celo Sepolia
.requireAge(18)
.requireOFAC()
.sybilLimit(3) // at most 3 agents per human
.rateLimit({ perMinute: 10 })
.build();
All builder methods and their defaults are in Verifier options.
3. Add the middleware
The signature covers the exact request body bytes, so keep the raw body:
import express from 'express';
const app = express();
app.use(
express.json({
verify: (req, _res, buf) => {
(req as { rawBody?: Buffer }).rawBody = buf;
},
}),
);
app.use('/api', verifier.auth());
app.post('/api/data', (req, res) => {
const agent = req.agent!; // VerifiedAgent
res.json({ agentId: agent.agentId.toString(), address: agent.address });
});
Don’t mutate or re-serialize the body before verifying. auth() rejects failed checks with 401. The status codes and the fields of req.agent are listed in Responses from auth(). Without Express, call verifier.verify(...) (Without Express).
4. Restrict by credentials
const verifier = SelfAgentVerifier.create()
.requireAge(21)
.requireOFAC()
.requireNationality('USA', 'GBR', 'DEU')
.includeCredentials()
.build();
app.post('/api/restricted', verifier.auth(), (req, res) => {
const { nationality, olderThan } = req.agent!.credentials!;
res.json({ nationality, olderThan: olderThan.toString() });
});
An agent passes only if its human disclosed those credentials at registration.
5. Set sybil and provider policy
.sybilLimit(1)(default) allows one agent per human,.sybilLimit(n)allowsn, and.sybilLimit(0)removes the cap (group byreq.agent.nullifierinstead)..requireSelfProvider()is on by default, so the proof must come from Self’s provider. Turn it off only if you intend to accept other approved providers.
6. Test before going live
Run these checks against a registered agent:
| Check | Action | Expected |
|---|---|---|
| Tampering | Sign body A, send body B with the same headers | 401 Invalid signature |
| Expiry | Send a timestamp older than maxAge (5 min) | 401 timestamp rejection |
| Replay | Send the identical signed request twice | First accepted, second 401 |
Replay protection and rate limits are in memory, per process. If you run several instances, also enforce them in a shared layer such as your gateway.
Next steps
Thanks. What went wrong?
Thanks for your feedback!