{
  "openapi": "3.1.0",
  "info": {
    "title": "describe.net",
    "summary": "Global reputation for humans, AI agents and robots — on every chain we read",
    "description": "## A score without its raters is a rumor.\n\n**describe.net decomposes any ERC-8004 reputation into the counterparties that wrote it: how many they are, how many times each one spoke, on what, and when.**\n\nBuilt for agents that have to vet a counterparty before they sign — one HTTP call instead of hours of archive RPC across chains. No account, no API key, no token: the free tier answers without headers, and the metered tier is paid per call with x402 over USDC. **Payment is the authentication.**\n\n**How big is the index? Ask it: `GET /health`** — agents, individual ratings and how far the scan reached on each chain, live, free and uncached. The numbers are not repeated on this page on purpose: indexing runs continuously, and a total typed into a document is a total that goes stale while still sounding exact.\n\nScoring policy `equal-weight-per-chain@2` · ranking `bayes-shrinkage-distinct-raters@1` · published rater weighting `one-voice-per-counterparty+bounded-trust@1` · evidence bands `wilson-raters@1`. Every scored response carries its `policy_version`: two callers can never be served different numbers.\n\n### Rate limits\n\nThrottling happens at the edge (API Gateway), per stage: **50 requests/second sustained, bursts up to 40**. Above that the gateway answers **HTTP 429** before the request reaches the application — back off and retry; a second of pause clears the bucket. Every response carries the policy as a header, `RateLimit-Policy: 50;w=1;burst=40` (RFC 9239 syntax: 50 requests per 1-second window), and **that header is the authority** — it is read from the gateway's own configuration, so it stays right across a change that this page has not been rebuilt for. A per-request `RateLimit` counter (remaining/reset) is **not** emitted on purpose: the application does not see the gateway's budget, and a fabricated counter is worse than none. Cached free routes (`/health`, `/pricing`, `/leaderboard`, `/stats/*`) are served by CloudFront and do not consume the budget on a cache hit.\n\n### Versioning & deprecation\n\nThe API is versioned by this document's `info.version` (semver), not by URL paths — there is no `/v1/`; every route is the current contract. **A breaking change to a served field bumps the major version and ships with a new `API_VERSION`, never silently**: that is the standing rule this repo already applies (a field changing *type* counts as breaking, a new optional field does not). Scored numbers are versioned separately and per-response via `policy_version` — if the scoring policy changes, that string changes with it, so any cached or stored number can be traced to the policy that produced it. Deprecations are announced in this description and in [llms.txt](https://describe.net/llms.txt) before removal; there is no fixed sunset window to cite yet, and this page will say so when one exists rather than inventing one here.\n\n### Features\n\n| Feature | What it means |\n|---|---|\n| Cross-chain ERC-8004 index | One row per individual rating, scanned from `Registered` and `NewFeedback` logs and joined to the agent's **current** owner — never the minter, which is usually the facilitator that minted the id before transferring it. |\n| Equal weight per chain | A wallet's score is the mean of its per-chain scores, so a chain carrying hundreds of thousands of ratings cannot drown one carrying four. The per-chain detail ships in `per_chain`, never collapsed. |\n| Bayesian shrinkage in the ranking | The leaderboard is ordered by `shrunk_score` — the observed mean pulled toward the index prior by how many **distinct raters** back it. Both numbers and that N travel in every row, so the order can be recomputed by hand instead of believed. |\n| Self-ratings excluded, not hidden | A subject rating itself is out of every score and published separately as `self_rated`, with `gap = self - others`. Positive means it overrates itself. |\n| Facets by tag | The rater's `tag1` kept apart — `trust`, `liveness`, `contractRisk`, `activity` are separate judgements, not a mean. The overall score and one facet can disagree by more than 30 points on the same wallet; that gap is the product. |\n| Concentration signal | `distinct_raters`, `top_client_share` and `top_client` on every profile. Advisory — it never moves a score. Read `distinct_raters` against `total_reviews`: the largest campaign in this index sits **under 0.15 on share**, so any threshold on share alone waves it through. What gives it away is thousands of ratings per distinct rater. Verify the campaign for free — `miner-vouch` in `GET /stats/facets` carries 300,001 ratings with `distinct_agents: 1`, against the index total in `GET /health`. |\n| Provenance per rating | Agent detail returns every rating with its `tx_hash`, block number and log index — revoked ones included, with the transaction that killed them. A revocation nobody can audit is not a revocation. |\n| Time series on on-chain time | History buckets by the **block** timestamp, never by when we indexed the row, and reports `coverage.undated_reviews` so a series shorter than the score reads as coverage rather than as a bug. |\n| Second weighting published alongside | `weighted_score` and `rater_weight_policy` travel next to `final_score`. Changing how hundreds of thousands of ratings are weighted is not something a reputation index gets to do silently. |\n| Citable snapshot | `?snapshot=true` persists a durable row with `inputs_digest` + `policy_version`: a receipt you can hand a counterparty and we can recompute identically. |\n| MCP server | The same index as thirteen tools inside your MCP client: five are priced like the routes behind them, the other eight never charge. |\n\n### Pricing\n\nOne rule decides the line, and it is a rule about cost: **free is what a cache serves everybody; charged is what only you asked for.** The aggregate view of the index is free forever. The question about *your* counterparty is what costs money, which is exactly when the answer is worth something.\n\n**Free — HTTP 200, no headers, no account.** Start here: reading the free tier costs nothing and tells you whether the metered tier is worth it.\n\n| Route | What it gives you |\n|---|---|\n| `GET /health` | Liveness, plus how far the scan reached on each chain and the four policy versions in force. |\n| `GET /pricing` | Every price, machine-readable, each with the reason it costs that. The only route that never touches Postgres. |\n| `GET /docs` · `/redoc` · `/openapi.json` | **The live schema** — what this build is serving at this instant, rendered two ways. The written documentation (a guide for every door, plus this schema sealed with its date so you can cite it) is at [docs.describe.net](https://docs.describe.net/). |\n| `/a2a/?` | — |\n| `/mcp/?` | — |\n| `/api/?` | — |\n| `/\\.well-known/.*` | — |\n| `/manifesto/?` | — |\n| `/wallets/[^/]+/chains/?` | — |\n| `/badge/.+` | — |\n| `/search(/.*)?` | — |\n| `/chains(/[A-Za-z0-9_-]+)?/?` | — |\n| `/feed/?` | — |\n| `GET /leaderboard` | The first 100 rows, no parameters — the shop window. Ranked by evidence, not by bare average. |\n| `GET /stats/facets` | Every facet in the index with its volume, mean and how many distinct agents carry it. |\n| `GET /stats/types` | Declared-type coverage across indexed agents, including how many declared none. **The heaviest aggregate in the index** — it used to exceed the gateway timeout, and since 2026-08-11 it is served from a materialised snapshot instead of the live grain. For the same grain cut by tag use `GET /stats/facets`. |\n| `GET /stats/issuers` | Every issuing service (the host of each rating's `feedbackURI`) with its volume and reach — and the route that audits how much of this index its own operators wrote (`issuer_org`). |\n\n**Metered — HTTP 402 until paid.** These answer a question about one named subject.\n\n| Route | Price | What it gives you |\n|---|---|---|\n| `/reputation/rater/[^/]+/?` | $0.01 | — |\n| `GET /leaderboard/page` | $0.01 | Paging past the shop window, widening it (`limit` up to 500), or filtering by declared type. Every call is metered. |\n| `GET /reputation/wallet/{wallet}` | $0.01 | The composition: per-chain scores, facets, self-rating gap, concentration, first and last rating. |\n| `GET /reputation/agent/{network}/{agent_id}` | $0.02 | One agent and EVERY rating it received, each with its transaction — revoked ones included. |\n| `GET /reputation/wallet/{wallet}/history` | $0.03 | The series by on-chain time, weekly or monthly, with the coverage it could actually plot. |\n| `GET /reputation/wallet/{wallet}?snapshot=true` | $0.05 | The only route that WRITES. Records a durable receipt row with `inputs_digest` + `policy_version` you can cite to a counterparty. |\n\nPricing table `cost-tiered@5`, served machine-readable and free at `GET /pricing`, each price with the reason it costs that. **The authoritative price for one call is the `accepts[]` array of that call's own 402** — the price can depend on the parameters, which a table per route cannot express.\n\n### How to pay\n\nFive steps, no registration, nothing to do before your first call. If you can sign an EIP-3009 authorization you are already a customer.\n\n1. **Call the route you want. Do not pre-authorize.** Asking without paying is free and is the intended first move — the 402 is a price quote, not an error.\n2. **Read the challenge.** The 402 body carries `amount`, `resource`, `recipient`, the pricing tier with its reason, and `accepts[]` — one entry per network. **`accepts[]` is the only thing you sign against.** Its `amount` is in token base units (USDC has 6 decimals, so `\"10000\"` is $0.01); the top-level `amount` is the human dollar figure and must not be signed. Take the USDC contract from `accepts[].asset`, never from a document.\n3. **Check who you are about to pay, every time.** The only address describe.net will ever ask you to pay is `0xe4dc963c56979E0260fc146b87eE24F18220e545`, published here and pinned out of band in the MCP server and in `auth.md`. If `payTo` differs, **do not pay** — either the challenge did not come from us, or the treasury changed and this page is stale. Both are resolved by asking, not by signing.\n4. **Sign an EIP-3009 `TransferWithAuthorization`** for that `amount`, to that `payTo`, for that `asset`, on that network, and base64-encode the x402 payload. You sign locally and send a signature — no private key ever leaves your side. An MCP wallet (`ows_sign_eip3009`, `pay_x402`) or `uvd-x402-sdk` will produce the string for you.\n5. **Replay the identical request with the `X-PAYMENT` header.** Same method, same path, same query string, one header more. Payment is verified and settled on-chain **before** the query runs; the 200 comes back with `X-Payment-Receipt` (the settlement transaction hash — public, keep it to reconcile) and `X-Payment-Reused`.\n\nNetworks the challenge will offer, chosen by settlement gas rather than by price — we pay the gas, and paying L1 gas to collect one cent would be selling at a loss:\n\n| Network | Chain ID |\n|---|---|\n| `base` | 8453 |\n| `avalanche` | 43114 |\n| `arbitrum` | 42161 |\n| `optimism` | 10 |\n| `polygon` | 137 |\n| `celo` | 42220 |\n\n**Three rules that save money.** One authorization per paid call: the nonce is consumed at settlement, so a credential that already paid will not pay again. A receipt is reusable, but only for the byte-identical resource — wallet A's receipt cannot unlock wallet B, and the reused read answers `X-Payment-Reused: true`. And **a 503 is not a 402**: `facilitator_unavailable` means your authorization was NOT used, so retry the same request with the *same* `X-PAYMENT` after `Retry-After` — signing a fresh one there can make you pay twice. Full flow, with worked examples: https://describe.net/auth.md\n\n### What we do NOT do\n\n| Not this | Why it matters to you |\n|---|---|\n| Not a block explorer | No balances, no transactions, no contract state. We serve the ratings; each one carries its `tx_hash` so you can go verify it in an explorer, which is the point. |\n| Not a price oracle | No token prices, no TVL, no market data. The only dollar figures here are our own prices. |\n| Not a credit score | Nothing here predicts default and nothing here is an approval. We report who rated a subject, how many of them there were, on what, and when. The decision stays yours — and a single number was never enough to make it. |\n| Not a writer to any chain | Zero contracts deployed, zero transactions signed by us. This is a read-only index; the only thing that moves on-chain is your own payment, settled by the facilitator. |\n| Not an identity service | A wallet is a wallet. There is no name lookup and no person search, and no amount of on-chain behaviour makes us call a subject a person. |\n\n### Links\n\n**The documentation lives at [docs.describe.net](https://docs.describe.net/)** — a guide for every door, and this schema sealed with its date and provenance so a link to it still means the same thing next month. The pages below on `api.describe.net` are the **live schema**: the same document, rendered from the build that is answering you right now. Read the hub; check the live pages when what you need to know is what is deployed at this instant.\n\n| Resource | URL |\n|---|---|\n| Documentation (canonical) | https://docs.describe.net/ |\n| Site | https://describe.net |\n| Swagger UI — live schema (this page) | https://api.describe.net/docs |\n| ReDoc — live schema | https://api.describe.net/redoc |\n| OpenAPI 3.1 schema (live) | https://api.describe.net/openapi.json |\n| OpenAPI 3.1 schema (sealed, citable) | https://docs.describe.net/openapi.json |\n| llms.txt | https://describe.net/llms.txt |\n| Agent skill (umbrella) | https://describe.net/skill.md |\n| Agent workflows | https://describe.net/workflows.md |\n| Agent skills index | https://describe.net/.well-known/agent-skills/index.json |\n| API catalog (RFC 9727 linkset) | https://describe.net/.well-known/api-catalog |\n| How to authenticate — i.e. how to pay | https://describe.net/auth.md |\n| API guide | https://describe.net/api.md |\n| MCP server card | https://describe.net/.well-known/mcp/server-card.json |\n| Live prices | https://api.describe.net/pricing |\n| Live index coverage | https://api.describe.net/health |",
    "contact": {
      "name": "describe.net",
      "url": "https://describe.net/"
    },
    "version": "2.0.0",
    "x-api-version-policy": {
      "scheme": "semver",
      "where": "info.version (no path or header versioning; every route is the current contract)",
      "breaking_change": "major bump + new API_VERSION, never silent; a served field changing type is breaking, a new optional field is not",
      "scoring_policy": "policy_version travels in every scored response and changes whenever the scoring policy does",
      "deprecation_notice": "announced in this description and in https://describe.net/llms.txt before removal; no fixed sunset window is promised",
      "changelog": "https://describe.net/api.md"
    },
    "x-rate-limit-policy": {
      "enforced_at": "API Gateway (edge), per stage",
      "sustained_rps": 50,
      "burst": 40,
      "on_exceed": "HTTP 429 from the gateway before the application runs; back off and retry",
      "policy_header": "RateLimit-Policy on every response (RFC 9239 syntax), with the gateway's live values — read the header, do not hardcode it",
      "per_request_counter": "none on purpose — the application cannot see the gateway budget and will not fabricate RateLimit remaining/reset",
      "cache_note": "CloudFront-cached free routes do not consume the budget on a cache hit",
      "source": "DESCRIBENET_THROTTLE_RATE / DESCRIBENET_THROTTLE_BURST, set by Terraform from var.throttling_rate_limit / var.throttling_burst_limit — the same variables that configure the stage. null here means this build was not told them, never that there is no limit."
    }
  },
  "servers": [
    {
      "url": "https://api.describe.net",
      "description": "canonical"
    },
    {
      "url": "https://api.describe.net/v1",
      "description": "same contract, major version in the path"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "Free"
        ],
        "summary": "Index size and scan coverage — free",
        "description": "How big the index is and how far the scan reached on each chain, live and uncached: `agents`, `feedback_entries`, and one row per chain with `last_scanned_block` against `head_at_last_sync`.\n\n**This is where the numbers live.** No total is written into this page by hand — indexing runs continuously, and a total typed into a document goes stale while still sounding exact. It also publishes the four policy versions in force, which is what lets you tell whether two answers are comparable.\n\n`chains[].network` is also the vocabulary for the `{network}` path parameter of the agent route.",
        "operationId": "getHealth",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [],
        "x-payment": "x402",
        "x-pricing-tier": "health",
        "x-price-usd": "0"
      }
    },
    "/pricing": {
      "get": {
        "tags": [
          "Free"
        ],
        "summary": "Every price and why it costs that — free",
        "description": "The full pricing table, machine-readable, each price with the reason it costs that — plus `pricing_version` and the networks payment is accepted on. Read it to budget before signing anything, and to reconcile afterwards against what you were charged.\n\nThe only route that never touches Postgres, so it keeps answering with the index down.\n\n**The authoritative price for one call is the `accepts[]` array of that call's own 402**, not this table: the price can depend on the parameters (`?snapshot=true`), which a table per route cannot express.",
        "operationId": "getPricing",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PricingCatalog"
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [],
        "x-payment": "x402",
        "x-pricing-tier": "pricing",
        "x-price-usd": "0"
      }
    },
    "/manifesto": {
      "get": {
        "tags": [
          "Free"
        ],
        "summary": "The manifesto, as data — free",
        "description": "**What this service believes, wired to the code that enforces it.**\n\nEach principle carries `enforced_by`: the file or the route that makes it true, so the claim can be checked instead of believed. `inherited_from` says which of DescribeMe's ten Principles from 2012 it comes from — and which ones it *corrects*, which is the more useful half.\n\n`verifications` is the part worth calling: every claim in the manifesto that can be checked from outside, each with the exact command that checks it. All but one are free, because a document that charges to be verified is not a document, it is an ad.\n\n**No figure of the index is copied into this response** — the routes named in `verifications` serve them live, and a number frozen inside a manifesto is the oldest way to lie with true statements. The one exception is `origin`, which counts a database that stopped taking writes in 2021.\n\nThe prose is not here either: it is published in full at the URLs in `editions`, in English and Spanish. This route never touches Postgres, so it keeps answering with the index down.",
        "operationId": "getManifesto",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Manifesto"
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [],
        "x-payment": "x402",
        "x-pricing-tier": "manifesto",
        "x-price-usd": "0"
      }
    },
    "/reputation/wallet/{wallet}": {
      "get": {
        "tags": [
          "Metered (x402)"
        ],
        "summary": "Decompose one wallet's reputation — $0.01 USDC ($0.05 USDC with ?snapshot=true)",
        "description": "**The core product.** Everything that produced the score, not just the score: `per_chain` (never collapsed), `facets` by the rater's tag — each one with `distinct_raters` and what it threw away (`revoked_count`, `out_of_domain_count`, `self_rated_count`) — `self_rated` with its `gap`, `concentration` (`distinct_raters`, `top_client_share`, `top_client`), `confidence` (the named evidence band, its Wilson interval, the thresholds and what to do about it), `activity` by on-chain time, `caveats` (the traps these particular numbers trigger, already written out), and both `final_score` and `weighted_score` side by side with the policy that produced each.\n\n`caveats` costs nothing extra — it is derived from the fields above, not from a second query — and it is the same list the MCP tool returns, from the same module. An empty array means no trap fired, not 'verified clean'.\n\n`confidence` is **advisory**: it never moves the score and never fills a null one. When the band is `low`, treat the point value as no evidence and decide on the interval — the same instruction QuickNode publishes for its own confidence tier.\n\nA wallet we have no evidence about is a **200 with a null score**, not a 404: *we hold nothing on this counterparty* is a useful answer to someone deciding whether to sign.\n\nInside a facet, `distinct_raters` is **how many spoke**, not how many times: hundreds of thousands of ratings from a few dozen raters and a few thousand from thousands of raters look identical as `{score, count}` and are not the same evidence. The free `GET /stats/facets` publishes, live, how many ratings and how many distinct agents every facet in this index carries. **Do not add `distinct_raters` across facets** — one rater who judged two areas counts in both; the subject's total is `concentration.distinct_raters`. The three discard counters are advisory and mutually exclusive (revoked > out-of-domain > self), so per facet `count + revoked_count + out_of_domain_count + self_rated_count` is every row that carried that tag. A facet whose ratings were **all** discarded is not listed at all.\n\n`concentration` may come back null on its own while the rest of the answer stands — a side signal that timed out must not take the answer with it. `confidence` follows `concentration`: its N is read once, in SQL, so when that signal degrades both come back null. Handle the null.",
        "operationId": "getWalletReputation",
        "parameters": [
          {
            "name": "wallet",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The subject wallet: EVM (`0x…`, case-insensitive) or a Solana base58 id (case-SENSITIVE). This is the identity the index keys on: agent ids are resolved to their **current owner** before the grain is grouped here, so one wallet carries everything its agents earned across every chain.",
              "examples": [
                "0x715dc035ffb97dd7bb4095c6670138ba05bb4e6d",
                "DevFFyNWxZPtYLpEjzUnN1PFc9Po6PH7eZCi9f3tTkTw"
              ],
              "title": "Wallet"
            },
            "description": "The subject wallet: EVM (`0x…`, case-insensitive) or a Solana base58 id (case-SENSITIVE). This is the identity the index keys on: agent ids are resolved to their **current owner** before the grain is grouped here, so one wallet carries everything its agents earned across every chain."
          },
          {
            "name": "snapshot",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "description": "Persist this answer to `score_snapshots` and return the row. **Costs more — this is the only route that writes.** What you buy is not the number, it is the commitment to it: a durable receipt with `inputs_digest` + `policy_version` that you can cite to a counterparty and we can recompute identically.",
              "default": false,
              "title": "Snapshot"
            },
            "description": "Persist this answer to `score_snapshots` and return the row. **Costs more — this is the only route that writes.** What you buy is not the number, it is the commitment to it: a durable receipt with `inputs_digest` + `policy_version` that you can cite to a counterparty and we can recompute identically."
          }
        ],
        "responses": {
          "200": {
            "description": "The answer. When the call was metered it also carries the settlement receipt headers; `X-Payment-Reused: true` means a retry was served from the receipt window instead of charging again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WalletScore"
                }
              }
            },
            "headers": {
              "X-Payment-Receipt": {
                "description": "Only on metered calls: the on-chain settlement transaction hash — public, keep it to reconcile — or `pending` if settlement has not reported one.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Payment-Reused": {
                "description": "Only on metered calls: `true` when this response was served from the idempotency window of a payment already settled — a retry, not a second charge.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true",
                    "false"
                  ]
                }
              },
              "Cache-Control": {
                "description": "`no-store` on every response that went through a charge.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "402": {
            "description": "**Payment required — $0.01 USDC, or $0.05 USDC with `?snapshot=true`.** Not an error: this is the price quote, and asking for it is free.\n\nThe body carries `amount` (human dollars, **never sign this one**), `resource`, `recipient`, `x402Version`, `scheme`, `maxTimeoutSeconds`, `retryable`, `pricing` (version, tier and the reason it costs that), `next_action`, `see_also` (every free route next to this one; the ones carrying `{…}` are templates, not paste-ready), and **`accepts[]` — one entry per network, and the only thing you sign against**. Each entry gives `network`, `asset` (the USDC contract), `amount` in token base units, and `payTo`.\n\n`payTo` must equal `0xe4dc963c56979E0260fc146b87eE24F18220e545`. If it does not, do not sign.\n\nHeaders: `Payment-Required` (the same challenge, base64), `X-Accept-Payment`, `X-Pricing-Version`, `Link` (RFC 8288: the human page for this subject, and this service's `llms.txt`), `Cache-Control: no-store`.\n\nThen replay this identical request with the `X-PAYMENT` header. The 200 comes back with `X-Payment-Receipt` and `X-Payment-Reused`.",
            "headers": {
              "Payment-Required": {
                "description": "The same challenge body, base64-encoded. If it and the body ever diverge, one of them is lying.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Accept-Payment": {
                "description": "Payment protocol advertisement: `x402 USDC 2.0`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Pricing-Version": {
                "description": "The pricing table this quote came from (currently `cost-tiered@5`).",
                "schema": {
                  "type": "string"
                }
              },
              "Link": {
                "description": "RFC 8288, for clients that do not speak x402 at all. `rel=\"alternate\"` — the human page for this same subject, omitted when the resource has no subject. `rel=\"describedby\"` — the machine-readable index of this service, always present.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store`: a cached price quote is a stale price quote.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "The x402 challenge. `accepts[]` is the only thing you sign against; the authoritative example is the live 402 itself, which is free to ask for.",
                  "required": [
                    "service",
                    "error",
                    "retryable",
                    "resource",
                    "price_usd",
                    "pricing",
                    "next_action",
                    "accepts",
                    "see_also"
                  ],
                  "properties": {
                    "service": {
                      "type": "string",
                      "description": "What this service is, in one line — for the agent whose first contact is this 402."
                    },
                    "error": {
                      "type": "string",
                      "description": "Stable snake_case code to branch on: `payment_required`, or the SDK's rejection code on a failed payment."
                    },
                    "retryable": {
                      "type": "boolean",
                      "description": "`true` on a fresh quote; `false` on a rejected payment — then read `next_action` before signing anything else."
                    },
                    "resource": {
                      "type": "string",
                      "description": "Canonical identity of what you are buying: `GET /path?…`, query sorted."
                    },
                    "price_usd": {
                      "type": "string",
                      "description": "Human dollar figure, as a string so it never rounds. Never sign this one: sign `accepts[].amount`."
                    },
                    "pricing": {
                      "type": "object",
                      "required": [
                        "version",
                        "tier",
                        "why",
                        "free_tier"
                      ],
                      "properties": {
                        "version": {
                          "type": "string",
                          "description": "Pricing table version this quote came from."
                        },
                        "tier": {
                          "type": "string",
                          "description": "Tier id in `GET /pricing`."
                        },
                        "why": {
                          "type": "string",
                          "description": "The cost reason behind this price."
                        },
                        "free_tier": {
                          "type": "string",
                          "description": "What can be read without paying, subject and aggregate."
                        }
                      }
                    },
                    "next_action": {
                      "type": "string",
                      "description": "Plain-text instruction for exactly this state — including when NOT to sign again."
                    },
                    "accepts": {
                      "type": "array",
                      "description": "One entry per payment network — the only thing you sign against.",
                      "items": {
                        "type": "object",
                        "required": [
                          "scheme",
                          "network",
                          "asset",
                          "amount",
                          "payTo",
                          "maxTimeoutSeconds"
                        ],
                        "properties": {
                          "scheme": {
                            "type": "string"
                          },
                          "network": {
                            "type": "string",
                            "description": "CAIP-2 chain id, e.g. `eip155:8453`."
                          },
                          "asset": {
                            "type": "string",
                            "description": "The USDC contract on that network. Take it from here, never from a document."
                          },
                          "amount": {
                            "type": "string",
                            "description": "Token base units (USDC has 6 decimals). This is the figure you sign."
                          },
                          "payTo": {
                            "type": "string",
                            "description": "Must equal `0xe4dc963c56979E0260fc146b87eE24F18220e545`. If it does not, do not sign."
                          },
                          "maxTimeoutSeconds": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "free_preview": {
                      "type": "object",
                      "description": "Only when the resource is about one wallet: the free route that answers whether there is anything to buy about it.",
                      "required": [
                        "endpoint",
                        "gives"
                      ],
                      "properties": {
                        "endpoint": {
                          "type": "string"
                        },
                        "gives": {
                          "type": "string"
                        }
                      }
                    },
                    "see_also": {
                      "type": "array",
                      "description": "Every free route that exists next to this one — the same invitation `pricing.free_tier` makes in prose, as keys. Most are callable as-is; the ones carrying `{…}` are templates to interpolate, never to paste (`GET /search/{query}` pasted literally is a search for the string `{query}`). The by-subject pointer for THIS resource is already interpolated, lives in `free_preview`, and is not repeated here.",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "**`facilitator_unavailable` — and a 503 is not a 402.** Your authorization was NOT used and nothing was charged. Retry this same request with the **same** `X-PAYMENT` header after `Retry-After`; signing a fresh authorization here is how you pay twice.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before replaying the same request with the same `X-PAYMENT`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Pricing-Version": {
                "description": "The pricing table this quote came from (currently `cost-tiered@5`).",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store`: a 503 is cacheable by default, and a frozen outage outlives the outage.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "The Facilitator did not answer: nothing was charged and your authorization is still valid.",
                  "required": [
                    "error",
                    "message",
                    "retryable",
                    "retry_after_seconds",
                    "resource",
                    "price_usd",
                    "pricing_version",
                    "next_action"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Always `facilitator_unavailable` here."
                    },
                    "message": {
                      "type": "string"
                    },
                    "details": {
                      "type": "object",
                      "description": "Transport or upstream detail, for diagnostics."
                    },
                    "facilitator_error": {
                      "type": "string"
                    },
                    "retryable": {
                      "type": "boolean",
                      "description": "Always `true`: retry the same request with the same `X-PAYMENT`."
                    },
                    "retry_after_seconds": {
                      "type": "integer"
                    },
                    "resource": {
                      "type": "string"
                    },
                    "price_usd": {
                      "type": "string"
                    },
                    "pricing_version": {
                      "type": "string"
                    },
                    "next_action": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-payment": "x402",
        "x-pricing-tier": "wallet-lookup",
        "x-price-usd": "0.01",
        "security": [
          {
            "x402": []
          }
        ]
      }
    },
    "/reputation/wallet/{wallet}/history": {
      "get": {
        "tags": [
          "Metered (x402)"
        ],
        "summary": "How that reputation moved over time — $0.03 USDC",
        "description": "The series bucketed by the **block** timestamp — when the rating was written on chain, never when we indexed it. Substituting our own ingestion time would date every historical rating to the day of the backfill.\n\n`coverage.undated_reviews` reports how much of the grain could not be placed in time, so a series shorter than the score reads as coverage rather than as a bug.",
        "operationId": "getWalletHistory",
        "parameters": [
          {
            "name": "wallet",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The subject wallet: EVM (`0x…`, case-insensitive) or a Solana base58 id (case-SENSITIVE).",
              "examples": [
                "0x715dc035ffb97dd7bb4095c6670138ba05bb4e6d",
                "DevFFyNWxZPtYLpEjzUnN1PFc9Po6PH7eZCi9f3tTkTw"
              ],
              "title": "Wallet"
            },
            "description": "The subject wallet: EVM (`0x…`, case-insensitive) or a Solana base58 id (case-SENSITIVE)."
          },
          {
            "name": "bucket",
            "in": "query",
            "required": false,
            "schema": {
              "enum": [
                "week",
                "month"
              ],
              "type": "string",
              "description": "Size of each point in the series",
              "default": "week",
              "title": "Bucket"
            },
            "description": "Size of each point in the series"
          }
        ],
        "responses": {
          "200": {
            "description": "The answer. When the call was metered it also carries the settlement receipt headers; `X-Payment-Reused: true` means a retry was served from the receipt window instead of charging again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WalletHistory"
                }
              }
            },
            "headers": {
              "X-Payment-Receipt": {
                "description": "Only on metered calls: the on-chain settlement transaction hash — public, keep it to reconcile — or `pending` if settlement has not reported one.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Payment-Reused": {
                "description": "Only on metered calls: `true` when this response was served from the idempotency window of a payment already settled — a retry, not a second charge.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true",
                    "false"
                  ]
                }
              },
              "Cache-Control": {
                "description": "`no-store` on every response that went through a charge.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "402": {
            "description": "**Payment required — $0.03 USDC.** Not an error: this is the price quote, and asking for it is free.\n\nThe body carries `amount` (human dollars, **never sign this one**), `resource`, `recipient`, `x402Version`, `scheme`, `maxTimeoutSeconds`, `retryable`, `pricing` (version, tier and the reason it costs that), `next_action`, `see_also` (every free route next to this one; the ones carrying `{…}` are templates, not paste-ready), and **`accepts[]` — one entry per network, and the only thing you sign against**. Each entry gives `network`, `asset` (the USDC contract), `amount` in token base units, and `payTo`.\n\n`payTo` must equal `0xe4dc963c56979E0260fc146b87eE24F18220e545`. If it does not, do not sign.\n\nHeaders: `Payment-Required` (the same challenge, base64), `X-Accept-Payment`, `X-Pricing-Version`, `Link` (RFC 8288: the human page for this subject, and this service's `llms.txt`), `Cache-Control: no-store`.\n\nThen replay this identical request with the `X-PAYMENT` header. The 200 comes back with `X-Payment-Receipt` and `X-Payment-Reused`.",
            "headers": {
              "Payment-Required": {
                "description": "The same challenge body, base64-encoded. If it and the body ever diverge, one of them is lying.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Accept-Payment": {
                "description": "Payment protocol advertisement: `x402 USDC 2.0`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Pricing-Version": {
                "description": "The pricing table this quote came from (currently `cost-tiered@5`).",
                "schema": {
                  "type": "string"
                }
              },
              "Link": {
                "description": "RFC 8288, for clients that do not speak x402 at all. `rel=\"alternate\"` — the human page for this same subject, omitted when the resource has no subject. `rel=\"describedby\"` — the machine-readable index of this service, always present.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store`: a cached price quote is a stale price quote.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "The x402 challenge. `accepts[]` is the only thing you sign against; the authoritative example is the live 402 itself, which is free to ask for.",
                  "required": [
                    "service",
                    "error",
                    "retryable",
                    "resource",
                    "price_usd",
                    "pricing",
                    "next_action",
                    "accepts",
                    "see_also"
                  ],
                  "properties": {
                    "service": {
                      "type": "string",
                      "description": "What this service is, in one line — for the agent whose first contact is this 402."
                    },
                    "error": {
                      "type": "string",
                      "description": "Stable snake_case code to branch on: `payment_required`, or the SDK's rejection code on a failed payment."
                    },
                    "retryable": {
                      "type": "boolean",
                      "description": "`true` on a fresh quote; `false` on a rejected payment — then read `next_action` before signing anything else."
                    },
                    "resource": {
                      "type": "string",
                      "description": "Canonical identity of what you are buying: `GET /path?…`, query sorted."
                    },
                    "price_usd": {
                      "type": "string",
                      "description": "Human dollar figure, as a string so it never rounds. Never sign this one: sign `accepts[].amount`."
                    },
                    "pricing": {
                      "type": "object",
                      "required": [
                        "version",
                        "tier",
                        "why",
                        "free_tier"
                      ],
                      "properties": {
                        "version": {
                          "type": "string",
                          "description": "Pricing table version this quote came from."
                        },
                        "tier": {
                          "type": "string",
                          "description": "Tier id in `GET /pricing`."
                        },
                        "why": {
                          "type": "string",
                          "description": "The cost reason behind this price."
                        },
                        "free_tier": {
                          "type": "string",
                          "description": "What can be read without paying, subject and aggregate."
                        }
                      }
                    },
                    "next_action": {
                      "type": "string",
                      "description": "Plain-text instruction for exactly this state — including when NOT to sign again."
                    },
                    "accepts": {
                      "type": "array",
                      "description": "One entry per payment network — the only thing you sign against.",
                      "items": {
                        "type": "object",
                        "required": [
                          "scheme",
                          "network",
                          "asset",
                          "amount",
                          "payTo",
                          "maxTimeoutSeconds"
                        ],
                        "properties": {
                          "scheme": {
                            "type": "string"
                          },
                          "network": {
                            "type": "string",
                            "description": "CAIP-2 chain id, e.g. `eip155:8453`."
                          },
                          "asset": {
                            "type": "string",
                            "description": "The USDC contract on that network. Take it from here, never from a document."
                          },
                          "amount": {
                            "type": "string",
                            "description": "Token base units (USDC has 6 decimals). This is the figure you sign."
                          },
                          "payTo": {
                            "type": "string",
                            "description": "Must equal `0xe4dc963c56979E0260fc146b87eE24F18220e545`. If it does not, do not sign."
                          },
                          "maxTimeoutSeconds": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "free_preview": {
                      "type": "object",
                      "description": "Only when the resource is about one wallet: the free route that answers whether there is anything to buy about it.",
                      "required": [
                        "endpoint",
                        "gives"
                      ],
                      "properties": {
                        "endpoint": {
                          "type": "string"
                        },
                        "gives": {
                          "type": "string"
                        }
                      }
                    },
                    "see_also": {
                      "type": "array",
                      "description": "Every free route that exists next to this one — the same invitation `pricing.free_tier` makes in prose, as keys. Most are callable as-is; the ones carrying `{…}` are templates to interpolate, never to paste (`GET /search/{query}` pasted literally is a search for the string `{query}`). The by-subject pointer for THIS resource is already interpolated, lives in `free_preview`, and is not repeated here.",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "**`facilitator_unavailable` — and a 503 is not a 402.** Your authorization was NOT used and nothing was charged. Retry this same request with the **same** `X-PAYMENT` header after `Retry-After`; signing a fresh authorization here is how you pay twice.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before replaying the same request with the same `X-PAYMENT`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Pricing-Version": {
                "description": "The pricing table this quote came from (currently `cost-tiered@5`).",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store`: a 503 is cacheable by default, and a frozen outage outlives the outage.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "The Facilitator did not answer: nothing was charged and your authorization is still valid.",
                  "required": [
                    "error",
                    "message",
                    "retryable",
                    "retry_after_seconds",
                    "resource",
                    "price_usd",
                    "pricing_version",
                    "next_action"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Always `facilitator_unavailable` here."
                    },
                    "message": {
                      "type": "string"
                    },
                    "details": {
                      "type": "object",
                      "description": "Transport or upstream detail, for diagnostics."
                    },
                    "facilitator_error": {
                      "type": "string"
                    },
                    "retryable": {
                      "type": "boolean",
                      "description": "Always `true`: retry the same request with the same `X-PAYMENT`."
                    },
                    "retry_after_seconds": {
                      "type": "integer"
                    },
                    "resource": {
                      "type": "string"
                    },
                    "price_usd": {
                      "type": "string"
                    },
                    "pricing_version": {
                      "type": "string"
                    },
                    "next_action": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-payment": "x402",
        "x-pricing-tier": "wallet-history",
        "x-price-usd": "0.03",
        "security": [
          {
            "x402": []
          }
        ]
      }
    },
    "/reputation/agent/{network}/{agent_id}": {
      "get": {
        "tags": [
          "Metered (x402)"
        ],
        "summary": "One agent and EVERY rating it received — $0.02 USDC",
        "description": "The evidence itself, not a summary of it: every individual rating with its `tx_hash`, block number and log index, so each one can be checked in an explorer. **Revoked ratings included**, each with the transaction that killed it — a revocation nobody can audit is not a revocation.\n\n`score` applies the full policy — revoked out, domain 0 < v ≤ 100, no self-rating — and what it excludes stays listed and counted: `out_of_domain_count`, `self_rated` with its gap, and `is_self` on every row, so `review_count + revoked_count + out_of_domain_count + self_rated.count` always equals the length of `ratings`.\n\n**That same arithmetic now holds inside each facet**: `count + revoked_count + out_of_domain_count + self_rated_count` is every row that carried that tag, under the same precedence (revoked > out-of-domain > self). It does NOT have to add up to the totals above: a facet whose ratings were all discarded is not listed at all, so the per-facet discards can sum to LESS than the agent-level ones. Each facet also carries `distinct_raters`, how many spoke rather than how many times; it is advisory and **must not be added across facets** (one rater who judged two areas counts in both, so the agent's total stays `concentration.distinct_raters`).\n\n`ownership` splits that same population in two by ON-CHAIN POSITION: the ratings that landed **before** the current owner took the identity (`inherited_*`) and the ones it earned itself (`current_era_*`), plus the ones the log scan has not placed yet (`undetermined_review_count`). The three counts add up to `review_count`, and it is **advisory: it never moves `score` and never fills a null one**. It exists because minting, getting rated and then transferring is the cheapest attack on an index like this one — agent `base/888` carries 1.178 of its 1.200 eligible ratings on the inherited side, all from a single rater (measured 2026-08-25). **A null here means the era cannot be established, never that nothing was inherited**: no identity row, no `owner_updated_block`, or `network` is `solana`, where that column is the SLOT AT WHICH WE READ the asset rather than the position of a transfer — on Solana ownership is present state, not an event, so the predicate would flag 100,00 % of that chain's ratings as inherited. And it is not a verdict: a business sale, a custody migration or a wallet rotation produce exactly the same figure as a bought reputation.\n\n`confidence` names how much evidence backs `score` — band, Wilson interval, thresholds and what to do about it — and is **advisory**: it never moves the score and never fills a null one. It follows `concentration`, whose `distinct_raters` it reads: when that side signal degrades, both come back null. Handle the null.\n\n`caveats` comes **before** `ratings` on purpose: that array can carry hundreds of thousands of rows, and a warning printed after it is a warning nobody reads. Same rules and same module as the wallet route.\n\nAn agent id we have never seen is a **404**, unlike the wallet route: an id is a claim about a specific registry entry, and we either indexed it or we did not.",
        "operationId": "getAgentReputation",
        "parameters": [
          {
            "name": "network",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Chain the agent id lives on. **The valid values are the `chains[].network` names in `GET /health`** (free), which is also how you see how far the scan reached on each one. An id is only unique per chain: the same number is a different agent elsewhere.",
              "examples": [
                "base",
                "solana",
                "avalanche"
              ],
              "title": "Network"
            },
            "description": "Chain the agent id lives on. **The valid values are the `chains[].network` names in `GET /health`** (free), which is also how you see how far the scan reached on each one. An id is only unique per chain: the same number is a different agent elsewhere."
          },
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "ERC-8004 registry id, as minted on that chain. A string, not a number: EVM registries mint integers, while on solana the id is the Metaplex Core asset address in base58 — case-sensitive, so pass it exactly as the chain wrote it. Ratings are joined to the agent's **current** owner, never the minter — which is usually the facilitator that minted the id before transferring it.",
              "examples": [
                "25975",
                "4qNFzd4bQvdspEDmrAMnk3shcrEYHo4n52zfW9VVFLhZ"
              ],
              "title": "Agent Id"
            },
            "description": "ERC-8004 registry id, as minted on that chain. A string, not a number: EVM registries mint integers, while on solana the id is the Metaplex Core asset address in base58 — case-sensitive, so pass it exactly as the chain wrote it. Ratings are joined to the agent's **current** owner, never the minter — which is usually the facilitator that minted the id before transferring it."
          }
        ],
        "responses": {
          "200": {
            "description": "The answer. When the call was metered it also carries the settlement receipt headers; `X-Payment-Reused: true` means a retry was served from the receipt window instead of charging again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentScore"
                }
              }
            },
            "headers": {
              "X-Payment-Receipt": {
                "description": "Only on metered calls: the on-chain settlement transaction hash — public, keep it to reconcile — or `pending` if settlement has not reported one.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Payment-Reused": {
                "description": "Only on metered calls: `true` when this response was served from the idempotency window of a payment already settled — a retry, not a second charge.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true",
                    "false"
                  ]
                }
              },
              "Cache-Control": {
                "description": "`no-store` on every response that went through a charge.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "402": {
            "description": "**Payment required — $0.02 USDC.** Not an error: this is the price quote, and asking for it is free.\n\nThe body carries `amount` (human dollars, **never sign this one**), `resource`, `recipient`, `x402Version`, `scheme`, `maxTimeoutSeconds`, `retryable`, `pricing` (version, tier and the reason it costs that), `next_action`, `see_also` (every free route next to this one; the ones carrying `{…}` are templates, not paste-ready), and **`accepts[]` — one entry per network, and the only thing you sign against**. Each entry gives `network`, `asset` (the USDC contract), `amount` in token base units, and `payTo`.\n\n`payTo` must equal `0xe4dc963c56979E0260fc146b87eE24F18220e545`. If it does not, do not sign.\n\nHeaders: `Payment-Required` (the same challenge, base64), `X-Accept-Payment`, `X-Pricing-Version`, `Link` (RFC 8288: the human page for this subject, and this service's `llms.txt`), `Cache-Control: no-store`.\n\nThen replay this identical request with the `X-PAYMENT` header. The 200 comes back with `X-Payment-Receipt` and `X-Payment-Reused`.",
            "headers": {
              "Payment-Required": {
                "description": "The same challenge body, base64-encoded. If it and the body ever diverge, one of them is lying.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Accept-Payment": {
                "description": "Payment protocol advertisement: `x402 USDC 2.0`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Pricing-Version": {
                "description": "The pricing table this quote came from (currently `cost-tiered@5`).",
                "schema": {
                  "type": "string"
                }
              },
              "Link": {
                "description": "RFC 8288, for clients that do not speak x402 at all. `rel=\"alternate\"` — the human page for this same subject, omitted when the resource has no subject. `rel=\"describedby\"` — the machine-readable index of this service, always present.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store`: a cached price quote is a stale price quote.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "The x402 challenge. `accepts[]` is the only thing you sign against; the authoritative example is the live 402 itself, which is free to ask for.",
                  "required": [
                    "service",
                    "error",
                    "retryable",
                    "resource",
                    "price_usd",
                    "pricing",
                    "next_action",
                    "accepts",
                    "see_also"
                  ],
                  "properties": {
                    "service": {
                      "type": "string",
                      "description": "What this service is, in one line — for the agent whose first contact is this 402."
                    },
                    "error": {
                      "type": "string",
                      "description": "Stable snake_case code to branch on: `payment_required`, or the SDK's rejection code on a failed payment."
                    },
                    "retryable": {
                      "type": "boolean",
                      "description": "`true` on a fresh quote; `false` on a rejected payment — then read `next_action` before signing anything else."
                    },
                    "resource": {
                      "type": "string",
                      "description": "Canonical identity of what you are buying: `GET /path?…`, query sorted."
                    },
                    "price_usd": {
                      "type": "string",
                      "description": "Human dollar figure, as a string so it never rounds. Never sign this one: sign `accepts[].amount`."
                    },
                    "pricing": {
                      "type": "object",
                      "required": [
                        "version",
                        "tier",
                        "why",
                        "free_tier"
                      ],
                      "properties": {
                        "version": {
                          "type": "string",
                          "description": "Pricing table version this quote came from."
                        },
                        "tier": {
                          "type": "string",
                          "description": "Tier id in `GET /pricing`."
                        },
                        "why": {
                          "type": "string",
                          "description": "The cost reason behind this price."
                        },
                        "free_tier": {
                          "type": "string",
                          "description": "What can be read without paying, subject and aggregate."
                        }
                      }
                    },
                    "next_action": {
                      "type": "string",
                      "description": "Plain-text instruction for exactly this state — including when NOT to sign again."
                    },
                    "accepts": {
                      "type": "array",
                      "description": "One entry per payment network — the only thing you sign against.",
                      "items": {
                        "type": "object",
                        "required": [
                          "scheme",
                          "network",
                          "asset",
                          "amount",
                          "payTo",
                          "maxTimeoutSeconds"
                        ],
                        "properties": {
                          "scheme": {
                            "type": "string"
                          },
                          "network": {
                            "type": "string",
                            "description": "CAIP-2 chain id, e.g. `eip155:8453`."
                          },
                          "asset": {
                            "type": "string",
                            "description": "The USDC contract on that network. Take it from here, never from a document."
                          },
                          "amount": {
                            "type": "string",
                            "description": "Token base units (USDC has 6 decimals). This is the figure you sign."
                          },
                          "payTo": {
                            "type": "string",
                            "description": "Must equal `0xe4dc963c56979E0260fc146b87eE24F18220e545`. If it does not, do not sign."
                          },
                          "maxTimeoutSeconds": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "free_preview": {
                      "type": "object",
                      "description": "Only when the resource is about one wallet: the free route that answers whether there is anything to buy about it.",
                      "required": [
                        "endpoint",
                        "gives"
                      ],
                      "properties": {
                        "endpoint": {
                          "type": "string"
                        },
                        "gives": {
                          "type": "string"
                        }
                      }
                    },
                    "see_also": {
                      "type": "array",
                      "description": "Every free route that exists next to this one — the same invitation `pricing.free_tier` makes in prose, as keys. Most are callable as-is; the ones carrying `{…}` are templates to interpolate, never to paste (`GET /search/{query}` pasted literally is a search for the string `{query}`). The by-subject pointer for THIS resource is already interpolated, lives in `free_preview`, and is not repeated here.",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "**`facilitator_unavailable` — and a 503 is not a 402.** Your authorization was NOT used and nothing was charged. Retry this same request with the **same** `X-PAYMENT` header after `Retry-After`; signing a fresh authorization here is how you pay twice.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before replaying the same request with the same `X-PAYMENT`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Pricing-Version": {
                "description": "The pricing table this quote came from (currently `cost-tiered@5`).",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store`: a 503 is cacheable by default, and a frozen outage outlives the outage.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "The Facilitator did not answer: nothing was charged and your authorization is still valid.",
                  "required": [
                    "error",
                    "message",
                    "retryable",
                    "retry_after_seconds",
                    "resource",
                    "price_usd",
                    "pricing_version",
                    "next_action"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Always `facilitator_unavailable` here."
                    },
                    "message": {
                      "type": "string"
                    },
                    "details": {
                      "type": "object",
                      "description": "Transport or upstream detail, for diagnostics."
                    },
                    "facilitator_error": {
                      "type": "string"
                    },
                    "retryable": {
                      "type": "boolean",
                      "description": "Always `true`: retry the same request with the same `X-PAYMENT`."
                    },
                    "retry_after_seconds": {
                      "type": "integer"
                    },
                    "resource": {
                      "type": "string"
                    },
                    "price_usd": {
                      "type": "string"
                    },
                    "pricing_version": {
                      "type": "string"
                    },
                    "next_action": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`agent not indexed` — no entry for that id on that network. Check the chain is one of `chains[].network` in `GET /health`, and that the scan has reached the block the agent was registered in.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "string",
                      "description": "Always `agent not indexed` here."
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-payment": "x402",
        "x-pricing-tier": "agent-detail",
        "x-price-usd": "0.02",
        "security": [
          {
            "x402": []
          }
        ]
      }
    },
    "/leaderboard": {
      "get": {
        "tags": [
          "Free"
        ],
        "summary": "Top agents ranked by evidence — the free first page, no parameters",
        "description": "Ordered by `shrunk_score`: the observed mean pulled toward the index prior by how many **distinct raters** back it, so twenty honest ratings outrank one perfect one. Both numbers and that N travel in every row, so the order can be recomputed by hand instead of believed.\n\n**Always free, always the same answer**: the first 100 rows, unfiltered. This route takes **no query parameters** — one URL, one response, identical for everybody, which is what lets the CDN serve it from the edge instead of costing a Lambda invocation per visitor. Want fewer rows? Slice client-side.\n\nSend any parameter (`limit`, `offset`, `type`) and you get a **422 pointing at `GET /leaderboard/page`** — that is where paging and filtering moved on 2026-08-21, at **$0.01 USDC** per page. This route never answers 402.\n\n`rank` here is always 1-based: this is the first page by definition.\n\n**There is deliberately no `?network=` here.** Narrowing to a chain does not filter this list, it redefines the number: a wallet's `final_score` averages its per-chain means, and on one chain it is that chain's mean alone. Two endpoints answering the same question with different arithmetic is how a caller ends up comparing numbers that were never comparable. The chain-scoped ranking lives in `GET /chains/{network}`, scored — and shrunk against that chain's own prior — accordingly.",
        "operationId": "getLeaderboard",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/LeaderboardRow"
                  },
                  "type": "array",
                  "title": "Response Getleaderboard"
                }
              }
            }
          },
          "422": {
            "description": "`leaderboard_takes_no_params` — this route stopped taking query parameters when paging split off to its own path (2026-08-21). Not your error: it is the new door. The paged, metered ranking lives at `GET /leaderboard/page`, and `detail.paged_route` carries that pointer as a field so you do not have to parse this prose. This route never answers 402.",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "detail": {
                      "properties": {
                        "error": {
                          "type": "string",
                          "description": "Always `leaderboard_takes_no_params` here."
                        },
                        "params": {
                          "items": {
                            "type": "string"
                          },
                          "type": "array",
                          "description": "The query parameters you sent, sorted."
                        },
                        "paged_route": {
                          "type": "string",
                          "description": "Where paging and filtering live now: `GET /leaderboard/page`."
                        }
                      },
                      "type": "object",
                      "required": [
                        "error",
                        "params",
                        "paged_route"
                      ]
                    }
                  },
                  "type": "object",
                  "required": [
                    "detail"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [],
        "x-payment": "x402",
        "x-pricing-tier": "leaderboard-shop-window",
        "x-price-usd": "0"
      }
    },
    "/leaderboard/page": {
      "get": {
        "tags": [
          "Metered (x402)"
        ],
        "summary": "Page or filter the same ranking — $0.01 USDC per page",
        "description": "The same ranking as `GET /leaderboard`, same ordering, same row shape — with the knobs: `offset`, `limit` up to 500, and `type`. Split off from `/leaderboard` on 2026-08-21 so the free first page could live in the CDN: free and metered separate by **path**, which is the only boundary an edge cache can respect.\n\n**$0.01 USDC per page, every call.** Paging is no longer looking, it is extracting: each page recomputes over the full grain plus the index prior, and `offset` × `type` opens a key space no cache amortizes. The first page, unfiltered, is free at `GET /leaderboard` — calling this route without parameters buys you that same page, so don't.\n\n`rank` is relative to the filter and page that produced it, not a stored position.\n\n**There is deliberately no `?network=` here** — same reason as the free page: the chain-scoped ranking is different arithmetic and lives in `GET /chains/{network}`.",
        "operationId": "getLeaderboardPage",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "description": "Self-declared type to filter by; 'unknown' selects the agents that declared none",
              "title": "Type",
              "type": [
                "string",
                "null"
              ]
            },
            "description": "Self-declared type to filter by; 'unknown' selects the agents that declared none"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 500,
              "minimum": 1,
              "description": "Rows per page, up to 500.",
              "default": 100,
              "title": "Limit"
            },
            "description": "Rows per page, up to 500."
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "description": "Rows to skip.",
              "default": 0,
              "title": "Offset"
            },
            "description": "Rows to skip."
          }
        ],
        "responses": {
          "200": {
            "description": "The answer. When the call was metered it also carries the settlement receipt headers; `X-Payment-Reused: true` means a retry was served from the receipt window instead of charging again.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LeaderboardRow"
                  },
                  "title": "Response Getleaderboardpage"
                }
              }
            },
            "headers": {
              "X-Payment-Receipt": {
                "description": "Only on metered calls: the on-chain settlement transaction hash — public, keep it to reconcile — or `pending` if settlement has not reported one.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Payment-Reused": {
                "description": "Only on metered calls: `true` when this response was served from the idempotency window of a payment already settled — a retry, not a second charge.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true",
                    "false"
                  ]
                }
              },
              "Cache-Control": {
                "description": "`no-store` on every response that went through a charge.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "402": {
            "description": "**Payment required — $0.01 USDC.** Not an error: this is the price quote, and asking for it is free.\n\nThe body carries `amount` (human dollars, **never sign this one**), `resource`, `recipient`, `x402Version`, `scheme`, `maxTimeoutSeconds`, `retryable`, `pricing` (version, tier and the reason it costs that), `next_action`, `see_also` (every free route next to this one; the ones carrying `{…}` are templates, not paste-ready), and **`accepts[]` — one entry per network, and the only thing you sign against**. Each entry gives `network`, `asset` (the USDC contract), `amount` in token base units, and `payTo`.\n\n`payTo` must equal `0xe4dc963c56979E0260fc146b87eE24F18220e545`. If it does not, do not sign.\n\nHeaders: `Payment-Required` (the same challenge, base64), `X-Accept-Payment`, `X-Pricing-Version`, `Link` (RFC 8288: the human page for this subject, and this service's `llms.txt`), `Cache-Control: no-store`.\n\nThen replay this identical request with the `X-PAYMENT` header. The 200 comes back with `X-Payment-Receipt` and `X-Payment-Reused`.",
            "headers": {
              "Payment-Required": {
                "description": "The same challenge body, base64-encoded. If it and the body ever diverge, one of them is lying.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Accept-Payment": {
                "description": "Payment protocol advertisement: `x402 USDC 2.0`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Pricing-Version": {
                "description": "The pricing table this quote came from (currently `cost-tiered@5`).",
                "schema": {
                  "type": "string"
                }
              },
              "Link": {
                "description": "RFC 8288, for clients that do not speak x402 at all. `rel=\"alternate\"` — the human page for this same subject, omitted when the resource has no subject. `rel=\"describedby\"` — the machine-readable index of this service, always present.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store`: a cached price quote is a stale price quote.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "The x402 challenge. `accepts[]` is the only thing you sign against; the authoritative example is the live 402 itself, which is free to ask for.",
                  "required": [
                    "service",
                    "error",
                    "retryable",
                    "resource",
                    "price_usd",
                    "pricing",
                    "next_action",
                    "accepts",
                    "see_also"
                  ],
                  "properties": {
                    "service": {
                      "type": "string",
                      "description": "What this service is, in one line — for the agent whose first contact is this 402."
                    },
                    "error": {
                      "type": "string",
                      "description": "Stable snake_case code to branch on: `payment_required`, or the SDK's rejection code on a failed payment."
                    },
                    "retryable": {
                      "type": "boolean",
                      "description": "`true` on a fresh quote; `false` on a rejected payment — then read `next_action` before signing anything else."
                    },
                    "resource": {
                      "type": "string",
                      "description": "Canonical identity of what you are buying: `GET /path?…`, query sorted."
                    },
                    "price_usd": {
                      "type": "string",
                      "description": "Human dollar figure, as a string so it never rounds. Never sign this one: sign `accepts[].amount`."
                    },
                    "pricing": {
                      "type": "object",
                      "required": [
                        "version",
                        "tier",
                        "why",
                        "free_tier"
                      ],
                      "properties": {
                        "version": {
                          "type": "string",
                          "description": "Pricing table version this quote came from."
                        },
                        "tier": {
                          "type": "string",
                          "description": "Tier id in `GET /pricing`."
                        },
                        "why": {
                          "type": "string",
                          "description": "The cost reason behind this price."
                        },
                        "free_tier": {
                          "type": "string",
                          "description": "What can be read without paying, subject and aggregate."
                        }
                      }
                    },
                    "next_action": {
                      "type": "string",
                      "description": "Plain-text instruction for exactly this state — including when NOT to sign again."
                    },
                    "accepts": {
                      "type": "array",
                      "description": "One entry per payment network — the only thing you sign against.",
                      "items": {
                        "type": "object",
                        "required": [
                          "scheme",
                          "network",
                          "asset",
                          "amount",
                          "payTo",
                          "maxTimeoutSeconds"
                        ],
                        "properties": {
                          "scheme": {
                            "type": "string"
                          },
                          "network": {
                            "type": "string",
                            "description": "CAIP-2 chain id, e.g. `eip155:8453`."
                          },
                          "asset": {
                            "type": "string",
                            "description": "The USDC contract on that network. Take it from here, never from a document."
                          },
                          "amount": {
                            "type": "string",
                            "description": "Token base units (USDC has 6 decimals). This is the figure you sign."
                          },
                          "payTo": {
                            "type": "string",
                            "description": "Must equal `0xe4dc963c56979E0260fc146b87eE24F18220e545`. If it does not, do not sign."
                          },
                          "maxTimeoutSeconds": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "free_preview": {
                      "type": "object",
                      "description": "Only when the resource is about one wallet: the free route that answers whether there is anything to buy about it.",
                      "required": [
                        "endpoint",
                        "gives"
                      ],
                      "properties": {
                        "endpoint": {
                          "type": "string"
                        },
                        "gives": {
                          "type": "string"
                        }
                      }
                    },
                    "see_also": {
                      "type": "array",
                      "description": "Every free route that exists next to this one — the same invitation `pricing.free_tier` makes in prose, as keys. Most are callable as-is; the ones carrying `{…}` are templates to interpolate, never to paste (`GET /search/{query}` pasted literally is a search for the string `{query}`). The by-subject pointer for THIS resource is already interpolated, lives in `free_preview`, and is not repeated here.",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "**`facilitator_unavailable` — and a 503 is not a 402.** Your authorization was NOT used and nothing was charged. Retry this same request with the **same** `X-PAYMENT` header after `Retry-After`; signing a fresh authorization here is how you pay twice.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before replaying the same request with the same `X-PAYMENT`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Pricing-Version": {
                "description": "The pricing table this quote came from (currently `cost-tiered@5`).",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store`: a 503 is cacheable by default, and a frozen outage outlives the outage.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "The Facilitator did not answer: nothing was charged and your authorization is still valid.",
                  "required": [
                    "error",
                    "message",
                    "retryable",
                    "retry_after_seconds",
                    "resource",
                    "price_usd",
                    "pricing_version",
                    "next_action"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Always `facilitator_unavailable` here."
                    },
                    "message": {
                      "type": "string"
                    },
                    "details": {
                      "type": "object",
                      "description": "Transport or upstream detail, for diagnostics."
                    },
                    "facilitator_error": {
                      "type": "string"
                    },
                    "retryable": {
                      "type": "boolean",
                      "description": "Always `true`: retry the same request with the same `X-PAYMENT`."
                    },
                    "retry_after_seconds": {
                      "type": "integer"
                    },
                    "resource": {
                      "type": "string"
                    },
                    "price_usd": {
                      "type": "string"
                    },
                    "pricing_version": {
                      "type": "string"
                    },
                    "next_action": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-payment": "x402",
        "x-pricing-tier": "leaderboard-deep",
        "x-price-usd": "0.01",
        "security": [
          {
            "x402": []
          }
        ]
      }
    },
    "/stats/types": {
      "get": {
        "tags": [
          "Free"
        ],
        "summary": "Declared-type coverage — free, and the slowest route here",
        "description": "Agent counts and average score per self-declared type. The `unknown` bucket is always present, even at zero: how much of the universe the classification actually covers is the point, and an absent row would read as full coverage.\n\n**It used to answer 500** — a full scan of the grain against a gateway timeout — and since 2026-08-11 it is materialised: ~0,5 s at the origin, ~70 ms from the edge cache. It is a snapshot, so it is worth what the last refresh is worth.\n\n**There is no `?network=` here, on purpose.** The type breakdown is 84 % `unknown`, so slicing it by chain would multiply a weak signal rather than sharpen it. For a per-chain view use `GET /chains/{network}`, which carries the cuts that do have data behind them.",
        "operationId": "getTypeStats",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypeBreakdown"
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [],
        "x-payment": "x402",
        "x-pricing-tier": "stats-types",
        "x-price-usd": "0"
      }
    },
    "/stats/facets": {
      "get": {
        "tags": [
          "Free"
        ],
        "summary": "Every facet in the index, by volume — free",
        "description": "One entry per rater tag with its `count`, `avg_score` and `distinct_agents`, ordered by volume. Facets are separate judgements — `trust`, `liveness`, `contractRisk`, `activity` — not something to average together.\n\nIt is also the cheapest way to audit this index: the loudest tag in it is a single campaign, and `distinct_agents: 1` on a six-figure `count` is what says so.",
        "operationId": "getFacetStats",
        "parameters": [
          {
            "name": "network",
            "in": "query",
            "required": false,
            "schema": {
              "description": "Restrict to one chain. Facets are chain-local in practice: `miner-vouch` dominates base and does not exist on celo.",
              "title": "Network",
              "type": [
                "string",
                "null"
              ]
            },
            "description": "Restrict to one chain. Facets are chain-local in practice: `miner-vouch` dominates base and does not exist on celo."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/FacetStat"
                  },
                  "title": "Response Getfacetstats"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-payment": "x402",
        "x-pricing-tier": "stats-facets",
        "x-price-usd": "0",
        "security": []
      }
    },
    "/stats/issuers": {
      "get": {
        "tags": [
          "Free"
        ],
        "summary": "Every issuing service in the index, by volume — free",
        "description": "One row per ISSUING SERVICE — the host of each rating's `feedbackURI`, a field the ERC-8004 standard already carries — with its volume and its reach: how many distinct agents it touches and how many distinct raters signed through it.\n\nReach is what separates a public from a campaign, and the cut by who WRITES is where it bites hardest: measured 2026-08-29, the loudest issuer here puts 301,180 ratings on 16 agents, while the next spreads 48,852 ratings over 7,436 agents signed by exactly 3 raters. Volume alone would rank them as the two healthiest sources in the index.\n\n`no_host` is the coverage of the axis itself — ratings whose URI names no http(s) host (`data:`, `ipfs:`, empty). And `issuer_org: \"uvd\"` marks the hosts that are products of this index's own operators, by allowlist: this route is how you audit how much of the index we wrote ourselves, instead of taking our word for it.",
        "operationId": "getIssuerStats",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IssuerStats"
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [],
        "x-payment": "x402",
        "x-pricing-tier": "stats-issuers",
        "x-price-usd": "0"
      }
    },
    "/chains": {
      "get": {
        "tags": [
          "Free"
        ],
        "summary": "Every indexed chain, side by side — free",
        "description": "One row per chain. **The indexed chains are not slices of one market, they are different markets**, and this is the endpoint that says so — measured 2026-08-21: ethereum held 50.339 agents at 0,06 ratings per agent — identity without reputation — while avalanche's 14.395 ratings came from **eleven** raters, one of whom wrote 64 % of them.\n\n`avg_value` is never meaningful without `distinct_raters` and `top_rater_share`, so all three travel together. A mean of 76,95 from eleven accounts is not better than 22,95 from twelve thousand; it is a different question.\n\n`orphan_ratings` is our own coverage, published: ratings pointing at an agent we never indexed. It is the number that exposed a chain sitting at 0,1 % coverage while every other signal looked healthy.",
        "operationId": "listChains",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/ChainStat"
                  },
                  "type": "array",
                  "title": "Response Listchains"
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [],
        "x-payment": "x402",
        "x-pricing-tier": "chains",
        "x-price-usd": "0"
      }
    },
    "/chains/{network}": {
      "get": {
        "tags": [
          "Free"
        ],
        "summary": "One chain in full — stats, top wallets, facets and activity",
        "description": "Everything about a single chain in one call: its profile, its top wallets **scored with that chain's ratings only**, its facets by volume, and its daily activity.\n\nThe scores here are not the global scores filtered. Narrowing to a chain redefines the aggregate: a wallet's global `final_score` averages its per-chain means, while here it is this chain's mean and nothing else — and its `distinct_raters` changes too. The shrinkage prior is **this chain's** prior, so a chain is never judged with another chain's yardstick.",
        "operationId": "getChainDetail",
        "parameters": [
          {
            "name": "network",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Chain slug, case-insensitive. The live vocabulary is `chains[].network` in `GET /chains` (or `GET /health`); an unknown slug answers 404 with the full list.",
              "examples": [
                "base",
                "solana",
                "avalanche"
              ],
              "title": "Network"
            },
            "description": "Chain slug, case-insensitive. The live vocabulary is `chains[].network` in `GET /chains` (or `GET /health`); an unknown slug answers 404 with the full list."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 200,
              "minimum": 1,
              "description": "Top wallets to return",
              "default": 25,
              "title": "Limit"
            },
            "description": "Top wallets to return"
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 365,
              "minimum": 1,
              "description": "Days of activity history",
              "default": 90,
              "title": "Days"
            },
            "description": "Days of activity history"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChainDetail"
                }
              }
            }
          },
          "404": {
            "description": "That chain is not in the index. The body lists `known_networks` so a typo costs one call, not two.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "object",
                      "required": [
                        "error",
                        "network",
                        "known_networks"
                      ],
                      "properties": {
                        "error": {
                          "type": "string",
                          "description": "Always `unknown_network` here."
                        },
                        "network": {
                          "type": "string",
                          "description": "The slug as you sent it."
                        },
                        "known_networks": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Every network the index holds today."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-payment": "x402",
        "x-pricing-tier": "chains",
        "x-price-usd": "0",
        "security": []
      }
    },
    "/wallets/{wallet}/chains": {
      "get": {
        "tags": [
          "Free"
        ],
        "summary": "Every ERC-8004 identity a wallet holds, and its reputation per chain — free",
        "description": "**The number, free.** Which ERC-8004 identities a wallet holds on each chain, and the reputation it accumulated there — scored with that chain's ratings only.\n\nThis is free on purpose, and the reason is this site's own promise: *we do not sell you the number — the chain is public and you can compute it yourself. We sell you who it is made of.* What stays paid is exactly that decomposition: which counterparties rated, how many times each, in what facet, on what date and in which transaction — `GET /reputation/wallet/{wallet}`.\n\nChains where the wallet holds an identity but has **no** ratings are listed with `final_score: null`. That is a fact, not a gap: 'registered here and nobody rated it' and 'not here at all' are different answers and must not collapse into the same empty row.\n\n**Where these numbers come from, and how old they are.** The identities are read live from the index; the reputation comes from the materialized view named in `source`, which is recomputed after each indexing run. `refreshed_at` is when that last succeeded — and it is `null` when no refresh was ever recorded, never a stand-in timestamp. This matters because the paid decomposition (`GET /reputation/wallet/{wallet}`) reads the live grain: if the two disagree, the gap is the time between `refreshed_at` and now, and you are entitled to know that instead of guessing which number is broken.",
        "operationId": "getWalletChains",
        "parameters": [
          {
            "name": "wallet",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The wallet address: EVM (`0x…`, case-insensitive) or a Solana base58 id (case-SENSITIVE — lowercasing it silently names a different key).",
              "examples": [
                "0x715dc035ffb97dd7bb4095c6670138ba05bb4e6d",
                "DevFFyNWxZPtYLpEjzUnN1PFc9Po6PH7eZCi9f3tTkTw"
              ],
              "title": "Wallet"
            },
            "description": "The wallet address: EVM (`0x…`, case-insensitive) or a Solana base58 id (case-SENSITIVE — lowercasing it silently names a different key)."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WalletChains"
                }
              }
            }
          },
          "422": {
            "description": "`not_an_address` — the path segment is neither an EVM address (`0x` + 40 hex) nor a Solana base58 id (32-44 chars). A deliberate 422 instead of an empty answer: 'this is not an address' and 'this address holds nothing' are different facts, and collapsing them sends you hunting an index bug when what you have is a typo.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "object",
                      "required": [
                        "error",
                        "wallet"
                      ],
                      "properties": {
                        "error": {
                          "type": "string",
                          "description": "Always `not_an_address` here."
                        },
                        "wallet": {
                          "type": "string",
                          "description": "The path segment as you sent it."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-payment": "x402",
        "x-pricing-tier": "wallet-chains",
        "x-price-usd": "0",
        "security": []
      }
    },
    "/badge/{wallet}.svg": {
      "get": {
        "tags": [
          "Free"
        ],
        "summary": "Embeddable SVG reputation badge (free)",
        "description": "One URL = one badge = one cache entry. Copy the snippet from `https://describe.net/badge.html` or build it yourself:\n\n```html\n<a href=\"https://describe.net/agent.html?wallet=0x...\">\n  <img src=\"https://api.describe.net/badge/0x....svg\"\n       alt=\"reputacion en describe.net\" height=\"20\">\n</a>\n```\n\nFree by the cost rule, not by generosity: a badge is a cache entry, not a visitor. Every impression is the subject showing its reputation, linking back to the evidence.",
        "operationId": "getWalletBadge",
        "parameters": [
          {
            "name": "wallet",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Wallet"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The embeddable reputation badge for this wallet, as an SVG. Designed to live inside an `<img>` tag: no scripts, no cookies, no external fonts -- the browser's non-scriptable image mode makes 'this is not a tracker' verifiable, not promised. The number is the wallet's global score (same as `GET /wallets/{wallet}/chains`), rendered with the canonical display rule (2 decimals, trailing zeros trimmed), with the distinct-rater count and the data's refresh date INSIDE the image -- a stale badge that states its date is honest. A wallet with no ratings gets a 'sin calificaciones' badge, never a 0: no data is not bad data. Cached for an hour at the edge and in the browser (`max-age=3600`); embed it with `<img src>` and link it to the profile page.",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "422": {
            "description": "`not_an_address` -- same rule as the profile routes."
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-payment": "x402",
        "x-pricing-tier": "badge",
        "x-price-usd": "0",
        "security": []
      }
    },
    "/reputation/rater/{wallet}": {
      "get": {
        "tags": [
          "Metered (x402)"
        ],
        "summary": "The wallet as RATER: its emitted ratings, decomposed (paid)",
        "description": "The OTHER side of the grain. Every other route answers about a wallet as the RATED subject; this one answers about it as the RATER: how many ratings it emitted, over how many distinct subjects, with what dispersion (`value_stddev` -- a rater stamping 100 on everything has stddev 0), how concentrated (`top_subject_share`), on which chains, with which declared roles (from `tag1`, same allowlist as facet directions). **No score is emitted** -- ranking raters would be a new verdict, and this index returns evidence. EVM emitter addresses are case-insensitive (normalized; the grain guarantees lowercase); Solana emitters are case-SENSITIVE because base58 case IS identity.",
        "operationId": "getRaterProfile",
        "parameters": [
          {
            "name": "wallet",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Wallet"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The answer. When the call was metered it also carries the settlement receipt headers; `X-Payment-Reused: true` means a retry was served from the receipt window instead of charging again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RaterProfile"
                }
              }
            },
            "headers": {
              "X-Payment-Receipt": {
                "description": "Only on metered calls: the on-chain settlement transaction hash — public, keep it to reconcile — or `pending` if settlement has not reported one.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Payment-Reused": {
                "description": "Only on metered calls: `true` when this response was served from the idempotency window of a payment already settled — a retry, not a second charge.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true",
                    "false"
                  ]
                }
              },
              "Cache-Control": {
                "description": "`no-store` on every response that went through a charge.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "402": {
            "description": "**Payment required — $0.01 USDC.** Not an error: this is the price quote, and asking for it is free.\n\nThe body carries `amount` (human dollars, **never sign this one**), `resource`, `recipient`, `x402Version`, `scheme`, `maxTimeoutSeconds`, `retryable`, `pricing` (version, tier and the reason it costs that), `next_action`, `see_also` (every free route next to this one; the ones carrying `{…}` are templates, not paste-ready), and **`accepts[]` — one entry per network, and the only thing you sign against**. Each entry gives `network`, `asset` (the USDC contract), `amount` in token base units, and `payTo`.\n\n`payTo` must equal `0xe4dc963c56979E0260fc146b87eE24F18220e545`. If it does not, do not sign.\n\nHeaders: `Payment-Required` (the same challenge, base64), `X-Accept-Payment`, `X-Pricing-Version`, `Link` (RFC 8288: the human page for this subject, and this service's `llms.txt`), `Cache-Control: no-store`.\n\nThen replay this identical request with the `X-PAYMENT` header. The 200 comes back with `X-Payment-Receipt` and `X-Payment-Reused`.",
            "headers": {
              "Payment-Required": {
                "description": "The same challenge body, base64-encoded. If it and the body ever diverge, one of them is lying.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Accept-Payment": {
                "description": "Payment protocol advertisement: `x402 USDC 2.0`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Pricing-Version": {
                "description": "The pricing table this quote came from (currently `cost-tiered@5`).",
                "schema": {
                  "type": "string"
                }
              },
              "Link": {
                "description": "RFC 8288, for clients that do not speak x402 at all. `rel=\"alternate\"` — the human page for this same subject, omitted when the resource has no subject. `rel=\"describedby\"` — the machine-readable index of this service, always present.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store`: a cached price quote is a stale price quote.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "The x402 challenge. `accepts[]` is the only thing you sign against; the authoritative example is the live 402 itself, which is free to ask for.",
                  "required": [
                    "service",
                    "error",
                    "retryable",
                    "resource",
                    "price_usd",
                    "pricing",
                    "next_action",
                    "accepts",
                    "see_also"
                  ],
                  "properties": {
                    "service": {
                      "type": "string",
                      "description": "What this service is, in one line — for the agent whose first contact is this 402."
                    },
                    "error": {
                      "type": "string",
                      "description": "Stable snake_case code to branch on: `payment_required`, or the SDK's rejection code on a failed payment."
                    },
                    "retryable": {
                      "type": "boolean",
                      "description": "`true` on a fresh quote; `false` on a rejected payment — then read `next_action` before signing anything else."
                    },
                    "resource": {
                      "type": "string",
                      "description": "Canonical identity of what you are buying: `GET /path?…`, query sorted."
                    },
                    "price_usd": {
                      "type": "string",
                      "description": "Human dollar figure, as a string so it never rounds. Never sign this one: sign `accepts[].amount`."
                    },
                    "pricing": {
                      "type": "object",
                      "required": [
                        "version",
                        "tier",
                        "why",
                        "free_tier"
                      ],
                      "properties": {
                        "version": {
                          "type": "string",
                          "description": "Pricing table version this quote came from."
                        },
                        "tier": {
                          "type": "string",
                          "description": "Tier id in `GET /pricing`."
                        },
                        "why": {
                          "type": "string",
                          "description": "The cost reason behind this price."
                        },
                        "free_tier": {
                          "type": "string",
                          "description": "What can be read without paying, subject and aggregate."
                        }
                      }
                    },
                    "next_action": {
                      "type": "string",
                      "description": "Plain-text instruction for exactly this state — including when NOT to sign again."
                    },
                    "accepts": {
                      "type": "array",
                      "description": "One entry per payment network — the only thing you sign against.",
                      "items": {
                        "type": "object",
                        "required": [
                          "scheme",
                          "network",
                          "asset",
                          "amount",
                          "payTo",
                          "maxTimeoutSeconds"
                        ],
                        "properties": {
                          "scheme": {
                            "type": "string"
                          },
                          "network": {
                            "type": "string",
                            "description": "CAIP-2 chain id, e.g. `eip155:8453`."
                          },
                          "asset": {
                            "type": "string",
                            "description": "The USDC contract on that network. Take it from here, never from a document."
                          },
                          "amount": {
                            "type": "string",
                            "description": "Token base units (USDC has 6 decimals). This is the figure you sign."
                          },
                          "payTo": {
                            "type": "string",
                            "description": "Must equal `0xe4dc963c56979E0260fc146b87eE24F18220e545`. If it does not, do not sign."
                          },
                          "maxTimeoutSeconds": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "free_preview": {
                      "type": "object",
                      "description": "Only when the resource is about one wallet: the free route that answers whether there is anything to buy about it.",
                      "required": [
                        "endpoint",
                        "gives"
                      ],
                      "properties": {
                        "endpoint": {
                          "type": "string"
                        },
                        "gives": {
                          "type": "string"
                        }
                      }
                    },
                    "see_also": {
                      "type": "array",
                      "description": "Every free route that exists next to this one — the same invitation `pricing.free_tier` makes in prose, as keys. Most are callable as-is; the ones carrying `{…}` are templates to interpolate, never to paste (`GET /search/{query}` pasted literally is a search for the string `{query}`). The by-subject pointer for THIS resource is already interpolated, lives in `free_preview`, and is not repeated here.",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "**`facilitator_unavailable` — and a 503 is not a 402.** Your authorization was NOT used and nothing was charged. Retry this same request with the **same** `X-PAYMENT` header after `Retry-After`; signing a fresh authorization here is how you pay twice.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before replaying the same request with the same `X-PAYMENT`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Pricing-Version": {
                "description": "The pricing table this quote came from (currently `cost-tiered@5`).",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store`: a 503 is cacheable by default, and a frozen outage outlives the outage.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "The Facilitator did not answer: nothing was charged and your authorization is still valid.",
                  "required": [
                    "error",
                    "message",
                    "retryable",
                    "retry_after_seconds",
                    "resource",
                    "price_usd",
                    "pricing_version",
                    "next_action"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Always `facilitator_unavailable` here."
                    },
                    "message": {
                      "type": "string"
                    },
                    "details": {
                      "type": "object",
                      "description": "Transport or upstream detail, for diagnostics."
                    },
                    "facilitator_error": {
                      "type": "string"
                    },
                    "retryable": {
                      "type": "boolean",
                      "description": "Always `true`: retry the same request with the same `X-PAYMENT`."
                    },
                    "retry_after_seconds": {
                      "type": "integer"
                    },
                    "resource": {
                      "type": "string"
                    },
                    "price_usd": {
                      "type": "string"
                    },
                    "pricing_version": {
                      "type": "string"
                    },
                    "next_action": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-payment": "x402",
        "x-pricing-tier": "rater-profile",
        "x-price-usd": "0.01",
        "security": [
          {
            "x402": []
          }
        ]
      }
    },
    "/search/{q}": {
      "get": {
        "tags": [
          "Free"
        ],
        "summary": "One box: paste a wallet, an agent id or a Solana asset — free",
        "description": "**Paste what you have.** An EVM address, a Solana base58 id, or an ERC-8004 agent id — the answer says what it was read as and where it exists, one row per chain, each with the wallet that owns it **there**.\n\nFree for the same reason `GET /wallets/{wallet}/chains` is: this is the number and the location, never the decomposition. Who rated, how many times, in what facet, on what date and in which transaction stays paid at `GET /reputation/wallet/{wallet}` and `GET /reputation/agent/{network}/{agent_id}` — and every row here carries the exact route for its own subject in `detail_route`. Charging for this would also mean billing your typos: the paywall runs before any validation.\n\n**Two layers, and the split is the point.** `interpretations` at the root is what the *shape* allows; `matches[].interpreted_as` is the reading under which the *index* found that row. A base58 string can be a wallet **and** a Metaplex Core asset at once, and those are different subjects with different owners — so both readings are answered and both are labelled. **Ambiguity is `interpretations` having more than one entry**; there is no `\"ambiguous\"` value, because a single label erases *which* readings were possible.\n\nThe same numeric id is a different agent on every chain: an id that exists on three chains answers with three rows, three owners and three scores. A chain where the identity exists but nobody rated it answers `final_score: null` — registered and unrated is not the same as absent.\n\n**Exact match only** — no prefix, no wildcard, no listing, no paging. You get back what you already knew how to name. And an input we have never seen is a **200 with `matches: []`**, never a 404: a 404 here would claim the registry has no such entry, and a loose string names no entry to be missing.",
        "operationId": "search",
        "parameters": [
          {
            "name": "q",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Whatever you have, as one path segment: an EVM address (`0x` + 40 hex, case-insensitive), a Solana base58 id (32-44 chars, case-SENSITIVE — lowercasing it names a different key), or an EVM agent id (digits). Anything else answers 422 with the list of accepted shapes.",
              "examples": [
                "25975",
                "0x715dc035ffb97dd7bb4095c6670138ba05bb4e6d",
                "DevFFyNWxZPtYLpEjzUnN1PFc9Po6PH7eZCi9f3tTkTw"
              ],
              "title": "Q"
            },
            "description": "Whatever you have, as one path segment: an EVM address (`0x` + 40 hex, case-insensitive), a Solana base58 id (32-44 chars, case-SENSITIVE — lowercasing it names a different key), or an EVM agent id (digits). Anything else answers 422 with the list of accepted shapes."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResult"
                }
              }
            }
          },
          "422": {
            "description": "`unrecognized_query` — the text you pasted has none of the shapes this index can key on, so there is nothing to look up. `accepted_shapes` lists them. **This is the only error this route has: a well-formed query the index has never seen is a 200 with `matches: []`**, because 'this is not an address' and 'nobody by that name here' are different facts and collapsing them sends you hunting an index bug when what you have is a typo.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "object",
                      "required": [
                        "error",
                        "query",
                        "accepted_shapes"
                      ],
                      "properties": {
                        "error": {
                          "type": "string",
                          "description": "Always `unrecognized_query` here."
                        },
                        "query": {
                          "type": "string",
                          "description": "The path as you sent it, empty string included."
                        },
                        "accepted_shapes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "The shapes this box takes, in prose."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-payment": "x402",
        "x-pricing-tier": "search",
        "x-price-usd": "0",
        "security": []
      }
    },
    "/feed": {
      "get": {
        "tags": [
          "Free"
        ],
        "summary": "The index's pulse — the newest ratings, in chain order, free",
        "description": "**A feed of facts, not of opinions.** Every row already happened in a finalised block and is already public: who rated which identity, with what value, under which facet, and in which transaction. No third party writes free text about anyone here — `facet` is the on-chain tag the rater chose, not a comment — which is why this endpoint needs no moderation.\n\nOrdered by `block_time`, the chain's own clock, never by when we indexed it: this index backfilled years of history in one afternoon, so ordering by our clock would pile the whole past onto that day.\n\nSame validity domain as every score on this site: revoked ratings are out, and so is anything outside `0 < value <= 100`. A feed that showed what the score discards would teach you a reality no other endpoint confirms.\n\n**`?network=` filters to one chain, since 2026-08-23.** Same rows, same order, same shape — one predicate added. Until that date this text said there was *no* `network` filter, and that it was measured, not forgotten: filtering made the planner walk the global time index backwards until it collected enough rows of that chain — 0,12 ms on base but **26,7 s on avalanche**, past the function's own 25 s timeout. That measurement was real, and it predates the index that now serves the filter: `idx_feedback_network_time (network, block_time DESC)`, migration 012, applied 2026-08-22 once the database moved to provisioned disk. With it the planner enters by the chain and reads an already-ordered index — `limit` rows and stop. Avalanche measured 26.711 ms → 3.058 ms (new disk, no index) → 0,77 ms (with the index). An unknown `network` answers 422 with `known_networks`; for one chain's profile — stats, top wallets, daily activity — `GET /chains/{network}` is still the richer door.",
        "operationId": "getFeed",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 200,
              "minimum": 1,
              "description": "How many events to return",
              "default": 50,
              "title": "Limit"
            },
            "description": "How many events to return"
          },
          {
            "name": "network",
            "in": "query",
            "required": false,
            "schema": {
              "description": "Restrict to one chain, case-insensitive. The live vocabulary is `chains[].network` in `GET /chains` (or `GET /health`); an unknown slug answers 422 with the full list. Omit it for the global feed.",
              "examples": [
                "base",
                "solana",
                "avalanche"
              ],
              "title": "Network",
              "type": [
                "string",
                "null"
              ]
            },
            "description": "Restrict to one chain, case-insensitive. The live vocabulary is `chains[].network` in `GET /chains` (or `GET /health`); an unknown slug answers 422 with the full list. Omit it for the global feed."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/FeedEvent"
                  },
                  "title": "Response Getfeed"
                }
              }
            }
          },
          "422": {
            "description": "Two shapes, one status. Either FastAPI's validation error (`limit` outside 1..200) or `unknown_network` — the `network` you sent is not a chain this index holds. The body lists `known_networks` so a typo costs one call, not two; the live vocabulary is `chains[].network` in `GET /chains`. 'Not a chain we index' and 'a chain with no ratings yet' are different facts and do not collapse into one empty list.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/HTTPValidationError"
                    },
                    {
                      "type": "object",
                      "required": [
                        "detail"
                      ],
                      "properties": {
                        "detail": {
                          "type": "object",
                          "required": [
                            "error",
                            "network",
                            "known_networks"
                          ],
                          "properties": {
                            "error": {
                              "type": "string",
                              "description": "Always `unknown_network` here."
                            },
                            "network": {
                              "type": "string",
                              "description": "The slug as you sent it."
                            },
                            "known_networks": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              },
                              "description": "Every network the index holds today."
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-payment": "x402",
        "x-pricing-tier": "feed",
        "x-price-usd": "0",
        "security": []
      }
    },
    "/mcp": {
      "post": {
        "tags": [
          "Agent protocols"
        ],
        "operationId": "mcpStreamableHttp",
        "summary": "Remote MCP server (Streamable HTTP): the whole index as tools",
        "description": "The same index as MCP tools, in-process behind this same host — point your MCP client at `POST https://api.describe.net/mcp`. Free tools answer straight away; a priced tool relays the **402 of the route behind it** and forwards the credential you signed, because this server does not charge, does not sign and holds no key: one policy, two doors.\n\nTransport is Streamable HTTP, stateless JSON. Send `Accept: application/json, text/event-stream`; a `GET` here is an honest **405** (there is no server-initiated stream to open). The tool catalogue, the resources and the stdio launcher live in the server card linked from this tag.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "const": "2.0"
                  },
                  "id": {
                    "description": "Request id; omit it for a notification.",
                    "type": [
                      "string",
                      "integer",
                      "null"
                    ]
                  },
                  "method": {
                    "type": "string",
                    "description": "The protocol method to call."
                  },
                  "params": {
                    "type": "object",
                    "description": "Method parameters."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The JSON-RPC envelope. A tool that needs paying answers 200 with the **402 relayed inside the result** — the transport succeeded, the purchase did not happen yet.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "406": {
            "description": "The `Accept` header does not admit `application/json` or `text/event-stream`."
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-payment": "x402",
        "x-pricing-tier": "mcp",
        "x-price-usd": "0",
        "security": []
      }
    },
    "/a2a": {
      "post": {
        "tags": [
          "Agent protocols"
        ],
        "operationId": "a2aJsonRpc",
        "summary": "A2A server (JSON-RPC 2.0): message/send over the free preview",
        "description": "The Agent2Agent door. `message/send` answers about a wallet with the SAME free preview `GET /wallets/{wallet}/chains` serves, plus the state of the index — never the metered decomposition, and the reply says so and points at the route that sells it.\n\nFree by the same cost rule as the preview it wraps. The agent card that describes the skills is at `/.well-known/agent-card.json`. JSON-RPC answers **200 even when the envelope carries an `error`**; the only non-200 is a body that is not JSON (**400**, parse error).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "const": "2.0"
                  },
                  "id": {
                    "description": "Request id; omit it for a notification.",
                    "type": [
                      "string",
                      "integer",
                      "null"
                    ]
                  },
                  "method": {
                    "type": "string",
                    "description": "The protocol method to call."
                  },
                  "params": {
                    "type": "object",
                    "description": "Method parameters."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The JSON-RPC envelope — result or `error`, both 200.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`-32700` parse error: the body is not JSON."
          },
          "429": {
            "description": "**Too many requests.** Throttled at the edge by the gateway **before the request reaches the application**, so nothing was queried, nothing was charged and no `X-PAYMENT` you may have sent was consumed — replay the identical request, credential included. This deployment's budget is **50 requests/second sustained, bursts up to 40** (`RateLimit-Policy: 50;w=1;burst=40`). Back off and retry; one second of pause clears the bucket. Read `RateLimit-Policy` on any response for the live budget instead of hardcoding it. The budget is shared by every caller: cached free routes served by CloudFront do not consume it on a cache hit.",
            "headers": {
              "RateLimit-Policy": {
                "description": "The gateway's policy in RFC 9239 syntax (`<requests>;w=1;burst=<n>`), read from its own configuration. Emitted on every response, not only on this one.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-payment": "x402",
        "x-pricing-tier": "a2a",
        "x-price-usd": "0",
        "security": []
      }
    }
  },
  "components": {
    "schemas": {
      "Activity": {
        "properties": {
          "first_rating_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "First Rating At"
          },
          "last_rating_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Rating At"
          }
        },
        "type": "object",
        "required": [
          "first_rating_at",
          "last_rating_at"
        ],
        "title": "Activity",
        "description": "First and last rating BY ON-CHAIN TIME, never by when we indexed it.\n\nBoth are null until the log scan backfills `block_time`, which is the\nhonest answer: we do not know yet. Substituting our own ingestion timestamp\nwould date every historical rating to the day of the backfill."
      },
      "AgentScore": {
        "properties": {
          "network": {
            "type": "string",
            "title": "Network",
            "description": "Chain this identity lives on. The valid vocabulary is `chains[].network` in the free `GET /health`."
          },
          "agent_id": {
            "type": "string",
            "title": "Agent Id",
            "description": "The id as minted on that chain: digits on EVM, a base58 Metaplex Core asset on Solana."
          },
          "current_owner": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Current Owner",
            "description": "**Today's owner, never the minter.** Attribution follows this field and not the `Registered` event, because `Registered` names whoever minted the id — almost always the facilitator — and attributing by it would file the entire index under one address. Null when no ownership row is indexed yet."
          },
          "declared_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Declared Type",
            "description": "What the agent says it is, taken from its own metadata. A declaration, never a verification — and null is common."
          },
          "agent_uri": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Agent Uri",
            "description": "The identity's own metadata URI, as registered on chain."
          },
          "indexed_identity": {
            "type": "boolean",
            "title": "Indexed Identity",
            "description": "Whether this index holds the identity row itself, and not just ratings pointing at it. False means the ratings are orphans of our scan — the coverage signal that once exposed a chain indexed at 0.1%."
          },
          "score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Score",
            "description": "This agent's score, 0-100, over eligible ratings only: revoked out, domain `0 < v <= 100`, no self-rating. **Null, never 0**, with no eligible ratings. Until policy `@1` this pair discarded only revoked rows and could serve a 1.9e37 average off one absurd rating; the domain filter is what closed that."
          },
          "review_count": {
            "type": "integer",
            "title": "Review Count",
            "description": "Eligible ratings behind `score` — the N of that mean."
          },
          "revoked_count": {
            "type": "integer",
            "title": "Revoked Count",
            "description": "Ratings the rater took back. Excluded from `score`, returned in `ratings`."
          },
          "out_of_domain_count": {
            "type": "integer",
            "title": "Out Of Domain Count",
            "description": "Ratings whose normalized value fell outside `0 < v <= 100`. Counted here so the arithmetic closes by hand: `len(ratings) == review_count + revoked_count + out_of_domain_count + self_rated.count`."
          },
          "self_rated": {
            "$ref": "#/components/schemas/SelfRated",
            "description": "What the owner said about its own agent — out of `score`, published here with its `gap` against everyone else."
          },
          "facets": {
            "additionalProperties": {
              "$ref": "#/components/schemas/Facet"
            },
            "type": "object",
            "title": "Facets",
            "description": "Reputation by area for this agent, keyed by the rater's `tag1`, ordered by volume desc. Same shape as the wallet route."
          },
          "concentration": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Concentration"
              },
              {
                "type": "null"
              }
            ],
            "description": "ADVISORY: who the ratings came from. Nullable — a degraded side query drops the signal, not the answer."
          },
          "ownership": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Ownership"
              },
              {
                "type": "null"
              }
            ],
            "description": "ADVISORY: what today's owner earned of this score and what it inherited, split by the rating's block against the handover block. Only on the agent route — a wallet has N identities with N handovers, so its era is another question. **Null means the era cannot be established, never that nothing was inherited**, and it is always null on `solana`."
          },
          "confidence": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Confidence"
              },
              {
                "type": "null"
              }
            ],
            "description": "ADVISORY band over how much evidence backs `score`. Goes null with `concentration`."
          },
          "caveats": {
            "items": {
              "$ref": "#/components/schemas/Caveat"
            },
            "type": "array",
            "title": "Caveats",
            "description": "The traps THESE numbers trigger, already written out. Derived from the fields of this same response — `score`, `review_count`, `concentration`, `self_rated` — never from a second query. **Advisory: it never moves the score and never fills a null one.** An empty list means no trap fired, NOT 'verified clean'. Spanish prose, same rules as the wallet route, from the same module. Each entry is `{code, text}`: branch on `code`, never on `text`."
          },
          "ratings": {
            "items": {
              "$ref": "#/components/schemas/Rating"
            },
            "type": "array",
            "title": "Ratings",
            "description": "**Every** rating this agent received, one row each, revoked and out-of-domain included, each with its `tx_hash`, block and log index. This is what the price buys: not a number, the population that produced it. It can be very long — the loudest agent in this index carries 300,001 rows, measured 2026-08-11."
          },
          "policy_version": {
            "type": "string",
            "title": "Policy Version",
            "description": "The aggregation policy that produced these numbers. Travels in every scored response; also published free in `GET /health`."
          }
        },
        "type": "object",
        "required": [
          "network",
          "agent_id",
          "current_owner",
          "declared_type",
          "agent_uri",
          "indexed_identity",
          "score",
          "review_count",
          "revoked_count",
          "out_of_domain_count",
          "self_rated",
          "facets",
          "concentration",
          "ownership",
          "confidence",
          "caveats",
          "ratings",
          "policy_version"
        ],
        "title": "AgentScore",
        "description": "One agent identity and EVERY rating it received.\n\nThe most detailed thing this API sells: not a number but the population\nthat produced it, row by row, with the transaction behind each one and the\nrevoked ones included. `caveats` comes BEFORE `ratings` on purpose — that\nlist can carry 300,001 rows, and a warning that only shows up after it is a\nwarning nobody reads."
      },
      "Caveat": {
        "properties": {
          "code": {
            "type": "string",
            "title": "Code",
            "description": "Stable identifier of the trap that fired. Branch on THIS, never on `text`. Codes are permanent; text is not."
          },
          "text": {
            "type": "string",
            "title": "Text",
            "description": "Spanish prose meant to be shown to whoever is deciding. May be rewritten, re-measured or translated without notice."
          }
        },
        "type": "object",
        "required": [
          "code",
          "text"
        ],
        "title": "Caveat",
        "description": "Una trampa que ESTOS números disparan, con su código estable.\n\n🔴 `code` ES EL CONTRATO; `text` NO. El texto puede re-redactarse,\nre-medirse o traducirse sin aviso — el código nunca cambia. Nace del hueco\nH14 del 2026-08-28: los tres consumidores del ecosistema (Execution Market,\nKarmaKadabra, MeshRelay) fueron instruidos ese mismo día de MOSTRAR estas\nadvertencias, y lo único que tenían para ramificar era la prosa. El texto ya\nse había movido una vez (el corte de evidencia flaca, el 2026-08-25) y\ncualquiera colgado de esa prosa se rompió ese día sin enterarse.\n\nSe cambió la forma AHORA y no después a propósito: es el momento más barato\nde la vida del proyecto para hacerlo — los tres consumidores están migrando\ny ninguno construyó todavía contra la forma vieja. Dentro de dos semanas el\nmismo cambio rompe tres integraciones vivas."
      },
      "ChainDay": {
        "properties": {
          "day": {
            "type": "string",
            "format": "date",
            "title": "Day",
            "description": "Day, by on-chain time (UTC)."
          },
          "ratings": {
            "type": "integer",
            "title": "Ratings",
            "description": "Ratings written that day."
          },
          "raters": {
            "type": "integer",
            "title": "Raters",
            "description": "Distinct wallets that wrote them. Read together with `ratings`: the gap between the two is the shape of the activity."
          }
        },
        "type": "object",
        "required": [
          "day",
          "ratings",
          "raters"
        ],
        "title": "ChainDay",
        "description": "One day of activity on a chain, by on-chain time."
      },
      "ChainDetail": {
        "properties": {
          "stats": {
            "$ref": "#/components/schemas/ChainStat"
          },
          "top": {
            "items": {
              "$ref": "#/components/schemas/ChainRow"
            },
            "type": "array",
            "title": "Top"
          },
          "facets": {
            "items": {
              "$ref": "#/components/schemas/ChainFacet"
            },
            "type": "array",
            "title": "Facets"
          },
          "activity": {
            "items": {
              "$ref": "#/components/schemas/ChainDay"
            },
            "type": "array",
            "title": "Activity"
          }
        },
        "type": "object",
        "required": [
          "stats",
          "top",
          "facets",
          "activity"
        ],
        "title": "ChainDetail",
        "description": "Everything we can say about one chain: totals, top wallets and facets."
      },
      "ChainFacet": {
        "properties": {
          "facet": {
            "type": "string",
            "title": "Facet",
            "description": "Area of reputation, as the raters declared it in `tag1`."
          },
          "count": {
            "type": "integer",
            "title": "Count",
            "description": "Ratings in this area on this chain."
          },
          "distinct_agents": {
            "type": "integer",
            "title": "Distinct Agents",
            "description": "How many different subjects were rated in this area. A facet concentrated on one subject is not a facet of the chain."
          },
          "distinct_raters": {
            "type": "integer",
            "title": "Distinct Raters",
            "description": "How many different wallets did the rating here."
          },
          "avg_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Avg Score",
            "description": "Mean over this area, or **null** when there is nothing to average. Never zero for absent."
          }
        },
        "type": "object",
        "required": [
          "facet",
          "count",
          "distinct_agents",
          "distinct_raters",
          "avg_score"
        ],
        "title": "ChainFacet",
        "description": "One area of reputation aggregated over a whole chain."
      },
      "ChainRow": {
        "properties": {
          "rank": {
            "type": "integer",
            "title": "Rank",
            "description": "Position in this chain's ranking, ordered by `shrunk_score` — not by `final_score`."
          },
          "wallet": {
            "type": "string",
            "title": "Wallet",
            "description": "The ranked wallet."
          },
          "final_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Final Score",
            "description": "Mean score **on this chain**, not the global one. They are different numbers: the global averages this wallet's per-chain means. Null when there is nothing to average."
          },
          "shrunk_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Shrunk Score",
            "description": "What decides the position: the score pulled toward this network's prior by how little evidence backs it. **This orders the index; it is not the score we publish about a wallet.** A perfect 100 from one rater ranks below an 85 from four."
          },
          "distinct_raters": {
            "type": "integer",
            "title": "Distinct Raters",
            "description": "Distinct rating wallets. The number that separates evidence from a campaign."
          },
          "total_reviews": {
            "type": "integer",
            "title": "Total Reviews",
            "description": "Ratings counted. Volume, not agreement."
          },
          "agent_count": {
            "type": "integer",
            "title": "Agent Count",
            "description": "ERC-8004 identities this wallet owns on this chain."
          },
          "declared_types": {
            "anyOf": [
              {
                "items": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Declared Types",
            "description": "What the identities SAY they are, verbatim from their own card. We never verify it — a declaration is not a fact, and null entries are identities that declared nothing."
          }
        },
        "type": "object",
        "required": [
          "rank",
          "wallet",
          "final_score",
          "shrunk_score",
          "distinct_raters",
          "total_reviews",
          "agent_count",
          "declared_types"
        ],
        "title": "ChainRow",
        "description": "Una wallet dentro de UNA cadena."
      },
      "ChainScore": {
        "properties": {
          "score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Score",
            "description": "Mean over this chain's eligible ratings, 0-100. Null, never 0, when none are eligible here."
          },
          "review_count": {
            "type": "integer",
            "title": "Review Count",
            "description": "Eligible ratings on this chain — the N behind `score`."
          },
          "agent_ids": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Agent Ids",
            "description": "The identities this wallet owns on this chain that carry those ratings. Each one is callable at `GET /reputation/agent/{network}/{agent_id}` for the row-by-row breakdown."
          }
        },
        "type": "object",
        "required": [
          "score",
          "review_count",
          "agent_ids"
        ],
        "title": "ChainScore",
        "description": "What one chain contributed to a wallet's score.\n\nPublished per chain and never collapsed: the wallet score is the mean of\nthese, one vote each, so this is the breakdown that lets the mean be\nrecomputed by hand."
      },
      "ChainStat": {
        "properties": {
          "network": {
            "type": "string",
            "title": "Network",
            "description": "Chain name — the exact vocabulary every `{network}` path and query parameter in this API accepts."
          },
          "agents": {
            "type": "integer",
            "title": "Agents",
            "description": "Agent identities indexed on this chain."
          },
          "owners": {
            "type": "integer",
            "title": "Owners",
            "description": "Distinct current owners behind those identities. Far below `agents` means a few wallets hold many ids."
          },
          "typed_agents": {
            "type": "integer",
            "title": "Typed Agents",
            "description": "How many of them declare a type. The rest declare none — a fact about the chain's population, not a gap in the index."
          },
          "ratings": {
            "type": "integer",
            "title": "Ratings",
            "description": "Individual ratings indexed on this chain, eligible or not."
          },
          "distinct_raters": {
            "type": "integer",
            "title": "Distinct Raters",
            "description": "**How many wallets spoke, not how many times.** This is the column that makes `avg_value` readable: avalanche averages 76.95 and base 22.95, and whoever reads only those two concludes avalanche is better — avalanche's mean comes from ELEVEN raters with one of them writing 64% of it, base's from 12,624."
          },
          "revoked": {
            "type": "integer",
            "title": "Revoked",
            "description": "Ratings taken back by their rater, on this chain."
          },
          "orphan_ratings": {
            "type": "integer",
            "title": "Orphan Ratings",
            "description": "Ratings pointing at an agent this index does NOT have registered. A health metric of our own indexer, published as public data on purpose: it was the only thing that exposed one chain sitting at 0.1% coverage. Whoever integrates deserves to know how complete what we sell them is."
          },
          "facets": {
            "type": "integer",
            "title": "Facets",
            "description": "Distinct `tag1` areas seen on this chain."
          },
          "out_of_domain": {
            "type": "integer",
            "title": "Out Of Domain",
            "description": "Ratings whose normalized value fell outside `0 < v <= 100` and are therefore excluded from every score."
          },
          "self_rated": {
            "type": "integer",
            "title": "Self Rated",
            "description": "Ratings written by the rated agent's own owner. Excluded from scores."
          },
          "avg_value": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Avg Value",
            "description": "Mean normalized value across eligible ratings. **Never read it alone** — `distinct_raters` and `top_rater_share` sit in this same object precisely so it cannot be. Null when nothing is eligible."
          },
          "top_rater_share": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Top Rater Share",
            "description": "Percentage of this chain's ratings written by its single loudest rater. Advisory. Null with no ratings."
          },
          "ratings_per_agent": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Ratings Per Agent",
            "description": "Ratings divided by agents. High here is repetition, not consensus — the same read as `distinct_raters` at wallet level."
          },
          "first_rating_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "First Rating At",
            "description": "Oldest rating by on-chain time. Null until the log scan dates one."
          },
          "last_rating_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Rating At",
            "description": "Newest rating by on-chain time. Null until the log scan dates one."
          },
          "last_scanned_block": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Scanned Block",
            "description": "How far the scan reached. On signature-scanned chains (solana) it carries the slot. Null before the first sync."
          },
          "blocks_behind": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Blocks Behind",
            "description": "`head_at_last_sync - last_scanned_block`. 🔴 **This number cannot detect a dead chain**: both values freeze together when the sync stops, so a chain nine days dead publishes the same figure it had when it was healthy. Read `stale_hours` for that. Kept because it is the right measure of a scan that IS running."
          },
          "last_sync_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Sync At",
            "description": "When the last successful sync of this chain finished. Null if it never synced."
          },
          "stale_hours": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Stale Hours",
            "description": "Hours since the last successful sync, **measured against the clock** — which is why it cannot lie in the direction `blocks_behind` lies in: a chain that stops syncing climbs on its own, tick after tick. Measured on two real cases: one chain nine days unindexed still published `blocks_behind: 647`, and another published a perfect **0** after five hours without syncing. **Null means it never synced** — unknown lag, not zero lag."
          }
        },
        "type": "object",
        "required": [
          "network",
          "agents",
          "owners",
          "typed_agents",
          "ratings",
          "distinct_raters",
          "revoked",
          "orphan_ratings",
          "facets",
          "out_of_domain",
          "self_rated",
          "avg_value",
          "top_rater_share",
          "ratings_per_agent",
          "first_rating_at",
          "last_rating_at",
          "last_scanned_block",
          "blocks_behind",
          "last_sync_at",
          "stale_hours"
        ],
        "title": "ChainStat",
        "description": "El perfil de una cadena.\n\n`avg_value` NUNCA viaja sola: `distinct_raters` y `top_rater_share` van al\nlado en el mismo objeto, y esa es una decisión de producto, no de esquema.\nMedido en este índice: avalanche promedia 76,95 y base 22,95, y el que lea\nsólo esos dos números concluye que avalanche es mejor. La media de avalanche\nsale de ONCE calificadores, con uno solo poniendo el 64 %; la de base, de\n12.624. Publicar una media sin su N es el rumor que este sitio dice combatir.\n\nEl `example` del esquema es la fila REAL de solana leída del dominio el\n2026-08-21 — no un payload inventado — y se eligió porque muestra el punto\nde `stale_hours`: `blocks_behind` en 0 con casi ocho horas sin sincronizar.\nLa fila viva sale de `GET /chains`.",
        "examples": [
          {
            "agents": 1153,
            "avg_value": 59.8976,
            "blocks_behind": 0,
            "distinct_raters": 33,
            "facets": 21,
            "first_rating_at": "2026-03-03T00:35:28Z",
            "last_rating_at": "2026-08-15T23:31:18Z",
            "last_scanned_block": 440538888,
            "last_sync_at": "2026-08-20T20:56:40Z",
            "network": "solana",
            "orphan_ratings": 0,
            "out_of_domain": 951,
            "owners": 195,
            "ratings": 9901,
            "ratings_per_agent": 8.5872,
            "revoked": 0,
            "self_rated": 0,
            "stale_hours": 7.89,
            "top_rater_share": 30.76,
            "typed_agents": 66
          }
        ]
      },
      "Concentration": {
        "properties": {
          "distinct_raters": {
            "type": "integer",
            "title": "Distinct Raters",
            "description": "**How many wallets spoke.** Read it against `total_reviews`: this is the pair that separates consensus from repetition, and it is the one that catches what share alone misses."
          },
          "top_client_share": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Top Client Share",
            "description": "Fraction of the ratings written by the single loudest rater, 0-1. **Not the tell on its own** — the largest campaign in this index sits near 0.14, so any threshold on share alone waves it through. Null when there is no denominator."
          },
          "top_client": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Top Client",
            "description": "Address of that loudest rater, so the claim can be checked."
          }
        },
        "type": "object",
        "required": [
          "distinct_raters",
          "top_client_share",
          "top_client"
        ],
        "title": "Concentration",
        "description": "ADVISORY. Who the ratings came from. Never blocks, never moves a score.\n\nNullable ON PURPOSE, and consumers must handle the null: this is a\nsecondary aggregate, so when its query fails the endpoint drops the signal\nrather than the answer. A reputation service that returns nothing because a\nside-signal timed out has failed at its actual job.\n\n`top_client_share` alone is not the tell. The campaign in this index — one\nagent, 300.001 ratings, 57 raters, measured 2026-08-11 — sits near 0,14.\nRead `distinct_raters` against `total_reviews`."
      },
      "Confidence": {
        "properties": {
          "band": {
            "type": "string",
            "enum": [
              "no_ratings",
              "low",
              "medium",
              "high"
            ],
            "title": "Band",
            "description": "How much evidence backs the score — **never the score itself**. `no_ratings` always travels beside a null score, never a zero. Branch on this value, which is a published enum; the wording of `advice` may change."
          },
          "distinct_raters": {
            "type": "integer",
            "title": "Distinct Raters",
            "description": "The N the band was computed from: distinct rating wallets, not ratings."
          },
          "interval": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ConfidenceInterval"
              },
              {
                "type": "null"
              }
            ],
            "description": "Plausible range for the score. Null when the band is `no_ratings`: with no ratings the underlying function returns the whole 0-100 domain, and serialising a `lower: 0.0` next to a null score would publish a zero that is not a zero."
          },
          "thresholds": {
            "additionalProperties": {
              "type": "integer"
            },
            "type": "object",
            "title": "Thresholds",
            "description": "The cuts that produced the band, shipped with it so you can reproduce the banding without trusting a stale copy of the policy. Live values also at `GET /health.confidence_thresholds`."
          },
          "advice": {
            "type": "string",
            "title": "Advice",
            "description": "One sentence on what to do with this much evidence. Prose, and it can change without notice — never branch on it, branch on `band`."
          },
          "confidence_policy": {
            "type": "string",
            "title": "Confidence Policy",
            "description": "Version of the banding policy that produced this object, so a stored answer stays interpretable after the cuts move."
          }
        },
        "type": "object",
        "required": [
          "band",
          "distinct_raters",
          "interval",
          "thresholds",
          "advice",
          "confidence_policy"
        ],
        "title": "Confidence",
        "description": "ADVISORY. How much evidence backs the number — never the number itself.\n\nNullable for exactly the same reason as `concentration`, and it goes null\nwith it: `distinct_raters` is read once, in SQL, and when that side query\ndegrades there is no N to band. A band invented over an N nobody could read\nwould be a label with no data under it.\n\n`band == \"no_ratings\"` travels next to a **null** score, never a zero, and\n`interval` is null there too: with no ratings the pure function returns the\nwhole domain (0-100) on purpose, but serialising a `lower: 0.0` beside a\nnull score would publish a 0 that is not a 0. Thresholds and policy version\nride along so the band can be reproduced without trusting a stale copy of\nthe cuts."
      },
      "ConfidenceInterval": {
        "properties": {
          "lower": {
            "type": "number",
            "title": "Lower"
          },
          "upper": {
            "type": "number",
            "title": "Upper"
          }
        },
        "type": "object",
        "required": [
          "lower",
          "upper"
        ],
        "title": "ConfidenceInterval",
        "description": "Wilson al 95 %, reescalado al dominio 0-100. NO es un score."
      },
      "Facet": {
        "properties": {
          "score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Score",
            "description": "Score for this area alone, 0-100, or **null** when there is nothing to average. Never coerce null to zero: a wallet nobody rated on `trust` is not a wallet rated badly on `trust`."
          },
          "count": {
            "type": "integer",
            "title": "Count",
            "description": "Ratings in this area. Volume, not agreement."
          },
          "distinct_raters": {
            "type": "integer",
            "title": "Distinct Raters"
          },
          "revoked_count": {
            "type": "integer",
            "title": "Revoked Count"
          },
          "out_of_domain_count": {
            "type": "integer",
            "title": "Out Of Domain Count"
          },
          "self_rated_count": {
            "type": "integer",
            "title": "Self Rated Count"
          },
          "direction": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Direction",
            "description": "Rating direction as declared by the rater in `tag1` (`worker_rating`, `agent_rating`, `executor_rating`), or null when this facet declares none — which is the case for the vast majority of the index. Null means undeclared, not symmetric. Read `GET /stats/facets` free to measure the split yourself."
          },
          "direction_category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Direction Category",
            "description": "Task category the rater appended to the direction (`worker_rating:data_collection` -> `data_collection`), or null."
          },
          "direction_meaning": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Direction Meaning",
            "description": "Plain-language translation of `direction`, declared by its emitter — Execution Market ratified it in writing on 2026-08-28 — never derived by this service: `publisher_rates_executor` (`worker_rating`) or `executor_rates_requester` (`agent_rating`, `executor_rating`). Null when `direction` is null or carries no ratified translation. Emitter's caveat: on the signed rail the buyer/seller labelling is best-effort and may degrade to requester/executor; the signer identity never does."
          }
        },
        "type": "object",
        "required": [
          "score",
          "count",
          "distinct_raters",
          "revoked_count",
          "out_of_domain_count",
          "self_rated_count"
        ],
        "title": "Facet",
        "description": "One area of reputation, taken from the rater's `tag1`.\n\nA wallet is not a number. `trust`, `liveness`, `contractRisk` and\n`activity` are separate judgements that live side by side on chain, and a\ncaller picking a counterparty acts on the areas, not on their mean."
      },
      "FacetStat": {
        "properties": {
          "count": {
            "type": "integer",
            "title": "Count"
          },
          "avg_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Avg Score"
          },
          "distinct_agents": {
            "type": "integer",
            "title": "Distinct Agents"
          },
          "direction": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Direction",
            "description": "Rating direction declared by the rater in `tag1`, or null when this facet declares none. Null means undeclared, not symmetric."
          },
          "direction_category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Direction Category",
            "description": "Task category appended to the direction, or null."
          },
          "direction_meaning": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Direction Meaning",
            "description": "Plain-language translation of `direction`, declared by its emitter (Execution Market, ratified in writing 2026-08-28), never derived here: `publisher_rates_executor` or `executor_rates_requester`. Null when `direction` is null or carries no ratified translation."
          }
        },
        "type": "object",
        "required": [
          "count",
          "avg_score",
          "distinct_agents"
        ],
        "title": "FacetStat",
        "description": "A facet across the whole index.\n\n`distinct_agents` is what separates a facet from a campaign: volume alone\nranks a single-target vouching run above every honest signal there is."
      },
      "FeedEvent": {
        "properties": {
          "network": {
            "type": "string",
            "title": "Network",
            "description": "Chain the rating was written on."
          },
          "agent_id": {
            "type": "string",
            "title": "Agent Id",
            "description": "ERC-8004 identity that was rated, within its chain. The pair (network, agent_id) is what identifies a subject."
          },
          "rater": {
            "type": "string",
            "title": "Rater",
            "description": "Wallet that wrote the rating. A public address already on chain — what never travels here is free text one party wrote about another."
          },
          "subject_wallet": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Subject Wallet",
            "description": "Owner of the rated identity, or **null** when we do not know who owns it today. Null is not «has no owner»: the Reputation scan can run ahead of the Identity one."
          },
          "value": {
            "type": "number",
            "title": "Value",
            "description": "The rating as written on chain, unmodified. This is evidence, not a score: no weighting, no shrinkage, no policy."
          },
          "facet": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Facet",
            "description": "Area of reputation the rater declared (`tag1`), or null when they declared none."
          },
          "block_time": {
            "type": "string",
            "format": "date-time",
            "title": "Block Time",
            "description": "**On-chain** time of the rating, never when we indexed it. Dating a historical rating with our own ingestion timestamp would make every backfilled rating look like it happened today."
          },
          "tx_hash": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tx Hash",
            "description": "Transaction that carries this rating, so you can verify it against the chain instead of trusting us. Null only where the backfill has not reached yet."
          }
        },
        "type": "object",
        "required": [
          "network",
          "agent_id",
          "rater",
          "subject_wallet",
          "value",
          "facet",
          "block_time",
          "tx_hash"
        ],
        "title": "FeedEvent",
        "description": "Una calificación que ya ocurrió en un bloque finalizado.\n\nEl `example` del esquema es un evento REAL del feed, leído del dominio el\n2026-08-21: su `tx_hash` se puede abrir en un explorador, que es todo el\npunto de este producto. `block_time` lo fecha solo.",
        "examples": [
          {
            "agent_id": "25975",
            "block_time": "2026-08-21T03:40:21Z",
            "facet": "miner-vouch",
            "network": "base",
            "rater": "0x6b51d0d67ff41dab76e499546abe6b8b03cf8732",
            "subject_wallet": "0x69747c4ce6185d21a33b3bcdba980d659600ac7b",
            "tx_hash": "0x58791c7ac8cadb7d1dab242543dafe37c90af09101e8a6fd79b725ae80af964a",
            "value": 1.0
          }
        ]
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
          }
        },
        "type": "object",
        "title": "HTTPValidationError"
      },
      "Health": {
        "properties": {
          "status": {
            "type": "string",
            "title": "Status",
            "description": "`ok` when the service answered. Liveness, not a claim about coverage."
          },
          "policy_version": {
            "type": "string",
            "title": "Policy Version",
            "description": "Version of the SCORE policy: equal weight per chain, value domain, revoked out, join by the identity's current owner. It is the one that travels in every scored response."
          },
          "ordering_policy": {
            "type": "string",
            "title": "Ordering Policy",
            "description": "Version of how the leaderboard is ORDERED (Bayesian shrinkage). It never changes a published score — only the sort."
          },
          "rater_weight_policy": {
            "type": "string",
            "title": "Rater Weight Policy",
            "description": "Version of the second weighting published beside every score as `weighted_score`. Advisory: it is not the served number."
          },
          "confidence_policy": {
            "type": "string",
            "title": "Confidence Policy",
            "description": "Version of the evidence bands. Names how much evidence backs a number without touching the number. **The four versions are separate because they change separately**: fusing them would mark every rating as recomputed each time a list is edited, and then the score's own version would stop meaning anything."
          },
          "confidence_thresholds": {
            "additionalProperties": {
              "type": "integer"
            },
            "type": "object",
            "title": "Confidence Thresholds",
            "description": "The distinct-rater cuts of each confidence band, here and free. This is what lets a consumer of the FREE `/leaderboard` — which already receives `distinct_raters` — derive the exact band without paying, and it is why the band is not repeated on every row: repeating a derivable string a hundred times adds bytes to a cached response, not a fact."
          },
          "reading_policy": {
            "additionalProperties": true,
            "type": "object",
            "title": "Reading Policy",
            "description": "The **effective** cuts that fire a `caveats[]` entry, so a surface can READ them instead of typing them. This is the mechanism that keeps an environment override from being an invisible drift: the value actually in force is always legible here, free and without an account. Not a fifth policy and deliberately unversioned — it moves no score and no price. Left as a free-form object because its owner is the caveats module: a new cut has to reach whoever reads it without waiting for this file to catch up. A null inside IS data — `self_gap` is null because there is no self-rating threshold, the gap is always published."
          },
          "build_sha": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Build Sha",
            "description": "Git commit the running code was built from (stamped into the artifact at build time), or null when the build was not stamped."
          },
          "agents": {
            "type": "integer",
            "title": "Agents",
            "description": "Agent identities indexed right now, across every chain. **Live and uncached** — this is the authoritative total, and the reason no page in this product types one."
          },
          "feedback_entries": {
            "type": "integer",
            "title": "Feedback Entries",
            "description": "Individual ratings indexed right now — the grain everything else aggregates over. Live and uncached, same rule as `agents`."
          },
          "indexer_period_seconds": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Indexer Period Seconds",
            "description": "How often the indexer runs, in seconds, as the running service has it configured — or null when the environment does not declare it. Null means unknown, never a guessed cadence."
          },
          "chains": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "type": "array",
            "title": "Chains",
            "description": "One row per indexed chain, straight from the indexer's own sync state. Stable fields: `network` (the vocabulary for every `{network}` path parameter), `last_scanned_block`, `head_at_last_sync`, `started_at`, `updated_at` (when the last successful sync finished — the field that ages when a chain dies), and `last_error`. Chains scanned by signature instead of block number (solana) also carry `newest_signature`, `oldest_signature` and `backfill_complete`. Plus `next_sync_at`, computed here and not stored: `updated_at` plus `indexer_period_seconds`, or null when either is missing. A `next_sync_at` already in the past is not a bug — it means the tick that was due has not landed, which is exactly what you want it to say. For staleness measured against the clock, read `stale_hours` in `GET /chains`."
          }
        },
        "type": "object",
        "required": [
          "status",
          "policy_version",
          "ordering_policy",
          "rater_weight_policy",
          "confidence_policy",
          "confidence_thresholds",
          "reading_policy",
          "agents",
          "feedback_entries",
          "chains"
        ],
        "title": "Health",
        "description": "Liveness, index size and every policy in force — live, free and uncached.\n\n🔴 **This route is the authority on every figure about this index.** No\ntotal is typed by hand anywhere else in this API, on the site or in the\ndocs: indexing runs continuously, and a number written into a document goes\nstale while still sounding exact. Ask here instead.\n\nIt is also where the effective value of every tunable parameter is\npublished, which is what keeps an environment override auditable rather\nthan an invisible drift."
      },
      "HistoryCoverage": {
        "properties": {
          "dated_reviews": {
            "type": "integer",
            "title": "Dated Reviews"
          },
          "undated_reviews": {
            "type": "integer",
            "title": "Undated Reviews"
          }
        },
        "type": "object",
        "required": [
          "dated_reviews",
          "undated_reviews"
        ],
        "title": "HistoryCoverage",
        "description": "How much of the wallet's reputation the series could actually plot.\n\nRatings without `block_time` are excluded from every point — the log scan\nhas not dated them yet. Reported instead of hidden: while\n`undated_reviews > 0` the last point of the series is legitimately below\n`final_score`, and without this field that gap reads as a bug."
      },
      "HistoryPoint": {
        "properties": {
          "period": {
            "type": "string",
            "format": "date-time",
            "title": "Period"
          },
          "score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Score"
          },
          "review_count": {
            "type": "integer",
            "title": "Review Count"
          },
          "cumulative_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Cumulative Score"
          }
        },
        "type": "object",
        "required": [
          "period",
          "score",
          "review_count",
          "cumulative_score"
        ],
        "title": "HistoryPoint",
        "description": "One bucket of the time series.\n\n`score` is what that period's ratings said; `cumulative_score` is the score\ncomputed over everything up to and including it — the number the API would\nhave served on that date. They answer different questions and the graph\nneeds both: the first is spiky by nature, the second is the trend line."
      },
      "IssuerNoHost": {
        "properties": {
          "count": {
            "type": "integer",
            "title": "Count"
          },
          "distinct_agents": {
            "type": "integer",
            "title": "Distinct Agents"
          },
          "distinct_raters": {
            "type": "integer",
            "title": "Distinct Raters"
          }
        },
        "type": "object",
        "required": [
          "count",
          "distinct_agents",
          "distinct_raters"
        ],
        "title": "IssuerNoHost",
        "description": "The coverage bucket: ratings whose URI names no http(s) host."
      },
      "IssuerStat": {
        "properties": {
          "issuer_host": {
            "type": "string",
            "title": "Issuer Host",
            "description": "Host of `feedbackURI`, lowercased and without `www.` — the service this rating was published from. A fact of the URI, never a judgement."
          },
          "count": {
            "type": "integer",
            "title": "Count"
          },
          "distinct_agents": {
            "type": "integer",
            "title": "Distinct Agents"
          },
          "distinct_raters": {
            "type": "integer",
            "title": "Distinct Raters"
          },
          "issuer": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Issuer",
            "description": "Canonical name when the host is a known Ultravioleta DAO product (allowlist, never inferred), else null. Null does NOT mean unknown issuer — `issuer_host` is the issuer."
          },
          "issuer_org": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Issuer Org",
            "description": "`uvd` when this issuer is a product of the house, else null. This route is how you audit how much of the index its own operators wrote."
          }
        },
        "type": "object",
        "required": [
          "issuer_host",
          "count",
          "distinct_agents",
          "distinct_raters"
        ],
        "title": "IssuerStat",
        "description": "One issuing service, with the reach that says what its volume is made of.\n\n`distinct_agents`/`distinct_raters` beside `count` are what separate a\npublic from a campaign, and the cut by WHO WRITES is where they bite\nhardest: measured 2026-08-29, the loudest issuer here is 301,180 ratings\non 16 agents, and the second is 48,852 ratings over 7,436 agents signed by\nexactly 3 raters."
      },
      "IssuerStats": {
        "properties": {
          "issuers": {
            "items": {
              "$ref": "#/components/schemas/IssuerStat"
            },
            "type": "array",
            "title": "Issuers"
          },
          "no_host": {
            "$ref": "#/components/schemas/IssuerNoHost",
            "description": "Ratings with no http(s) host in `feedbackURI` (`data:`, `ipfs:`, empty). Absent provenance is data, not a hole: this bucket is how much of the index declares no issuing service at all."
          },
          "issuer_source": {
            "type": "string",
            "title": "Issuer Source"
          },
          "policy_version": {
            "type": "string",
            "title": "Policy Version"
          },
          "source": {
            "type": "string",
            "title": "Source"
          },
          "refreshed_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Refreshed At"
          }
        },
        "type": "object",
        "required": [
          "issuers",
          "no_host",
          "issuer_source",
          "policy_version",
          "source",
          "refreshed_at"
        ],
        "title": "IssuerStats",
        "description": "Who wrote the index: the hosts that emitted the ratings we read.\n\nAdvisory and descriptive — an issuer's volume moves no score. It is\npublished so a reader can see how concentrated the evidence is."
      },
      "LeaderboardRow": {
        "properties": {
          "rank": {
            "type": "integer",
            "title": "Rank",
            "description": "Position in this ordering, 1-based. It comes from `shrunk_score`, not from `final_score`."
          },
          "wallet": {
            "type": "string",
            "title": "Wallet",
            "description": "The ranked wallet."
          },
          "final_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Final Score",
            "description": "The observed mean, untouched — the same number the paid wallet route serves. **It is not what decides the position.** Null, never 0, with no eligible ratings."
          },
          "shrunk_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Shrunk Score",
            "description": "**What decides the position**: the Bayesian mean — the observed score pulled toward the index prior by how many distinct raters back it, so four glowing ratings cannot outrank four hundred. Null when the index cannot form a prior."
          },
          "distinct_raters": {
            "type": "integer",
            "title": "Distinct Raters",
            "description": "The N of that formula: **distinct raters, not ratings**. Without it `shrunk_score` cannot be recomputed by hand and the order is not auditable. It is also what lets a free caller derive the confidence band without paying — the cuts are in `GET /health`, which is why the band is not repeated on every row."
          },
          "chain_count": {
            "type": "integer",
            "title": "Chain Count",
            "description": "How many chains contributed a score to this wallet."
          },
          "total_reviews": {
            "type": "integer",
            "title": "Total Reviews",
            "description": "Eligible ratings behind `final_score`. Read against `distinct_raters`."
          },
          "networks": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Networks",
            "description": "**Which** chains the wallet has a score on, not just how many. Without it a UI can say '3 chains' and not which — and one icon per network is the fastest way to read a profile that spans them."
          },
          "declared_types": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ]
            },
            "type": "array",
            "title": "Declared Types",
            "description": "The types declared by this wallet's identities. `null` is a legitimate member of the list: it means an identity that declares no type, which is common."
          }
        },
        "type": "object",
        "required": [
          "rank",
          "wallet",
          "final_score",
          "shrunk_score",
          "distinct_raters",
          "chain_count",
          "total_reviews",
          "networks",
          "declared_types"
        ],
        "title": "LeaderboardRow",
        "description": "One row of the ranking, carrying the numbers that produced its position.\n\nThe row publishes BOTH scores and the N between them on purpose: a list\nordered by a number it does not show is a list you have to believe. With\nthese three fields the order can be recomputed by hand."
      },
      "Manifesto": {
        "properties": {
          "manifesto_version": {
            "type": "string",
            "title": "Manifesto Version"
          },
          "title": {
            "type": "string",
            "title": "Title"
          },
          "subtitle": {
            "type": "string",
            "title": "Subtitle"
          },
          "catchphrase": {
            "type": "string",
            "title": "Catchphrase"
          },
          "editions": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object",
            "title": "Editions"
          },
          "note": {
            "type": "string",
            "title": "Note"
          },
          "origin": {
            "additionalProperties": true,
            "type": "object",
            "title": "Origin"
          },
          "principles": {
            "items": {
              "$ref": "#/components/schemas/ManifestoPrinciple"
            },
            "type": "array",
            "title": "Principles"
          },
          "we_are_not": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "We Are Not"
          },
          "verifications": {
            "items": {
              "$ref": "#/components/schemas/ManifestoVerification"
            },
            "type": "array",
            "title": "Verifications"
          },
          "siblings": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object",
            "title": "Siblings"
          }
        },
        "type": "object",
        "required": [
          "manifesto_version",
          "title",
          "subtitle",
          "catchphrase",
          "editions",
          "note",
          "origin",
          "principles",
          "we_are_not",
          "verifications",
          "siblings"
        ],
        "title": "Manifesto",
        "description": "The principles this service is built on, machine-readable.\n\nStatic: it never touches the index, so it costs nothing to serve and\ncannot go stale against a number."
      },
      "ManifestoPrinciple": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "statement": {
            "type": "string",
            "title": "Statement"
          },
          "detail": {
            "type": "string",
            "title": "Detail"
          },
          "inherited_from": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Inherited From"
          },
          "enforced_by": {
            "type": "string",
            "title": "Enforced By"
          }
        },
        "type": "object",
        "required": [
          "id",
          "statement",
          "detail",
          "inherited_from",
          "enforced_by"
        ],
        "title": "ManifestoPrinciple",
        "description": "One principle, with what enforces it in the code."
      },
      "ManifestoVerification": {
        "properties": {
          "claim": {
            "type": "string",
            "title": "Claim"
          },
          "route": {
            "type": "string",
            "title": "Route"
          },
          "free": {
            "type": "boolean",
            "title": "Free"
          },
          "command": {
            "type": "string",
            "title": "Command"
          },
          "read": {
            "type": "string",
            "title": "Read"
          }
        },
        "type": "object",
        "required": [
          "claim",
          "route",
          "free",
          "command",
          "read"
        ],
        "title": "ManifestoVerification",
        "description": "One claim and the exact command that checks it from outside."
      },
      "Ownership": {
        "properties": {
          "owner_updated_block": {
            "type": "integer",
            "title": "Owner Updated Block",
            "description": "The block the handover happened at — the line that splits the two eras. Every rating below it was earned by somebody else."
          },
          "identity_transferred": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Identity Transferred",
            "description": "Whether the identity actually changed hands. **Not derivable from the counts**, which is why it travels: `inherited_review_count == 0` means two different things — the identity never moved, or it moved and nobody had rated it yet — and telling them apart is what this field is for. Null when no `Registered` row is indexed for it."
          },
          "inherited_review_count": {
            "type": "integer",
            "title": "Inherited Review Count",
            "description": "Eligible ratings that landed BEFORE the handover."
          },
          "inherited_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Inherited Score",
            "description": "Mean over those. Null when there are none."
          },
          "current_era_review_count": {
            "type": "integer",
            "title": "Current Era Review Count",
            "description": "Eligible ratings SINCE the handover. 'Current era' means since the last transfer, not since the mint: no Transfer history is kept, so an identity that changed hands three times publishes one era."
          },
          "current_era_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Current Era Score",
            "description": "Mean over those. Null when there are none."
          },
          "undetermined_review_count": {
            "type": "integer",
            "title": "Undetermined Review Count",
            "description": "Eligible ratings the log scan has not dated yet, so neither era can claim them. **They are not distributed to either side** — guessing would be the cheap lie. This is why the three counts sum to `review_count` exactly."
          },
          "inherited_share": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Inherited Share",
            "description": "Inherited fraction of the dated ratings, 0-1. Null with a zero denominator: a `0.0` would read as 'inherited nothing', which is a claim with no data behind it."
          }
        },
        "type": "object",
        "required": [
          "owner_updated_block",
          "identity_transferred",
          "inherited_review_count",
          "inherited_score",
          "current_era_review_count",
          "current_era_score",
          "undetermined_review_count",
          "inherited_share"
        ],
        "title": "Ownership",
        "description": "ADVISORY. What today's owner earned of this score, and what it inherited.\n\nThe split is the rating's `block_number` against the identity's\n`owner_updated_block`: whatever landed before the handover was earned by\nsomebody else. It exists because without it a bought reputation — mint, get\nrated, transfer — is indistinguishable from an earned one, and that is the\ncheapest attack there is on an index like this one.\n\n**It never moves `score`.** Both averages break down exactly the population\nthat produced `review_count`, which is why `inherited_review_count +\ncurrent_era_review_count + undetermined_review_count == review_count` adds\nup by hand.\n\nNULL MEANS \"THE ERA CANNOT BE ESTABLISHED\", NEVER \"NOTHING WAS INHERITED\".\nIt is null when there is no identity row, when `owner_updated_block` is\nNULL, and **always on `solana`**: there that column is not the position of a\nTransfer but the SLOT AT WHICH THE INDEXER READ the asset (ownership in\nMetaplex Core is present state, not an event), so the predicate would flag\n100,00 % of that chain's ratings as inherited — every single one. Publishing\nthat would accuse a whole network over a column-semantics bug.\n\nAND IT IS NOT A VERDICT: a business sale, a custody migration or a wallet\nrotation produce exactly the same figure as a bought reputation. The field\npublishes counts and averages; the judgement is the reader's."
      },
      "PricingCatalog": {
        "properties": {
          "pricing_version": {
            "type": "string",
            "title": "Pricing Version",
            "description": "Version of this whole table. It bumps on any price change **and on any move of the free/paid line**, and it rides in every 402 and in the `X-Pricing-Version` header — read it from there rather than pinning a copy."
          },
          "currency": {
            "type": "string",
            "title": "Currency",
            "description": "Settlement currency of every tier."
          },
          "networks": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Networks",
            "description": "Chains accepted for payment, **in the order challenges offer them**: the first entry of `accepts[]` in every 402 is the first network of this list."
          },
          "free_tier": {
            "type": "string",
            "title": "Free Tier",
            "description": "What the free tier is, in one line, so the free/paid boundary is legible without diffing the table."
          },
          "tiers": {
            "items": {
              "$ref": "#/components/schemas/PricingTier"
            },
            "type": "array",
            "title": "Tiers",
            "description": "Every priced route. This is the same table the paywall charges from — published, not a copy of it."
          }
        },
        "type": "object",
        "required": [
          "pricing_version",
          "currency",
          "networks",
          "free_tier",
          "tiers"
        ],
        "title": "PricingCatalog",
        "description": "La forma de `paywall.catalog()` — lo que se publica ES lo que se cobra.\n\nEl handler no arma este objeto: lo entrega el paywall montado, y este\nmodelo solo lo declara para el esquema. Un campo nuevo en `catalog()` tiene\nque agregarse acá o no sale publicado — que es la dirección correcta del\nerror: mejor un campo que falta que un catálogo que diverge del cobro."
      },
      "PricingTier": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id",
            "description": "Stable identifier of the tier. It also travels in every 402 as `pricing.tier`, so a client can match the challenge it got against this table."
          },
          "path": {
            "type": "string",
            "title": "Path",
            "description": "The route as a regex over the request path — the very pattern the middleware matches, published unaltered so you can replay the match instead of guessing which tier a URL falls into."
          },
          "price_usd": {
            "type": "string",
            "title": "Price Usd",
            "description": "Price in USD **as a string, never a float**: a price that JSON rounds on its own cannot be reconciled against what was settled on-chain. Parse it as a decimal."
          },
          "free": {
            "type": "boolean",
            "title": "Free",
            "description": "Whether this route answers with no payment. The line is a cost rule, not a mood: free is what a cache serves to everybody, paid is what only you asked for."
          },
          "conditional": {
            "type": "boolean",
            "title": "Conditional",
            "description": "True when the price depends on the query string (`?snapshot=true` is the one left after the leaderboard split by path). When true, the authoritative price for one concrete call is that call's own 402 — not this row."
          },
          "why": {
            "type": "string",
            "title": "Why",
            "description": "The cost reason behind the price, in one sentence. It also travels inside every 402: a charge that cannot explain its own number is indistinguishable from an invented one."
          }
        },
        "type": "object",
        "required": [
          "id",
          "path",
          "price_usd",
          "free",
          "conditional",
          "why"
        ],
        "title": "PricingTier",
        "description": "One row of the table the paywall actually charges from."
      },
      "RaterChain": {
        "properties": {
          "network": {
            "type": "string",
            "title": "Network"
          },
          "total": {
            "type": "integer",
            "title": "Total"
          },
          "active": {
            "type": "integer",
            "title": "Active"
          },
          "distinct_subjects": {
            "type": "integer",
            "title": "Distinct Subjects"
          }
        },
        "type": "object",
        "required": [
          "network",
          "total",
          "active",
          "distinct_subjects"
        ],
        "title": "RaterChain",
        "description": "One chain where this wallet acted as a RATER, not as a subject."
      },
      "RaterProfile": {
        "properties": {
          "wallet": {
            "type": "string",
            "title": "Wallet",
            "description": "The wallet, read as the author of ratings rather than their subject."
          },
          "ratings_emitted": {
            "type": "integer",
            "title": "Ratings Emitted",
            "description": "Every rating this wallet wrote, revoked and out-of-domain included."
          },
          "revoked_count": {
            "type": "integer",
            "title": "Revoked Count",
            "description": "How many of them it later took back."
          },
          "eligible_count": {
            "type": "integer",
            "title": "Eligible Count",
            "description": "The ones that survive the same eligibility rules the scores use. The denominator of `value_avg` and `value_stddev`."
          },
          "self_ratings": {
            "type": "integer",
            "title": "Self Ratings",
            "description": "Ratings this wallet wrote over agents it OWNS. **Not excluded** from the aggregates here — surfaced so you can subtract them yourself, which is the opposite of the rated side, where they are excluded from every score."
          },
          "distinct_subjects": {
            "type": "integer",
            "title": "Distinct Subjects",
            "description": "How many different agents it rated. Read against `ratings_emitted`: a big ratio is a rater talking about the whole index, a small one is a rater talking about one thing."
          },
          "value_avg": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Value Avg",
            "description": "Mean value this rater hands out. **Null, never 0**, with no eligible ratings: 'emitted nothing' and 'emitted all zeros' are different facts."
          },
          "value_stddev": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Value Stddev",
            "description": "Dispersion of those values. **A rater stamping the same number on everything has stddev 0** — we surface it, we do not verdict it. Null when there is nothing to disperse."
          },
          "first_rating_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "First Rating At",
            "description": "First rating written, by on-chain time. Null until the log scan dates it."
          },
          "last_rating_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Rating At",
            "description": "Last rating written, by on-chain time. Null until the log scan dates it."
          },
          "top_subject_share": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Top Subject Share",
            "description": "Fraction of this rater's ACTIVE ratings landing on its favourite subject — the mirror of `top_client_share` on the rated side. `1.0` means everything went to one agent. Null with nothing eligible."
          },
          "per_chain": {
            "items": {
              "$ref": "#/components/schemas/RaterChain"
            },
            "type": "array",
            "title": "Per Chain",
            "description": "The same activity split by chain, never collapsed into a total."
          },
          "roles": {
            "additionalProperties": {
              "type": "integer"
            },
            "type": "object",
            "title": "Roles",
            "description": "How many ratings it wrote under each declared role, read from `tag1` through the SAME allowlist the facet `direction` uses — one policy, one owner. Never by splitting the tag on `:`: this index carries hundreds of distinct tags and a split would invent roles that outweigh the real ones."
          },
          "unknown_tag_count": {
            "type": "integer",
            "title": "Unknown Tag Count",
            "description": "Ratings whose `tag1` matches no declared role. **Data, not errors** — most of the index declares no role at all."
          },
          "policy_version": {
            "type": "string",
            "title": "Policy Version",
            "description": "The aggregation policy behind these figures. Also in `GET /health`."
          }
        },
        "type": "object",
        "required": [
          "wallet",
          "ratings_emitted",
          "revoked_count",
          "eligible_count",
          "self_ratings",
          "distinct_subjects",
          "value_avg",
          "value_stddev",
          "first_rating_at",
          "last_rating_at",
          "top_subject_share",
          "per_chain",
          "roles",
          "unknown_tag_count",
          "policy_version"
        ],
        "title": "RaterProfile",
        "description": "The wallet as RATER: its emitted ratings, decomposed. No score.\n\nDeliberately: ranking raters would be a NEW verdict (\"this publisher is\ntrustworthy\"), and this index returns evidence, not verdicts. You get the\nactivity decomposed -- volume, distinct subjects, dispersion,\nconcentration, roles -- and YOU decide what it means."
      },
      "Rating": {
        "properties": {
          "client": {
            "type": "string",
            "title": "Client",
            "description": "Who wrote this rating: the rater's address. This is the field the whole product is about — a score without its raters is a rumor."
          },
          "feedback_index": {
            "type": "integer",
            "title": "Feedback Index",
            "description": "The rater's own counter for this subject, part of the natural key `(network, agent_id, client, feedback_index)`. It is what makes re-indexing idempotent."
          },
          "value": {
            "type": "integer",
            "title": "Value",
            "description": "The raw on-chain integer, untouched. Read it together with `value_decimals`; use `normalized_value` for anything comparable."
          },
          "value_decimals": {
            "type": "integer",
            "title": "Value Decimals",
            "description": "Decimal places the emitter declared for `value`."
          },
          "normalized_value": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Normalized Value",
            "description": "`value` brought to the 0-100 domain — the only figure comparable across emitters. Null when the row is out of domain, which is also why it was excluded from the score."
          },
          "tag1": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tag1",
            "description": "The rater's first tag: the AREA being judged (`trust`, `liveness`, `contractRisk`, `activity`, …) and, when the emitter declares it, the direction prefix. This is what `facets` are keyed by. Free vocabulary — the index carries hundreds of distinct values, so read it, never split it on `:` and hope."
          },
          "tag2": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tag2",
            "description": "The rater's second tag, if any."
          },
          "is_revoked": {
            "type": "boolean",
            "title": "Is Revoked",
            "description": "The rater took this rating back. Excluded from every score, returned anyway — with `revoked_tx` — because a revocation nobody can audit is not a revocation."
          },
          "is_self": {
            "type": "boolean",
            "title": "Is Self",
            "description": "The rater IS the rated agent's current owner. Attribution, not eligibility: it travels on every row — revoked and out-of-domain included — so the breakdown says where each point came from without the caller recomputing the comparison. Self-ratings are out of every score and summarised in `self_rated`."
          },
          "tx_hash": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tx Hash",
            "description": "The transaction that wrote this rating — go verify it in an explorer, which is the point. **Null until the log scan reaches this entry**, not null forever."
          },
          "block_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Block Number",
            "description": "Block the rating landed in. Null until the log scan dates it."
          },
          "log_index": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Log Index",
            "description": "Position within the block. Null until the log scan dates it."
          },
          "feedback_uri": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Feedback Uri",
            "description": "The `feedbackURI` the standard carries — where the rater published the rating. The three `issuer*` fields below are read from it."
          },
          "issuer_host": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Issuer Host",
            "description": "Host of `feedback_uri`, lowercased and without `www.` — where this rating was published. Null when the rating carries no http(s) URI. Present for ~82% of the index."
          },
          "issuer": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Issuer",
            "description": "Canonical name of the issuing product when the host is a known Ultravioleta DAO service (e.g. `execution-market`), else null. Null does NOT mean unknown issuer — read `issuer_host` for that."
          },
          "issuer_org": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Issuer Org",
            "description": "`uvd` when this rating was written by an Ultravioleta DAO product, else null. One stable label, independent of how many products there are or what each is called."
          },
          "feedback_hash": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Feedback Hash",
            "description": "Content hash the rater committed to, when it declared one."
          },
          "revoked_tx": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Revoked Tx",
            "description": "The transaction that killed this rating. Without it a revocation is unauditable — which is why it travels instead of the row simply disappearing."
          }
        },
        "type": "object",
        "required": [
          "client",
          "feedback_index",
          "value",
          "value_decimals",
          "normalized_value",
          "tag1",
          "tag2",
          "is_revoked",
          "is_self",
          "tx_hash",
          "block_number",
          "log_index",
          "feedback_uri",
          "feedback_hash",
          "revoked_tx"
        ],
        "title": "Rating",
        "description": "One individual rating, exactly as it was written on chain.\n\nThis is the grain of the whole index — every score in this API is an\naggregate over rows of this shape, and the paid agent route returns them\none by one, revoked ones included, each with the transaction that proves\nit. A revocation nobody can audit is not a revocation."
      },
      "SearchMatch": {
        "properties": {
          "interpreted_as": {
            "type": "string",
            "enum": [
              "evm_wallet",
              "solana_wallet",
              "evm_agent_id",
              "solana_asset"
            ],
            "title": "Interpreted As"
          },
          "network": {
            "type": "string",
            "title": "Network"
          },
          "wallet": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Wallet"
          },
          "agent_count": {
            "type": "integer",
            "title": "Agent Count"
          },
          "agent_ids": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Agent Ids"
          },
          "declared_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Declared Type"
          },
          "final_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Final Score"
          },
          "total_reviews": {
            "type": "integer",
            "title": "Total Reviews"
          },
          "distinct_raters": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Distinct Raters"
          },
          "detail_route": {
            "type": "string",
            "title": "Detail Route"
          }
        },
        "type": "object",
        "required": [
          "interpreted_as",
          "network",
          "wallet",
          "agent_count",
          "agent_ids",
          "declared_type",
          "final_score",
          "total_reviews",
          "distinct_raters",
          "detail_route"
        ],
        "title": "SearchMatch",
        "description": "Una fila que el índice reconoció, con la LECTURA bajo la cual la encontró.\n\n`interpreted_as` viaja por fila y no en la raíz porque un mismo string\nbase58 puede ser wallet Y asset a la vez: `wallet_chains` lo toma como\nwallet de Solana y `agent_reputation` lo toma como asset de Metaplex Core —\ndos sujetos distintos, con dueños distintos, escritos igual. Fundir las dos\nlecturas en una etiqueta de raíz borraría cuál produjo cuál."
      },
      "SearchResult": {
        "properties": {
          "query": {
            "type": "string",
            "title": "Query",
            "description": "Exactly what you sent, untouched. It is the only thing that lets you see you pasted a trailing space or an invisible character."
          },
          "normalized_query": {
            "type": "string",
            "title": "Normalized Query",
            "description": "What we actually looked up. It differs from `query` only for EVM input, which is lowercased; a base58 travels byte for byte, because lowercasing it would silently invent a different key."
          },
          "interpretations": {
            "items": {
              "type": "string",
              "enum": [
                "evm_wallet",
                "solana_wallet",
                "evm_agent_id",
                "solana_asset"
              ]
            },
            "type": "array",
            "title": "Interpretations"
          },
          "matches": {
            "items": {
              "$ref": "#/components/schemas/SearchMatch"
            },
            "type": "array",
            "title": "Matches",
            "description": "What the index knows under that string. An empty list with a non-empty `interpretations` means the shape was valid and nothing matched — not that the query was wrong."
          },
          "match_count": {
            "type": "integer",
            "title": "Match Count",
            "description": "How many matches came back."
          },
          "policy_version": {
            "type": "string",
            "title": "Policy Version"
          },
          "source": {
            "type": "string",
            "title": "Source"
          },
          "refreshed_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Refreshed At"
          }
        },
        "type": "object",
        "required": [
          "query",
          "normalized_query",
          "interpretations",
          "matches",
          "match_count",
          "policy_version",
          "source",
          "refreshed_at"
        ],
        "title": "SearchResult",
        "description": "Lo que la caja entendió y dónde lo encontró, en dos capas separadas.\n\nLa separación ES el diseño: `interpretations` (raíz) es lo que la **forma**\npermite —léxico, barato, no toca la base—, y `matches[].interpreted_as` es\nbajo qué lectura el **índice** encontró esa fila. Una entrada puede tener dos\ninterpretaciones y cero matches, o una interpretación y once matches.\n\n**No existe el valor `\"ambiguous\"` y no hay un `ambiguous: bool`.** La señal\nde ambigüedad es `len(interpretations) > 1`, que además dice CUÁLES son;\ncolapsarlas en una etiqueta borra esa información, que es el modo de falla\nque este repo persigue.\n\nEl `example` del esquema es una respuesta REAL, leída del índice el\n2026-08-23 — no una maqueta. Es un base58 de Solana a propósito: la forma\nadmite dos lecturas (`interpretations` trae dos) y el índice sólo encontró\nfilas bajo una (`matches` trae una, `interpreted_as: solana_asset`), y ese\ndesfase entre las dos capas es justamente lo que un ejemplo con una sola\ninterpretación no muestra. Su `refreshed_at` en `null` también es real: esta\nfila salió del grano vivo, no de una vista materializada.",
        "examples": [
          {
            "interpretations": [
              "solana_wallet",
              "solana_asset"
            ],
            "match_count": 1,
            "matches": [
              {
                "agent_count": 1,
                "agent_ids": [
                  "7tm97nbKeXDNLfTwMFA84tfCuuULeYzQSrJcYNS5MdJf"
                ],
                "detail_route": "GET /reputation/agent/solana/7tm97nbKeXDNLfTwMFA84tfCuuULeYzQSrJcYNS5MdJf",
                "final_score": 97.368421,
                "interpreted_as": "solana_asset",
                "network": "solana",
                "total_reviews": 19,
                "wallet": "4x4ZhcqiT1FnirM8Ne97iVupkN4NcQgc2YYbE2jDZbZn"
              }
            ],
            "normalized_query": "7tm97nbKeXDNLfTwMFA84tfCuuULeYzQSrJcYNS5MdJf",
            "policy_version": "equal-weight-per-chain@2",
            "query": "7tm97nbKeXDNLfTwMFA84tfCuuULeYzQSrJcYNS5MdJf",
            "source": "feedback_entries"
          }
        ]
      },
      "SelfRated": {
        "properties": {
          "count": {
            "type": "integer",
            "title": "Count"
          },
          "score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Score"
          },
          "gap": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Gap"
          }
        },
        "type": "object",
        "required": [
          "count",
          "score",
          "gap"
        ],
        "title": "SelfRated",
        "description": "Lo que el sujeto dice de si mismo — excluido del score, no del perfil.\n\n`gap` = self - others. Positivo significa que se sobrevalora. Es la lectura\nmas informativa del perfil y sale gratis del predicado que ya excluye la\nautocalificacion del promedio."
      },
      "Snapshot": {
        "properties": {
          "id": {
            "type": "integer",
            "title": "Id",
            "description": "Identifier of this stored snapshot."
          },
          "inputs_digest": {
            "type": "string",
            "title": "Inputs Digest",
            "description": "Digest of the inputs the answer was computed from. Two snapshots with the same digest and the same `policy_version` say the same thing — this is what makes an answer citable."
          },
          "policy_version": {
            "type": "string",
            "title": "Policy Version",
            "description": "Aggregation policy in force when this was computed. Without it a stored score cannot be interpreted after the rules move."
          },
          "computed_at": {
            "type": "string",
            "format": "date-time",
            "title": "Computed At",
            "description": "When the snapshot was taken (UTC)."
          }
        },
        "type": "object",
        "required": [
          "id",
          "inputs_digest",
          "policy_version",
          "computed_at"
        ],
        "title": "Snapshot",
        "description": "A stored answer with the digest and policy version it was computed under.\n\nIt exists so a decision made on this data can be re-read later and\nstill mean the same thing."
      },
      "TypeBreakdown": {
        "properties": {
          "classification_source": {
            "type": "string",
            "title": "Classification Source",
            "description": "Where the types came from. They are **declared** by each identity and never verified by us: a declaration is not a fact, and most identities declare nothing."
          },
          "total_agents": {
            "type": "integer",
            "title": "Total Agents",
            "description": "Identities considered for this breakdown."
          },
          "types": {
            "additionalProperties": {
              "$ref": "#/components/schemas/TypeStats"
            },
            "type": "object",
            "title": "Types",
            "description": "One entry per declared type. The `unknown` bucket is the largest by a wide margin and means «did not say», never «we could not classify it»."
          }
        },
        "type": "object",
        "required": [
          "classification_source",
          "total_agents",
          "types"
        ],
        "title": "TypeBreakdown",
        "description": "The index split by declared agent type.\n\nMost identities declare nothing, so the unknown bucket is the largest\nby a wide margin — read it as «did not say», never as\n«unclassified by us»."
      },
      "TypeStats": {
        "properties": {
          "agent_count": {
            "type": "integer",
            "title": "Agent Count",
            "description": "Identities that declared this type."
          },
          "rated_agent_count": {
            "type": "integer",
            "title": "Rated Agent Count",
            "description": "How many of them have any rating at all. The rest have no score — not a score of zero."
          },
          "avg_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Avg Score",
            "description": "Mean across the rated ones, or null when none are rated."
          },
          "total_reviews": {
            "type": "integer",
            "title": "Total Reviews",
            "description": "Ratings across this type."
          }
        },
        "type": "object",
        "required": [
          "agent_count",
          "rated_agent_count",
          "avg_score",
          "total_reviews"
        ],
        "title": "TypeStats",
        "description": "Aggregate for one declared agent type.\n\nThe type is what the identity SAYS it is, taken verbatim from its own\ncard. We never verify it."
      },
      "ValidationError": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          },
          "input": {
            "title": "Input"
          },
          "ctx": {
            "type": "object",
            "title": "Context"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "WalletChain": {
        "properties": {
          "network": {
            "type": "string",
            "title": "Network",
            "description": "Chain this row is about, by our own name for it. The full list is in `GET /health.chains` — that is the authority."
          },
          "agent_count": {
            "type": "integer",
            "title": "Agent Count",
            "description": "How many ERC-8004 identities this wallet owns on this chain. **Zero means something different from absent**: there are ratings here but we have not indexed the registry that issued them."
          },
          "agent_ids": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Agent Ids",
            "description": "The identity ids themselves, to feed `GET /reputation/agent/{network}/{id}`. Null when the row came from a materialised view that does not carry them."
          },
          "final_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Final Score",
            "description": "Score on this chain, 0-100, or **null**. Null is not zero: an identity with no ratings has no score, and a zero would be asserting somebody rated it badly. Branch on null, never coerce it."
          },
          "total_reviews": {
            "type": "integer",
            "title": "Total Reviews",
            "description": "Ratings counted on this chain. Volume, not agreement — read it next to `distinct_raters`."
          },
          "distinct_raters": {
            "type": "integer",
            "title": "Distinct Raters",
            "description": "How many distinct wallets did the rating, which is the number that separates evidence from a campaign. 642 ratings from 467 raters and 642 from one rater are the same `total_reviews` and not remotely the same fact."
          }
        },
        "type": "object",
        "required": [
          "network",
          "agent_count",
          "agent_ids",
          "final_score",
          "total_reviews",
          "distinct_raters"
        ],
        "title": "WalletChain",
        "description": "Una cadena en la que esta wallet existe, tenga o no reputación ahí."
      },
      "WalletChains": {
        "properties": {
          "wallet": {
            "type": "string",
            "title": "Wallet",
            "description": "The wallet asked about, normalised. EVM addresses come back lowercased; Solana keeps its base58 case."
          },
          "chains": {
            "items": {
              "$ref": "#/components/schemas/WalletChain"
            },
            "type": "array",
            "title": "Chains",
            "description": "One row per chain where this wallet exists, whether or not it has reputation there. An empty list means «not registered anywhere we index» — which is an answer, not an error."
          },
          "caveats": {
            "items": {
              "$ref": "#/components/schemas/Caveat"
            },
            "type": "array",
            "title": "Caveats",
            "description": "SUBSET of the caveats: only those computable from public data without touching the grain (today: `burn-address`, for well-known burn addresses -- nobody controls them, nobody can answer for their reputation). The full evidence-quality set (concentration, single rater, campaign...) travels ONLY in the paid decomposition `GET /reputation/wallet/{wallet}`. An empty list here means 'no public-data caveat', NOT 'no caveats at all'."
          },
          "identity_count": {
            "type": "integer",
            "title": "Identity Count",
            "description": "Total ERC-8004 identities this wallet owns across every chain we read."
          },
          "chains_with_identity": {
            "type": "integer",
            "title": "Chains With Identity",
            "description": "On how many chains it holds at least one identity."
          },
          "chains_with_reputation": {
            "type": "integer",
            "title": "Chains With Reputation",
            "description": "On how many of those it actually has ratings. The gap against `chains_with_identity` is registered-but-unrated, which is a different fact from unregistered."
          },
          "total_reviews": {
            "type": "integer",
            "title": "Total Reviews",
            "description": "Ratings across every chain. Volume, not agreement."
          },
          "distinct_raters": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Distinct Raters",
            "description": "Distinct raters across the WHOLE wallet, deduplicated between chains. **Do not sum `chains[].distinct_raters`** — the same rater can rate on several chains and the sum double-counts them (measured: a wallet whose per-chain counts add to 10 has 9 real raters). `null` means the wallet is not in the ranking, which is not the same as zero raters."
          },
          "global_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Global Score"
          },
          "policy_version": {
            "type": "string",
            "title": "Policy Version"
          },
          "source": {
            "type": "string",
            "title": "Source"
          },
          "refreshed_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Refreshed At"
          }
        },
        "type": "object",
        "required": [
          "wallet",
          "chains",
          "caveats",
          "identity_count",
          "chains_with_identity",
          "chains_with_reputation",
          "total_reviews",
          "global_score",
          "policy_version",
          "source",
          "refreshed_at"
        ],
        "title": "WalletChains",
        "description": "El mapa gratis de una wallet: identidades y reputación por cadena.\n\nEl `example` del esquema es la respuesta REAL del dominio para la wallet\ndel ejemplo de la ruta, leída el 2026-08-21 — su `refreshed_at` la fecha\nsolo. La respuesta viva es la de la propia ruta, que es gratis.",
        "examples": [
          {
            "caveats": [],
            "chains": [
              {
                "agent_count": 3,
                "agent_ids": [
                  "2284",
                  "2290",
                  "29368"
                ],
                "distinct_raters": 467,
                "final_score": 98.270763,
                "network": "base",
                "total_reviews": 642
              }
            ],
            "chains_with_identity": 1,
            "chains_with_reputation": 1,
            "global_score": 98.270763,
            "identity_count": 3,
            "policy_version": "equal-weight-per-chain@2",
            "refreshed_at": "2026-08-21T04:00:28Z",
            "source": "chain_rankings_mv",
            "total_reviews": 642,
            "wallet": "0x715dc035ffb97dd7bb4095c6670138ba05bb4e6d"
          }
        ]
      },
      "WalletHistory": {
        "properties": {
          "wallet": {
            "type": "string",
            "title": "Wallet",
            "description": "The wallet this history is about."
          },
          "bucket": {
            "type": "string",
            "title": "Bucket",
            "description": "Size of each time bucket the points are grouped into."
          },
          "points": {
            "items": {
              "$ref": "#/components/schemas/HistoryPoint"
            },
            "type": "array",
            "title": "Points",
            "description": "One point per bucket, by **on-chain** time — never by when we indexed it."
          },
          "coverage": {
            "$ref": "#/components/schemas/HistoryCoverage",
            "description": "How much of the range we can actually speak for. Read it before reading the curve: a gap here is missing evidence, not a drop in reputation."
          }
        },
        "type": "object",
        "required": [
          "wallet",
          "bucket",
          "points",
          "coverage"
        ],
        "title": "WalletHistory",
        "description": "How a wallet's reputation moved over time, bucketed by on-chain time.\n\nRead `coverage` before the curve: a flat stretch can be a quiet\nperiod or a range we cannot speak for, and those are different facts."
      },
      "WalletScore": {
        "properties": {
          "wallet": {
            "type": "string",
            "title": "Wallet",
            "description": "The subject, echoed back as it was resolved. EVM addresses come back lowercased (the grain guarantees it); Solana base58 ids keep their case, because there case IS identity."
          },
          "final_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Final Score",
            "description": "The published score, 0-100: the **mean of the per-chain scores**, one vote per chain, so a chain carrying hundreds of thousands of ratings cannot drown one carrying four. Revoked, out-of-domain and self-ratings are out of it. **Null, never 0**, when there is no eligible rating: no data is not bad data."
          },
          "weighted_score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Weighted Score",
            "description": "The SAME grain under a second weighting (`rater_weight_policy`), published beside the served number rather than replacing it. **It is not the policy**: `final_score` is what this API serves and what a snapshot records. It travels because changing how hundreds of thousands of ratings are weighted is not something a reputation index gets to do silently. Null means two different things and `final_score` tells them apart: if that one has a number and this one does not, the side signal degraded; if both are null, the wallet has no evidence."
          },
          "rater_weight_policy": {
            "type": "string",
            "title": "Rater Weight Policy",
            "description": "Version string of the weighting that produced `weighted_score`. Also published free in `GET /health`."
          },
          "chain_count": {
            "type": "integer",
            "title": "Chain Count",
            "description": "How many chains contributed a score — the size of the vote in the mean above, not how many chains the wallet merely appears on."
          },
          "total_reviews": {
            "type": "integer",
            "title": "Total Reviews",
            "description": "Eligible ratings behind the score, summed across chains. Read it against `concentration.distinct_raters`: the two together are what separates consensus from repetition."
          },
          "per_chain": {
            "additionalProperties": {
              "$ref": "#/components/schemas/ChainScore"
            },
            "type": "object",
            "title": "Per Chain",
            "description": "The score per chain, never collapsed — keyed by network. This is the breakdown the mean above is made of, so the mean can be recomputed by hand."
          },
          "facets": {
            "additionalProperties": {
              "$ref": "#/components/schemas/Facet"
            },
            "type": "object",
            "title": "Facets",
            "description": "Reputation by area, keyed by the rater's `tag1` — `trust`, `liveness`, `contractRisk` and `activity` are separate judgements, not a mean. **Ordered by volume desc**: dict order is the ranking, so a client can take the first N without re-sorting. The overall score and one facet can disagree by more than 30 points on the same wallet, and that gap is the product."
          },
          "self_rated": {
            "$ref": "#/components/schemas/SelfRated",
            "description": "What the subject said about itself — excluded from every score above, published here instead of hidden. `gap = self - others`: positive means it overrates itself."
          },
          "concentration": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Concentration"
              },
              {
                "type": "null"
              }
            ],
            "description": "ADVISORY, and **nullable on purpose**: this is a secondary aggregate, so when its query degrades the endpoint drops the signal rather than the answer. Never moves a score."
          },
          "confidence": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Confidence"
              },
              {
                "type": "null"
              }
            ],
            "description": "ADVISORY band over how much evidence backs the number — never the number itself. Goes null together with `concentration`, and for the same reason: the band is computed over `distinct_raters`, and a band invented over an N nobody could read would be a label with no data under it."
          },
          "activity": {
            "$ref": "#/components/schemas/Activity",
            "description": "First and last rating **by on-chain time**, never by when we indexed the row."
          },
          "caveats": {
            "items": {
              "$ref": "#/components/schemas/Caveat"
            },
            "type": "array",
            "title": "Caveats",
            "description": "The traps THESE numbers trigger, already written out. Derived from the fields of this same response — `final_score`, `total_reviews`, `concentration`, `self_rated` — never from a second query, which is why it costs nothing extra. **Advisory: it never moves the score and never fills a null one.** An empty list means no trap fired, NOT 'verified clean'. Each entry is `{code, text}`: branch on `code`, which is permanent; `text` is Spanish prose meant to be shown to whoever is deciding and may be rewritten or re-measured without notice. Same objects the MCP tool `describe_lookup_wallet` returns: one policy, two doors."
          },
          "policy_version": {
            "type": "string",
            "title": "Policy Version",
            "description": "The aggregation policy that produced these numbers. It travels in every scored response so two callers can never be served different numbers without the difference being visible, and so a stored number can be traced back to the rules that made it."
          },
          "snapshot": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Snapshot"
              },
              {
                "type": "null"
              }
            ],
            "description": "Only on `?snapshot=true`: the durable receipt row that was written, with its `inputs_digest` and `policy_version` — something you can hand a counterparty and we can recompute identically. Null on a plain read, which writes nothing."
          }
        },
        "type": "object",
        "required": [
          "wallet",
          "final_score",
          "weighted_score",
          "rater_weight_policy",
          "chain_count",
          "total_reviews",
          "per_chain",
          "facets",
          "self_rated",
          "concentration",
          "confidence",
          "activity",
          "caveats",
          "policy_version"
        ],
        "title": "WalletScore",
        "description": "One wallet's reputation, decomposed into what produced it.\n\nThe headline number is `final_score`, but it is the least interesting field\nhere: everything beside it exists so the number can be checked instead of\nbelieved — which chains it came from, which areas disagree, how many\ndistinct counterparties are behind it, and what the subject says about\nitself."
      }
    },
    "securitySchemes": {
      "x402": {
        "type": "apiKey",
        "in": "header",
        "name": "X-PAYMENT",
        "description": "**Payment is the authentication.** There is no account, no API key and nothing to register: the credential for a metered call is a base64 x402 payload carrying an EIP-3009 USDC authorization that YOU sign locally — no private key ever leaves your side.\n\nHow to get one: call the operation with no header at all. It answers **402** with the challenge (asking is free and is the intended first move), you sign `accepts[]` — the only thing you sign against — and you replay the identical request with this header. The 200 comes back with `X-Payment-Receipt`.\n\nTwo properties no `apiKey` scheme can express, so read them here: the credential is **per call** (the nonce is consumed at settlement, so it will not pay twice) and a receipt is reusable only for the byte-identical resource — wallet A's receipt cannot unlock wallet B. Full contract: https://describe.net/auth.md"
      }
    }
  },
  "tags": [
    {
      "name": "Free",
      "description": "No payment, no headers, no account. Two kinds, one cost rule: the aggregate view of the index (one query that serves everybody) and the per-subject previews a cache serves to everyone asking the same thing (`/wallets/{wallet}/chains`, `/search/{query}`, `/badge/{wallet}.svg`) — either way the marginal caller costs nothing. Start here: reading these tells you whether the metered tier is worth it.",
      "externalDocs": {
        "description": "The API guide, with worked calls",
        "url": "https://describe.net/api.md"
      }
    },
    {
      "name": "Metered (x402)",
      "description": "Answers a question about one named subject. Returns **HTTP 402 with a signable challenge** until paid; calling it unpaid is free and is the intended first move. See **How to pay** at the top of this page — and pay only the address published there.",
      "externalDocs": {
        "description": "How to authenticate — i.e. how to pay",
        "url": "https://describe.net/auth.md"
      }
    },
    {
      "name": "Agent protocols",
      "description": "The same index behind a protocol instead of behind a route: **MCP** (Streamable HTTP) and **A2A** (JSON-RPC 2.0), each on a single POST path. Free at the door and priced underneath — the MCP server neither charges nor signs nor holds a key: it relays the 402 of the route behind the tool and forwards the credential **you** signed. Listed here because an agent that only reads this schema had no way to discover either one.",
      "externalDocs": {
        "description": "MCP server card",
        "url": "https://describe.net/.well-known/mcp/server-card.json"
      }
    }
  ],
  "x-mcp-server-card": "https://describe.net/.well-known/mcp/server-card.json",
  "x-llms-txt": "https://describe.net/llms.txt",
  "x-agent-skills": "https://describe.net/.well-known/agent-skills/index.json",
  "externalDocs": {
    "description": "Guides, worked calls and how to pay",
    "url": "https://describe.net/api.md"
  },
  "security": []
}