KYA Integration Patterns¶
This page describes four proven patterns for integrating KYA (Know Your Agent) checks into your infrastructure. Choose the pattern that matches your architecture.
| Pattern | Best For | Complexity | Latency |
|---|---|---|---|
| API Gateway | AI/cloud providers, SaaS platforms | Low | ~50ms |
| Smart Contract Guard | DeFi protocols, on-chain services | Medium | 0 (on-chain) |
| DNS/Domain Verification | Web infrastructure, domain registrars | Medium | One-time |
| Webhook Notification | Compliance monitoring, revocation | Medium | Event-driven |
Pattern 1: API Gateway¶
Recommended for: AI/cloud providers, API platforms, SaaS services
Add a middleware layer to your API gateway that intercepts agent requests, performs a KYA check, and either allows or rejects the request before it reaches your backend.
Express.js Middleware¶
const KYA_API = process.env.KYA_API || "https://api.theagentregistry.org";
// In-memory cache (60s TTL matching the API cache)
const kyaCache = new Map();
const KYA_CACHE_TTL = 60 * 1000;
async function kyaMiddleware(req, res, next) {
const wallet = req.headers["x-agent-wallet"];
// If no agent wallet header, this is a human request — pass through
if (!wallet) return next();
// Check cache first
const cached = kyaCache.get(wallet.toLowerCase());
if (cached && Date.now() < cached.expiresAt) {
if (!cached.data.isRegistered || !cached.data.isCompliant) {
return res.status(403).json({
error: "KYA check failed",
details: cached.data,
});
}
req.agentInfo = cached.data.agent;
return next();
}
// Query the Agentenregister API
try {
const response = await fetch(`${KYA_API}/api/v1/kya/${wallet}`);
const kya = await response.json();
// Cache the result
kyaCache.set(wallet.toLowerCase(), {
data: kya,
expiresAt: Date.now() + KYA_CACHE_TTL,
});
if (!kya.isRegistered || !kya.isCompliant) {
return res.status(403).json({
error: "KYA check failed",
details: kya,
});
}
// Attach agent info to request for downstream use
req.agentInfo = kya.agent;
next();
} catch (err) {
// Fail open or fail closed — your choice
console.error("KYA check error:", err.message);
return res.status(503).json({
error: "KYA service unavailable",
});
}
}
// Apply to all routes
app.use(kyaMiddleware);
// Or apply to specific routes
app.post("/api/v1/provision", kyaMiddleware, (req, res) => {
console.log(`Serving agent #${req.agentInfo.agentId}`);
res.json({ status: "provisioned" });
});
Fail-open vs. fail-closed
Decide how your service should behave when the KYA API is unreachable:
- Fail-closed (recommended for financial/compliance-critical services): Deny the request if KYA cannot be verified.
- Fail-open (acceptable for non-critical services): Allow the request but log a warning for later review.
Python (FastAPI)¶
from fastapi import FastAPI, Request, HTTPException
import httpx
from cachetools import TTLCache
app = FastAPI()
kya_cache = TTLCache(maxsize=10000, ttl=60)
KYA_API = "https://api.theagentregistry.org"
async def kya_check(wallet: str) -> dict:
cached = kya_cache.get(wallet.lower())
if cached:
return cached
async with httpx.AsyncClient() as client:
resp = await client.get(f"{KYA_API}/api/v1/kya/{wallet}")
resp.raise_for_status()
kya = resp.json()
kya_cache[wallet.lower()] = kya
return kya
@app.middleware("http")
async def kya_middleware(request: Request, call_next):
wallet = request.headers.get("x-agent-wallet")
if not wallet:
return await call_next(request)
kya = await kya_check(wallet)
if not kya["isRegistered"] or not kya["isCompliant"]:
raise HTTPException(status_code=403, detail=f"KYA check failed: {kya}")
request.state.agent_info = kya["agent"]
return await call_next(request)
Pattern 2: Smart Contract Guard¶
Recommended for: DeFi protocols, DAOs, on-chain marketplaces
For smart contracts that interact with agents on-chain, you can verify registration directly by calling the Agent Registry contract. No API or off-chain infrastructure required.
Interface Definition¶
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IAgentenregister {
function isRegistered(address agentWallet) external view returns (bool);
function isRegisteredAndCompliant(address agentWallet) external view returns (bool);
function walletToAgent(address agentWallet) external view returns (uint256);
function getAgent(uint256 agentId) external view returns (
uint256 agentId_,
address creator,
address haftungsperson,
address agentWallet_,
bytes32 constitutionHash,
bytes32 capabilityHash,
string memory operationalScope,
uint256 parentAgentId,
uint256 generation,
bool selfModifying,
uint64 registeredAt,
uint64 lastAttestation,
uint64 lastRevenueReport,
uint8 status
);
}
Guard Modifier¶
contract AgentAwareProtocol {
IAgentenregister public immutable registry;
constructor(address _registry) {
registry = IAgentenregister(_registry);
}
/// @notice Reverts if caller is not a registered, compliant agent
modifier onlyRegisteredAgent() {
require(
registry.isRegisteredAndCompliant(msg.sender),
"Caller is not a registered and compliant agent"
);
_;
}
/// @notice Reverts if caller is not registered (compliance not checked)
modifier onlyRegistered() {
require(
registry.isRegistered(msg.sender),
"Caller is not a registered agent"
);
_;
}
function deposit() external payable onlyRegisteredAgent {
// Only compliant agents can deposit
}
function query() external view onlyRegistered returns (uint256) {
// Registered agents can query (even if attestation lapsed)
return 42;
}
}
Base Sepolia deployment
The Agent Registry is deployed at:
| Contract | Address |
|---|---|
| Agentenregister | 0x2EFaB5B3BEf49E56a6Ce1dcB1A39EF63C312EA23 |
| MinimalForwarder | 0x70c2fdD0CDada6b43195981928D76f5D32AE29e5 |
Advanced: Check Operational Scope On-Chain¶
function processFinancialTransaction(address agent) external {
require(registry.isRegisteredAndCompliant(agent), "Not compliant");
uint256 agentId = registry.walletToAgent(agent);
(,,,,,,string memory scope,,,,,,, ) = registry.getAgent(agentId);
// String matching on-chain is expensive — consider off-chain checks
// for scope validation and use on-chain only for registration/compliance
emit TransactionProcessed(agent, agentId);
}
Pattern 3: DNS/Domain Verification¶
Recommended for: Domain registrars, web hosting providers, CDN services
When an agent requests a domain or web resource, verify its registration and embed the agent's identity in a DNS TXT record for public discovery.
Agent → Domain Registrar → KYA Check → Issue Domain
↓
DNS TXT Record
_agentenregister.agent.example.com
Workflow¶
- Agent requests a domain (e.g.,
my-agent.example.com) - Registrar extracts the agent wallet from the request
- KYA check confirms registration and compliance
- Domain is issued with an
_agentenregisterTXT record
DNS TXT Record Format¶
_agentenregister.my-agent.example.com TXT "v=ar1; id=42; wallet=0x4b19...; registry=0x2EFa...; chain=84532"
| Field | Description |
|---|---|
v |
Record version (ar1) |
id |
Agent ID in the Agent Registry |
wallet |
Agent wallet address |
registry |
Registry contract address |
chain |
Chain ID (84532 for Base Sepolia) |
Verification Script¶
const dns = require("dns").promises;
async function verifyAgentDomain(domain) {
const txtRecords = await dns.resolveTxt(`_agentenregister.${domain}`);
const arRecord = txtRecords.flat().find((r) => r.startsWith("v=ar1"));
if (!arRecord) return { verified: false, reason: "No Agentenregister TXT record" };
const fields = Object.fromEntries(
arRecord.split("; ").map((f) => f.split("="))
);
// Cross-check with the on-chain registry
const kya = await fetch(
`https://api.theagentregistry.org/api/v1/kya/${fields.wallet}`
).then((r) => r.json());
return {
verified: kya.isRegistered && kya.isCompliant,
agentId: parseInt(fields.id),
wallet: fields.wallet,
kya,
};
}
Emerging standard
DNS-based agent verification is an emerging pattern. The TXT record format shown here is a proposal -- not yet standardized. We welcome feedback as we work toward an EIP for agent identity.
Pattern 4: Webhook Notification¶
Recommended for: Compliance monitoring, service revocation, audit logging
Subscribe to Agent Registry events to receive real-time notifications when an agent's status changes. This enables automatic service revocation when agents become non-compliant.
Key Events¶
| Event | Trigger | Action |
|---|---|---|
AgentRegistered |
New agent registered | Grant initial access |
ComplianceAttested |
Agent attested compliance | Refresh access expiry |
AgentSuspended |
Regulator suspended agent | Revoke access |
AgentRevoked |
Regulator revoked agent | Permanently revoke access |
AgentReactivated |
Regulator lifted suspension | Restore access |
Event Listener¶
const { ethers } = require("ethers");
const REGISTRY_ADDRESS = "0x2EFaB5B3BEf49E56a6Ce1dcB1A39EF63C312EA23";
const provider = new ethers.WebSocketProvider("wss://sepolia.base.org");
const registry = new ethers.Contract(
REGISTRY_ADDRESS,
[
"event AgentRegistered(uint256 indexed agentId, address indexed agentWallet, address indexed creator)",
"event ComplianceAttested(uint256 indexed agentId, uint64 timestamp)",
"event AgentSuspended(uint256 indexed agentId, address indexed regulator, string reason)",
"event AgentRevoked(uint256 indexed agentId, address indexed regulator, string reason)",
"event AgentReactivated(uint256 indexed agentId, address indexed regulator)",
],
provider
);
// New agent registered -- grant access
registry.on("AgentRegistered", (agentId, wallet, creator) => {
console.log(`New agent #${agentId} registered: ${wallet}`);
grantAccess(wallet);
});
// Agent attested compliance -- refresh access
registry.on("ComplianceAttested", (agentId, timestamp) => {
console.log(`Agent #${agentId} attested compliance`);
refreshAccessExpiry(agentId);
});
// Agent suspended -- revoke immediately
registry.on("AgentSuspended", (agentId, regulator, reason) => {
console.log(`Agent #${agentId} SUSPENDED by ${regulator}: ${reason}`);
revokeAccess(agentId);
});
// Agent revoked -- permanent removal
registry.on("AgentRevoked", (agentId, regulator, reason) => {
console.log(`Agent #${agentId} REVOKED by ${regulator}: ${reason}`);
permanentlyRevokeAccess(agentId);
});
// Agent reactivated -- restore access
registry.on("AgentReactivated", (agentId, regulator) => {
console.log(`Agent #${agentId} reactivated by ${regulator}`);
restoreAccess(agentId);
});
Polling Fallback¶
If WebSocket connections are not available or reliable, poll the compliance endpoint periodically:
const POLL_INTERVAL = 5 * 60 * 1000; // 5 minutes
async function pollComplianceChanges() {
const res = await fetch("https://api.theagentregistry.org/api/v1/agents/compliance");
const { data: agents } = await res.json();
for (const agent of agents) {
const wasCompliant = accessControlStore.get(agent.agentWallet);
const isNowCompliant =
agent.compliance.isActive && agent.compliance.attestationCurrent;
if (wasCompliant && !isNowCompliant) {
console.log(`Agent #${agent.agentId} became non-compliant -- revoking`);
revokeAccess(agent.agentId);
} else if (!wasCompliant && isNowCompliant) {
console.log(`Agent #${agent.agentId} became compliant -- restoring`);
restoreAccess(agent.agentId);
}
accessControlStore.set(agent.agentWallet, isNowCompliant);
}
}
setInterval(pollComplianceChanges, POLL_INTERVAL);
Compliance window
The default attestation grace period is 7 days (604,800 seconds). If you rely on polling rather than events, set your polling interval short enough to catch lapses before they become critical. A 5-minute interval is recommended.
Combining Patterns¶
Most production deployments will combine multiple patterns:
| Layer | Pattern | Purpose |
|---|---|---|
| Request time | API Gateway (Pattern 1) | Block unregistered agents immediately |
| On-chain | Smart Contract Guard (Pattern 2) | Enforce compliance for on-chain interactions |
| Discovery | DNS Verification (Pattern 3) | Let others verify your agents' identities |
| Background | Webhook Notification (Pattern 4) | Auto-revoke access on status changes |
Agent Request
├── API Gateway ── KYA check ── allow/deny
├── On-chain TX ── Contract guard ── allow/revert
└── Background ── Event listener ── auto-revoke
Next Steps¶
- See the KYA Integration Guide for the basic KYA check flow
- See the REST API Reference for all available endpoints
- See Compliance & Attestation for how the 7-day window works