Skip to content

Start typing to search the documentation.

Verification patterns

On this page

Every pattern ends in the same check: the agent is verified when hasHumanProof && isProofFresh on the registry.

PatternThe agentThe verifier
Agent-to-serviceSigns HTTP requestsRecovers the signer and checks the registry (SelfAgentVerifier)
Agent-to-agentSigns requests and responsesBoth sides verify each other, and can check for a shared human
Agent-to-chainCalls a contract as msg.senderThe contract reads the registry
LookupShares its agent ID or addressReads the registry, the REST API, A2A or MCP, with no signature

Agent-to-service

Agent                                Service
  │── POST /api (signed) ─────────────▶│
  │   x-self-agent-address              │── recover signer from the signature
  │   x-self-agent-signature            │── agentKey = zeroPad(signer, 32)
  │   x-self-agent-timestamp            │── isVerifiedAgent + isProofFresh
  │                                     │── provider, sybil, credential policy
  │◀── 200 OK ──────────────────────────│

The agent calls agent.fetch(). The service uses verifier.auth() (Express) or verifier.verify(). Options are in SDK integration, and the setup walkthrough is in Verifying agents.

Agent-to-agent

Both agents sign their messages with SelfAgent and verify the other’s with SelfAgentVerifier. To find out whether two agents are backed by the same human, without learning who that human is:

  • compare req.agent.nullifier (or getInfo().nullifier) of the two agents, or
  • call sameHuman(agentIdA, agentIdB) on the registry.

Agent-to-chain

The agent sends a transaction from its own EVM key, and the contract derives the agent key from msg.sender:

bytes32 agentKey = bytes32(uint256(uint160(msg.sender)));
require(
    registry.isVerifiedAgent(agentKey) && registry.isProofFresh(registry.getAgentId(agentKey)),
    "Agent not human-verified"
);

isVerifiedAgent alone does not check expiry, so always pair it with isProofFresh. See Gating smart contracts.

Lookup without a signature

When you only need to know whether an agent ID or key is verified (dashboards, allowlists, A2A peers):

  • SDK: verifyAgent(agentKey, { network })
  • REST: GET /api/agent/verify/{chainId}/{agentId} or /api/verify-status/{chainId}/{agentId} (Agent queries)
  • A2A: the verify or freshness intent
  • MCP: self_verify_agent

A lookup proves the agent exists and is human-backed, not that the caller controls it. For access control, verify a signature.

Sybil resistance

Each human has one nullifier, and the registry caps active agents per human (maxAgentsPerHuman, default 1). Services set their own limit on top:

PolicyBuilder
One agent per human.sybilLimit(1) (default)
Up to N agents per human.sybilLimit(n)
No limit (detection only).sybilLimit(0), then compare nullifiers or call sameHuman()

Credential policies

Require ZK-attested credentials disclosed at registration:

const verifier = SelfAgentVerifier.create()
  .requireAge(18) // the human proved 18+
  .requireOFAC() // passed OFAC screening
  .requireNationality('USA', 'GBR') // ISO 3166-1 alpha-3
  .build();

An agent passes a credential check only if the human disclosed that credential when registering, so pick the matching verification config at registration.

Was this page helpful?