{"name":"Roundhouse API","description":"The x402 settlement index and agent identity record, as JSON. Public, read-only and free to start — no key required.","version":"0","documentation":"https://roundhouseai.io/docs/api","openapi":{"json":"https://preview-api.roundhouseai.io/openapi.json","yaml":"https://preview-api.roundhouseai.io/openapi.yaml","version":"3.1.0"},"servers":[{"url":"https://api.roundhouseai.io/v0","description":"Canonical base URL. The API worker on its own host, isolated from the dashboard."},{"url":"https://roundhouseai.io/api/v0","description":"The same API through the dashboard origin. Same responses, one more hop; needs no CORS from a browser on that origin."}],"mcp":{"url":"https://preview-api.roundhouseai.io/v0/mcp","transport":"streamable-http","protocol_version":"2025-06-18","stateless":true,"tool_count":17,"documentation":"https://roundhouseai.io/docs/api/mcp"},"agent_skill":"https://roundhouseai.io/skill.md","health":"https://preview-api.roundhouseai.io/health","authentication":{"required":false,"methods":["Authorization: Bearer rh_live_…","x-api-key: rh_live_…"],"get_a_key":"https://roundhouseai.io/dashboard/connections","buy_a_trial_key":"https://preview-api.roundhouseai.io/v0/test/x402"},"conventions":{"errors":"A refused query answers 503 upstream_unavailable naming the resource — never 200 with an empty list. An empty array is a real answer; a failure is not.","pagination":"Cursor lists return next_before; pass it back as ?before=. It encodes the whole sort key, so do not parse or construct one. The leaderboard is the exception and pages by ?offset=.","aggregates":"Every figure is one entity's or one pair's own. Site-wide totals are confined to the /stats page and are not served here."},"tags":[{"name":"Account","description":"What the credential in front of a request is entitled to, what it has left, and what each route costs on each of the two paid lanes."},{"name":"Settlements","description":"The settlement ledger: the feed, and one wallet's slice of it."},{"name":"Entities","description":"Wallets resolved to identities, their own aggregates, and reputation."},{"name":"Discovery","description":"The service catalog, the relayers, and the payment graph."},{"name":"Capabilities","description":"Endpoints grouped by the job they do, so providers of one capability compare on a shared request, response and price."},{"name":"SQL","description":"Read-only SQL for questions the fixed endpoints do not answer."},{"name":"MCP","description":"The same endpoints as Model Context Protocol tools, gated by the same API key. See `./mcp-tools` for the tool registry."},{"name":"KYA","description":"Signed agent attestations and the payment memos that commit to them."},{"name":"x402","description":"Paying for a Roundhouse resource over x402."},{"name":"Telemetry","description":"Self-reported call telemetry from x402 clients. Kept apart from the settlement index, which is chain-verified."}],"endpoint_count":26,"endpoints":[{"method":"GET","path":"/v0/account","url":"https://preview-api.roundhouseai.io/v0/account","operation_id":"getAccount","tag":"Account","summary":"What this caller is entitled to, and what it would be charged","auth":"optional","payment":"none","params":[],"returns":{"authenticated":"Whether a credential was accepted.","key_present":"Whether a credential was sent at all.","key_recognised":"Whether it resolved, or NULL when none was sent — three-valued, because \"no key\" and \"a key that did not resolve\" need different fixes and one `false` for both would conflate them.","tier":"`anonymous`, `trial`, `oauth`, or the organization key tier.","plan":"The organization's plan key, or null when there is no organization.","lane":"Which lane this caller would take on a metered route, so it can be PREDICTED rather than discovered by being refused: `credit` draws down Query Units, `x402` pays per call, `invalid_key` resolves to nothing, `no_account` is a caller with no organisation behind it on the MCP surface, where there is no x402 door, and `upgrade_required` is that same surface reached by a caller that HAS an organisation whose plan excludes the route. The last two were one value until a signed-in connection with an organisation and a balance was told it had no account behind it.","organisation":"Id and display name, or null. Named so an operator with several accounts knows which one to top up.","balance":"Units available and the low-balance threshold, or NULL with a `balance_note` saying why there is no balance to report — an organizationless caller has none, which is not the same as having zero. `low_balance` is already computed here so a client does not have to know the threshold to apply it.","metered_routes":"Every route with what one call costs on each lane: `qu` on the credit lane, `x402_usd` on the payment lane. Routes that cost nothing are listed AT ZERO rather than omitted, because a caller cannot tell \"free\" from \"not thought about\" by absence. `runSql`'s `qu` is its MINIMUM — a query is priced by the time it takes, and `sql_tiers` gives the ceilings.","balance_note":"Present only when `balance` is null, saying WHICH kind of nothing it is: no credential, one that did not resolve, a trial key that holds no balance by design, or an organization row that could not be read. A null balance is never a zero.","sql_tiers":"What one `run_sql` call may cost, which is no longer one number: a query is priced by the time it takes, at one Query Unit per started five seconds with a floor of one. Each tier gives its `timeout_seconds`, its `max_qu` (what is HELD for the call and the most it can cost), its own rate limit, and two separate booleans — `allowed` is whether this caller's plan may buy it, `available` is whether this deployment can serve it at all. They are separate because \"upgrade your plan\" and \"report a misconfiguration\" are different things to go and do, and `note` says which applies.","top_up_url":"Org-scoped deep link to the top-up page, or the plans page for a caller with no organization.","as_of":"When this answer was assembled.","summary":"One sentence saying what this lane means, for a human reading it in an agent transcript."}},{"method":"GET","path":"/v0/flows","url":"https://preview-api.roundhouseai.io/v0/flows","operation_id":"getFlows","tag":"Settlements","summary":"Live flow of funds","auth":"none","payment":"none","params":[{"name":"limit","in":"query","required":false,"type":"integer","description":"Rows to return. Clamped to 100; values outside the range are clamped, not rejected.","default":50},{"name":"before","in":"query","required":false,"type":"string","description":"Opaque cursor from a previous response's `next_before`. It encodes the whole sort key, tiebreaker included, so a page boundary inside a group of rows sharing a timestamp does not skip the rest. Do not parse it or build one by hand."}],"returns":{"flows":"Settlement rows, newest first, decorated with resolved display names. Each row also carries `is_self_payment` (payer = payee, annotated rather than only excluded — a third of the feed can be self-payment) and `evidence`: `{ verified, method, probability, tier }`. `verified` is THREE-VALUED — `true` a named facilitator relayed it, `false` examined with no EIP-3009 marker, and `null` gasless and authorized but NOT ESTABLISHED as x402, which is not the same as disproven. `tier` is `proven` | `probable` | `unproven` | `not_x402` | `ungraded`.","next_before":"Cursor for the next page, or `null` at the end of the list.","meta":"The chain served and `as_of`. On a non-empty page `meta` also carries `page_self_payments`, `page_self_payment_usd`, `page_gross_usd` and `page_organic_usd`. All four are THIS PAGE only, as the names say: they are not site-wide figures and summing them across pages is not a total. On an EMPTY list it instead carries `dataset_last_written_at`, `dataset_row_count` and any `warnings`."}},{"method":"GET","path":"/v0/transactions","url":"https://preview-api.roundhouseai.io/v0/transactions","operation_id":"getTransactions","tag":"Settlements","summary":"Global settlement feed","auth":"none","payment":"none","params":[{"name":"limit","in":"query","required":false,"type":"integer","description":"Rows to return. Clamped to 100; values outside the range are clamped, not rejected.","default":50},{"name":"before","in":"query","required":false,"type":"string","description":"Opaque cursor from a previous response's `next_before`. It encodes the whole sort key, tiebreaker included, so a page boundary inside a group of rows sharing a timestamp does not skip the rest. Do not parse it or build one by hand."},{"name":"min_probability","in":"query","required":false,"type":"number","description":"Keep only rows at or above this x402 confidence. `1.0` is proven by a named facilitator having relayed it; `0.05` is a bare transfer to a catalogued address. Read `evidence` on the row rather than thresholding this number blind — the ladder has five rungs and the gap that matters is `verified: true` against `verified: null`, not a decimal."},{"name":"exclude_self_payments","in":"query","required":false,"type":"string","description":"Set to `true` to drop rows where payer = payee. Off by default: a feed is a record of what happened and a wallet paying itself did happen, so the rows are ANNOTATED (`is_self_payment`) rather than hidden. Applied after the page is read, so a filtered page can be short of `limit` while more rows remain — keep following `next_before`.","values":["true","false","1","0"]}],"returns":{"transactions":"Settlement rows, newest first. Each row also carries `is_self_payment` (payer = payee, annotated rather than only excluded — a third of the feed can be self-payment) and `evidence`: `{ verified, method, probability, tier }`. `verified` is THREE-VALUED — `true` a named facilitator relayed it, `false` examined with no EIP-3009 marker, and `null` gasless and authorized but NOT ESTABLISHED as x402, which is not the same as disproven. `tier` is `proven` | `probable` | `unproven` | `not_x402` | `ungraded`.","next_before":"Cursor for the next page, or `null`. Minted from the rows READ, not the rows shown, so `exclude_self_payments` cannot end the walk early.","meta":"The chain served and `as_of`. On a non-empty page `meta` also carries `page_self_payments`, `page_self_payment_usd`, `page_gross_usd` and `page_organic_usd`. All four are THIS PAGE only, as the names say: they are not site-wide figures and summing them across pages is not a total. `self_payments_excluded` is set when the page was filtered."}},{"method":"GET","path":"/v0/entities/{wallet}/settlements","url":"https://preview-api.roundhouseai.io/v0/entities/{wallet}/settlements","operation_id":"getEntitySettlements","tag":"Settlements","summary":"Settlements for one wallet","auth":"none","payment":"none","params":[{"name":"wallet","in":"path","required":true,"type":"string","description":"An EVM address (`0x` + 40 hex digits; case-insensitive, lowercased before lookup) or a Solana address (base58; case-sensitive, used exactly as given). An address the index has not observed answers `coverage.known: false`, not zeros."},{"name":"limit","in":"query","required":false,"type":"integer","description":"Rows to return. Clamped to 100; values outside the range are clamped, not rejected.","default":50},{"name":"before","in":"query","required":false,"type":"string","description":"Opaque cursor from a previous response's `next_before`. It encodes the whole sort key, tiebreaker included, so a page boundary inside a group of rows sharing a timestamp does not skip the rest. Do not parse it or build one by hand."}],"returns":{"wallet":"The wallet queried, lowercased.","settlements":"Settlement rows, newest first, decorated with resolved display names. Each row also carries `is_self_payment` (payer = payee, annotated rather than only excluded — a third of the feed can be self-payment) and `evidence`: `{ verified, method, probability, tier }`. `verified` is THREE-VALUED — `true` a named facilitator relayed it, `false` examined with no EIP-3009 marker, and `null` gasless and authorized but NOT ESTABLISHED as x402, which is not the same as disproven. `tier` is `proven` | `probable` | `unproven` | `not_x402` | `ungraded`.","next_before":"Cursor for the next page, or `null`.","meta":"The chain served and `as_of`. On a non-empty page `meta` also carries `page_self_payments`, `page_self_payment_usd`, `page_gross_usd` and `page_organic_usd`. All four are THIS PAGE only, as the names say: they are not site-wide figures and summing them across pages is not a total."}},{"method":"GET","path":"/v0/agents/{wallet}","url":"https://preview-api.roundhouseai.io/v0/agents/{wallet}","operation_id":"getAgent","tag":"Entities","summary":"Per-agent stats and identity","auth":"none","payment":"none","params":[{"name":"wallet","in":"path","required":true,"type":"string","description":"An EVM address (`0x` + 40 hex digits; case-insensitive, lowercased before lookup) or a Solana address (base58; case-sensitive, used exactly as given). An address the index has not observed answers `coverage.known: false`, not zeros."},{"name":"recent_limit","in":"query","required":false,"type":"integer","description":"Settlement rows to embed in the profile. 0 omits them. Default 5 — this was an unbounded 25, which is most of the payload on a surface where a response is billed as context tokens.","default":5}],"returns":{"wallet":"The wallet queried, lowercased.","role":"The role it was queried in: `agent` or `merchant`.","coverage":"**Read this first.** `known` false means the index has never observed this address, so every figure below is a zero meaning \"not observed\" — not \"observed to be nothing\". Also `first_seen`, `last_seen`, `settlements_scanned`, `scan_truncated` and `scan_cap`.","entities":"Resolved identity rows: display name, ENS, ERC-8004 card, role, first and last seen.","erc8004_agents":"ERC-8004 registrations whose on-chain agentWallet is this address.","aggregates":"That wallet's own totals: inbound/outbound USD and counts, distinct counterparties, chain split. `basis` says what they were computed over — `index_rollup` is the whole history, `scanned_settlements` is the capped scan and is a floor when `coverage.scan_truncated` is set. `distinct_counterparties` excludes the wallet itself on both bases. `chain_split` is the exception and carries its own `chain_split_basis`: the rollup is keyed by wallet and holds no chain dimension, so that split is ALWAYS the scan and is capped with it, whatever the object-level `basis` says.","data_quality":"The risk annotation. **Route on `verdict`, not on `note`.** `verdict` is one of `unobserved` (never seen — nothing assessed), `patterns_detected`, `no_patterns_detected`, or `insufficient_evidence` — the last meaning the address WAS observed, nothing fired, and not enough of its history was examined for that to rule anything out. `checks` carries every check with its `threshold`, what was `observed`, its own `basis`, and `conclusive`: a check that FIRED is always conclusive (a pattern seen in a sample happened), a check that did not fire on a truncated scan is not. `note` says the same thing in a sentence and is derived from the two.","recent":"A slice of its activity, `recent_limit` rows (default 5). Each row also carries `is_self_payment` (payer = payee, annotated rather than only excluded — a third of the feed can be self-payment) and `evidence`: `{ verified, method, probability, tier }`. `verified` is THREE-VALUED — `true` a named facilitator relayed it, `false` examined with no EIP-3009 marker, and `null` gasless and authorized but NOT ESTABLISHED as x402, which is not the same as disproven. `tier` is `proven` | `probable` | `unproven` | `not_x402` | `ungraded`."}},{"method":"GET","path":"/v0/merchants/{wallet}","url":"https://preview-api.roundhouseai.io/v0/merchants/{wallet}","operation_id":"getMerchant","tag":"Entities","summary":"Per-merchant stats and identity","auth":"none","payment":"none","params":[{"name":"wallet","in":"path","required":true,"type":"string","description":"An EVM address (`0x` + 40 hex digits; case-insensitive, lowercased before lookup) or a Solana address (base58; case-sensitive, used exactly as given). An address the index has not observed answers `coverage.known: false`, not zeros."},{"name":"recent_limit","in":"query","required":false,"type":"integer","description":"Settlement rows to embed in the profile. 0 omits them. Default 5 — this was an unbounded 25, which is most of the payload on a surface where a response is billed as context tokens.","default":5}],"returns":{"wallet":"The wallet queried, lowercased.","role":"The role it was queried in: `agent` or `merchant`.","coverage":"**Read this first.** `known` false means the index has never observed this address, so every figure below is a zero meaning \"not observed\" — not \"observed to be nothing\". Also `first_seen`, `last_seen`, `settlements_scanned`, `scan_truncated` and `scan_cap`.","entities":"Resolved identity rows: display name, ENS, ERC-8004 card, role, first and last seen.","erc8004_agents":"ERC-8004 registrations whose on-chain agentWallet is this address.","aggregates":"That wallet's own totals: inbound/outbound USD and counts, distinct counterparties, chain split. `basis` says what they were computed over — `index_rollup` is the whole history, `scanned_settlements` is the capped scan and is a floor when `coverage.scan_truncated` is set. `distinct_counterparties` excludes the wallet itself on both bases. `chain_split` is the exception and carries its own `chain_split_basis`: the rollup is keyed by wallet and holds no chain dimension, so that split is ALWAYS the scan and is capped with it, whatever the object-level `basis` says.","data_quality":"The risk annotation. **Route on `verdict`, not on `note`.** `verdict` is one of `unobserved` (never seen — nothing assessed), `patterns_detected`, `no_patterns_detected`, or `insufficient_evidence` — the last meaning the address WAS observed, nothing fired, and not enough of its history was examined for that to rule anything out. `checks` carries every check with its `threshold`, what was `observed`, its own `basis`, and `conclusive`: a check that FIRED is always conclusive (a pattern seen in a sample happened), a check that did not fire on a truncated scan is not. `note` says the same thing in a sentence and is derived from the two.","recent":"A slice of its activity, `recent_limit` rows (default 5). Each row also carries `is_self_payment` (payer = payee, annotated rather than only excluded — a third of the feed can be self-payment) and `evidence`: `{ verified, method, probability, tier }`. `verified` is THREE-VALUED — `true` a named facilitator relayed it, `false` examined with no EIP-3009 marker, and `null` gasless and authorized but NOT ESTABLISHED as x402, which is not the same as disproven. `tier` is `proven` | `probable` | `unproven` | `not_x402` | `ungraded`."}},{"method":"GET","path":"/v0/agents","url":"https://preview-api.roundhouseai.io/v0/agents","operation_id":"listAgents","tag":"Entities","summary":"Indexed ERC-8004 agents","auth":"none","payment":"none","params":[{"name":"limit","in":"query","required":false,"type":"integer","description":"Rows to return. Clamped to 100; values outside the range are clamped, not rejected.","default":50},{"name":"before","in":"query","required":false,"type":"string","description":"Opaque cursor from a previous response's `next_before`. It encodes the whole sort key, tiebreaker included, so a page boundary inside a group of rows sharing a timestamp does not skip the rest. Do not parse it or build one by hand."},{"name":"q","in":"query","required":false,"type":"string","description":"Free-text search over name and description."},{"name":"x402_declared","in":"query","required":false,"type":"string","description":"Set to `true` to keep only agents whose own ERC-8004 registration card declares x402 support (`x402Support: true`). This is the agent's CLAIM ABOUT ITSELF, copied out of a document its owner wrote, and it is not evidence of anything: four of five agents on a page of this filter had `last_activity_at: null`, `activity_status: \"unknown\"` and no feedback — never observed doing anything. It was named `x402` and documented as \"observed on-chain x402 payments\", which is the one confusion this index exists to prevent. The old name still works and means the same thing. There is no observed equivalent yet: `agents` carries no per-wallet settlement column, and the weekly activity scan that would populate one covers five agents a run against tens of thousands, so a filter built on it would answer \"not observed\" about agents nobody has looked at. To screen on evidence today, take the `agent_wallet` and call `get_agent` / `list_entity_settlements` with it.","values":["true"]},{"name":"x402","in":"query","required":false,"type":"string","description":"The earlier name for `x402_declared`, kept because it is published and a caller sending it wants the same rows. Prefer `x402_declared`, whose name says what it reads. Sending both uses `x402_declared`.","values":["true"]}],"returns":{"agents":"Agent rows with identity, wallet and the ERC-8004 `score` (null where the registry holds no feedback). `x402_support` is the registration card's own declaration; `last_activity_at` and `activity_status` are the observed halves, derived from settlements and feedback.","next_before":"Cursor for the next page, or `null`.","meta":"The chain served and `as_of`. On an EMPTY list it also carries `dataset_last_written_at`, `dataset_row_count` and any `warnings` (e.g. `endpoints_catalog_stale`) — so a zero can be read as a quiet market or a stalled ingest rather than guessed at."}},{"method":"GET","path":"/v0/wallets","url":"https://preview-api.roundhouseai.io/v0/wallets","operation_id":"listWallets","tag":"Entities","summary":"Every address the index has seen","auth":"none","payment":"none","params":[{"name":"limit","in":"query","required":false,"type":"integer","description":"Rows to return. Clamped to 100; values outside the range are clamped, not rejected.","default":50},{"name":"before","in":"query","required":false,"type":"string","description":"Opaque cursor from a previous response's `next_before`. It encodes the whole sort key, tiebreaker included, so a page boundary inside a group of rows sharing a timestamp does not skip the rest. Do not parse it or build one by hand."},{"name":"role","in":"query","required":false,"type":"string","description":"Restrict to one observed role.","values":["agent","merchant","both","unknown"]}],"returns":{"wallets":"Wallet rows with role and last seen.","next_before":"Cursor for the next page, or `null`.","meta":"The chain served and `as_of`. On an EMPTY list it also carries `dataset_last_written_at`, `dataset_row_count` and any `warnings` (e.g. `endpoints_catalog_stale`) — so a zero can be read as a quiet market or a stalled ingest rather than guessed at."}},{"method":"GET","path":"/v0/leaderboard","url":"https://preview-api.roundhouseai.io/v0/leaderboard","operation_id":"getLeaderboard","tag":"Entities","summary":"ERC-8004 agents by registry feedback score","auth":"none","payment":"none","params":[{"name":"limit","in":"query","required":false,"type":"integer","description":"Rows to return. Clamped to 100; values outside the range are clamped, not rejected.","default":50},{"name":"offset","in":"query","required":false,"type":"integer","description":"Rows to skip. Use the returned `next_offset`.","default":0},{"name":"include_spam","in":"query","required":false,"type":"string","description":"Set to `1` to include agents with no feedback, which are hidden by default.","values":["1"]}],"returns":{"leaderboard":"Agent rows in score order, unscored last.","next_offset":"Offset for the next page, or `null`.","meta":"The chain served, `as_of`, and `page_scored_agents` — rows on this page with a non-null score. A `no_agent_on_this_page_has_a_score` warning when that is zero."}},{"method":"GET","path":"/v0/agents/{agentId}/feedback","url":"https://preview-api.roundhouseai.io/v0/agents/{agentId}/feedback","operation_id":"getAgentFeedback","tag":"Entities","summary":"ERC-8004 feedback for one agent","auth":"none","payment":"none","params":[{"name":"agentId","in":"path","required":true,"type":"string","description":"ERC-8004 agent id."},{"name":"limit","in":"query","required":false,"type":"integer","description":"Rows to return. Clamped to 100; values outside the range are clamped, not rejected.","default":50},{"name":"chain","in":"query","required":false,"type":"string","description":"CAIP-2 chain id. An agent id is not unique across chains."}],"returns":{"agent_id":"The id queried.","chain":"The chain the feedback was read from.","feedback":"Feedback entries.","meta":"The chain served and `as_of`. On an EMPTY list it also carries `dataset_last_written_at`, `dataset_row_count` and any `warnings` (e.g. `endpoints_catalog_stale`) — so a zero can be read as a quiet market or a stalled ingest rather than guessed at."}},{"method":"GET","path":"/v0/endpoints","url":"https://preview-api.roundhouseai.io/v0/endpoints","operation_id":"listEndpoints","tag":"Discovery","summary":"Indexed x402 service directory","auth":"none","payment":"none","params":[{"name":"limit","in":"query","required":false,"type":"integer","description":"Rows to return. Clamped to 200; values outside the range are clamped, not rejected.","default":100},{"name":"q","in":"query","required":false,"type":"string","description":"Free-text search over service name, description and resource URL."},{"name":"offset","in":"query","required":false,"type":"integer","description":"Rows to skip. Use the returned `next_offset`.","default":0},{"name":"fields","in":"query","required":false,"type":"string","description":"`compact` returns name, resource, price, network, payTo and liveness only — no description, icon or tags — which is roughly a third of the payload.","values":["full","compact"],"default":"full"}],"returns":{"endpoints":"Catalog rows with price, network, payTo, liveness and observed demand.","next_offset":"Offset for the next page, or `null` when this page was the last.","meta":"The chain served and `as_of`. On an EMPTY list it also carries `dataset_last_written_at`, `dataset_row_count` and any `warnings` (e.g. `endpoints_catalog_stale`) — so a zero can be read as a quiet market or a stalled ingest rather than guessed at."}},{"method":"GET","path":"/v0/unified","url":"https://preview-api.roundhouseai.io/v0/unified","operation_id":"listCapabilities","tag":"Capabilities","summary":"The capability catalogue","auth":"none","payment":"none","params":[{"name":"fields","in":"query","required":false,"type":"string","description":"`compact` returns slug, name, group and unit only — enough to pick a capability, without 34 pairs of IO schemas in the payload.","values":["full","compact"],"default":"full"}],"returns":{"capabilities":"Slug, name, group, priced unit and — on `fields=full` — summary and the canonical input/output fields.","count":"How many capabilities the registry defines.","fields":"Which shape was served: `full` or `compact`.","note":"Where the market figures for one capability live."}},{"method":"GET","path":"/v0/unified/{slug}","url":"https://preview-api.roundhouseai.io/v0/unified/{slug}","operation_id":"getCapability","tag":"Capabilities","summary":"One capability: who offers it, and what it costs","auth":"none","payment":"none","params":[{"name":"slug","in":"path","required":true,"type":"string","description":"Capability slug, from `/v0/unified`."},{"name":"limit","in":"query","required":false,"type":"integer","description":"Rows to return. Clamped to 200; values outside the range are clamped, not rejected.","default":50},{"name":"probable","in":"query","required":false,"type":"boolean","description":"Include endpoints the matcher was not confident about. Off by default: grouping two endpoints that do different jobs invents a cheaper competitor that does not exist.","default":false}],"returns":{"capability":"The registry entry: the job, its priced unit, and its canonical request/response.","price":"Going rate (`spot`), its basis, and the min / p25 / median / p75 / max spread.","stats":"How many offers, distinct providers, distinct hosts, and how many were answering when last probed.","models":"For inference capabilities: which model families are sold here, and by how many providers.","offers":"Each endpoint, its listed price, its index against spot (100 = the going rate), and its reported 30-day demand.","truncated":"True when the candidate read hit its cap, so the offer list is a floor rather than the whole market."}},{"method":"GET","path":"/v0/unified/{slug}/recommend","url":"https://preview-api.roundhouseai.io/v0/unified/{slug}/recommend","operation_id":"recommendCapabilityOffers","tag":"Capabilities","summary":"Shortlist endpoints for a capability","auth":"none","payment":"none","params":[{"name":"slug","in":"path","required":true,"type":"string","description":"Capability slug, from `/v0/unified`."},{"name":"prefer","in":"query","required":false,"type":"string","description":"What to weight: cheapest, most-used, or an even split.","values":["balanced","price","demand"],"default":"balanced"},{"name":"live","in":"query","required":false,"type":"boolean","description":"Only endpoints answering when last probed.","default":false},{"name":"max_price","in":"query","required":false,"type":"number","description":"Hard ceiling in USDC per call. Endpoints with no published price are excluded when set."},{"name":"model","in":"query","required":false,"type":"string","description":"Require the endpoint to name this model family (inference capabilities only)."},{"name":"limit","in":"query","required":false,"type":"integer","description":"Rows to return. Clamped to 25; values outside the range are clamped, not rejected.","default":5}],"returns":{"capability":"The registry entry for the job being shortlisted.","criteria":"Your criteria, echoed — a recommendation whose basis is invisible is an opinion.","price":"The capability's going rate, so each candidate's index has context.","candidates":"Ranked endpoints, each with `reasons` for where it placed.","considered":"How many confident offers were ranked before filters.","excluded":"How many were dropped, by which filter.","truncated":"True when the candidate read hit its cap.","note":"What the ranking is and is not — Roundhouse endorses no provider."}},{"method":"GET","path":"/v0/facilitators","url":"https://preview-api.roundhouseai.io/v0/facilitators","operation_id":"listFacilitators","tag":"Discovery","summary":"Relayers, per chain","auth":"none","payment":"none","params":[{"name":"chain","in":"query","required":false,"type":"string","description":"CAIP-2 chain id. Defaults to the chain this deployment indexes (`eip155:8453` in production, `eip155:84532` on staging) — the same default every endpoint on this API uses. A chain the index does not carry is refused with 400 listing the supported set, never answered with an empty list."},{"name":"limit","in":"query","required":false,"type":"integer","description":"Rows to return. Clamped to 200; values outside the range are clamped, not rejected.","default":25},{"name":"offset","in":"query","required":false,"type":"integer","description":"Rows to skip. Use the returned `next_offset`.","default":0},{"name":"enriched_only","in":"query","required":false,"type":"string","description":"Keep only relayers whose nonce and gas balance have been read. A row without `enriched_at` has nothing populated but its address.","values":["true"]}],"returns":{"chain":"The chain served — this deployment's default when none was named.","facilitators":"Facilitator rows, busiest first. Null-valued fields are omitted.","next_offset":"Offset for the next page, or `null`.","meta":"The chain served and `as_of`. On an EMPTY list it also carries `dataset_last_written_at`, `dataset_row_count` and any `warnings` (e.g. `endpoints_catalog_stale`) — so a zero can be read as a quiet market or a stalled ingest rather than guessed at."}},{"method":"GET","path":"/v0/graph","url":"https://preview-api.roundhouseai.io/v0/graph","operation_id":"getGraph","tag":"Discovery","summary":"Payment graph: nodes and payer→payee edges","auth":"none","payment":"none","params":[{"name":"window","in":"query","required":false,"type":"integer","description":"Row count back from the live tip. Mutually exclusive with `days` — sending both is refused with 400, not silently resolved in favour of one.","default":500},{"name":"days","in":"query","required":false,"type":"integer","description":"Whole UTC days, excluding today, honoured exactly: `days=1` is one day. Mutually exclusive with `window`. Out of `[1,180]` is refused rather than clamped."},{"name":"wallet","in":"query","required":false,"type":"string","description":"Restrict to edges touching this wallet. Must be a well-formed EVM address (case-insensitive) or Solana address (base58, case-sensitive)."},{"name":"chain","in":"query","required":false,"type":"string","description":"CAIP-2 chain id. Defaults to the chain this deployment indexes (`eip155:8453` in production, `eip155:84532` on staging) — the same default every endpoint on this API uses. A chain the index does not carry is refused with 400 listing the supported set, never answered with an empty list."},{"name":"via","in":"query","required":false,"type":"string","description":"Restrict to settlements relayed by this facilitator key or wallet."},{"name":"min_usd","in":"query","required":false,"type":"number","description":"Drop settlements below this USD value."},{"name":"min_probability","in":"query","required":false,"type":"number","description":"Minimum x402 confidence, in `[0,1]`. Default 0.5.","default":0.5},{"name":"edges","in":"query","required":false,"type":"integer","description":"Hard cap on edges returned, highest volume first. Honoured exactly; `truncated` says whether it bit."}],"returns":{"applied":"Every parameter as APPLIED, not as requested — both windows, the edge cap, the facilitator caps, the chain, the filters. Read this rather than assuming the request was honoured verbatim. `days` and `window` can BOTH be non-null: a wallet-focused day window is read row by row, so a row cap applies inside the declared range and `complete` says whether it bit.","complete":"Whether the declared window was read in full. False means a row cap stopped the read inside the range — the response then covers the newest `applied.window` settlements in it, not the whole of it, and `meta.warnings` says so. Only `?days=` with `?wallet=` can be false.","days":"The whole-UTC-day window applied, or `null` on the `window=N` path.","since":"Inclusive start of the day window, or `null` on the `window=N` path.","until":"Exclusive end of the day window (midnight today), or `null` on the `window=N` path.","since_day":"The `since` bound as a UTC date, or `null`.","until_day":"The `until` bound as a UTC date, or `null`.","mode":"`daily` when the answer came from the per-day rollup, which covers the window exactly; `raw` when it was folded from settlement rows.","bucket":"What one source row represented: `day` under `mode: daily`, `settlement` under `raw`.","truncated":"True when the edge cap dropped edges the window contained.","nodes":"One entry per wallet: role, display name, ENS, and its window totals.","edges":"Aggregated payer→payee pairs with volume and count.","series":"Per-day bucket totals across the window. `null` on the `window=N` path, which is a row count with no day grain to bucket into.","facilitators":"Relayers seen in the window, largest first and capped to `applied.facilitators`. `usd`/`count` are the relayer's own window totals; `links` name only wallets present in `nodes`, capped to `applied.facilitator_links`, with `links_total` and `links_truncated` beside them.","window":"How the window was read: rows requested, fetched and used.","meta":"The chain served, `as_of`, and `excluded` — the self-payments, low-confidence rows, over-cap edges and over-cap facilitators the window held but the graph does not show, so node and edge totals can be reconciled. `below_min_usd` is `null` when `min_usd` was sent: that filter runs at read time, so its exclusions never reach this response and cannot be counted."}},{"method":"GET","path":"/v0/health/coverage","url":"https://preview-api.roundhouseai.io/v0/health/coverage","operation_id":"getCoverage","tag":"Discovery","summary":"Per-dataset freshness — is a zero real?","auth":"none","payment":"none","params":[],"returns":{"as_of":"When the report was taken.","chain":"The chain this deployment indexes.","datasets":"One entry per dataset: `last_written_at`, `row_count`, `indexer_lag_seconds`, the `stale_after_seconds` threshold and the `stale` verdict. A null `row_count` means the probe failed and is NOT zero.","warnings":"The datasets currently over their staleness threshold, by name."}},{"method":"POST","path":"/v0/telemetry","url":"https://preview-api.roundhouseai.io/v0/telemetry","operation_id":"ingestTelemetry","tag":"Telemetry","summary":"Submit client-reported x402 call telemetry","auth":"optional","payment":"none","params":[],"returns":{"ok":"Always true on a stored batch.","accepted":"Records written. A record already held counts here too.","invalid":"Records the schema rejected. Reported, never fatal to the batch.","truncated":"Records dropped for exceeding the per-request cap."}},{"method":"POST","path":"/v0/sql","url":"https://preview-api.roundhouseai.io/v0/sql","operation_id":"runSql","tag":"SQL","summary":"Read-only SQL over the public dataset","auth":"optional","payment":"none","params":[],"returns":{"rows":"Result rows.","row_count":"Number of rows returned.","duration_ms":"Server-side execution time. This is what the call is priced on.","authenticated":"Whether a key was accepted.","tier":"The CREDENTIAL: `anonymous`, `trial`, or an organization tier.","sql_tier":"The QUERY: which ceiling this run was given and priced at. A separate field from `tier`, which has always meant the credential.","quote":"What the query was quoted at before it ran: the tier's ceiling and price bounds, plus the planner's own `planner_cost` and `plan_rows`. The planner figures are ADVISORY — arbitrary units, uncalibrated against this schema — and never charge anything; settlement is from measured duration, which needs no calibration to be right.","usage":"What this call cost and what is left. `units_charged` is SETTLED, not the hold: the tier maximum is taken up front and the unused part released once the duration is known."}},{"method":"POST","path":"/v0/mcp","url":"https://preview-api.roundhouseai.io/v0/mcp","operation_id":"mcp","tag":"MCP","summary":"Model Context Protocol endpoint","auth":"optional","payment":"none","params":[],"returns":{"jsonrpc":"Always `2.0`.","id":"The request's own id, echoed.","result":"On a `tools/call`: `content` (one text part carrying the endpoint's JSON response verbatim) and `isError`. A failed read sets `isError` and keeps the reason — it is never an empty result.","error":"Present instead of `result` when the caller cannot proceed: `-32001` no or bad API key, `-32002` a paid plan or an x402 payment is needed (the requirements are in `data.body`), `-32003` rate limited (`data.retry_after` seconds), `-32601` unknown method, `-32602` unknown tool or argument."}},{"method":"GET","path":"/v0/kya/{digest}","url":"https://preview-api.roundhouseai.io/v0/kya/{digest}","operation_id":"getAttestation","tag":"KYA","summary":"Read one KYA attestation","auth":"none","payment":"none","params":[{"name":"digest","in":"path","required":true,"type":"string","description":"sha256 of the canonical attestation, hex with `0x`."}],"returns":{"attestation":"The canonical document.","signature":"The signature over it, and which method recovered.","settlement":"The settlement whose `authorization.nonce` committed to this digest, if one is indexed."}},{"method":"GET","path":"/v0/kya/agents/{wallet}","url":"https://preview-api.roundhouseai.io/v0/kya/agents/{wallet}","operation_id":"listAgentAttestations","tag":"KYA","summary":"Attestations by one signer","auth":"none","payment":"none","params":[{"name":"wallet","in":"path","required":true,"type":"string","description":"EVM address. Case-insensitive — it is lowercased before lookup. A Solana address is refused with `unsupported_chain_family`: attestations are EVM signatures."},{"name":"limit","in":"query","required":false,"type":"integer","description":"Rows to return. Clamped to 100; values outside the range are clamped, not rejected.","default":50}],"returns":{"wallet":"The signer queried.","attestations":"Published attestations, newest first."}},{"method":"POST","path":"/v0/kya/verify","url":"https://preview-api.roundhouseai.io/v0/kya/verify","operation_id":"verifyAttestation","tag":"KYA","summary":"Verify an attestation without storing it","auth":"none","payment":"none","params":[],"returns":{"valid":"Whether the signature recovered to `attestation.agent`.","digest":"The memo — sha256 of the canonical document.","method":"`eip712` or `personal_sign`, whichever recovered."}},{"method":"GET","path":"/v0/queries/{slug}/results","url":"https://preview-api.roundhouseai.io/v0/queries/{slug}/results","operation_id":"runSavedQuery","tag":"x402","summary":"Run a saved query — pay per call, or use an API key","auth":"optional","payment":"none","params":[{"name":"slug","in":"path","required":true,"type":"string","description":"The query's slug, from its page at `/queries/<slug>`."}],"returns":{"query":"The slug and name of what ran.","parameters":"The values it ran at, after defaults were applied.","columns":"Column names, in order.","rows":"The result rows.","row_count":"How many rows came back.","row_cap":"The server's ceiling, so a full page is distinguishable from a truncated one.","truncated":"`true` when the answer hit `row_cap`.","paid_with":"`x402`, `api_key` or `trial_key`.","settlement":"The transaction hash, when the call was paid over x402."}},{"method":"GET","path":"/v0/test/x402","url":"https://preview-api.roundhouseai.io/v0/test/x402","operation_id":"testX402","tag":"x402","summary":"x402 test drive — pay $0.01, get an API key","auth":"none","payment":"x402","params":[],"returns":{"paid":"`true` once settled.","api_key":"The key, returned exactly once — only its hash is stored.","tier":"The key's tier.","expires_at":"When the key stops working.","limits":"Per-minute query limits for this key and for anonymous callers.","payment":"Network, payer, payTo, asset, amount, transaction hash and facilitator."}},{"method":"GET","path":"/v0/topup","url":"https://preview-api.roundhouseai.io/v0/topup","operation_id":"topupQueryUnits","tag":"x402","summary":"Top up Query Units over x402","auth":"optional","payment":"none","params":[{"name":"usd","in":"query","required":false,"type":"string","description":"Amount in USD, up to two decimals. Min $1, max $1,000, default $10; anything unreadable falls back to the default rather than guessing upward."}],"returns":{"paid":"`true` once settled and credited.","credited_qu":"Query Units credited by this settlement.","new_balance":"The organization's balance after the credit.","rate_qu_per_usd":"The published conversion rate.","payment":"Network, payer, payTo, asset, amount, transaction hash and facilitator."}}]}