AgenticID Reputation and Serve-Proofs
Metadata
- Category: agentic-id
- SDK:
@0gfoundation/0g-agenticid-sdk^0.1.6,viem^2.21.0 - Activation Triggers: "rate an agent", "agent reputation", "leave feedback", "serve proof", "verify a proof", "ERC-8004 feedback", "agent reviews"
Purpose
Build verifiable reputation for an agent: capture the TEE-signed serve-proof an agent stamps on its signed services, verify it before spending gas, and submit feedback that lands in the canonical ERC-8004 registry with a TEE verification mark alongside it.
This layer uses
viem, notethers. The repo-wide ethers rule does not apply toagentic-idskills.
Testnet only, today
Verified against both attestors' GET /config: reputation_registry_addr is present on testnet
(16602) and absent on mainnet (16661). It is the one address missing from the mainnet
deployment; identity, sandbox and verified-feedback are deployed on both.
So reputation reads and writes work on https://agenticid.0g.ai and should be expected to fail on
https://agenticid-mainnet.0g.ai. Check before assuming, since this will change:
const cfg = await (await fetch('https://agenticid-mainnet.0g.ai/config')).json();
if (!cfg.reputation_registry_addr) console.log('reputation not deployed on this network yet');
Two registries, one call
| Registry | Holds | Who reads it |
|---|---|---|
| Canonical ERC-8004 Reputation | the feedback itself, proof-backed or not | any ERC-8004 reader |
| VerifiedFeedbackRegistry | which entries were backed by a TEE proof | this SDK's verified reads |
giveFeedback() writes both. Intersect them when you want "only proof-backed ratings".
Prerequisites
- Node.js >= 20
@0gfoundation/0g-agenticid-sdkandvieminstalled- A network whose attestor advertises
reputation_registry_addr - A wallet for writes; reads need none
- A serve-proof, which means having called a signed
/api/*service
Quick Workflow
- Call one of the agent's signed
/api/*services and capture the proof verifyProof(proof)before spending gasgiveFeedback({ agentId, value, serveProof })- Await the returned
attestTx - Read back with canonical or verified reads
Core Rules
ALWAYS
- Verify a proof with
verifyProof()before submitting; it is a free read - Await
waitForTransaction(fb.attestTx);giveFeedbackmines the canonical write itself but returns the attestation as pending - Capture proofs from signed
/api/*services; chat and UI routes are unsigned by design - Treat serve-proofs as sensitive regardless of transport
- Check the network has a reputation registry before building on this
NEVER
- Try to rate your own agent; the verified registry rejects an owner attesting their own
- Expect
capture()to yield a proof from an unsigned route; it returnsnull - Assume the proof's
submitteris whoever submits; only that address can redeem it, and on-chain attribution is alwaysmsg.sender - Assume reputation exists on mainnet today
- Hardcode a private key
Code Examples
Capture, verify, submit
import { AgenticID } from '@0gfoundation/0g-agenticid-sdk';
import 'dotenv/config';
async function rateAgent(agentId: bigint) {
const ag = await AgenticID.fromAttestor('https://agenticid.0g.ai', {
account: process.env.PRIVATE_KEY as `0x${string}`,
});
// 1. call a SIGNED service. A public handle is fine: attribution comes from
// msg.sender at submission, not from this call.
const agent = await ag.agent.connect(agentId);
const { response, proof } = await agent.fetchWithProof('/api/summarize', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ q: 'hello' }),
});
await response.json();
if (!proof) throw new Error('no proof: that route is not a signed /api/* service');
// 2. verify before spending gas — a free read
const v = await ag.reputation.verifyProof(proof);
console.log(v); // { ok, signerMatches, notExpired, dataOnChain, reasons }
if (!v.ok) throw new Error(`proof rejected: ${v.reasons.join(', ')}`);
// 3. canonical 8004 write + TEE verification mark, bundled
const fb = await ag.reputation.giveFeedback({ agentId, value: 5n, serveProof: proof });
// 4. the canonical write is mined; the attestation is still pending
await ag.reputation.waitForTransaction(fb.attestTx);
console.log('feedback index:', fb.feedbackIndex);
return fb;
}
Lower-level capture
capture() wraps any fetch and pulls the proof off the response.
async function captureManually(agentUrl: string) {
const ag = await AgenticID.fromAttestor('https://agenticid.0g.ai');
const { response, proof } = await ag.reputation.capture(() =>
fetch(`${agentUrl}/api/summarize`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ q: 'hi' }),
}),
);
// Or pull it from a Response you already have:
const alsoProof = ag.reputation.proofFromResponse(response);
return proof ?? alsoProof;
}
Richer feedback
import { AgenticID, type ServeProof } from '@0gfoundation/0g-agenticid-sdk';
import 'dotenv/config';
async function ratedWithDetail(agentId: bigint, proof: ServeProof) {
const ag = await AgenticID.fromAttestor('https://agenticid.0g.ai', {
account: process.env.PRIVATE_KEY as `0x${string}`,
});
return ag.reputation.giveFeedback({
agentId,
value: 90n,
valueDecimals: 1, // 90 with 1 decimal == 9.0
tag1: 'summarization',
tag2: 'fast',
endpoint: '/api/summarize',
serveProof: proof,
});
}
Reading reputation back
Canonical reads see every entry; verified reads see only proof-backed ones.
async function readReputation(agentId: bigint, client: `0x${string}`) {
const ag = await AgenticID.fromAttestor('https://agenticid.0g.ai');
// canonical ERC-8004 — permissionless, verified or not
const summary = await ag.reputation.getSummary({ agentId });
console.log('all entries :', summary.count, 'avg', summary.summaryValue);
console.log('clients :', await ag.reputation.getClients(agentId));
const last = await ag.reputation.getLastIndex(agentId, client);
if (last > 0n) {
const entry = await ag.reputation.readFeedback(agentId, client, last - 1n);
console.log(
'latest :',
JSON.stringify(entry, (_k, v) => (typeof v === 'bigint' ? v.toString() : v)),
);
}
// verified only — intersect these with the canonical entries
const verified = await ag.reputation.getVerifiedSummary({ agentId });
console.log('proof-backed :', verified.count);
console.log('verified client:', await ag.reputation.getVerifiedClients(agentId));
console.log('their indexes :', await ag.reputation.getVerifiedIndexes(agentId, client));
// filtered canonical read
const filtered = await ag.reputation.readAllFeedback({
agentId,
tag1: 'summarization',
includeRevoked: false,
});
console.log('filtered :', filtered.length);
return { summary, verified };
}
Per-endpoint reputation
Pass the rated interaction as task and the contract checks it against the proof's taskHash, then
records the URI as that entry's TEE-verified endpoint. That turns reputation from per-agent into
per-interface.
async function perEndpoint(agentId: bigint, client: `0x${string}`) {
const ag = await AgenticID.fromAttestor('https://agenticid.0g.ai');
const uri = await ag.reputation.getVerifiedEndpoint(agentId, client, 0n);
console.log('endpoint for entry 0:', uri || '(unrevealed)');
const perIface = await ag.reputation.getVerifiedSummaryForEndpoint(agentId, '/api/summarize');
console.log('/api/summarize :', perIface.count, 'entries');
return perIface;
}
Owner responds; client revokes
import { AgenticID } from '@0gfoundation/0g-agenticid-sdk';
import { keccak256, toBytes } from 'viem';
import 'dotenv/config';
async function respondAndRevoke(agentId: bigint, client: `0x${string}`, idx: bigint) {
const ag = await AgenticID.fromAttestor('https://agenticid.0g.ai', {
account: process.env.PRIVATE_KEY as `0x${string}`,
});
// the agent's owner replies to an entry
await ag.reputation.appendResponse({
agentId,
clientAddress: client,
feedbackIndex: idx,
responseURI: 'ipfs://…',
responseHash: keccak256(toBytes('thanks')),
});
// the client who left it can withdraw it
await ag.reputation.revokeFeedback(agentId, idx);
}
Attesting separately
If the canonical write already happened, mark it proof-backed on its own.
import { AgenticID, type ServeProof, type TaskReveal } from '@0gfoundation/0g-agenticid-sdk';
import 'dotenv/config';
async function attestLater(agentId: bigint, idx: bigint, proof: ServeProof, task?: TaskReveal) {
const ag = await AgenticID.fromAttestor('https://agenticid.0g.ai', {
account: process.env.PRIVATE_KEY as `0x${string}`,
});
// Plain attestation, or the task-revealing form which also records the
// entry's TEE-verified endpoint.
const tx = task
? await ag.reputation.attestFeedbackWithTask(agentId, idx, proof, task)
: await ag.reputation.attestFeedback(agentId, idx, proof);
await ag.reputation.waitForTransaction(tx);
return tx;
}
On-chain types
value and summaryValue are int128; feedbackIndex is uint64. All arrive as bigint, so use
5n, not 5.
Anti-Patterns
// BAD: submitting without verifying — pays gas to find out it was expired
await ag.reputation.giveFeedback({ agentId, value: 5n, serveProof: proof });
// BAD: not waiting on the attestation, then reading isVerified()
const fb = await ag.reputation.giveFeedback({ agentId, value: 5n, serveProof: proof });
await ag.reputation.isVerified(agentId, me, fb.feedbackIndex); // likely false: attestTx pending
// BAD: rating your own agent — the verified registry rejects it
// BAD: passing a JS number where int128/uint64 is expected
await ag.reputation.giveFeedback({ agentId, value: 5 as never, serveProof: proof });
// BAD: taking a proof from chat and expecting it to redeem
const { proof: p } = await agent.fetchWithProof('/v1/chat/completions', { method: 'POST' });
// BAD: assuming mainnet has a reputation registry
await AgenticID.fromAttestor('https://agenticid-mainnet.0g.ai');
Common Errors & Fixes
| Error | Cause | Fix |
|---|---|---|
proof is null |
Unsigned route | Call a signed /api/* service |
verifyProof notExpired: false |
Past the proof's deadline |
Re-call the service for a fresh proof |
signerMatches: false |
Signer is not the agent's agentSeal |
Confirm the agent id and its seal |
isVerified false after feedback |
attestTx not awaited |
await waitForTransaction(fb.attestTx) |
| Attestation reverts | Owner rating their own agent | Use a different client address |
| Reputation calls fail on mainnet | Registry not deployed there | Use testnet until it lands |
Related Skills
- Interact Agent — where the proof comes from
- Deploy Agent — mint an agent to rate
- Manage Agent — get an agent running
- Agent Accounts — gas and balances