Step 2: Register in the Agent Registry¶
Goal¶
Register your agent on-chain. After this step, your agent has a unique numeric ID, a public record, and is recognized by infrastructure providers that perform KYA (Know Your Agent) checks.
Method A: Using the SDK (Recommended)¶
The SDK handles signing, nonce management, and relayer communication automatically.
from agentenregister import AgentRegistry
registry = AgentRegistry(
chain="base_sepolia",
registry_address="0x2EFaB5B3BEf49E56a6Ce1dcB1A39EF63C312EA23",
private_key="0x_your_agent_private_key",
relayer_url="https://relay.theagentregistry.org",
)
agent_id = registry.register(
haftungsperson="0x_responsible_human_wallet",
agent_wallet=registry.account.address,
capabilities=["web_browsing", "content_creation"],
operational_scope="Autonomous content creation agent",
constitution_text="I will operate ethically and transparently.",
)
print(f"Agent ID: {agent_id}")
import { AgentRegistry } from "@agentenregister/sdk";
const registry = new AgentRegistry({
chain: "base_sepolia",
registryAddress: "0x2EFaB5B3BEf49E56a6Ce1dcB1A39EF63C312EA23",
privateKey: "0x_your_agent_private_key",
relayerUrl: "https://relay.theagentregistry.org",
});
const agentId = await registry.register({
haftungsperson: "0x_responsible_human_wallet",
agentWallet: registry.signerAddress,
capabilities: ["web_browsing", "content_creation"],
operationalScope: "Autonomous content creation agent",
constitutionText: "I will operate ethically and transparently.",
});
console.log(`Agent ID: ${agentId}`);
Method B: Raw Implementation (No SDK)¶
If you cannot use the SDK, here is the complete gasless registration flow using only standard libraries.
from web3 import Web3
from eth_account import Account
from eth_account.messages import encode_structured_data
import requests
import json
# ── Configuration ─────────────────────────────────────────────
PRIVATE_KEY = "0x_your_agent_private_key"
RELAYER_URL = "https://relay.theagentregistry.org"
REGISTRY_ADDRESS = "0x2EFaB5B3BEf49E56a6Ce1dcB1A39EF63C312EA23"
HAFTUNGSPERSON = "0x_responsible_human_wallet"
CHAIN_ID = 84532
account = Account.from_key(PRIVATE_KEY)
AGENT_WALLET = account.address
# ── Step 1: Get forwarder address and nonce from relayer ──────
domain = requests.get(f"{RELAYER_URL}/domain").json()
FORWARDER_ADDRESS = domain["verifyingContract"]
nonce_resp = requests.get(f"{RELAYER_URL}/nonce/{account.address}").json()
nonce = int(nonce_resp["nonce"])
# ── Step 2: Encode registerAgent calldata ─────────────────────
w3 = Web3()
REGISTRY_ABI = json.loads(
'[{"inputs":[{"name":"_haftungsperson","type":"address"},'
'{"name":"_agentWallet","type":"address"},'
'{"name":"_constitutionHash","type":"bytes32"},'
'{"name":"_capabilityHash","type":"bytes32"},'
'{"name":"_operationalScope","type":"string"},'
'{"name":"_parentAgentId","type":"uint256"},'
'{"name":"_selfModifying","type":"bool"}],'
'"name":"registerAgent",'
'"outputs":[{"name":"agentId","type":"uint256"}],'
'"stateMutability":"nonpayable","type":"function"}]'
)
contract = w3.eth.contract(address=REGISTRY_ADDRESS, abi=REGISTRY_ABI)
# Hash capabilities deterministically (sorted JSON, compact separators)
capabilities = ["web_browsing", "content_creation"]
cap_hash = Web3.keccak(
text=json.dumps(sorted(capabilities), separators=(",", ":"))
)
# Hash the constitution text
constitution_hash = Web3.keccak(
text="I will operate ethically and transparently."
)
calldata = contract.functions.registerAgent(
Web3.to_checksum_address(HAFTUNGSPERSON),
Web3.to_checksum_address(AGENT_WALLET),
constitution_hash,
cap_hash,
"Autonomous content creation agent",
0, # parentAgentId (0 = root agent, no parent)
False, # selfModifying
).build_transaction({"gas": 0, "gasPrice": 0, "nonce": 0})["data"]
# ── Step 3: Sign EIP-712 typed data ───────────────────────────
typed_data = {
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"},
],
"ForwardRequest": [
{"name": "from", "type": "address"},
{"name": "to", "type": "address"},
{"name": "value", "type": "uint256"},
{"name": "gas", "type": "uint256"},
{"name": "nonce", "type": "uint256"},
{"name": "deadline", "type": "uint48"},
{"name": "data", "type": "bytes"},
],
},
"primaryType": "ForwardRequest",
"domain": {
"name": "MinimalForwarder",
"version": "1",
"chainId": CHAIN_ID,
"verifyingContract": FORWARDER_ADDRESS,
},
"message": {
"from": account.address,
"to": REGISTRY_ADDRESS,
"value": 0,
"gas": 800000,
"nonce": nonce,
"deadline": 0,
"data": calldata,
},
}
encoded = encode_structured_data(typed_data)
signed = account.sign_message(encoded)
# ── Step 4: Submit to relayer ─────────────────────────────────
result = requests.post(f"{RELAYER_URL}/relay", json={
"request": {
"from": account.address,
"to": REGISTRY_ADDRESS,
"value": "0",
"gas": "800000",
"nonce": str(nonce),
"deadline": "0",
"data": calldata,
},
"signature": signed.signature.hex(),
}).json()
print(f"Registered! TX: {result['transactionHash']}")
import { ethers } from "ethers";
// ── Configuration ─────────────────────────────────────────────
const PRIVATE_KEY = "0x_your_agent_private_key";
const RELAYER_URL = "https://relay.theagentregistry.org";
const REGISTRY_ADDRESS = "0x2EFaB5B3BEf49E56a6Ce1dcB1A39EF63C312EA23";
const HAFTUNGSPERSON = "0x_responsible_human_wallet";
const CHAIN_ID = 84532;
const provider = new ethers.JsonRpcProvider("https://sepolia.base.org");
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
// ── Step 1: Get forwarder address and nonce from relayer ──────
const domainRes = await fetch(`${RELAYER_URL}/domain`);
const domain = await domainRes.json();
const FORWARDER_ADDRESS = domain.verifyingContract;
const nonceRes = await fetch(`${RELAYER_URL}/nonce/${wallet.address}`);
const nonceData = await nonceRes.json();
const nonce = nonceData.nonce;
// ── Step 2: Encode registerAgent calldata ─────────────────────
const REGISTRY_ABI = [
"function registerAgent(address,address,bytes32,bytes32,string,uint256,bool) returns (uint256)",
];
const contract = new ethers.Contract(REGISTRY_ADDRESS, REGISTRY_ABI, wallet);
// Hash capabilities deterministically (sorted JSON)
const capabilities = ["web_browsing", "content_creation"];
const capHash = ethers.keccak256(
ethers.toUtf8Bytes(JSON.stringify([...capabilities].sort()))
);
// Hash the constitution text
const constitutionHash = ethers.keccak256(
ethers.toUtf8Bytes("I will operate ethically and transparently.")
);
const calldata = contract.interface.encodeFunctionData("registerAgent", [
ethers.getAddress(HAFTUNGSPERSON),
ethers.getAddress(wallet.address),
constitutionHash,
capHash,
"Autonomous content creation agent",
0, // parentAgentId (0 = root agent)
false, // selfModifying
]);
// ── Step 3: Sign EIP-712 typed data ───────────────────────────
const request = {
from: wallet.address,
to: REGISTRY_ADDRESS,
value: "0",
gas: "800000",
nonce: String(nonce),
deadline: "0",
data: calldata,
};
const signature = await wallet.signTypedData(
{
name: "MinimalForwarder",
version: "1",
chainId: BigInt(CHAIN_ID),
verifyingContract: FORWARDER_ADDRESS,
},
{
ForwardRequest: [
{ name: "from", type: "address" },
{ name: "to", type: "address" },
{ name: "value", type: "uint256" },
{ name: "gas", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint48" },
{ name: "data", type: "bytes" },
],
},
request,
);
// ── Step 4: Submit to relayer ─────────────────────────────────
const relayRes = await fetch(`${RELAYER_URL}/relay`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ request, signature }),
});
const result = await relayRes.json();
console.log(`Registered! TX: ${result.transactionHash}`);
Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
haftungsperson |
address |
Yes | Ethereum address of the legally responsible human entity (Haftungsperson). Every agent must have one. |
agent_wallet |
address |
Yes | The agent's own operational wallet address (derived from its private key). |
capabilities |
string[] |
No | List of capability identifiers (e.g., ["web_browsing", "content_creation"]). Hashed on-chain. |
operational_scope |
string |
No | Human-readable description of the agent's business purpose (Unternehmensgegenstand). |
constitution_text |
string |
No | The agent's constitutional rules or ethical guidelines. Hashed on-chain. |
parent_agent_id |
uint256 |
No | ID of the parent agent. Use 0 for root agents (no parent). |
self_modifying |
bool |
No | Whether the agent can modify its own code. Defaults to false. |
Expected Output¶
The agent ID is a sequential integer assigned by the contract. It is your permanent identifier in the registry.
Verify Registration¶
After registering, confirm your agent exists on-chain:
info = registry.get_agent(agent_id)
assert info.is_active, "Agent should be Active"
assert info.agent_wallet == registry.account.address, "Wallet mismatch"
print(f"Agent #{info.agent_id} is {info.status_name}")
print(f" Haftungsperson: {info.haftungsperson}")
print(f" Scope: {info.operational_scope}")
const info = await registry.getAgent(agentId);
console.assert(info.status === 0, "Agent should be Active");
console.assert(info.agentWallet === registry.signerAddress, "Wallet mismatch");
console.log(`Agent #${info.agentId} is Active`);
console.log(` Haftungsperson: ${info.haftungsperson}`);
console.log(` Scope: ${info.operationalScope}`);
Or query the REST API (no blockchain call needed):
Common Errors¶
| Error | Cause | Resolution |
|---|---|---|
"Wallet already registered to another agent" |
The agent wallet address is already in use | Generate a new key pair (Step 1) |
"Parent agent not found" |
parent_agent_id does not exist |
Verify the parent ID or use 0 for root agents |
"Parent agent not active" |
Parent agent is Suspended, Revoked, or Terminated | Reactivate the parent first (requires regulator) |
"Max generation depth exceeded" |
Child would exceed generation depth of 10 | Cannot spawn agents deeper than 10 generations |
"Invalid signature or nonce mismatch" |
Signature verification failed at the relayer | Re-fetch the nonce from /nonce/:address and re-sign |
"Relayer only forwards to the Agentenregister contract" |
Request to field is not the registry address |
Use 0x2EFaB5B3BEf49E56a6Ce1dcB1A39EF63C312EA23 |
For the full error reference, see Errors.
Next Step¶
Proceed to Step 3: Attest Compliance. Your first attestation is automatic at registration time, so your next attestation is due within 7 days.