Skip to content

Start typing to search the documentation.

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) allows n, and .sybilLimit(0) removes the cap (group by req.agent.nullifier instead).
  • .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:

CheckActionExpected
TamperingSign body A, send body B with the same headers401 Invalid signature
ExpirySend a timestamp older than maxAge (5 min)401 timestamp rejection
ReplaySend the identical signed request twiceFirst 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

Was this page helpful?