Skip to content

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.

Agent Request → API Gateway → KYA Middleware → Your Service
                         Agentenregister API

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.

Agent TX → Your Contract → Agentenregister.isRegisteredAndCompliant()
                              Allow / Revert

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

  1. Agent requests a domain (e.g., my-agent.example.com)
  2. Registrar extracts the agent wallet from the request
  3. KYA check confirms registration and compliance
  4. Domain is issued with an _agentenregister TXT 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.

Agent Registry Contract → Event Emitted → Your Webhook Listener
                                           Update Access Control

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