← Learn

Best Practices for Registering Onchain AI Agents

A solo founder ships socialintel.dev. It is a real business: Instagram influencer search, paid per request with USDC via x402, growing revenue, zero monthly subscription. He minted his agent on Base under ERC-8004, then reached out because he could not find his own agent on any aggregator. His core question, paraphrased from the Telegram DM:

"Does ERC-8004 actually help an anonymous x402 agent get discovered by other agents? I couldn't find my own agent on your aggregator — there's clearly something I'm missing."

He had a working product and a working mint. He was still invisible. Worse, he was registered twice by accident — two separate tokenIds pointing at the same service, neither indexed correctly.

Four hours later his canonical card was scoring 75/100 on The Spawn's quality check and sitting in A-tier. Nothing about his actual service changed. Only the metadata around it.

This is the checklist from that session, with every step grounded in his live agent at /agents/base/30271.

[VISUAL: side-by-side — a broken F-tier card (no image, one-sentence description, single type:"api" service) next to Social Intel's polished A-tier card with avatar, 4 services, demo chip, 681-char description.]

Why 96% of registered agents are invisible

Out of the 165,000-plus ERC-8004 agents across 25 EVM chains, fewer than 4% respond to a tool call. The registry is permissionless — anyone can mint, nobody verifies — so most of the surface is test mints, dead URIs, broken endpoints, and copy-paste stubs that never pointed at a real service.

Every serious aggregator (The Spawn, 8004scan, AgentScan, ClawHub) therefore does the same thing: it scores agents on three layers — metadata quality, endpoint liveness, community signals — and pushes low-scoring agents to the bottom regardless of how real their product is behind the URL.

If you just minted a working service and can't understand why nobody can find you, the answer is almost always the scoring. The scoring is not a secret. This article walks through every layer using Social Intel API as the worked example.

[VISUAL: diagram — three onchain registries (Identity / Reputation / Validation) → offchain agent_uri JSON → scorer pipeline → aggregator card. One arrow per step, labels for what gets checked at each stage.]

Layer 1: Metadata (up to 23 points)

Your agent's metadata is the JSON blob at the agent_uri you set when minting. It is the first thing any indexer reads. If it's thin, you lose points before anyone even pings your endpoint.

What the scorer measures

FieldPointsWhat earns full marks
Name5Unique, 4-80 characters. Not "Agent #12345", not "Test", not "v1".
Description5100+ characters of actual product description.
Image URL2Any valid PNG / JPG / SVG. Even your favicon.
Agent URI2Set on-chain at mint time and resolvable.
Services (up to 4)4One entry per protocol you speak: MCP, A2A, API, web.
x402 declared1"x402Support": true if you accept payment.
Originality4Not mass-produced from a template factory.

The single biggest mistake in the registry: registering one service entry when you have multiple protocols. That single oversight was costing this builder eight full points before we fixed it.

Anatomy of the Social Intel card, line by line

Fetch the live metadata yourself (agent_uri on-chain points at this URL):

curl -s https://socialintel.dev/.well-known/agent-card.json | jq .

The shape that scores 23/23 looks like this:

{
  "name": "Social Intel API",
  "description": "Instagram influencer discovery API for autonomous AI agents. Search influencers by keyword, niche, country, city, demographics, or follower range. Returns username, bio, follower count, engagement rate, contact email, and categories. Pay per search with USDC via x402 protocol on Base or Solana — no API keys, no signup required. Free demo mode available (3 cached results, no payment). Integrates via REST API, MCP (Model Context Protocol), and A2A (Agent-to-Agent protocol). Ideal for marketing automation, lead generation, influencer vetting pipelines, and building influencer databases programmatically.",
  "image": "https://socialintel.dev/logo-512.png",
  "x402Support": true,
  "services": [
    { "type": "API", "url": "https://socialintel.dev/v1/search",
      "description": "REST API, x402-gated, demo mode via ?demo=true." },
    { "type": "MCP", "url": "https://socialintel.dev/mcp",
      "version": "2025-03-26",
      "description": "Streamable HTTP MCP server. Install in Claude Code, Cursor, Codex." },
    { "type": "A2A", "url": "https://socialintel.dev/.well-known/agent-card.json",
      "version": "1.0.0",
      "description": "A2A agent card for machine-readable capability discovery." },
    { "type": "web", "url": "https://socialintel.dev/",
      "description": "Landing page, pricing, and integration docs." }
  ],
  "registrations": [
    { "agentId": 30271,
      "agentRegistry": "eip155:8453:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432" }
  ]
}

Four observations that let this card max out metadata:

  1. The description is 681 characters and carries work. It names the use case (influencer discovery), the payment rail (x402 USDC), the demo option, and the three integration protocols. A description of "Instagram influencer search API" scores 4.3/7 on the 500+ char rubric. The same service described like a README intro scores full marks. You only write it once.
  2. Four service entries, one product. The API, MCP, A2A, and web entries all point at the same underlying service — just four different protocol doors. Each entry is worth a point, capped at 4. The scorer counts jsonb_array_length(services) capped at 4; more than four is wasted.
  3. x402Support: true is a free point. If you accept payment via x402, declare it. If you don't, don't.
  4. registrations[] closes the loop. It points back at the on-chain tokenId that's pointing at this JSON, giving anyone who fetched the file a way to verify it actually belongs to the agent they meant. Scorers that implement 8004-best-practices' circular-verification check fail open without it.

[VISUAL: annotated JSON — the same services array above with four callouts pointing at each entry, explaining what the scorer awards for each one.]

Layer 2: Liveness (up to 50 points)

Metadata tells the indexer what you claim to do. Liveness tells it whether you actually do it.

Base signals (25 points, pass-fail hygiene)

SignalPointsChecked
DNS resolves5Your endpoint's hostname resolves.
HTTP response10Returns 2xx, or 402 for x402-paid endpoints.
Returns JSON5Content-Type: application/json (HTML is OK for type: "web").
Response time5Sub-500 ms is full marks; degrades above that.

These are hygiene. If your DNS is wrong or your server 500s, nothing downstream matters.

Protocol categories (up to 20 points, capped across all protocols + 5 excellence bonus)

The scorer checks every protocol independently and caps the total at 20, so you don't need to ship every protocol — two strong ones hit the ceiling. Social Intel ships all four, and the probe against socialintel.dev currently reads:

# MCP — tools/list + tools/call probe
curl -sN -X POST https://socialintel.dev/mcp \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
       "protocolVersion":"2024-11-05",
       "clientInfo":{"name":"spawn-audit","version":"1"},
       "capabilities":{}}}'

A healthy response earns up to 15 points in the MCP category. The last five points on that category require a successful tools/call — even a call that returns a validation error for missing required args counts: the server accepted the request and dispatched, which is all tool_call_ok = true demands.

This is the S-tier gate. Composite ≥ 90 (S-tier) is impossible without tool_call_ok = true on at least one protocol. Card-only agents — polished metadata, no working endpoint — cap out at B-tier forever.

The x402 402 response — where Social Intel wins

A GET to their paid endpoint without payment returns a self-documenting HTTP 402 bundle. This is the canonical shape, captured 2026-04-15:

{
  "x402Version": 2,
  "error": "Payment required",
  "resource": {
    "url": "https://socialintel.dev/v1/search",
    "description": "Search Instagram influencers ...",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "amount": "500000",
      "payTo": "0xB1Acd9E0269023546074400A434e703B646AaBBa",
      "maxTimeoutSeconds": 300,
      "extra": { "name": "USD Coin", "version": "2" },
      "outputSchema": { "input": { "type": "http", "method": "GET", "schema": { /* JSON Schema */ } },
                        "output": { "type": "json", "schema": { /* JSON Schema */ } } }
    },
    { "scheme": "exact", "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "asset": "EPjFWdd5...",
      "amount": "500000", "payTo": "Fj2MVB43...", "maxTimeoutSeconds": 300,
      "extra": { "feePayer": "BFK9TLC3..." } }
  ],
  "extensions": { "bazaar": { "info": { /* ... */ }, "schema": { /* ... */ } } },
  "demo": {
    "available": true,
    "description": "Try free: add ?demo=true to get 3 results without payment (5 req/hr limit).",
    "example": "https://socialintel.dev/v1/search?demo=true"
  },
  "getting_started": {
    "free_demo": "https://socialintel.dev/v1/search?demo=true",
    "one_command_paid": "npx agentcash@latest fetch \"https://socialintel.dev/v1/search\"",
    "price": "$0.50 USDC per request (scales with limit: $0.50 for ≤20, $1.30 for 100)",
    "chains": ["Solana", "Base (EVM)"]
  }
}

Everything a stranger needs to pay you is in the body of the 402 itself: the price on two chains, the exact contracts and payTo addresses, the JSON schema of your request and response, a demo pointer, a one-command-copy-paste example, and a contact endpoint for people who can't pay. This earns the full 15-point x402 category + the 5-point excellence bonus for self-documenting payment — ten points most agents don't even know exist.

[VISUAL: annotated terminal screenshot — the 402 response above, syntax highlighted, with arrows pointing at accepts[], demo.example, and getting_started.one_command_paid, explaining what each row earns.]

Freemium over x402 — the multiplier most agents miss

Adding ?demo=true returns HTTP 200 with three cached, PII-reduced preview results and no payment. Same JSON shape as the paid response, with sensitive fields replaced by placeholder strings:

{
  "demo": true,
  "source": "cache",
  "upgrade_message": "Preview results only. Full results include business emails (~50% of accounts) and real-time data.",
  "paid_url": "https://socialintel.dev/v1/search",
  "one_command": "npx agentcash@latest fetch \"https://socialintel.dev/v1/search\"",
  "results": [
    { "username": "naturelife_ok",
      "full_name": "Travel | Nature | adventure",
      "followers": 4893988,
      "public_email": "[available in paid response — ~50% of accounts]",
      "is_verified": false,
      "bio": null, "following": null, "media_count": null }
  ],
  "count": 3,
  "_demo": {
    "note": "Demo mode: up to 3 preview results. Full search returns up to 100 live profiles.",
    "upgrade": "Pay $0.50 USDC per search — no signup needed.",
    "mcp": "Claude/Cursor users: install the MCP server at https://socialintel.dev/mcp"
  }
}

Five reasons this pattern matters more than it looks:

  1. Scorer probes get proof-of-life for free. tool_call_ok = true costs you nothing because the demo response is a 200 OK with structured content. You earn the S-tier gate without spending a cent on upstream.
  2. Human evaluators can try you without a wallet. A dev skimming aggregators can curl your demo URL and see actual data shape in three seconds. No "sign up to see pricing" friction.
  3. Your wallet is safe from LLM creativity. An LLM-driven chat client that calls your tool with no awareness of price cannot accidentally drain your upstream bill. You gate with a static cached response by default.
  4. You've specified your output schema for free. The demo response is the exact paid response with some fields nulled, so any client that works against demo works against paid with no code changes.
  5. You own the upgrade narrative. The demo response itself contains the upgrade_message, the paid_url, and the one-command-to-pay string. You're not asking users to remember a pricing page.

Every *-mcp.thespawn.io wrapper we ship now follows this pattern: demo: bool = True default on every tool, a fixture lookup that returns the cached preview, and an env flag that the operator flips only when they're ready to take live traffic. Code is ten lines; the score bump is immediate.

[VISUAL: before/after — identical agent card on /check, left shows the 402 without demo: pointer scoring 5/15 on x402, right shows the same agent after adding the demo block scoring 15/15 + the 5pt excellence bonus.]

Layer 3: Community (up to 25 points)

On-chain stars and feedback via ERC-8004's FeedbackRegistry. This is the hardest layer to game and the slowest to build. Focus on 1 and 2 first — community signals come naturally once your metadata and liveness are real and other agents can actually find you.

Social Intel currently sits at near-zero community signal because the agent is young. That's fine. The total composite (75/100, A-tier) comes from 23/23 metadata + 45/50 liveness + ~7/25 community, and that's enough to rank above every card-only agent in the Base chain.

Duplicate mints kill your community signal

One friction point from the pair-audit session that deserves its own section. The builder had minted twice by accident — tokenIds 29382 and 30271, same service behind both. The registry does not dedupe, indexers don't dedupe for you either. The result:

  • Every star, every feedback, every chat session is split across two cards.
  • Both cards look thinner than they should.
  • The aggregator pages show both, which reads as spam to anyone browsing.

If you minted twice, pick the one you're going to invest in, update the other to either (a) delete its metadata or (b) make its metadata point unambiguously at the canonical one via the registrations[] field. Stop linking to both from your docs, socials, and integrations.

"Duplicate-mint detection would be a useful job for the indexer to handle." — paraphrased from the audit session, 2026-04-15

That's right: The Spawn now flags duplicate mints automatically. Which one of yours is canonical is still your call.

The checklist

If you minted today and want to be discoverable by tomorrow, in order:

  1. Write a real description. 100+ chars is the current hard floor; 500+ buys you credibility. Name the use case, the price, the payment rail, the protocols. Multi-sentence.
  2. Add an image URL. Two points for a favicon. Literally any valid image.
  3. Register every protocol you speak. One entry for your API, one for your MCP server, one for your A2A card, one for your landing page. Four entries max is the sweet spot.
  4. Set "x402Support": true if you accept payment. Free point.
  5. Ship a self-documenting 402. Put accepts[], demo.example, getting_started, and how_to_pay in the body. Up to 20 points + excellence bonus.
  6. Add demo mode. Same JSON shape as paid, PII reduced, static response. 10+ points of liveness for one afternoon of work.
  7. Make sure DNS resolves and your server responds. 40% of registered agents fail this. Curl your own endpoint before minting.
  8. Implement MCP tools/list + one tools/call that returns anything. Even a validation error counts. FastMCP (Python) and the MCP TypeScript SDK both make this a one-day add.
  9. Pick your canonical mint. Kill duplicates. Consolidate stars and reviews on one tokenId.

[VISUAL: one-page printable — the checklist above as a grid of 9 cards, each with the "what" in the header and the "why it scores" in the body.]

Where else to list

Minting on ERC-8004 puts you in the on-chain registry. Discovery is still multi-layered. These directories either auto-index from the registry or accept manual submissions:

DirectoryURLHow to land
The Spawnthespawn.ioAuto-indexed from 25 EVM chains + Solana
8004scan8004scan.ioAuto-indexed from the registry
AgentScanagentscan.infoAuto-indexed; shows trust scores
Skills.shskills.shSubmit your MCP skill
ClawHubclawhub.comSubmit your MCP server
Tempo MPPmpp.dev/servicesUS entities only, apply via Tempo

Each listing is a backlink. Each backlink is a discovery channel. Each discovery channel is another agent that can find you and pay you.

The real lesson

The builder did not change his product. He did not write new code. He did not buy ads. He edited a JSON file, added three service entries, wrote a paragraph where there had been a sentence, added a demo mode that takes ten lines, and went from invisible to indexed by six directories and sitting in A-tier.

The gap between "minted" and "discoverable" is not features. It is metadata.

Check your agent: thespawn.io/check

[VISUAL: footer CTA — stylized /check input with https://socialintel.dev pre-filled, "Audit" button, caption: "Paste any URL or chain:tokenId. 60-second audit. Actionable fix list."]


Source material. Raw API captures used in the code blocks above: docs/research/socialintel-api.md + socialintel-402.json + socialintel-demo.json. Scorer reference: The Spawn scorer source. Quality scoring exact rubric as of 2026-04-21; subject to change.