ASPERN MCP
An MCP server providing autonomous capital intelligence, including counterparty checks for agent addresses, vault due-diligence data, and agent discovery based on on-chain records rather than claims.
Endpoint and access
No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.
https://aspern.org/mcpWhat its tools do
44 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.
Read-only (24)
identifycheck_service_endpointpreflight_paymentvault_assessmentvault_exitsportfolio_screencapital_coverageexit_lifecycle_coverageendpoint_coveragepayment_evidenceevidence_forevaluate_actionendpoint_disagreementslist_x402_endpointslist_policiescounterparty_checkvault_checkfind_vaultsoperator_checklist_vaultsfind_agentsvaults_earnedagent_changesreceipt_status
Can change data (20)
vault_risk_dimensionsevaluate_vault_decisionsimulate_withdrawaltest_purchaseknock_on_endpointvault_sanctions_screeningvault_due_diligencevault_driftcounterparty_evidencecounterparty_historycounterparty_bulkagent_dependenciesagent_businessagent_economic_profileexit_screenvault_exit_confidencecheck_actioninspect_paymentrecord_receiptmonitor_subject
Checks and signals
Each line is separate evidence, not a trust score.
- Passed Endpoint answered the live check ·
- Passed Official MCP Registry namespace is tied to aspern.org ·
- Passed Endpoint is on the registry namespace domain ·
- Availability 1 of 1 checks passed in 30 days
What you can do with it
- Check counterparty risk for an agent address
- Find agents by their on-chain record
- Retrieve vault drift and due-diligence data
- Access agent identity and trust evidence
Source and maintenance
- Maintenance checked
Protocol and capabilities
- Server
- aspern 1.3.0
- MCP protocol negotiated in check
- 2025-06-18
- Tools
- 44 advertised
- Resources
- Not advertised
- Resource templates
- Not advertised
- Prompts
- Not advertised
{
"tools": {}
}Server-provided instructions
Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.
Tools
44 advertised
identify — Identify the party behind an identifier
Call this FIRST whenever you hold something other than a wallet address. Give it a transaction hash, an http(s) URL, a hostname or an ERC-8004 registration number and it says which party that is, with the evidence for the link, so the other tools here can then be called with the address. It never picks between candidates: where a hostname or id matches several parties, `address` comes back null and every candidate is listed, because choosing one would be an identification the evidence does not support. A link through a hostname or a declared endpoint is the subject’s own claim, never proof that they control it. An identifier it cannot resolve is not evidence of anything wrong — read `says`, which distinguishes "no such thing" from "we do not read that chain". Free.
{
"type": "object",
"required": [
"q"
],
"properties": {
"q": {
"type": "string",
"description": "What you have: an address (returned as given), a transaction hash (the payee is read from the transfer inside it), a URL or hostname (matched against the x402 catalogue and declared agent endpoints), or an ERC-8004 registration number, optionally as `chain:id`."
}
}
}{
"type": "object",
"required": [
"says"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "The answer in words, including the difference between \"no such thing\" and \"we do not read that chain\"."
},
"check": {
"type": [
"string",
"null"
],
"description": "The free check to call next with the address."
},
"given": {
"type": "string",
"description": "What you sent, as sent."
},
"kinds": {
"type": "object",
"description": "What each reading would have meant, so an unrecognised identifier is not a dead end."
},
"format": {
"type": "object",
"description": "What kind of address it is and what we read for it."
},
"address": {
"type": [
"string",
"null"
],
"description": "The party, or null where the evidence names more than one. Never a guess between candidates."
},
"evidence": {
"type": "array",
"items": {
"type": "object"
},
"description": "Why each link is claimed, and whether it is the subject’s own claim or something we read."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"looksLike": {
"type": "string",
"description": "What the identifier was read as: address, transaction, endpoint, host, agent-id or unrecognised."
},
"candidates": {
"type": "array",
"items": {
"type": "object"
},
"description": "Every party the identifier could be, where it is not one."
}
}
}{
"title": "Identify the party behind an identifier",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}check_service_endpoint — Check a service endpoint before connecting
Call this before connecting to a service. Give it the http(s) URL of an MCP or A2A endpoint and it says who declares it, whether our daily probe reached it, what it answered with, and how many of the last readings answered. It keeps three things apart and never merges them: what an identity DECLARES the endpoint offers, what a probe OBSERVED, and the readings behind that. Read `drift` where present — tools declared but not answering is the signal that an endpoint has changed under the people relying on it. It also answers the three things a buyer wants BEFORE calling a paid endpoint, as separate fields and never folded into one number: `price` is the seller’s own published amount, asset, network and payee; `handshake` says whether it answered without credentials, which is the nearest observable thing to “can I try it”; and `measured` is how many of how many days answered and how slow it was, which is what we read rather than a guarantee anybody made. `manifest` and `changes` carry the series: a registration says what an agent offers now, and the readings say what it offered LAST WEEK. Store `manifest.hash` and compare it next time to detect an endpoint that changed under you. Two things this is NOT: an unreachable endpoint is an availability fact and never evidence of bad faith (weigh `latest` against `history`, since one bad day and a dead service look identical in a single reading), and an endpoint we have never probed is outside our reading rather than absent from the world. For a CATALOGUED x402 resource that no agent registered, read `unpaidProbe`: our own unpaid 402 handshake, which is free for both sides and says whether the endpoint is still there and what it charges now against a catalogue entry that may be months old. Free.
{
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"description": "The absolute http(s) URL of the service endpoint you are about to connect to."
}
}
}{
"type": "object",
"required": [
"says"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "The reading in one sentence. An endpoint we have never probed says so here: that is our gap, not evidence the service is down."
},
"drift": {
"type": [
"object",
"null"
],
"description": "Declared but not observed, and the reverse. Null where either side is unknown — subtracting silence would manufacture a finding."
},
"price": {
"type": [
"object",
"null"
],
"description": "The seller’s OWN published terms for this URL: `amount` in the asset’s own units with `asset` named beside it (a bare number would be read as dollars), `network` as CAIP-2, `payTo`, which catalogues list it, and `cardPriceUsd` where the registration states a price too. Null throughout is UNPRICED, which is not free. Where the card and the catalogue disagree, both are shown — two claims by the same party, and picking one would hide that."
},
"latest": {
"type": [
"object",
"null"
],
"description": "The most recent probe: outcome, latency, and what it answered with."
},
"changes": {
"type": "array",
"items": {
"type": "object"
},
"description": "Every move we saw in what it offers, oldest first, each with `added`, `removed`, a protocol-version move where there was one, and the two hashes. `at` is the day we SAW the change, not the day it happened: we probe daily and cannot place it more precisely. An empty array where we read a list and it never moved; a tool list we could NOT read is skipped entirely rather than folded in as an empty one, which would invent a “removed everything” out of our own gap."
},
"history": {
"type": "array",
"items": {
"type": "object"
},
"description": "Up to fourteen readings. One bad day and a dead service look identical in a single reading."
},
"endpoint": {
"type": "string",
"description": "The URL as we read it back, so a typo is visible before anything is concluded from the answer."
},
"manifest": {
"type": [
"object",
"null"
],
"description": "What the endpoint offers NOW, as something you can bind to: `hash` over the sorted tool names and the declared protocol version and nothing else, with `tools`, `firstSeen`, `lastSeen` and how many readings carried it. Store the hash and compare it on your next call to know whether the offering moved under you. Null where we have never read a tool list here."
},
"measured": {
"type": "object",
"description": "What we measured, which is NOT a guarantee: nothing on these rails publishes one. `serving` of `days`, every outcome by how often, and median and worst latency of the readings that answered. Left as counts with the denominator stated, never a rate and never a grade — 12 of 14 over two weeks means something different from the same fraction over two days, and only the caller can pick the denominator that matters to them."
},
"outcomes": {
"type": "object",
"description": "What each outcome word means, so a caller never has to guess."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"handshake": {
"enum": [
"open",
"gated",
"no-answer",
null
],
"type": [
"string",
"null"
],
"description": "Whether the protocol HANDSHAKE completed without credentials on the latest reading. `open` is the closest thing here to “you can try it” and is NOT a statement that calling its tools is free — a server can answer openly and charge for every call, and we complete a handshake rather than call a tool. `gated` means it asked for something we do not have, which is working as intended. Null where we have no reading."
},
"declaredBy": {
"type": "array",
"items": {
"type": "object"
},
"description": "Identities that declare this endpoint, and the tools each one claims. Their claim, not our verification."
},
"unpaidProbe": {
"type": [
"object",
"null"
],
"description": "OUR OWN unpaid x402 handshake with this URL, which is the free availability test for an endpoint no agent registered. x402 requires a server to answer an unpaid request with 402 and its requirements BEFORE doing any work, so the first half of every real client’s handshake costs nothing and proves three things: the host is there, it still speaks x402, and what it asks for NOW. `outcome` is one of quoting, open, refusing, gone, error, unreachable — `open` means it answered 200 unpaid, so it is up and not charging on this path. `quoted.amount` is in the asset’s own smallest unit and is NOT scaled to dollars, because nothing in the requirements document says how many decimals the asset has. Read it beside `price`, which is what the catalogue recorded whenever that entry was written; a price that has moved is a finding neither one shows alone. Null means we have not run this probe here, which is not a finding about the endpoint."
}
}
}{
"title": "Check a service endpoint before connecting",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}preflight_payment — Weigh one payment before sending it
Call this immediately before sending a payment, every time — not once per counterparty. It weighs THIS payment (the amount, the chain, the endpoint) against what the address has actually done: the price the seller themselves published for that endpoint, the address that endpoint names as its payee, the largest payment this address has ever received, and whether it has ever been paid on the chain you are about to use. Answers one of three verdicts. `nothing-against-it` means every check ran and none objected — it is NOT a statement that the payment is safe, because nothing here can see what the payment is for or what you agreed. `look-first` means at least one thing we could READ does not match, and the findings say which; a check that could not run never produces it, it produces `cannot-say`. `cannot-say` means we did not read enough to have an opinion, and must never be read as the first. Free. Give as much of amountUsd, chain and resource as you have: each one left out is a check that did not run, and the answer says so rather than passing. THE ENDPOINT’S OWN WORD OUTRANKS THE CATALOGUE: where we have probed it, branch on `againstEndpoint.price.againstQuote`, and **`below-quote` is not a pass**. Each named finding (`live-price-differs-from-listing`, `quoted-payee-is-not-the-one-being-paid`, `below-listed-price`, `network-not-comparable` and the rest) is explained under `findings`.
{
"type": "object",
"required": [
"to"
],
"properties": {
"to": {
"type": "string",
"description": "The address you are about to pay."
},
"chain": {
"type": "string",
"description": "The chain you are about to send it on, e.g. base, polygon, solana."
},
"resource": {
"type": "string",
"description": "The http(s) URL of the endpoint you are buying, where there is one. This is the sharpest check available: its catalogue entry carries the seller’s own price and their own payee address."
},
"amountUsd": {
"type": "number",
"description": "What you are about to send, in US dollars."
}
}
}{
"type": "object",
"required": [
"verdict",
"observations",
"says"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "The verdict in one sentence, naming what did not match. Written to be shown to a person deciding whether to go ahead."
},
"check": {
"type": [
"string",
"null"
],
"description": "The free counterparty check for this address."
},
"limits": {
"type": "string",
"description": "What this check cannot establish, in full. Quote it whenever you quote the verdict: a clean answer here is the absence of an objection, never a guarantee about the payment."
},
"listing": {
"type": [
"object",
"null"
],
"description": "The seller’s own catalogue entry for the resource, where there is one — their word, written once, possibly months ago."
},
"verdict": {
"enum": [
"nothing-against-it",
"look-first",
"cannot-say"
],
"type": "string",
"description": "`nothing-against-it` means every check ran and none objected — NOT that the payment is safe. `cannot-say` means too little was read to have an opinion, and must never be read as the first. It is also the answer when nothing contradicted the payment but a check could not run, because reporting our own gap as a clean result would be the worst of the three."
},
"evidence": {
"type": "object",
"description": "The common payment record, the same object as on inspect_payment: `listedPayee` from the seller’s catalogue entry, `onChainRecipient` (the address you are about to pay), `recipientIsListedPayee` (false is a finding, null means one side is missing), `facilitator`, `settlement` (`unknown`: nothing has been sent), `delivery` (`unevidenceable` on x402) and `says`."
},
"findings": {
"type": "array",
"items": {
"type": "object"
},
"description": "Every check that ran, each with the record behind it and its own `tone` and `kind`. NOT only the ones that objected: a `tone` of `good` is a check that MATCHED, so reading this array’s length as a count of problems overstates them. Bucket on `kind` — `fact` and `signal` were established, and `unknown` means we could not see, which never counts as something standing against the payment. FINDINGS WORTH KNOWING BY NAME. `live-price-differs-from-listing` means the price has MOVED and the live figure is the one to pay. `quoted-payee-is-not-the-one-being-paid` means the endpoint asks to be paid somewhere other than where you are sending. `live-price-not-comparable` means the two prices are in different tokens or one is unnamed, so they were NOT compared rather than compared and found to agree. `below-listed-price` means the payment is under half the seller’s published price: ordinary, not an objection, and NOT a confirmation of the amount either, because the listing may price a different unit or tier; it is `neutral` and cannot improve the verdict. Paying less than the CATALOGUE’s price is a different question from `below-quote` and stays `neutral`, because a directory entry may price a different unit and was written once. `network-not-comparable` means the listing names a network we could not resolve, so the chains were not compared rather than compared and found to agree. A CAIP-2 id and a chain name are resolved through one shared map, so `eip155:8453` and `base` are the same network here and in check_action; they used to disagree."
},
"proposal": {
"type": [
"object",
"null"
],
"description": "Your payment as we understood it: payee, amount, chain and resource. Null where too little was given to form one. Check it before reading the verdict, which is about THIS proposal and no other."
},
"received": {
"type": "object",
"description": "What you told us, read back, so a typo is visible."
},
"verdicts": {
"type": "object",
"description": "What each verdict word means, so a caller never has to guess."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"observations": {
"type": "array",
"items": {
"type": "object"
},
"description": "Every check that ran, including the ones that could not: a field you left out appears here as a check that did not run, rather than as silence."
},
"againstEndpoint": {
"type": [
"object",
"null"
],
"description": "WHAT THE ENDPOINT ITSELF DEMANDED, from the 402 it answered our own unpaid request with, and how that compares. `price.againstQuote` is the one word to branch on — `exact-match`, `below-quote`, `above-quote`, `unknown` — and `below-quote` is NOT a pass: the endpoint stated what it wants for this call and less than that does not buy it unless its terms allow a partial payment, which we cannot see. `price.evidence` ranks the three parties who can state this endpoint’s price — the endpoint’s own quote first, the catalogue second, what the address has actually been paid third — and `price.lead` names the one to act on. `price.disagreements` keeps every conflict between them with BOTH figures: a price that has moved is a finding neither source alone can show, and we never resolve it into a single number. `price.liveQuoteMatchesCatalogue` is true, false, or NULL FOR NOT COMPARABLE, which is not the same as not matching — a 402 states an amount in the asset’s smallest unit and never its decimals, so two raw figures are only the same kind of number when the token is the same; `liveQuoteMatchesCatalogueWhy` says which half is missing. `quoted` is false where we have never probed the endpoint, which is our gap and not evidence against the payment. `below-quote` is a REVIEW and never a refusal."
}
}
}{
"title": "Weigh one payment before sending it",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}vault_assessment
THE HASH-COMMITTED ASSESSMENT OF A VAULT, in the shape ERC-8004’s Reputation Registry points at, and the only thing here a third party can publish on chain under their own name. Until 2026-10-21 this document was reachable from the API and the web and from NO tool, so an agent could read every figure behind it and not the document itself. Each dimension is its own score and they are never blended: a vault can have an excellent record and a poor provenance grade, and one number would destroy the distinction this platform exists to draw. `data.document` is what the hash covers, byte for byte, and `data.feedbackHash` is that hash. READ `data.document.refusals` BEFORE ANY MISSING DIMENSION: from aspern-0.4 an absent dimension is UNREAD unless a refusal names it, and a refusal means we hold a reading we will not stand behind — a liquidity share over 100% of the vault, for instance, which is a disagreement between two of our own readings rather than a fact about the vault. `data.alsoPublished` sits deliberately OUTSIDE the hash: the risk band, the lending figures and the books reconciliation are ASPERN’s own reading, and a consumer attesting the hash is attesting the document alone. `data.erc8004` carries the call arguments for whoever wants to publish it; ASPERN does not make those calls and never holds a mainnet key. Ask for an older `methodology` to rebuild a document at the version a hash was issued under; the list is on /v1/meta. Free.
{
"type": "object",
"required": [
"slug"
],
"properties": {
"slug": {
"type": "string",
"description": "The vault’s slug, or its contract address — either resolves."
},
"methodology": {
"type": "string",
"description": "Build the document at an older methodology version, so a hash issued under it stays checkable. Omit for the current one. THIS IS THE DOCUMENT’S OWN VERSION and not `meta.methodologyVersion`, which stamps every answer this API serves and moves for different reasons; the two were one number until 2026-10-21 and will drift apart."
}
}
}{
"type": "object",
"properties": {
"data": {
"type": [
"object",
"null"
],
"description": "The document and everything published beside it: `document` (what the hash covers, with `assessments`, `observations`, `refusals` and `limitations`), `feedbackHash`, `alsoPublished` (our own risk band, lending figures and books reconciliation, deliberately OUTSIDE the hash) and `erc8004` (the call arguments, one giveFeedback per dimension). Null where no document could be built."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"slug": {
"type": "string",
"description": "The vault this document is for, read back so an answer cannot be attached to the wrong vault."
},
"metric": {
"type": "string",
"description": "Always `assessment` here. The route answers in the availability envelope every per-vault reading uses."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"availability": {
"type": "object",
"description": "Whether a document could be built at all, and when it was read. An assessment rests on a record: where there is not enough of one, `status` says so and `data` is absent, which is a fact about OUR evidence for this vault and not a judgement of the vault."
}
}
}{
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}vault_exits — What actually left a vault
SEND `exitAmountUsd` WITH THE POSITION YOU ARE WEIGHING and this answers whether that size has ever cleared here: the schema has carried it since 2026-10-08, and an auditor still had to find it by guessing because this sentence did not say so. Without it the answer still describes the capacity; with it, `exitCapacity` says `covered-now`, `above-declared` or `unknown` for your amount, and whether a single withdrawal that large has ever been recorded. Before putting capital somewhere, ask what has actually LEFT it. Every liquidity figure elsewhere is a level at an instant — how much could be withdrawn right now. This is the other half: withdrawals that SETTLED, over 7, 30 and 90 days, with the largest single exit we have ever recorded and the day it happened. Measured on the largest vault we read on 29 September 2026: $52.8M declared withdrawable against $265.8M actually withdrawn over thirty days, so a holder reading only the declared figure would badly underestimate what the vault has been able to pay. A LEVEL and a FLOW are never divided by one another and this returns no ratio between them. We see withdrawals that settled and NOT an attempt that reverted, a queue somebody waited in, or a gate that refused them. An empty register therefore has FOUR readings, not two: “nobody withdrew”, “nobody could”, “a reader asked and found nothing at all”, and “no reader has ever asked”. `haveFlowHistory` separates the first two from the last two and `lastReadAt` separates those — null there means nobody has ever looked, so the register is not a weak reading of the vault but the absence of one. `haveFlowHistory` separates the third from the first two, and where it is false every figure is NULL rather than zero. Every answer dates itself: `capitalAsOf` and `declaredAsOf` say when each level was read, `flowsNewestAt` how far our movement history reaches, and `windowsEndAt` the instant the windows count back from. For a capital decision the age of the record is part of the record. Free.
{
"type": "object",
"required": [
"slug"
],
"properties": {
"slug": {
"type": "string",
"description": "The vault’s slug, or its contract address — either resolves. Until 2026-10-08 this route was the one of eight that took the slug only, so an agent holding an address got a 404 here and a 200 everywhere else."
},
"amountUsd": {
"type": "number",
"description": "The same thing as `exitAmountUsd`, under the name the rest of this catalogue uses for a position. Send whichever you have; if both arrive, `exitAmountUsd` is the one read."
},
"exitAmountUsd": {
"type": "number",
"description": "OPTIONAL, AND THE ANSWER IS USEFUL WITHOUT IT: send nothing and `exitCapacity` still describes the capacity — what the vault declares withdrawable right now, the largest single withdrawal ever recorded here, the basis and when it was read — with `amountUsd`, `now`, `everThisLarge` and `shareOfDeclared` null, because those are the three answers that need a position and NOBODY ASKED is not the same as nothing here. Send a position, in US dollars, e.g. 500000, and you get the verdict for THAT amount. Turns \"can we READ the withdrawable figure\" into \"could I get THIS much back out\". The verdict is deliberately named for what was observed rather than for a promise: `covered-now` means the amount is inside what the vault declares withdrawable AT THE MOMENT WE READ IT, and the answer carries when that was and what it cannot see — queues, notice periods, failed attempts, other holders leaving at the same time."
}
}
}{
"type": "object",
"required": [
"slug",
"windows",
"says",
"cannotSee",
"windowsEndAt",
"haveFlowHistory"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "What actually left, against what the vault advertises as withdrawable, in one sentence."
},
"slug": {
"type": "string",
"description": "The vault this register is for, read back so an answer cannot be attached to the wrong vault."
},
"readBy": {
"type": [
"string",
"null"
],
"description": "Which reader last asked, e.g. `vault-flows`, `morpho-flows`, `solana-flows`."
},
"tvlUsd": {
"type": [
"number",
"null"
],
"description": "What the vault holds, for scale beside the withdrawals. A LEVEL, never to be divided into the flow figures in `windows`. Null means unread, not empty."
},
"exitCap": {
"type": [
"object",
"null"
],
"description": "THE CONTRACT ASKED DIRECTLY, ON THE VAULTS THAT DECLARE NOTHING. Present only where `declaredWithdrawableUsd` is null, which is where, on 26 October 2026, 33 Concrete vaults holding $1.26bn and the ether.fi Liquid vaults sat: a strategy vault deploys its capital rather than holding it, so there is no free balance to read and the venue publishes no withdrawable figure. We call `maxWithdraw` on the LARGEST HOLDER WE KNOW — it is per owner, so asked about an address holding no shares it answers zero for every vault in existence — and compare it with what that holder’s shares are worth. READ `verdict` AND `mechanic` BEFORE ANY NUMBER HERE, because ONE SELECTOR ANSWERS TWO DIFFERENT QUESTIONS. On a SYNCHRONOUS vault `maxWithdraw` is capacity: `capped` means it returned LESS than the holding, so something binds and `maxWithdrawUnits` is where; `shut` means it returned zero to a holder who demonstrably has shares, which is a finding; `echoes-balance` means it returned the holding itself, so the selector carries NO liquidity cap and the figure is arithmetic about that holder rather than a reading of the vault, published as a refusal with `maxWithdrawUsd` null. On an ASYNCHRONOUS vault (ERC-7540, request-and-claim, request-via-queue) the SAME selector reports the CLAIMABLE amount from an already settled request instead: `claimable` means that much is set aside for that one holder, and `nothing-claimable` is zero, which is the ORDINARY STATE of somebody who has not asked to leave and says nothing whatever about whether they could. An earlier pass of this reader applied the synchronous meaning everywhere and called 53 vaults shut, among them Lagoon vaults holding $23.3m and $9.6m; that was our error and the verdict now depends on the mechanic. `mechanic-unread` means we could not establish which of the two questions was answered and are claiming neither. `saysSomethingAboutExit` is the single boolean to gate on and it is true for `capped` and `shut` only. It is ONE HOLDER at ONE BLOCK, never a vault-wide total and never a promise; `raw` carries the three words so the verdict can be re-derived rather than believed."
},
"windows": {
"type": "array",
"items": {
"type": "object"
},
"description": "FLOWS over 7, 30 and 90 days plus all time: count, total, largest single, most recent. Never a share of the level. Every figure is NULL where `haveFlowHistory` is false, because a zero would claim we looked. Each window carries `window` as well as `days`: `7d`, `30d`, `90d` or `allTime`. `days: 0` is the all-time one and reads as a zero-day window if you only look at the number, which is why the name is there."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"cannotSee": {
"type": "array",
"items": {
"type": "string"
},
"description": "What this register structurally cannot contain, so an empty one is never read as an easy exit."
},
"exitPhases": {
"type": [
"object",
"null"
],
"description": "GETTING OUT HAS THREE STEPS AND THEY ARE NOT THE SAME EVENT: a request FILED, an amount set aside to be CLAIMED, and a withdrawal SETTLED. Each carries its own `basis` and its own refusal. `filed` reads `queue-requests` where the vault leaves through a separate queue - a FLOOR, since a request whose only event predates our window is open and unseen - or `share-burns` where it is read from the token being burned. `claimable` IS USUALLY UNREADABLE AND THAT IS THE FINDING: where a vault takes a request and carries neither ERC-7540 getter, nothing can be seen of the middle step at all and a zero there would be an answer we did not read; where both getters exist it is readable one holder at a time and NO TOTAL is published, because summing a per-holder getter across holders we do not enumerate is a figure nobody could check; a synchronous vault has no middle step to begin with. `settled` is money that LEFT, over the register’s own window, not money that was promised."
},
"kaminoExit": {
"type": [
"object",
"null"
],
"description": "HOW MUCH OF A KAMINO STRATEGY CAN LEAVE TODAY, read from its position and its pool rather than assumed from its size. `measured` false carries `refused`, the reason (a Meteora pool, whose bin layout is not decoded, or an account that did not answer). Measured, `share` is the part that can leave today: the position’s tokens are checked to be in the pool’s own vaults, and the strategy’s daily withdrawal cap per token is applied, so `boundBy` is `withdrawal-cap` where the cap is tighter than the holding. `withdrawableUsd` is the size times that share, less the exit fee, and is the figure the vault’s withdrawable column carries. Null for every vault that is not a Kamino strategy."
},
"lastReadAt": {
"type": [
"string",
"null"
],
"format": "date-time",
"description": "When a reader last ASKED about this vault, found or not — which `flowsNewestAt` cannot tell you. Eighteen withdrawals with the newest eleven months ago is a vault that went quiet OR a vault we stopped reading, and only this separates them. NULL MEANS NO READER HAS EVER ASKED: the vault is outside what our flow readers cover, so an empty register is not a weak reading of it, it is the absence of one."
},
"redemption": {
"type": [
"object",
"null"
],
"description": "HOW a holder gets out, read from the contract, and the question no liquidity figure answers. `mechanic` is `synchronous`, `request-and-claim` (ERC-7540: you ask and are paid when the vault settles it), `request-via-queue` (the vault takes deposits and carries no exit: you approve a SEPARATE queue contract and file a request there for somebody else to fill) or `unread`. `paused` is the contract’s own switch. `hours` is a notice period the contract STATES, null where it states none, which is NOT zero. Same object and same wording as `vault_check` returns."
},
"resilience": {
"type": [
"object",
"null"
],
"description": "HOW THE WAY OUT HELD UP, WHICH IS THE ONLY FIGURE HERE ABOUT A PERIOD. Every other exit reading on this answer is a LEVEL at an instant: how much could be withdrawn now. This is whether that level has BEEN there. `state` is `read`, `too-short` or `nothing-to-read`, and TODAY IT IS `too-short` ON EVERY VAULT: the daily series began on 2026-10-16 and is about six days deep, so `days` says how many we hold and `daysShort` how many more before this answers. It opens by itself as the series grows and needs no release. Thirty days because the windows on this same answer are 7, 30 and 90, and because a worst day out of six reads as though it were the worst of a quarter. Where it is `read`, `worstShare` is the LOWEST daily share of capital that could have left and `medianShare` the middle day, never an average, and a day whose share reads outside 0 to 1 is dropped rather than averaged in. `coveredDays` is null unless a position was named, which is NOT zero. Every figure is what the venue declared that day and never a test of whether it would have paid."
},
"sharesLeft": {
"type": [
"object",
"null"
],
"description": "WHO LEFT, WHERE OUR FLOW REGISTER CANNOT SEE IT, read from the chain. A `Flow` row is written when a reader recognises a venue’s withdrawal, and some venues pay out by BURNING the holder’s shares and emitting nothing we recognise — one $14M vault had 885.30 shares leave in sixty days while the register held six deposits and not one exit. A share burned is a holder leaving, because nothing else destroys supply. `burns` and `shares` are the exits that settled, `shareOfSupply` scales them against what the vault holds in shares TODAY (so it is a scale, not a rate: the two are of different moments). `filed` is requests to leave read in the same window, NULL where the vault has no queue to file one in rather than zero. `oldestWaitingDays` is how long the front of the queue has waited with no payout behind it and `medianSettlementDays` how long a wait that ended actually took — both under ONE assumption which `assumes` states: requests settle in the order filed and one payout settles one. Both are FLOORS, because a request filed before the window is older than this can see. NO DOLLAR FIGURE on purpose: pricing a burn needs the share price at that block and the asset’s price at that moment, and a number built from two interpolations would be the most confident thing in the answer. Published BESIDE the register and never summed into it."
},
"capitalAsOf": {
"type": [
"string",
"null"
],
"format": "date-time",
"description": "When `tvlUsd` was read. Same name and same column as `vault_check` uses."
},
"readToBlock": {
"type": [
"integer",
"null"
],
"description": "How far through the chain that pass actually got. Not the same as `lastReadAt`: a reader that ran a minute ago but is 400,000 blocks behind looked recently at an old part of the chain, and only this says so. Null where the reader has no block to report."
},
"declaredAsOf": {
"type": [
"string",
"null"
],
"format": "date-time",
"description": "When `declaredWithdrawableUsd` was read. NULL means we hold no stamp for it rather than that it is fresh — 180 of the 809 declared levels we hold come from a writer that does not stamp them."
},
"exitCapacity": {
"type": [
"object",
"null"
],
"description": "ALWAYS PRESENT for a vault we hold. `basis` names what it rests on: `observed-liquidity` means a figure the VENUE declared and we read at `declaredAsOf`, and nothing else — it is not a promise, not a test of whether the vault would pay, and not a reading of any queue. WITHOUT `exitAmountUsd` it is the capacity alone: `declaredWithdrawableUsd` and `largestEverUsd`, with `amountUsd`, `now`, `everThisLarge` and `shareOfDeclared` all null because no position was named. WITH one, those four answer the fit. TWO READINGS, NEVER AVERAGED: `now` is `covered-now`, `above-declared` or `unknown` against what the vault declares withdrawable at this instant — THEIR figure, an instant and not a promise, and not a queue or a notice period; `everThisLarge` is whether a single withdrawal at least that size has ever been recorded here, and it is NULL rather than false where we hold no flow history, because never having seen one is not evidence it cannot happen. `shareOfDeclared` is the position as a share of that declared figure, null where there is nothing to divide by. `cannotSee` names what no withdrawable figure contains: queues, notice periods, failed attempts, and other holders leaving at the same moment as you. WHETHER THAT DISAGREEMENT MATTERS TO YOUR POSITION is on `exitCapacity.reconciliationMatters`, nested here and NOT a top-level field: `material` is TRUE where the position you named sits inside the gap between this vault’s two books, so which reading is right decides whether it clears; FALSE where the gap is too small to move it either way; NULL where no amount was sent or the vault declares no capacity to measure one against, and null is NOT a pass. Computed from the amount actually asked about, never a constant: Euler evUSD’s books differ by $14,381, which cannot change a $50 withdrawal and entirely decides one sitting $1,000 under the declared figure. The figures themselves are on `reconciliation`, so a caller can apply its own tolerance."
},
"queueWaiting": {
"type": [
"object",
"null"
],
"description": "HOW LONG THE PEOPLE ALREADY IN THE QUEUE HAVE BEEN THERE, where the vault is left through an AtomicQueue. `openRequests`, `holders`, `oldestWaitingDays`, `medianWaitingDays`, `oldestFiledAt`, `boundedByWindow`, `basis: open-requests-age`. THIS IS NOT A SETTLEMENT TIME AND THERE IS NONE TO HAVE: measured 2026-10-18, the queue emits AtomicRequestUpdated and nothing else over 150,000 blocks, and of 190 request triples only 4 ever reached zero — every one a cancellation with no token reaching the user. A fill leaves no event at this contract, so \"how long does a request take\" cannot be answered and is not. What this answers is the question a depositor is actually standing in front of: how long the holders ahead of them have ALREADY waited. Every figure is a FLOOR, because those holders are still waiting. `boundedByWindow: true` means `oldestWaitingDays` sits against the 90-day log sweep rather than against the queue, so a request filed before it is invisible and the true longest wait may be far longer — never quote a bounded figure as the longest anybody has waited. Null where the vault has no queue we have found, which is not a vault with no queue. HOW THE QUEUE MOVED SINCE THE PREVIOUS READING is on `queueWaiting.movement`, nested here and NOT a top-level field, because one reading cannot say it: `direction` is `clearing`, `growing`, `unchanged` or `first-reading`, `changed` is the difference in open requests and `overHours` the window. IT IS NOT A SETTLEMENT TIME AND MUST NOT BE QUOTED AS ONE: a request that leaves was filled OR cancelled, and this queue emits nothing when one is filled, so the two cannot be told apart from the count. `unchanged` does NOT mean the same requests, since arrivals and departures can cancel out. Null until a second reading exists to compare against."
},
"windowsEndAt": {
"type": "string",
"format": "date-time",
"description": "The instant every window counts back from, so the 7-day and 30-day figures share an edge and either can be reproduced. It is a window boundary and NOT a freshness claim: the ages of the data are `capitalAsOf`, `declaredAsOf` and `flowsNewestAt`, and `meta.freshness` is computed from those."
},
"flowsNewestAt": {
"type": [
"string",
"null"
],
"format": "date-time",
"description": "The newest movement of any kind we hold for this vault."
},
"largestEverAt": {
"type": [
"string",
"null"
],
"format": "date-time",
"description": "When `largestEverUsd` was taken out. An old date beside a large figure is the whole point: it says the exit was possible then, not now."
},
"largestEverUsd": {
"type": [
"number",
"null"
],
"description": "Somebody actually got this much out, on `largestEverAt`. Evidence about the past, never a promise about now."
},
"reconciliation": {
"type": [
"object",
"null"
],
"description": "CASH AGAINST THE BOOKS, AND THE TWO CLOCKS THEY WERE READ ON. `idleUsd` (what is not lent out) and `tvlUsd` (what a depositor has) are written by DIFFERENT JOBS ON DIFFERENT CADENCES, and on 56 open vaults the first reads higher than the second. READ `safeAsCapacity` BEFORE ANY EXIT SHARE ON THIS VAULT: false means a figure built from this pair is arithmetic and not a reading of what could leave, however it is worded elsewhere. `verdict` says which case it is: `consistent` (cash inside the books), `within-rounding` (over by under a hundredth of a point, which is float noise on two sums), `stamps-apart` (over, and the two readings are far enough apart for a deposit or a withdrawal to sit between them — a gap in OUR clocks, not a finding about the vault), `unexplained` (over by more than rounding with no gap in time to account for it), `unreadable` (we hold one figure or neither). `differenceUsd` and `differenceShare` are published UNCLAMPED: a share over one hundred per cent is not a vault that would pay you more than it holds, and rounding an impossible number into a plausible one is the worst of the three available outcomes. `stampsApartMinutes` is how far apart the two readings were, null where we do not hold both."
},
"capacityRefusal": {
"type": [
"object",
"null"
],
"description": "WHY THERE IS NO CAPACITY FIGURE, WHERE THERE IS NONE, and the one field to read before concluding we have a gap. Null wherever capacity IS readable: a declared figure, a `capped` reading or a `shut` door are findings, not absences. Measured over $2.4bn of open vaults with no withdrawable figure, that one blank was FIVE different readings, and `whose` separates them. `the-contract` means the answer does not exist to be read: asking again, or writing another adapter, returns the same thing. `ours` means we have not established it and the work is ours. `neither` means the exit IS read and is simply not on this contract, so nothing is missing at all. `reason` is the specific one. `selector-carries-no-cap`: `maxWithdraw` returned the holder’s whole holding, so no liquidity cap is inside the answer (60 vaults / $391.2M, plus the Concrete group below). `exit-is-not-on-this-contract`: a BoringVault takes deposits through a teller and pays exits through a SEPARATE queue, so the ERC-4626 selector does not exist here and its silence is not a finding (4 vaults / $443.5M, ether.fi Liquid and Veda) — `lookInstead` names the fields on this same answer that do carry it. `a-different-question`: on an async vault the selector reports what a settled request set aside, and zero is the ordinary state of somebody who has not asked to leave (57 vaults / $114.1M). `asked-and-established-nothing` and `never-asked` are both OURS. `postedValue` IS THE STRONGEST READING ON THIS ANSWER and travels independently of `reason`: where the strategy walk ended in `attested-accounting`, the contract the vault deploys into does not derive what it is worth from what it holds — a party POSTS the figure, and `postedBy`, `lastPostedAt`, `validForSeconds` and `expired` are the limits on the POSTING rather than checks on the number. `selectorReturnedThePosting` is TRUE only where what `maxWithdraw` answered IS that posted figure to within rounding, which is verified on 4 of the 8 attested vaults including the $740.3M one, where the two integers agree byte for byte: what a reader would take for exit capacity there is the number one party entered, converted into shares and back. IT IS NEVER FALSE. A figure that does not match may be the strategy’s own decimals or a vault deploying into several strategies while the accounting describes one, so a non-match is OUR uncertainty and is published as null rather than put in the vault’s mouth. `holdsAnyAssetItself` is false where every unit of the vault’s capital is deployed away from it."
},
"haveFlowHistory": {
"type": "boolean",
"description": "Whether we hold ANY movement for this vault, of either direction. FALSE means the empty register is our silence and not the vault’s: it says nothing about whether anybody got out. Of 3,672 open vaults on 2026-10-10, 2,673 are in this state."
},
"snapshotAlignment": {
"type": "object",
"description": "WHICH MOMENT EACH FIGURE HERE CAME FROM. `verdict` is `one-moment`, `minutes-apart`, `hours-apart`, `days-apart` or `unknown`; `parts` names each figure beside its stamp; `oldestPart` is the one to look at and `spanMinutes` the gap. An auditor read this vault’s capital as two different figures minutes apart and could not tell which was right — both were, and the capital had moved 0.016% between the calls. NOTHING HERE IS A NEW READING: every stamp already travels under its own key. A part with NO stamp makes the verdict `unknown` and stays in the list, because an absence must never read as agreement."
},
"venueWithdrawable": {
"type": [
"object",
"null"
],
"description": "WHAT THE VENUE’S OWN ACCOUNTING SAYS ABOUT FREE LIQUIDITY, AGAINST OURS. Null where no venue API has been read for this vault. `theirShare` and `ourShare` are both shares of the vault, never dollars — ours is derived in dollars and theirs is in asset units, and converting one would put a third party’s price into a comparison of two first-party readings. `agreement` is `agrees` (within a tenth of each other), `ours-is-higher`, `ours-is-lower` or `not-compared`, and a missing reading is NEVER an agreement. Measured on the first pass over Morpho V2: 112 vaults agree, and on 87 holding $1.73bn ours is on average 40.7 percentage points HIGHER than the vault’s own figure — read the lower one before planning an exit. `timelockSeconds` is a notice period the VENUE states, which is a different kind of evidence from the one our own contract walk measures and is reported as theirs."
},
"settlementObserved": {
"type": [
"object",
"null"
],
"description": "WHAT WE WATCHED HAPPEN WHEN HOLDERS LEFT, and NOT a redemption mechanic. In the withdrawals we opened on chain, `paidInSameTransaction` counts the ones where the holder received another token in the very transaction that burned their shares, and `burnedWithoutPayout` the ones where nothing reached them there. `examined` is the denominator. THIS IS WEAKER THAN READING THE PROGRAM and is published as such: a program can settle this way a thousand times and queue the next one, so nothing here may be read as `synchronous`. The strategy account’s own terms are on `kaminoStrategy` beside it and carry no mechanic deliberately, because that account does not state one. Null where no withdrawal has been examined, which is our reach rather than a vault nobody leaves."
},
"flowsNewestAnywhereAt": {
"type": [
"string",
"null"
],
"format": "date-time",
"description": "The newest movement we hold for ANY vault. Read beside the field above it tells a quiet vault from a stopped reader, which is what every zero here turns on."
},
"redemptionIntelligence": {
"type": [
"object",
"null"
],
"description": "GETTING OUT, AS ONE ORDERED THING. The same readings as the sibling blocks, composed in the order a depositor meets them and computing nothing of its own, so it cannot disagree with them: mechanism (how leaving works here at all), capacity (whether there is anything to leave with), filed (whether a request has to be made and who is ahead of you), claimable (whether what is owed has been set aside), settled (whether anybody has actually been paid). START HERE and read the parts for detail. Each step carries `applies`, `readable`, `basis`, `reading`, `usd` and `says`, and the two flags are NOT the same thing: `applies: false` means the step does not exist on this vault at all — a synchronous redemption settles in the transaction that asks for it, so there is no request and nothing is ever set aside — while `readable: false` on a step that applies is a gap in OUR reading or the contract’s silence. `weakestLink` names the first step that exists here and that we cannot see, and is the single most useful field on this answer: a chain is only as good as its first unreadable step, and every step after it is conditional on something we could not establish. `readableSteps` of `applicableSteps` says how much of the sequence we have."
},
"declaredWithdrawableUsd": {
"type": [
"number",
"null"
],
"description": "A LEVEL at this instant. Null where we cannot read it, which is not zero."
}
}
}{
"title": "What actually left a vault",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}vault_risk_dimensions — Six risks across every vault we score
SIX RISKS, NOT ONE WORD, across every vault we score. A risk band is a weighted whole and a reader takes it as a summary of everything, which it is not: ours weighs drawdown, crowding, exit, concentration and consistency, and it contains NO reading of who may move the money. Two vaults read `low` while a multisig needing a single signature controlled them. So this groups what is already measured into market, liquidity, concentration and operating risk, ADDS the controller as its own dimension, and ADDS evidence — how much of the vault we could read at all. There is deliberately NO overall figure: a composite would be the number everybody quotes and nobody can check. THE FINDING IS FREE: `population` says how many scored vaults carry a band that reads BETTER than one of their own dimensions, and how many of those read worse on a dimension the band never contained — which is the number to read before trusting any one word on this platform. THE LIST IS PAID (`dimensions.read`): which vaults, with each one’s six levels, what it holds, and a sentence naming the disagreement. `locked` says when it is withheld, so an empty `items` is never mistaken for no matches — `denominators.matchedTheFilter` counts them either way. For ONE vault the whole table is FREE at `vault_check`, which also computes what else it could not see. Filter with `dimension` and `level` to run the screen that matters, e.g. the vaults whose controller reads `high`.
{
"type": "object",
"properties": {
"level": {
"enum": [
"low",
"moderate",
"high",
"severe"
],
"type": "string",
"description": "The level to keep. With `dimension` it applies to that one; alone it keeps any vault reading this on ANY dimension, which is the fastest way to find everything that reads `severe` somewhere."
},
"limit": {
"type": "integer",
"description": "Rows to return, 1 to 200, default 50. The population figures are over EVERYTHING scored and never over the page."
},
"offset": {
"type": "integer",
"description": "Rows to skip, for walking a long list. `denominators.matchedTheFilter` is the total to walk."
},
"dimension": {
"enum": [
"market",
"liquidity",
"concentration",
"operating",
"controller",
"evidence"
],
"type": "string",
"description": "Keep only vaults with a READING on this dimension, and with `level` keep only those at that level. Without `level` it keeps every vault whose dimension read at all, which excludes the unread — an unread dimension is our coverage and never a level."
},
"disagreeing": {
"type": "string",
"description": "Send `true` to keep only the vaults whose overall band reads BETTER than at least one of their own dimensions. This is the screen the dimensions exist for: it is where reading the single word would have misled you."
}
}
}{
"type": "object",
"required": [
"population",
"denominators",
"items",
"withheld",
"limits"
],
"properties": {
"asOf": {
"type": "string",
"format": "date-time",
"description": "When this was assembled. Each row carries its own `asOf`: the scores were taken at different moments, so this is a register of readings and not a snapshot."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"items": {
"type": "array",
"items": {
"type": "object"
},
"description": "PAID. One row per vault: `slug`, `name`, `tvlUsd`, `band`, `levels` keyed by dimension, `coverage` with the component COUNTS behind the evidence dimension, `worseThanBand`, `outsideTheBand`, `says` and `asOf`. EMPTY where withheld, which `locked` states — never read an empty list here as no matches."
},
"limits": {
"type": "string",
"description": "What this route does not do: nothing here is a new measurement, and its evidence dimension counts components only — `coverage.cannotSeeCount` is 0 for every vault and means NOT COMPUTED HERE, not \"nothing was unreadable\". The per-vault `vault_check` computes it properly and is free."
},
"locked": {
"type": [
"object",
"null"
],
"description": "Null for a caller entitled to the list. Otherwise it names the capability, the number of free rows, and says plainly that the rest is withheld rather than absent."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"withheld": {
"type": "integer",
"description": "How many matching vaults are NOT in `items`. Zero for an entitled caller. For everybody else it is the difference between what matched and the free rows, so a short list is never mistaken for a short population."
},
"population": {
"type": "object",
"description": "FREE. `scored` is the denominator, `disagreeing` how many carry a band better than one of their dimensions, `withARiskOutsideTheBand` how many read worse on a dimension the band does not contain, `byDimension` which dimension does it and how often, and `says` the finding in a sentence. Computed over everything scored, BEFORE any filter: a headline over a filtered set would be a headline about the filter."
},
"denominators": {
"type": "object",
"description": "What every figure above is over: `openVaults`, `scored`, `neverScored` (named rather than left to subtraction, because a vault nobody scored must never read as one that scored well), `matchedTheFilter` and `returned`."
}
}
}{
"title": "Six risks across every vault we score",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}portfolio_screen — What the vaults you hold share
READ THE VAULTS YOU HOLD TOGETHER. This one is about a SET rather than a single subject; an allocator does not hold one subject. This answers the three questions that only exist across holdings and that no vault page can show you: a CONTROLLER behind more than one of them, a MARKET behind more than one of them, and an EXIT that is not the sum of the exits. It is a screen, not advice: there is no allocation percentage and no expected return in this answer and there never will be. THE EXIT IS FOUR FIGURES AND THEY ARE NEVER ADDED UP — `declared` (the venue publishes a withdrawable figure), `behind-a-queue` (we have READ that a request comes first, so nothing leaves today at any price whatever cash is sitting there), `floor-only` (the only reading is uninvested cash, a lower bound) and `unread` — because a portfolio that is 40% readable is not a portfolio that is 40% liquid. A queue OUTRANKS a declared figure: a vault can publish a number and still take a request first, and the number is then what a request settles from. The exit split, the coverage and the bands are FREE, and so is every per-vault reading behind them at `vault_check`; a plan buys what the holdings SHARE. It cannot verify that you hold any of this.
{
"type": "object",
"required": [
"holdings"
],
"properties": {
"holdings": {
"type": "string",
"description": "The vaults you hold, as `slug:usd,slug:usd`. The amount after each slug is OPTIONAL and the answer is useful without it: leave it off and every figure counts holdings rather than dollars, and `positionsNotStated` says how many. Where you price SOME of them, a shared exposure whose sharers are not all priced reports `positionUsd: null` rather than a partial sum, because a partial sum reads as a complete one. At most 50 holdings."
}
}
}{
"type": "object",
"required": [
"exit",
"coverage",
"says",
"cannotSee"
],
"properties": {
"exit": {
"type": "object",
"description": "FOUR CLASSES, NEVER SUMMED: `declared`, `behind-a-queue`, `floor-only`, `unread`, each with `holdings` and `positionUsd`. Read `behind-a-queue` first: that is money nothing can be taken from today."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "The findings in words, including what was not stated."
},
"asked": {
"type": "integer",
"description": "How many holdings you named."
},
"notHeld": {
"type": "array",
"items": {
"type": "string"
},
"description": "The slugs we do not hold at all, LISTED rather than dropped: a portfolio quietly one short is the wrong answer to the question you asked."
},
"coverage": {
"type": "object",
"description": "The share of your holdings carrying each reading at all: `risk`, `exit`, `control`, `markets`. A control band of `unknown` counts as UNREAD, because it is."
},
"resolved": {
"type": "integer",
"description": "How many of them we hold a vault for."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"cannotSee": {
"type": "array",
"items": {
"type": "string"
},
"description": "What this answer does not contain, starting with the fact that we cannot verify you hold any of it, and ending with the fact that it is not advice."
},
"byRiskBand": {
"type": "object",
"description": "Holdings per published risk band, with `unread` as its own key rather than folded into low."
},
"positionUsd": {
"type": [
"number",
"null"
],
"description": "The total you stated, over the holdings you priced. Null where you priced none."
},
"sharedVenues": {
"type": [
"array",
"null"
],
"description": "PAID. Venues and chains carrying more than one holding. NULL where withheld."
},
"byControlBand": {
"type": "object",
"description": "Holdings per control arrangement: `unguarded`, `thin`, `checked`, `unread`."
},
"sharedMarkets": {
"type": [
"array",
"null"
],
"description": "PAID. Markets or collateral more than one holding lends against. A vault page gives your share OF that market; this says how much of YOUR money is behind it. NULL where withheld."
},
"sharedControllers": {
"type": [
"array",
"null"
],
"description": "PAID. Controllers that can move the money in MORE THAN ONE of your holdings, with the slugs and your stated position behind each. Reading the vaults one at a time shows one controller each; held together they are one bet. NULL where withheld, which `detailRequires` states — never read a null here as \"nothing is shared\"."
},
"positionsNotStated": {
"type": "integer",
"description": "Holdings you named without an amount. Every dollar figure in this answer excludes them."
}
}
}{
"title": "What the vaults you hold share",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}capital_coverage — How much capital we can actually read
BEFORE YOU TRUST ANY COVERAGE FIGURE FROM US, ask this. It says how much of the market each vault reading actually covers, counted TWO WAYS: by number of open vaults and by the capital in them. They differ by a lot, and the difference is the finding — more than half our vault COUNT is one venue’s small automated vaults, so a count-weighted coverage figure is mostly a statement about that venue and tells you almost nothing about whether the capital you care about is covered. NEITHER is called "the coverage". Free, because it is a statement about OUR OWN limits and a coverage claim nobody can audit is marketing rather than evidence. It says what we CAN read and never whether a vault is any good; WHICH vaults are missing a reading is the paid screen, not this. Readings: would take a deposit, risk score, withdrawable now, crowding, how exit works, time to exit, worst fall, who can move the money, what a holder earned — the same 9 columns the vault screen shows, read from the same expressions.
{
"type": "object",
"properties": {}
}{
"type": "object",
"required": [
"rows",
"says",
"limits"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"rows": {
"type": "array",
"items": {
"type": "object"
},
"description": "One per reading: `key`, `label`, `vaults`, `tvlUsd`, `byCount` and `byCapital` (0..1, null where no capital was read at all — not a cover of nothing), plus `says` for what the reading lets you decide and `screenColumn` for the vault-screen column asking the same question. SOME READINGS CANNOT BE PUT TO SOME VAULTS AT ALL, and where that is so the row also carries `notApplicable` {vaults, tvlUsd, because} with `byCountApplicable` and `byCapitalApplicable` beside the plain shares. Read those two for the question \"how much of what we COULD read have we read\", and the plain ones for \"how much of everything\". A vault that mints one share per unit of asset has not failed to earn anything through its price, and a redeem that settles in the asking transaction has no notice period to state — counting either as a gap would make our coverage look worse than it is in a way that is just as dishonest as making it look better. The two shares are never equal where `notApplicable` is present, and `because` names what put those vaults outside."
},
"says": {
"type": "string",
"description": "The widest and thinnest readings by capital, and whether counting vaults and weighing capital disagree."
},
"limits": {
"type": "string",
"description": "What these figures are not."
},
"byVenue": {
"type": [
"array",
"null"
],
"description": "THE SAME QUESTION PER PROTOCOL, which is the one to ask if you hold something specific: the shares elsewhere on this answer say how much of OUR capital carries each reading, and this says whether we cover the venue you are in. Twenty venues, biggest by capital first, each with `tvlUsd`, a `readings` map of share-of-capital per reading, and `fullyEvidenced` asking the hardest question of that venue alone. Every share is BY CAPITAL and NULL where the venue has no capital we could weigh — a venue we cannot weigh is not a venue with no coverage, and a zero there would say the opposite."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"measuredAt": {
"type": [
"string",
"null"
],
"format": "date-time",
"description": "When the coverage was counted. Null means the job has never run, which `says` states rather than reporting zeros."
},
"tvlTotalUsd": {
"type": [
"number",
"null"
],
"description": "Capital across them, in US dollars. The denominator of every `byCapital`."
},
"vaultsTotal": {
"type": [
"integer",
"null"
],
"description": "Open vaults. The denominator of every `byCount`."
},
"fullyEvidenced": {
"type": [
"object",
"null"
],
"description": "THE NUMBER TO READ FIRST if you are deciding how far to trust this platform on a given market: the share of capital carrying EVERY reading at once — a realised return, free liquidity, a risk band, who may move the money, how redemption works, and capital re-read within 48 hours. It cannot be worked out from the shares below, because a vault missing a different one from its neighbour drops out of both, and it is deliberately the least flattering figure on this answer."
},
"vaultsWithNoSizeRead": {
"type": [
"integer",
"null"
],
"description": "Open vaults whose SIZE we never read. They are in the count denominator and cannot be in the capital one, which is why this is named rather than left to subtraction."
}
}
}{
"title": "How much capital we can actually read",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}exit_lifecycle_coverage — How much of the way out we can see
HOW MUCH OF THE WAY OUT WE CAN SEE, phase by phase, across every open vault. Getting out has four phases — a request filed, something set aside to claim, money that actually left, and an attempt that failed — and we read THREE. The fourth is on this answer anyway, with its reason: no vault state records a redemption somebody attempted and could not complete, so reading it needs every transaction sent to each contract and its receipt, and a zero there would be the most misleading figure available because it would say nobody was turned away. FOUR STATES PER PHASE and never collapsed into "missing", because two of them look identical and are not: `exempt` is a phase that CANNOT exist (a synchronous redeem settles in the transaction that asks, so nothing is ever filed and nothing is ever set aside) and `subjectRefuses` is a phase that EXISTS while the contract will not expose it (an ERC-7540 vault carrying neither standard getter — the holder is in that phase and cannot be told what became of their request). Only `unread` is work of ours. THERE IS NO SINGLE LIFECYCLE PERCENTAGE: the phases have different denominators, so averaging them would give a number no phase is answerable for. Free, because it is a statement about our own reach.
{
"type": "object",
"properties": {}
}{
"type": "object",
"required": [
"measuredAt",
"vaultsTotal",
"phases",
"says",
"cannotSee"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "How many phases we can read, which is thinnest, and why there is no single lifecycle number."
},
"phases": {
"type": "array",
"items": {
"type": "object"
},
"description": "One per phase, in the order a depositor meets them: `key`, `label`, then `read`, `exempt`, `subjectRefuses` and `unread` each as {vaults, tvlUsd}. `readNoSizeRead` counts the vaults with the reading whose SIZE is unread, and where it equals `read.vaults` the capital share is NULL rather than 0 — the reading covers vaults we cannot weigh rather than vaults that are small, and a 0% would say the opposite; measured on the day this shipped, all 349 vaults whose filed requests we can read were in exactly that state. `byCountApplicable` and `byCapitalApplicable` divide by what the phase COULD exist for, the total less the exempt, and are NULL for a phase we do not read at all rather than 0% — a zero would read as a failure to try instead of a reading nobody can take. `unreadableBecause` is set only on such a phase and says why. `says` gives the four states in prose."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"cannotSee": {
"type": "array",
"items": {
"type": "string"
},
"description": "What no figure here reaches, including who is in a queue as opposed to how much is in it."
},
"measuredAt": {
"type": "string",
"format": "date-time",
"description": "When these counts were taken. The readings behind them were taken earlier: each phase comes from a reader with its own cadence, so this says when they were COUNTED and never when the chain was read."
},
"tvlTotalUsd": {
"type": "number",
"description": "Capital across them, in US dollars."
},
"vaultsTotal": {
"type": "integer",
"description": "Open vaults, the widest denominator. Each phase states its own narrower one."
}
}
}{
"title": "How much of the way out we can see",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}endpoint_coverage — How well we know whether declared endpoints answer
BEFORE YOU TREAT ANY ENDPOINT AS RELIABLE, ask how well we know. There is no uptime number here and there will not be one, because the states mean different things and summing them produces a figure about nothing: a 402 is an x402 seller doing exactly what it advertises, a 401 or 403 is credentials WE do not hold, a 3xx is a move we chose not to follow, and a timeout describes OUR attempt rather than the host. Only the states marked `fault: true` are the endpoint failing. TWO POPULATIONS, each on its own denominator and NEVER added together: the HOSTS registered identities declare, probed daily under a deadline that sometimes expires before every host is reached — so a host we did not ask keeps whatever an earlier day found, and `notAsked` says how many — and the x402 RESOURCES sellers list, where a state is published only after three knocks because one failure is not a finding. The second number is the uncomfortable one and it is published anyway: most listed resources have never been knocked on at all, which is our reach and not a judgement of the sellers. Free, because a coverage claim nobody can audit is marketing rather than evidence.
{
"type": "object",
"properties": {}
}{
"type": "object",
"required": [
"measuredAt",
"hosts",
"resources",
"says",
"cannotSee"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "Both populations in prose, each with its own denominator, and what the fault count does and does not include."
},
"hosts": {
"type": "object",
"description": "The daily HEAD probe of hosts registered identities declare. `declared`, `asked` and `notAsked` — the probe runs under a deadline, so these differ and the difference is hosts whose state is a memory. `lastRunAt`, `ageHours` and `stale` against a daily cadence. `staleHosts` counts hosts nobody confirmed inside the freshness window. `byState` is one entry per state with `hosts`, `agents` (identities standing behind them — one dead host can carry hundreds), `share`, `fault` (true, false, or NULL where the state is about our attempt or our credentials rather than the endpoint) and `says`. `agentsBehindFaults` counts identities behind a fault state only, built from the states that ARE faults rather than by negating `answering`."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"cannotSee": {
"type": "array",
"items": {
"type": "string"
},
"description": "What no figure here can reach: endpoints nobody declares, whether the declaring identity controls the host, whether a 402 offer can actually be signed (that needs the response BODY and comes from the knock flow), and whether an endpoint that answered answered anything good."
},
"resources": {
"type": "object",
"description": "The x402 resources sellers list, on its OWN denominator. `declared`, `everKnocked` (knocked even once) and `confirmable` (knocked three times or more, the only ones whose state we will publish), with `everKnockedShare` and `confirmableShare` beside them, plus `knockedLastWeek` and `oldestKnockHours`. Read `confirmableShare` as the honest answer to \"can ASPERN tell me whether an x402 endpoint works\": today it is small."
},
"measuredAt": {
"type": "string",
"format": "date-time",
"description": "When these figures were counted. The host states come from the last probe RUN, whose own age is on `hosts.ageHours`; a fresh read of a stale probe is not a fresh fact."
}
}
}{
"title": "How well we know whether declared endpoints answer",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}payment_evidence — Settled, linked and delivered, across the rails
BEFORE YOU QUOTE ANY FIGURE FOR AGENT COMMERCE, ask which of three it is. Every payment shown to be one, x402 and ACP, is split into three figures that NEST and are never added up (x402 counts only the `protocol`, `route` and `facilitator` tiers below; the authorised transfers with nothing to show they were x402 are in `notShownToBeX402`, never in the figures): `settled` (paid on-chain), of that `serviceLinked` (paid to a payee that lists a paid service, or an ACP job), of that `deliveryEvidenced` (a recorded outcome: an ACP job completed). x402 records the payment and never the delivery, so its payments are counted in `deliveryUnevidenceable`: $0 delivered there is what the rail allows, not a failure of the seller. Beside it, `tiers` splits x402 payments by what can be shown about the payment itself: to a listed route, through a facilitator, or only an authorised transfer. Free, because it is an aggregate; the same split for one address is on agent_business.
{
"type": "object",
"properties": {}
}{
"type": "object",
"required": [
"tiers",
"note"
],
"properties": {
"at": {
"type": [
"string",
"null"
],
"description": "When the x402 tiers were computed."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"note": {
"type": "string",
"description": "What each x402 tier means."
},
"tiers": {
"type": "array",
"items": {
"type": "object"
},
"description": "x402 payments by what can be shown about them: `route`, `facilitator`, `candidate`, `candidate-unsampled`, each with payments and usd. No tier is protocol-confirmed: the HTTP 402 receipt never reaches a chain."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"commerceTiers": {
"type": [
"object",
"null"
],
"description": "`settled`, `serviceLinked`, `deliveryEvidenced` and `deliveryUnevidenceable`, each {count, usd}; `rails` per rail; `says` in one line; `limits`; `asOf` when the daily rollup ran. NULL until it has run, never a row of zeros."
}
}
}{
"title": "Settled, linked and delivered, across the rails",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}evidence_for — Every edge we hold about one subject
Everything we hold about one subject, as edges, each carrying where it came from. Use it when you need to explain a decision rather than just make one, or to see what is MISSING before you act. Every edge has `source`, `observedAt` (when the world was in that state), `asOf` (when we read it — a fresh read of a stale fact is not a fresh fact), a `confidence` that names what it is confident IN, its own `coverage`, and the `method` that established it. Edges come in three kinds and are NEVER summed: `declared` is the subject speaking about itself, `observed` is our reading, `derived` is arithmetic over the others. Adding them together rebuilds the reputation score this replaces. It infers NO identity: a shared host or funder is reported as exactly that, because being the same party is a conclusion no join supports. `lookedForAndMissing` lists what we searched for and did not find, so a thin subject never reads as a complete picture. Free.
{
"type": "object",
"required": [
"subject"
],
"properties": {
"subject": {
"type": "string",
"description": "An address, or the http(s) URL of an endpoint."
}
}
}{
"type": "object",
"required": [
"subject",
"edges",
"counts",
"says"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "What the evidence amounts to, in one sentence. Never a verdict: this tool reports edges and leaves the conclusion to the caller."
},
"edges": {
"type": "array",
"items": {
"type": "object"
},
"description": "Each with claim, object, kind, source, observedAt, asOf, confidence {value, in}, coverage and method."
},
"kinds": {
"type": "object",
"description": "What declared, observed and derived each mean."
},
"counts": {
"type": "object",
"description": "Edges by kind. NOT a score: the three are reported apart and never added."
},
"limits": {
"type": "string",
"description": "What this graph cannot contain. Declared, observed and derived are different kinds of fact and are never added together; `counts` keeps them apart for the same reason."
},
"subject": {
"type": "string",
"description": "What was asked about, read back. Where we resolved it to something else, this is the resolved form."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"subjectKind": {
"enum": [
"address",
"endpoint",
"vault",
"unknown"
],
"type": "string",
"description": "What we took the subject to BE. `unknown` means we could not classify it, so the empty edge list below is our failure to look rather than a finding about the subject."
},
"lookedForAndMissing": {
"type": "array",
"items": {
"type": "object"
},
"description": "What we searched for and did not find, with why. Listed rather than omitted."
}
}
}{
"title": "Every edge we hold about one subject",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}evaluate_action — Weigh one action against a named policy
Call this when you are about to DO something and need one answer you can act on: pay an address, connect to an MCP server, call a tool, or put capital into a vault. The answer depends on the AMOUNT, the ACTION, the POLICY you name and how much we actually read — so the same address can be `allow` for $5 and `abstain` for $5,000. Four decisions. `allow` means nothing we could check objects, up to `maxAmountUsd`, and is NOT a statement that the action is safe: nothing here sees what it is for or what you agreed. `review` means something we READ does not match, and `reasons` names which rule. `abstain` means we did not read enough to have an opinion — our gap, never approval. `unsupported` means the policy has no rule for this action, and inventing one would be worse than declining. A policy judges the risk LEVEL as well as whether it could be read: conservative will not allocate into a band worse than moderate, nor where control is `thin`, and returns `review` for that rather than `abstain`. A failed rule carries `detail` (`observed`, `accepted`, `failedCondition`); read it, and `rules`, before suspecting an error. Every rule id in `reasons` is published in full at the policies tool, including to the party being evaluated. Free.
{
"type": "object",
"required": [
"action",
"subject"
],
"properties": {
"chain": {
"type": "string",
"description": "The chain the action settles on, e.g. base, polygon, solana."
},
"action": {
"enum": [
"pay",
"connect_mcp",
"call_tool",
"allocate_capital"
],
"type": "string",
"description": "What you are about to do."
},
"policy": {
"type": "string",
"description": "A policy version or slug. Defaults to conservative-v1. An unknown name is REFUSED rather than silently replaced with the default."
},
"subject": {
"type": "string",
"description": "What you are about to do it to: an address to pay, an endpoint URL to connect to, or a vault slug."
},
"resource": {
"type": "string",
"description": "The http(s) endpoint being bought or connected to, where that differs from `subject`. This is what reaches the seller’s own catalogue entry, carrying their price and their payee."
},
"amountUsd": {
"type": "number",
"description": "What you are about to commit, in US dollars. Leave it out and the amount rules report that they did not run, rather than passing."
},
"requiredChecks": {
"type": "array",
"items": {
"type": "string"
},
"description": "Rule ids YOU will not act without, e.g. [\"quoted-payee-matches-proposal\",\"live-price-matches-proposal\"]. The policy is OUR view of what matters; this is yours. Any one of them that does not reach a verdict — because we could not read it, or because it never applied to what you sent — turns the answer into ABSTAIN instead of ALLOW. It can only ever make the answer more conservative: a required check that passes changes nothing, and a real objection stays a REVIEW rather than being buried as an abstain. An id we do not hold is NAMED in `required.unknownIds` rather than ignored, because a caller whose requirement was never looked at must not read `allow` as it having been met. `list_policies` has every id."
}
}
}{
"type": "object",
"required": [
"decision",
"reasons",
"coverage",
"policyVersion",
"says"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "The decision in one sentence, naming the rules that drove it. Written to be shown to whoever authorises the action."
},
"rules": {
"type": "array",
"items": {
"type": "object"
},
"description": "Every rule considered: `id`, `state` (pass, fail or unknown), `checks` and `says`. THE TWO SENTENCES ARE DIFFERENT THINGS and the distinction is the audit trail. `checks` is the QUESTION the rule asks and never moves; `says` is WHAT WE FOUND and follows the state, so a failing rule reads as a failure. Until 2026-10-13 `says` carried the question in every state, which meant a `fail` arrived beside the claim it had just refuted — a model reading the prose rather than the state got the opposite of the decision. Read `state` to decide and `says` to explain; they can no longer disagree. A FAILED RULE CARRIES `detail` WHERE IT CAN: `observed` is what we read in the rule’s own vocabulary, `accepted` is what the policy would have taken, and `failedCondition` is the sentence replacing the generic one. On `controller-risk-within-policy` that matters more than anywhere: a vault can read `controller.level: low` in vault_check and still fail here, because the LEVEL says how dangerous the arrangement is and the BAND says what it is; a timelock with no guardian is `thin`, conservative accepts `checked` only, and both readings are right. The detail says so rather than leaving a reader to find a computational error that is not there. SINCE 2026-10-11 A POLICY JUDGES THE RISK LEVEL, NOT ONLY ITS TRUSTWORTHINESS. `risk-band-is-supported` asks whether the band survives what we could not read; `risk-band-within-policy` and `controller-risk-within-policy` ask whether the band and the control we DID read are ones this policy will commit capital against. Conservative will not allocate into a band worse than moderate, nor where control is `thin` (one party able to move the money), and returns `review` rather than `abstain`, because a band we read as high is a finding about the vault and not a gap in our reading. Permissive declares neither threshold and is unchanged. This closed a real defect: a vault our own vault_check called high risk with 0.5% withdrawable used to come back `allow`, because every rule about the AVAILABILITY of risk data passed."
},
"byArea": {
"type": "array",
"items": {
"type": "object"
},
"description": "The same coverage, PER QUESTION: counterparty, payment-intent, service, capital. Read this rather than the single `coverage` when the action moves money. The denominator is the rules the policy holds for that area, NOT the ones that happened to apply — so an area where nothing could be checked reports `none`, which is the fact, instead of reporting high coverage of nothing."
},
"limits": {
"type": "string",
"description": "What a decision here is and is not. `allow` means no rule objected under this policy at this coverage, which is not a statement that the action is safe."
},
"policy": {
"type": "object",
"description": "The policy applied: its intent, its ceiling and the coverage floor below which it abstains."
},
"checked": {
"type": "object",
"description": "WHICH ACCEPTANCE QUESTIONS THIS POLICY PUT AT ALL: `riskBand`, `controller`, `exitRecord`, `amount`, as booleans. READ THIS BEFORE ACTING ON `decision`. FALSE is the dangerous value and the reason the field exists: it means the question was never put, which is NOT a question that passed. `permissive` returns `allow` on a vault that lost 92.8% of its value because it declares no risk ceiling, and a client reading only `decision` cannot see that. `exitRecord` is whether the policy weighed HOW OFTEN THE EXIT HAS BEEN SHUT over the days we watched, as against how it reads at the last reading: 20 vaults holding $190.8M read as open today and were shut on more than a tenth of the days we have watched them, so a policy that never asked has not cleared them. Derived from what the POLICY declares and never from how the subject turned out, so a refusal and a clean pass under one policy carry identical flags."
},
"reasons": {
"type": "array",
"items": {
"type": "string"
},
"description": "Rule ids that decided. Look them up in the policy to see the sentence each one checks."
},
"coverage": {
"enum": [
"none",
"low",
"medium",
"high"
],
"type": "string",
"description": "How much of what the policy wanted was actually there. Below the policy floor the decision is `abstain` rather than `allow`."
},
"decision": {
"enum": [
"allow",
"review",
"abstain",
"unsupported"
],
"type": "string",
"description": "`allow` is not “safe” and `abstain` is not “nothing found”. Read `decisions` in the answer for what each one means."
},
"proposal": {
"type": "object",
"description": "What you asked about, read back, so a typo is visible before you act on the answer."
},
"required": {
"type": "object",
"description": "Present only where you sent `requiredChecks`. `met`, `unmet` and `unknownIds`, with a sentence. An `unmet` entry is why the decision is ABSTAIN."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"decisions": {
"type": "object",
"description": "What each decision word means, so a caller never has to guess. Read it before treating `allow` as approval or `abstain` as a clean result."
},
"maxAmountUsd": {
"type": [
"number",
"null"
],
"description": "The most this policy permits for this proposal. Null where the action moves no money, and null on anything but an `allow`, because a limit printed beside a `review` reads as permission."
},
"policyIntent": {
"enum": [
"risk-and-evidence",
"evidence-availability"
],
"type": "string",
"description": "What the policy is FOR. `risk-and-evidence` puts questions about the level AND about what we could read. `evidence-availability` asks only whether we could read enough — which is what `permissive` is: an availability-of-evidence policy rather than an investment risk policy. An `allow` from one is not an `allow` from the other."
},
"notApplicable": {
"type": "array",
"items": {
"type": "object"
},
"description": "Rules this PROPOSAL does not raise, each with `why` and what to send to have it checked. Distinct from `missingEvidence`, which is a gap in our record: a payment naming no `resource` has no listing to be found in, so those rules do not apply and do not lower coverage. Send `resource` and two more checks run, both decisive."
},
"paymentIntent": {
"type": "object",
"description": "Whether this payment was checked against WHAT IT IS BUYING, in one field. `not-verified` means the policy holds such rules and none of them ran, which on a payment is almost always a missing `resource`: the counterparty may have an excellent record and nothing has compared this amount to this service. `verified` means at least one reached a verdict. `not-applicable` means the policy asks nothing about it, or the action is not a payment."
},
"policyVersion": {
"type": "string",
"description": "Carried so an answer given today can be reproduced after the rules change."
},
"missingEvidence": {
"type": "array",
"items": {
"type": "string"
},
"description": "Rule ids whose evidence we do not hold. These lower `coverage` and are NEVER counted as objections."
}
}
}{
"title": "Weigh one action against a named policy",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}evaluate_vault_decision — Judge a vault decision before capital moves (paid)
Call this before an agent moves capital into or out of a vault, with the decision it is about to make: the vault, the amount, the MANDATE it must stay inside and the CLAIMS the decision rests on. It is judged against our stored due-diligence snapshot of that vault, deterministically and without a model, and comes back with a `verdict`: `supported`, `conditional`, `human_review_required`, `insufficient_evidence` (the snapshot is older than your mandate accepts, or a claim states as known what the snapshot records as unread), `not_approved` (a mandate limit is broken or a claim is contradicted) or `out_of_coverage` (we hold no snapshot, nothing charged). Every finding cites the snapshot field it rests on as `dd:<digest>#<json pointer>`, so it can be checked and cannot be invented, and `requiredNextSteps` says what would change the verdict. A claim the snapshot contradicts is told the DIRECTION of its error, never the true figure: the figures are in the due-diligence report. PAID: $2 a judgement over x402, or inside a plan that reads due diligence. FREE with `dryRun: true` (can it be judged, how old is our snapshot, what would be checked; no verdict) and FREE in full on the two public sample vaults, ethereum-sentora-rlusd-main-6dc58a and ethereum-1337-usdc-94643e. A `supported` verdict means the decision is consistent with our evidence and its own mandate. It is not advice and not a guarantee about the vault. Keep the `digest`: anybody can confirm at /v1/reports/{digest} that we issued that judgement unchanged.
{
"type": "object",
"required": [
"vault",
"amountUsd"
],
"properties": {
"pass": {
"type": "string",
"description": "A decision pass from this vault’s due-diligence report (its X-ASPERN-Decision-Pass header): 24 hours of judgements on that vault without paying again."
},
"vault": {
"type": "string",
"description": "The vault slug, or an address we can resolve to one. `list_vaults` or `find_vaults` find a slug."
},
"action": {
"enum": [
"allocate",
"increase",
"reduce",
"exit"
],
"type": "string",
"description": "What the decision does. Default allocate."
},
"claims": {
"type": "array",
"items": {
"type": "object"
},
"description": "What the decision assumes, at most 20, each `{ field, value }` where `field` is a JSON pointer into the due-diligence document, e.g. /capital/withdrawableUsd, /capital/totalUsd, /subject/chain or /dimensions/0/level."
},
"dryRun": {
"type": "boolean",
"description": "True for the free preview: whether we can judge it and what would be checked, without a verdict."
},
"mandate": {
"type": "object",
"description": "The limits the decision must stay inside. Each is optional and a limit left out is not checked: `maxPositionUsd`, `maxShareOfVault` (0..1 of the vault’s capital), `maxShareOfWithdrawable` (0..1 of what the vault declares withdrawable now), `allowedChains` (list), `maxDimensionLevel` (the worst of low, moderate, elevated, high, severe that any of the six risk dimensions may read), `humanApprovalAboveUsd`, `maxEvidenceAgeHours` (default 48)."
},
"amountUsd": {
"type": "number",
"description": "The amount the decision moves, in US dollars."
},
"rationale": {
"type": "string",
"description": "Free text, carried with the decision and not read."
}
}
}{
"type": "object",
"required": [
"vault",
"verdict",
"snapshot",
"limitations",
"dryRun",
"judgeable",
"checks",
"price",
"says"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "Dry run only: the answer in one sentence."
},
"price": {
"type": "object",
"description": "Dry run only: the price of a judgement and the vaults where it is free."
},
"vault": {
"type": "string",
"description": "The vault slug the decision resolved to."
},
"checks": {
"type": "array",
"items": {
"type": "string"
},
"description": "Dry run only: the checks this decision would get."
},
"digest": {
"type": "string",
"description": "SHA-256 of this judgement, recorded so it can be verified at /v1/reports/{digest}."
},
"dryRun": {
"type": "boolean",
"description": "True on the free preview."
},
"paidBy": {
"type": "string",
"description": "free-sample, report-pass (a pass from this vault’s due-diligence report) or this-call. Absent on a dry run."
},
"sample": {
"type": "boolean",
"description": "True on the two free sample vaults, where findings quote values."
},
"verify": {
"type": "string",
"description": "The path that verifies the digest."
},
"verdict": {
"type": [
"string",
"null"
],
"description": "supported, conditional, human_review_required, insufficient_evidence, not_approved or out_of_coverage. Null on a dry run of a vault we can judge."
},
"findings": {
"type": "array",
"items": {
"type": "object"
},
"description": "Each check that said something: `kind`, `weight` (hard, evidence, soft, none), `evidence` (dd:<digest>#<pointer>, or null where it concerns the packet alone) and `says`."
},
"snapshot": {
"type": [
"object",
"null"
],
"description": "The snapshot judged against: `digest`, `readAt` and `ageHours`. Null where we hold none."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"judgeable": {
"type": "boolean",
"description": "Dry run only: whether we hold a snapshot to judge it against."
},
"limitations": {
"type": "array",
"items": {
"type": "string"
},
"description": "What this judgement does not cover. Always present."
},
"requiredNextSteps": {
"type": "array",
"items": {
"type": "string"
},
"description": "What would have to change for a better verdict, one line per finding."
}
}
}{
"title": "Judge a vault decision before capital moves (paid)",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}simulate_withdrawal — Try a withdrawal before making it (free up to $1,000)
TRY ONE WITHDRAWAL FROM A VAULT BEFORE YOU MAKE IT. Runs the vault’s own withdraw(amount, holder, holder) by eth_call at the head block, as the holder you name or, if you name none, as its largest depositor: the contract executes and nothing is signed, sent or spent. Says whether it would have gone through, the revert reason where it would not (e.g. NotEnoughLiquidity, TransferReverted past a V2 vault’s idle balance), the block, and what the holder’s position was worth; a size over what the holder owns is tried at their position and said so. It does NOT show a later block, or what happens once somebody else takes the same free money. Morpho vaults for now; anything else answers `simulated: false` and is not charged. FREE up to $1,000; over it PAID, $2 a simulation over x402, or free with a decision pass from this vault’s due-diligence report.
{
"type": "object",
"required": [
"vault",
"amountUsd"
],
"properties": {
"pass": {
"type": "string",
"description": "A decision pass from this vault’s due-diligence report (its X-ASPERN-Decision-Pass header): simulations over $1,000 on that vault without paying again."
},
"vault": {
"type": "string",
"description": "The vault slug, or an address we can resolve to one. `list_vaults` or `find_vaults` find a slug."
},
"holder": {
"type": "string",
"description": "The address to withdraw as. Leave it out to try as the vault’s largest depositor."
},
"amountUsd": {
"type": "number",
"description": "How much to withdraw, in US dollars. Up to 1000 is free."
}
}
}{
"type": "object",
"required": [
"vault",
"simulated",
"wouldGoThrough",
"price",
"says"
],
"properties": {
"at": {
"type": [
"string",
"null"
],
"description": "When, UTC."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "The answer in one paragraph."
},
"block": {
"type": [
"number",
"null"
],
"description": "The block it was simulated at."
},
"price": {
"type": "object",
"description": "`usd` for a paid simulation and `freeUpToUsd`."
},
"vault": {
"type": "string",
"description": "The vault slug the request resolved to."
},
"holder": {
"type": [
"string",
"null"
],
"description": "The address the withdrawal was tried as."
},
"paidBy": {
"type": [
"string",
"null"
],
"description": "free, report-pass or this-call."
},
"reason": {
"type": [
"string",
"null"
],
"description": "The revert reason where it would have reverted."
},
"askedUsd": {
"type": [
"number",
"null"
],
"description": "The amount asked."
},
"holderAs": {
"type": [
"string",
"null"
],
"description": "named or largest-depositor."
},
"triedUsd": {
"type": [
"number",
"null"
],
"description": "The amount tried: the amount asked, or the holder’s position less 0.1% where they own less."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"holderUsd": {
"type": [
"number",
"null"
],
"description": "What that holder’s shares were worth at the block."
},
"simulated": {
"type": "boolean",
"description": "Whether a withdrawal was run. False where the vault is not one we simulate, no endpoint answered, or there was no holder to try as; nothing is charged then."
},
"limitations": {
"type": "array",
"items": {
"type": "string"
},
"description": "What one simulation does not show."
},
"wouldGoThrough": {
"type": [
"boolean",
"null"
],
"description": "True if the withdrawal would have gone through at that block, false if it would have reverted, null where nothing was tried."
}
}
}{
"title": "Try a withdrawal before making it (free up to $1,000)",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}test_purchase — Test purchase, without paying
WALK THE WHOLE PURCHASE OF AN x402 ENDPOINT WITHOUT PAYING, in the order a buyer actually asks the questions, stopping at the first one that objects. All five are asked in one pass and in that order, so the walk stops where a buyer would stop. Five steps: is the endpoint there, what does it charge RIGHT NOW, does the seller’s own catalogue entry agree with what it just said, does the record say anything against paying that payee under a published policy, and — the step that only exists because this is a rehearsal — is the price still the one the decision was made on, re-read a moment later. A quote that moved between the decision and the signature would authorise something nobody decided on. THE VERDICT HAS THREE STATES AND cannot-tell IS NOT would-proceed: an unanswered question is never rounded up to a passed one. `quotedRaw` is what the endpoint said, in the asset’s smallest unit, and `quotedUsd` is NULL unless the catalogue carries both halves of the same token’s pair to scale it — nothing in a payment-requirements document names the decimals, and a figure scaled by a guessed exponent is a hundred-fold error waiting to be quoted. We knock only on endpoints already in the x402 catalogue we read, at most one live handshake a minute per resource, because we knock only on what the catalogue already lists: a URL named in a request is not by itself a reason to send traffic to somebody else’s host. Nothing is signed and nothing is spent. FREE.
{
"type": "object",
"required": [
"resource"
],
"properties": {
"fill": {
"type": "object",
"description": "Values for the slots the SELLER’s own catalogue URL declares, e.g. `{\"doi\": \"10.1000/xyz\"}` for a resource listed as `…?doi={doi}`. A SMALL PART of the catalogue is templates rather than plain addresses — `askedWithUnfilled` on the answer names the slots for the endpoint in front of you — and without this we knock with `{doi}` where a value belongs and answer a question nobody asked. ONLY declared slots are accepted: a name the template does not contain is REFUSED rather than ignored, because a parameter silently dropped would let you read the answer as being about the call you meant to make. Values are percent-encoded and the filled URL is verified to reach the same origin and path shape as the catalogue entry, so a value can never choose the host — not even a slot the seller put in their own hostname. A filled rehearsal is ALWAYS a live knock, so inside the one-a-minute cooldown it answers 429 rather than serving back the reading of the unfilled question; and it is NOT written to the endpoint history, because two different questions in one run series would look like a price that moved."
},
"policy": {
"type": "string",
"description": "A policy version or slug to decide under. Defaults to conservative-v1."
},
"resource": {
"type": "string",
"description": "The http(s) endpoint to rehearse buying. It must be one the x402 catalogue already holds; `list_x402_endpoints` finds it."
}
}
}{
"type": "object",
"required": [
"resource",
"says"
],
"properties": {
"live": {
"type": "boolean",
"description": "Whether the handshake happened just now or is the stored reading from within the last minute. A stored reading is a weaker claim and this says which it is."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "The verdict in one sentence: where a careful buyer stops and why, or what could not be answered. An unanswered question is named rather than passed over."
},
"payee": {
"type": [
"string",
"null"
],
"description": "The address the ENDPOINT named in its handshake, which is not always the one its listing names. That disagreement is step three."
},
"steps": {
"type": "array",
"items": {
"type": "object"
},
"description": "One per question, in the order a buyer asks them. Each carries `asks` in a person’s words, `state`, `stops` and `says`."
},
"because": {
"type": [
"string",
"null"
],
"description": "The NAME of the finding that decided it, two or three words, e.g. `Payee mismatch` or `Price moved`. NULL only where nothing decided, which is a clean walk: a verdict must not assert a cause it did not measure."
},
"history": {
"type": [
"object",
"null"
],
"description": "EVERY TIME WE HAVE KNOCKED, which is the half a checker cannot hold: it sees the endpoint now, only we hold the record of each time we looked. `state` is `answering`, `not-answering`, `unconfirmed` (one failure, which is a reason to ask again and not a finding), `seen-once` or `never-knocked` — the last two are about OUR reach and not about the seller. An outage is published as a RANGE and never as a duration: `downAtLeastHours` counts from the first knock that failed and `downAtMostHours` from the last that answered, because the stop happened somewhere between and that window is as wide as the gap between two knocks. `downAtMostHours` is NULL where nothing has ever answered, since there is then no moment to measure from and zero would read as a bound we do not have. `answered` of `knocks` is the denominator for everything here, `runs` is the timeline oldest first with a knock COUNT per run rather than a span, and `longestFailureRun` is counted in knocks for the same reason. NULL where the endpoint is not one we knock on."
},
"network": {
"type": [
"string",
"null"
],
"description": "The chain the endpoint asked to be paid on, as it stated it, usually CAIP-2."
},
"verdict": {
"type": "string",
"description": "`would-proceed`, `would-stop` or `cannot-tell`. The third is NOT the first."
},
"headline": {
"type": "string",
"description": "The outcome in three words or fewer: `Purchase stopped`, `Cannot tell` or `A careful buyer would go ahead`. Same fact as `verdict`, in a reader’s words."
},
"resource": {
"type": "string",
"description": "The endpoint this is about, read back."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"askedWith": {
"type": [
"object",
"null"
],
"description": "The values we put in, read back, so you can see the question that was actually asked. NULL where you sent none, which is the ordinary case."
},
"cannotSee": {
"type": "array",
"items": {
"type": "string"
},
"description": "What no rehearsal establishes however well it goes."
},
"decidedAt": {
"type": [
"string",
"null"
],
"description": "The id of the step that stopped it, so a caller acts on a step rather than parsing a sentence."
},
"knockedOn": {
"type": [
"string",
"null"
],
"description": "The URL we actually knocked on once your values were in it. NULL where you sent none, in which case it is `resource` unchanged."
},
"quotedRaw": {
"type": [
"number",
"null"
],
"description": "What it asked for, in the asset’s smallest unit. Always the number the endpoint actually said."
},
"quotedUsd": {
"type": [
"number",
"null"
],
"description": "The same amount in dollars, NULL where we could not scale it from the catalogue’s own pair."
},
"rehearsed": {
"type": "boolean",
"description": "False where the endpoint is not in the catalogue, with `says` explaining that we do not fetch a URL because a request named it."
},
"askedWithUnfilled": {
"type": "array",
"items": {
"type": "string"
},
"description": "Placeholders in the catalogue URL we sent UNFILLED, e.g. `doi`. Empty is the ordinary case: the URL is an address, not a template. Non-empty qualifies the PRICE and not the availability — the endpoint answered and quoted, but for a call with a gap in it, so the figure may be the generic one rather than the price of the call you mean to make. Templates are a small part of the catalogue and `askedWithUnfilled` names the slots on each answer."
},
"walkedPastTheStop": {
"type": "boolean",
"description": "TRUE where a question after the stopping one was still asked, which is deliberate: halting at the first objection would hide a second. FALSE means the stop was the last thing asked, or nothing stopped at all. Either way nothing financial happened, because none of these steps signs or spends."
}
}
}{
"title": "Test purchase, without paying",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": false
}knock_on_endpoint — Knock on an endpoint, with your own payment
KNOCK ON AN x402 ENDPOINT AND GET WHAT IT SERVED. `test_purchase` beside this walks a buy WITHOUT paying and stops at the quote; its own caveat has always said that only paying and reading the answer establishes what arrives, “which is the half that needs your own wallet”. This is that half, and it is the same capability the web and the plugin have. WITHOUT `payment` it reads the terms: `accepts` carries EVERY set of terms the seller published and `signable` the ones an EIP-3009 wallet can meet, and WHICH ONE TO PAY ON IS YOURS TO CHOOSE. WITH `payment` we forward the authorisation YOU signed and hand back what the seller served. WE NEVER HOLD YOUR KEY. A SERVED ANSWER MEANS THE SELLER ACCEPTED IT, NOT THAT MONEY SETTLED. A 402 on a knock that CARRIED a payment is reported as `rejected`, not as a fresh quote, and IT DOES NOT ESTABLISH THAT NOTHING WAS SPENT. `settlementState` and `authorizationState` are the fields to read: a presented authorisation stays usable until it expires or its nonce is spent, so signing again with a fresh nonce can be a SECOND payment. We knock only on endpoints already in the x402 catalogue we read, at most one UNPAID handshake a minute per endpoint shared by everybody, and never on our own host: what we sell has one set of terms and one door. The seller’s answer is yours and is not stored. FREE.
{
"type": "object",
"required": [
"resource"
],
"properties": {
"fill": {
"type": "object",
"description": "Values for slots the SELLER’s own catalogue URL declares, e.g. `{\"doi\": \"10.1000/xyz\"}`. Only declared slots are accepted; a name the template does not contain is REFUSED rather than ignored. Values are percent-encoded and the filled URL is verified to reach the same origin and path shape as the catalogue entry, so a value can never choose the host."
},
"send": {
"type": "object",
"description": "A JSON body, for the many x402 endpoints that take POST rather than GET."
},
"asset": {
"type": "string",
"description": "The token contract the payment authorisation is against (the offer’s `asset`). With it, the token’s own `authorizationState(from, nonce)` is read BEFORE the authorisation is handed over, and an already-used one is refused as a replay (409 `authorization_already_used`). The same read is made after the seller answers whether or not this is sent, from the offer when the seller published one."
},
"query": {
"type": "object",
"description": "Query parameters to add to the catalogue address, e.g. `{\"txid\": \"4a5e…\"}` for an endpoint whose 402 document declares `?txid=`. `inputs` on the unpaid answer names what the SELLER declares; a seller may need one it does not declare, so undeclared names are accepted too. The origin and path stay the catalogue’s and are verified unchanged, a parameter already in the catalogue address is never replaced, values are bounded and refused rather than trimmed, at most ten. Send the SAME values with the paid knock: paying for a call without its input usually buys an error."
},
"payment": {
"type": "string",
"description": "A finished x402 `exact` payment, base64, as YOUR wallet produced it. Sent to the seller under both `X-PAYMENT` and `PAYMENT-SIGNATURE`, because v1 and v2 sellers read different names. A v2 seller matches on the `accepted` field, which must echo the entry of `accepts` you chose VERBATIM — omit it and the answer is “No matching payment requirements”, which reads like a refusal and is an envelope fault. Omit `payment` entirely to read the terms first. WE NEVER HOLD YOUR KEY and this path cannot: it takes a finished authorisation and carries it, because a seller’s host sends no CORS headers and that failure is indistinguishable from the endpoint being down."
},
"describe": {
"type": "boolean",
"description": "True to include `blocks`, the answer read as a person reads it: tiles, tables, pairs and charts where the shape allows one. Always read as a STRANGER’s payload — no unit is added, no tone inferred, and nothing drawn that would claim their numbers are parts of one whole."
},
"resource": {
"type": "string",
"description": "The http(s) endpoint to knock on. It must be one the x402 catalogue already holds; `list_x402_endpoints` finds it. A URL with no scheme is accepted and resolved against the catalogue, so `example.com/api/x` works."
}
}
}{
"type": "object",
"required": [
"resource",
"knocked",
"says"
],
"properties": {
"json": {
"type": [
"object",
"null"
],
"description": "What was served, parsed, where it was JSON and whole. Theirs, not ours, and not stored by us."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"paid": {
"type": "boolean",
"description": "Whether an authorisation travelled with this knock. Stated rather than left to be inferred from the outcome."
},
"says": {
"type": "string",
"description": "What happened, in one sentence. On a `rejected` knock it says the seller refused the authorisation AND that the HTTP answer does not establish on-chain settlement either way; where `authorizationOnChain` was read, its sentence is appended."
},
"text": {
"type": [
"string",
"null"
],
"description": "What was served as text, where it was not JSON or was cut short at 512 KB."
},
"fault": {
"type": [
"string",
"null"
],
"description": "`not-evm`, `not-exact`, `no-token-domain` and the rest: why none of the offered terms can be signed. `not-evm` is a real offer on a chain an EVM wallet cannot sign for, which differs from a malformed one."
},
"offer": {
"type": [
"object",
"null"
],
"description": "The first signable leg, which is the SELLER’s preference and not a recommendation of ours. Null where none of the terms can be met."
},
"blocks": {
"type": "array",
"items": {
"type": "object"
},
"description": "Present only with `describe: true`."
},
"inputs": {
"type": [
"object",
"null"
],
"description": "WHAT THE SELLER SAYS THE CALL TAKES, from their own 402 document (bazaar `info.input` and its schema in v2, `outputSchema.input` in v1): `method`, and `query` / `body` as `{ name, required, description, example }`. `required` is true only where they say so. NULL WHERE THEY DECLARE NOTHING, which is not “it needs nothing”: some sellers need a value they never declare. Theirs and a claim."
},
"accepts": {
"type": [
"array",
"null"
],
"items": {
"type": "object"
},
"description": "EVERY set of terms the seller offered, unchanged, including the ones no browser or EIP-3009 wallet can meet."
},
"knocked": {
"type": "boolean",
"description": "False where the resource is not in the catalogue we read, which is OUR reach rather than a fault of theirs. Everything below is then absent."
},
"outcome": {
"type": "string",
"description": "`served` is an answer in hand. `quoting` is 402 with terms we could read and `charging` 402 without. `rejected` is 402 on a knock that CARRIED a payment, which is the seller refusing what you signed and a different fact from the first 402. `refused` is 401 or 403, where payment is not what is being asked for. On `rejected`, `sellerSays` carries their reason in their words, and the answer DOES NOT ESTABLISH THAT NOTHING WAS SPENT: the seller held your authorisation before answering, and a status code does not report what they did with it."
},
"offering": {
"type": [
"object",
"null"
],
"description": "What the SELLER says this endpoint is and returns: `name`, `description` and `example`, a specimen answer from their own bazaar extension. Theirs and a claim, not a promise of ours."
},
"resource": {
"type": "string",
"description": "The endpoint, as the CATALOGUE holds it, read back. A scheme you left off is resolved here."
},
"signable": {
"type": "array",
"items": {
"type": "object"
},
"description": "The legs an `exact` EVM wallet can sign, in the seller’s own order, each flattened to what signing needs. `amountRaw` is a STRING in the asset’s smallest unit; `approxUsd` is present ONLY where the seller’s own catalogue entry publishes both figures for the SAME token, since nothing in a payment-requirements document names a token’s decimals. `terms` is their entry VERBATIM, to echo back as `accepted`. A real offer has several (agent402.tools publishes nine), and WHICH ONE TO PAY ON IS YOURS TO CHOOSE because the chain is inside the signed message and a signature made on a chain you hold nothing on is valid and fails at the transfer."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"faultSays": {
"type": [
"string",
"null"
],
"description": "The same thing in a sentence."
},
"latencyMs": {
"type": [
"number",
"null"
],
"description": "How long it took US, which is a fact about this request as much as about the host."
},
"truncated": {
"type": "boolean",
"description": "TRUE where the answer was cut at 512 KB, and then `json` is null EVEN IF the cut happens to parse: half a list read as a whole one is a figure nobody could stand behind."
},
"httpStatus": {
"type": [
"number",
"null"
],
"description": "What the host answered, unchanged."
},
"sellerSays": {
"type": [
"string",
"null"
],
"description": "Why the seller refused, IN THEIR WORDS, lifted from their 402 document. Null where they gave none, which is said rather than filled in."
},
"settlement": {
"type": [
"string",
"null"
],
"description": "The seller’s settlement receipt, verbatim. THEIR claim that the authorisation reached a chain, carried as theirs and never restated as a reading of ours. A served answer means the seller accepted the authorisation, not that money settled: somebody still has to submit it on chain, and this is the seller saying so rather than us."
},
"askedWithQuery": {
"type": [
"object",
"null"
],
"description": "The query parameters we added, read back. Null where you sent none."
},
"settlementState": {
"enum": [
"none-presented",
"claimed-by-seller",
"unknown"
],
"type": "string",
"description": "WHAT THIS ANSWER ESTABLISHES ABOUT THE MONEY, which is less than the status code suggests. `none-presented`: no authorisation was handed over, so there is nothing that could settle. `claimed-by-seller`: they returned a receipt — THEIR claim, carried, never verified by us. `unknown`: an authorisation WAS presented and this answer says nothing about its fate. A 402 or a timeout after a payment reads `unknown`: it does NOT establish that the money is safe."
},
"authorizationState": {
"enum": [
"none-signed",
"presented-may-still-be-usable"
],
"type": "string",
"description": "WHAT MAY STILL BE DONE WITH WHAT THE CALLER SIGNED. `none-signed`: nothing was signed, so nothing can be submitted. `presented-may-still-be-usable`: it was handed over and a valid authorisation stays usable until it expires or its nonce is spent — so signing again with a fresh nonce can be a SECOND payment. Check the previous transaction before you do."
},
"authorizationOnChain": {
"type": [
"object",
"null"
],
"description": "The independent check (2026-11-04): the token contract’s own `authorizationState(from, nonce)` after the seller answered. `state` is `used` (the transfer happened on chain, whatever the seller said), `unused` (no money had moved at `block`; `usableUntil` says until when whoever holds it can still submit it, `expired` whether that has passed) or `not-checked` (the chain or token could not be read; nothing is then said about the money). Null on an unpaid knock."
}
}
}{
"title": "Knock on an endpoint, with your own payment",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": false
}endpoint_disagreements — Endpoints that contradict their own listing
WHICH ENDPOINTS CONTRADICT THEIR OWN LISTING: they ask to be paid at an address the catalogue entry they published does not name. The contradiction appears only when the live handshake and the published listing are held together: either half on its own looks ordinary, since a reachable endpoint and a stated payee are each unremarkable alone. A disagreement is between two statements by the SAME party, so it is theirs to explain and never ours to resolve, and it is NOT evidence of fraud: a seller may have moved their payee and not relisted. Measured only over endpoints we have knocked on, which is our reach and not the catalogue; `comparable` is the denominator, because an endpoint that names no payee cannot disagree and is counted neither way. THE COUNTS ARE FREE AND THE LIST IS NOT: `items` comes back NULL without a plan rather than empty, since an empty list would say we found none. Any SINGLE endpoint is free — `test_purchase` and `list_x402_endpoints` both carry its own reading — because that one is reproducible in two requests and because withholding something that can cost a buyer money is not a thing we do.
{
"type": "object",
"properties": {
"limit": {
"type": "number",
"description": "Rows once a plan is held. Default 50, capped at 200."
}
}
}{
"type": "object",
"required": [
"knocked",
"comparable",
"disagreeing",
"sellers",
"limits",
"says"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "The finding in a sentence, with its denominator."
},
"items": {
"type": [
"array",
"null"
],
"items": {
"type": "object"
},
"description": "One per disagreeing endpoint: `resource`, `listedPayee`, `quotedPayee`, `seenAt`. NULL without a plan — never an empty array, which would say we found none."
},
"limits": {
"type": "string",
"description": "What this is measured over and what a disagreement is not."
},
"knocked": {
"type": "number",
"description": "Endpoints we have knocked on at all. The population every other figure here is drawn from."
},
"sellers": {
"type": "number",
"description": "How many distinct listed payees those belong to. Worth more than the endpoint count: it says whether this is scattered or a few parties contradicting themselves repeatedly."
},
"serving": {
"type": "number",
"description": "Of those, how many answered with a price at the most recent knock."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"comparable": {
"type": "number",
"description": "How many could be compared at all: both the endpoint and its listing named a payee. The DENOMINATOR for `disagreeing`."
},
"notServing": {
"type": "number",
"description": "Gone, unreachable or erroring at the most recent knock. A fact about our last look, not a verdict on the seller."
},
"disagreeing": {
"type": "number",
"description": "How many name a payee their own listing does not."
}
}
}{
"title": "Endpoints that contradict their own listing",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}list_x402_endpoints — What this address sells
WHICH ENDPOINTS A PAYEE SELLS, or which ones match some text. The sharpest check a payment can get is the endpoint being bought, and until this a caller had to know its URL by heart. Nearly every catalogued resource names a payee, and the catalogue holds tens of thousands of them. Every field is the SELLER’s own catalogue entry and never our reading of it, and a listing is not evidence that the endpoint answers — `test_purchase` knocks on it. `priceUsd` is their published price and `priceRaw` is the figure in the asset’s own base units; the two are different claims. FREE.
{
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "Part of a URL, matched case-insensitively."
},
"limit": {
"type": "number",
"description": "Default 20, capped at 100."
},
"payee": {
"type": "string",
"description": "An address, to list what it sells."
}
}
}{
"type": "object",
"required": [
"items",
"count",
"says"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "What the list is and is not: the seller’s own entries, and a listing is not evidence the endpoint answers."
},
"count": {
"type": "number",
"description": "How many came back, which is at most `limit` and says nothing about how many exist."
},
"items": {
"type": "array",
"items": {
"type": "object"
},
"description": "One per catalogued endpoint: `resource`, `payTo`, `priceUsd` and `priceRaw`, `asset`, `network`, the seller’s own `description` and when the listing was last seen — every one of those is the SELLER’s claim. `seenByUs` is the only field here that is OURS and the only one they cannot write: `state` (`answering`, `not-answering`, `unconfirmed` — one failure, which is a reason to ask again and not a finding — `seen-once`, or `never-knocked`), `answered` of `knocks`, `lastKnocked`, and an outage as a RANGE in `downAtLeastHours` / `downAtMostHours` rather than as a duration, because a stop happened somewhere between two knocks and that window is as wide as the gap between them. `never-knocked` and `seen-once` are statements about OUR reach, not about the seller, and today they cover most of the catalogue. The full timeline is on `test_purchase`."
},
"capped": {
"type": "boolean",
"description": "TRUE where the page came back full, so there are probably more; FALSE means the catalogue holds no further match and the list is everything we have, not just the first slice. This replaced a declared `limit` that the answer never carried — the envelope owns the paging keys — and it answers the question that field was for in one go."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
}
}
}{
"title": "What this address sells",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}list_policies — The policies a decision can be made under
The policies evaluate_action can be run under, in full: every rule, its id and the sentence it checks. Published deliberately, including to the agents being evaluated — a rule nobody can read is a rule nobody can correct. Free.
{
"type": "object",
"properties": {}
}{
"type": "object",
"required": [
"data"
],
"properties": {
"data": {
"type": "array",
"items": {
"type": "object"
},
"description": "Each policy with its slug, version, intent, ceiling, coverage floor and rules."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "What these policies are and are not. They are rules a decision is checked against, never a ranking of the agents evaluated under them."
},
"default": {
"type": "string",
"description": "The slug of the policy used when a call names none. Read it rather than assume: the default is an operator setting and can change without any tool changing."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
}
}
}{
"title": "The policies a decision can be made under",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}counterparty_check — What the chain records about one address
Before paying or hiring an agent: what the chain records about that address. Independent payers (counterparties that paid it and were never paid back), the ones it does pay back, how concentrated its custom is, WHETHER THOSE INDEPENDENT PAYERS ARE INDEPENDENT OF EACH OTHER under `independence`, WHAT IT ITSELF RESTS ON under `restsOn` (how many parties it pays and how much of its spending goes to the largest of them), how its ACP jobs ended, whether its advertised service answers, and when it was last paid. Free. Three limits to repeat whenever quoting it: independent means no payment BACK on the rails we read, NOT proof the payers are different parties, since one owner can fund many addresses that never pay each other; an all-time record says nothing about whether the agent still works — 44% of agents ever paid had not been paid in 90 days, measured on 29 September 2026; and `restsOn` counts every party this address has PAID rather than purchases from identified sellers, so a recipient there has not been shown to supply anything, and `restsOn.suppliers` of zero means it has paid nobody on a rail we read, never that it depends on nobody. Takes an EVM address, a Cardano payment address (addr1…) or a Solana address.
{
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"type": "string",
"description": "The address you are about to pay or hire."
}
}
}{
"type": "object",
"required": [
"address",
"format",
"observations",
"limits"
],
"properties": {
"acp": {
"type": "object",
"description": "How its jobs ended, where it has any."
},
"mech": {
"type": "object",
"description": "Deliveries on the Olas mech marketplace, and what was paid for them where we hold it. A rail many agents never touch; zero here is not a mark against an address."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"x402": {
"type": "object",
"description": "Sales and payers on the x402 rail. Payers are counted as parties, not payments: one buyer paying fifty times is one payer."
},
"parts": {
"type": "object",
"description": "Which parts of the check answered inside their budget: `complete`, `ran` and `failed`. A part in `failed` is MISSING from this answer, not absent for the address: read its fields as unknown. If the record itself does not answer, the route returns 503 with Retry-After instead."
},
"format": {
"type": "object",
"description": "What kind of address this is and what we read for it."
},
"limits": {
"type": "string",
"description": "What this answer cannot tell you. Quote it with the numbers."
},
"masumi": {
"type": [
"object",
"null"
],
"description": "Escrows on the Masumi rail. Null where we hold no reading for this address at all, which is not the same as zero escrows."
},
"address": {
"type": "string",
"description": "The address as we read it, normalised. Compare it with what you sent before acting on the answer."
},
"paidFor": {
"type": [
"object",
"null"
],
"description": "What it was paid for, in the subject’s own words."
},
"restsOn": {
"type": [
"object",
"null"
],
"description": "suppliers, payments, spentUsd, largestShare and recent.{suppliers90,largestShare90}: every party this address has PAID. NOT the mirror of `dealings` — a dealing is a sale and needs a seller on the other end, while a payment out counts whoever receives it, so a recipient here may be a relayer, a settlement route or a customer being paid back. `largestShare` is null below three suppliers, where it could only be a half or a whole. Zero suppliers means it has paid nobody on a rail we read, never that it depends on nobody: a model, an endpoint, a host and a key are dependencies no payment rail shows."
},
"alsoHere": {
"type": [
"object",
"null"
],
"description": "The same address on the capital side — a vault or an operator — with the check to call. A match on the address, never an identification of the party."
},
"dealings": {
"type": "object",
"description": "A DEALING IS A PAYMENT RELATIONSHIP WE OBSERVED, AND NOT WORK PERFORMED. On the Olas mech rail a subject can show more than 330,000 DELIVERIES beside `dealings: 0`, and both figures are true: a delivery is work the mech returned, a dealing is money we watched move. Never read one as the other and never rank a mech subject against an ACP one on this field. What each rail can and cannot witness is fixed and published: x402 records payment and NO outcome; ACP records both, because a job carries its own payment and reaches a phase; Masumi records both through escrow release or refund; the mech rail records deliveries and no payment we see. rails, chains, counterparties, independent, largestShare, firstPaid, lastPaid. `independent` means no payment BACK on the rails we read — NOT proof the payers are different parties, since one owner can fund many addresses."
},
"identity": {
"type": "object",
"description": "Registrations it holds. Claims, never verification."
},
"services": {
"type": "object",
"description": "Callable services it declares, and how many answered our probe."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"independence": {
"type": [
"object",
"null"
],
"description": "payers, linked, viaParties, largestGroup: payers of this address that share an OBSCURE third party with another of its payers. This is the test of `dealings.independent`, which counts payers that never paid it back and has never been proof they are different parties. A party paid by two to ten addresses in all counts; a hub paid by more does not, because two agents both paying a hub have shown only that the hub is popular. EVIDENCE OF A LINK, NEVER PROOF OF ONE OPERATOR — two payers paying one small service are two customers of that service. `groups` names which payers and through what and needs the `independence.test` capability; where it is withheld, `capability` says so. A linked count of zero is not proof they are unrelated: one owner funding addresses that never touch each other again leaves nothing for this to find. `differentPopulation` IS PRESENT WHEN THIS BLOCK AND THE RECORD ABOVE IT COUNT DIFFERENT NUMBERS OF PAYERS, which happens because they are two jobs on two schedules: it carries `why: \"different_population_snapshots\"`, both counts and both dates. Neither figure is wrong, and the linkage here describes the population in THIS block, not the larger one. It is null when they agree, so its presence means something."
},
"observations": {
"type": "array",
"items": {
"type": "object"
},
"description": "The findings, each with its evidence class and its own words. Read these first."
}
}
}{
"title": "What the chain records about one address",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}vault_sanctions_screening — Sanctions list screening of a vault (paid)
One vault against the OFAC SDN list: its contract roles and its direct holders where the holder list is complete, exact address matches only, with the list version and whether it is current. Paid ($0.01). States: EXACT_LISTED_ADDRESS_MATCH (a match of an identifier, for review, not a finding about who controls it), NO_EXACT_MATCH (bounded: the list is not exhaustive, so this never means no sanctioned party), UNKNOWN (an incomplete holder list or a stale list). Never describe a result as clean, compliant or safe. Quote `says` and `limits`.
{
"type": "object",
"required": [
"slug"
],
"properties": {
"slug": {
"type": "string",
"description": "Vault slug or contract address."
}
}
}{
"type": "object",
"required": [
"state",
"list",
"says",
"limits"
],
"properties": {
"list": {
"type": "string",
"description": "Which list, e.g. OFAC SDN."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "The bounded statement to quote."
},
"scope": {
"type": "array",
"items": {
"type": "string"
},
"description": "roles, direct holders."
},
"state": {
"type": "string",
"description": "EXACT_LISTED_ADDRESS_MATCH, NO_EXACT_MATCH or UNKNOWN."
},
"limits": {
"type": "string",
"description": "What this screening cannot show. Quote it."
},
"matches": {
"type": "array",
"items": {
"type": "object"
},
"description": "Each exact match with role, listed entity, programme and the official record link."
},
"screened": {
"type": "number",
"description": "Addresses screened."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"candidates": {
"type": "array",
"items": {
"type": "object"
},
"description": "Same EVM hex listed under another currency: for review, never a match."
},
"listCurrent": {
"type": "boolean",
"description": "False when our copy is past 36 hours: the state is then UNKNOWN."
},
"notScreened": {
"type": "number",
"description": "Addresses that should have been screened and were not."
},
"listPublishedOn": {
"type": [
"string",
"null"
],
"description": "The list version screened against."
}
}
}{
"title": "Sanctions list screening of a vault (paid)",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}vault_due_diligence — Due-diligence report for agents (paid)
The due-diligence report on one vault, cut to what an agent acts on: overallAssessment (published, conditional or withheld; publishedBand is null when withheld and partialBand is never the verdict), findings above the band, each risk dimension, capital and withdrawal bounds, metricStatus (each input current, indicative, stale, unavailable or not-read, with its times and expiry), completeness, warnings and agentRules. Follow agentRules when you answer: they say what must be quoted and what must not be said. PAID ($5 over x402, or inside a plan with due diligence). The digest names the whole issued document; verify it at /v1/reports/{digest}.
{
"type": "object",
"required": [
"slug"
],
"properties": {
"slug": {
"type": "string",
"description": "Vault slug or contract address."
}
}
}{
"type": "object",
"required": [
"digest",
"overallAssessment",
"agentRules"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"digest": {
"type": "string",
"description": "SHA-256 of the whole issued document."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"agentRules": {
"type": "array",
"items": {
"type": "string"
},
"description": "What your answer must and must not say. Follow them."
},
"dimensions": {
"type": "array",
"items": {
"type": "object"
},
"description": "Each risk dimension with its level and what set it."
},
"completeness": {
"type": "object",
"description": "Coverage by area, missing fields, currentDecisionStatus, nextCurrentInputExpiryAt, snapshot."
},
"metricStatus": {
"type": "array",
"items": {
"type": "object"
},
"description": "Per input: status, times, expiry, whether the result used it."
},
"overallAssessment": {
"type": "object",
"description": "status, publishedBand, publishedScore, partialBand, partialScore, reasonCode, reason, findingsAboveBand, inputsNotCurrent, judgedAt."
}
}
}{
"title": "Due-diligence report for agents (paid)",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}vault_drift — What changed on a vault (paid)
What changed on one vault, day by day, between the stored daily due-diligence reports: the overall assessment (including when it became withheld or published again), each risk dimension, the withdrawable share, capital, control roles and the timelock, and the tested withdrawal. Paid ($0.01). A change can be the vault, what we could read, or our model; days with no stored report are listed under `gaps` and not compared. Quote `limits` with it.
{
"type": "object",
"required": [
"slug"
],
"properties": {
"days": {
"type": "number",
"description": "Window, 2 to 90 days. Default 30."
},
"slug": {
"type": "string",
"description": "Vault slug or contract address."
}
}
}{
"type": "object",
"required": [
"slug",
"changes",
"limits"
],
"properties": {
"gaps": {
"type": "array",
"items": {
"type": "object"
},
"description": "Days with no stored report; not compared."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "The answer in one sentence."
},
"slug": {
"type": "string",
"description": "The vault, as resolved."
},
"limits": {
"type": "string",
"description": "What a change here can and cannot mean. Quote it."
},
"changes": {
"type": "array",
"items": {
"type": "object"
},
"description": "Per pair of days with a change: from, to, both digests and changes[] with kind, what, before, after, direction and says."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"documents": {
"type": "number",
"description": "Stored reports in the window."
}
}
}{
"title": "What changed on a vault (paid)",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}counterparty_evidence — Evidence on a counterparty before trading (paid)
Before trading with an agent or service: what the records actually say about it, in one answer. Identity links with their strength (declared by the agent, observed by us, or corroborated by a second party), settled payment history with first and last payment, active months and long silences, counterparties with top-1 and top-3 share, Herfindahl and repeat ratio, each catalogued service with its price, how often it was paid at that price and whether delivery can be observed at all, what we cannot see, and whether there is enough history to conclude. Paid ($0.01). Settled payments, distinct counterparties and delivery evidence are SEPARATE figures and never a score: much measured agent-payment volume is self-dealing, wash or tests, and a payment does not show a service was delivered. Read `sufficiency` and `warnings` before any figure, and quote `guard` with them.
{
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"type": "string",
"description": "The agent or service address you are about to trade with."
}
}
}{
"type": "object",
"required": [
"address",
"sufficiency",
"guard"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"guard": {
"type": "string",
"description": "Quote it with the figures."
},
"address": {
"type": "string",
"description": "The address as we read it, normalised. Compare it with what you sent."
},
"history": {
"type": "object",
"description": "byRail, firstSettled, lastSettled, monthsActive, longestGapDays, gaps; status."
},
"delivery": {
"type": "object",
"description": "Per service: price, paid at that price, answered, deliveryObservable, says; status."
},
"identity": {
"type": "object",
"description": "links[] with kind, strength (declared | observed | corroborated), source, asOf; status."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"cannotSee": {
"type": "array",
"items": {
"type": "string"
},
"description": "What the records behind this answer cannot show for this address. Quote it with the figures."
},
"sufficiency": {
"type": "object",
"description": "verdict (sufficient | insufficient-history) and missed[] naming each threshold."
},
"counterparties": {
"type": "object",
"description": "unique, dealings, top1Share, top3Share, hhi (null below 10 dealings), repeatRatio, flags; status."
}
}
}{
"title": "Evidence on a counterparty before trading (paid)",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}vault_check — What the record shows about one vault
Before putting capital into a vault: what the record shows about it. The headline is what a holder ACTUALLY EARNED set against the advertised rate, with the risk band, the components that apply, who runs it, and `keys`: who can move the money. Read `keys.actsAlone` before any return figure, because a strategy can be excellent and one private key away from being emptied; across what we read on 6 October 2026, a single key controlled $1.26bn and 47 of 249 multisigs required one signature; `keys` gives today’s reading. Free. Read `earned.comparability` AND `earned.representativeness` before quoting the ratio: the first says whether a realised rate can be compared with the advertised one at all, the second whether the result is worth planning around rather than one day that moved, and `cannotSee` lists everything we could not establish for this vault rather than leaving it as an absence you have to notice. `changedHands` carries whether those keys have been MOVING, which is the due-diligence question `keys` cannot answer — read its `watchedSince`, because a count of zero means nothing moved since we started watching and not that the vault has never changed hands. Rates are decimal fractions: 0.0432 is 4.32% a year.
{
"type": "object",
"required": [
"slug"
],
"properties": {
"slug": {
"type": "string",
"description": "The vault’s slug, or an address we can resolve to one. Call list_vaults first if you have only a name."
}
}
}{
"type": "object",
"required": [
"slug",
"cannotSee",
"says",
"changedHands"
],
"properties": {
"keys": {
"type": [
"object",
"null"
],
"description": "Who can move the money, read from the contract. `holds` is multisig, eoa, contract or timelock; `signaturesRequired` is how many a multisig needs; `actsAlone` is true where ONE party can act with no second signature, which covers a single key AND a multisig with a threshold of one — read that rather than the word multisig. `canBeHalted` is null where neither pause function answered, which may mean nobody can stop the vault or may mean its pause is behind a name we do not read — do not read a null as nobody. `notice.basis` is the field to check before trusting a delay: `measured` means we read one on the contract, `venue-states` means the VENUE publishes a notice period and our own read of the controller found none — the figure is theirs, `statedBy` names them, and `cannotSee` says we could not confirm it, which covers 199 open vaults holding $4.99bn — `not-a-timelock` means the controller we read is not a timelock and nobody states one either, `unread` means we did not ask. A measurement always outranks a statement. Null means we have not read this vault’s control at all, which is not the same as nobody controlling it."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"name": {
"type": [
"string",
"null"
],
"description": "The vault’s name as the venue publishes it. Their words, and two venues may use the same one."
},
"risk": {
"type": [
"object",
"null"
],
"description": "The band, the components it was measured from, and `dimensions`. READ `dimensions` BEFORE THE BAND. The band covers market, liquidity, concentration and operating risk and **does not contain the controller at all** — `bandCovers` says so on the object. QUOTE `bandCarries` RATHER THAN `band` ALONE: it is the band in one sentence with every dimension reading worse and the controller, the same clause the report’s cover prints. A vault can read `low` while a single key can empty it, which is exactly what two did. Each dimension carries its own level, the components behind it, and `outsideTheBand`; `controller` and `evidence` are both outside it. A null level is UNREAD and never low. AND READ `dimensionsWorseThanBand`: it names any dimension reading WORSE than the band, which the band itself does not show — one vault reads band `low` with market risk `severe`, and that is not an error but it does surprise. `worseThanBand` lists them, `worstDimension` is the worst any of them reads, and `says` puts it in words. Null only where the rows are withheld. THERE IS NO OVERALL FIGURE and there will not be one: a composite would be the number every reader quotes and none can check. `dimensions` is NULL, with `dimensionsWithheld` beside it, where this caller has read more DIFFERENT vaults in the period than the level allows — 50 a day anonymously, 500 a month with a key, 2,000 on a plan, and never counted at all on the scale plan which buys the whole surface. Nothing is slowed down and nothing else on the answer is withheld; a vault already read in the period stays open whatever the count. Past that line a caller is not checking a vault, they are reading the population, and the population is the product. EACH COMPONENT NAMES THE QUANTITY IT READ: `readAs` is the reading, `basis` says which column it came from and `computation` is the arithmetic in words, so a figure here can be re-derived rather than taken on trust and compared with a figure elsewhere rather than merely disagreed with. On the exit component `basis` is `declared-withdrawable` (the venue’s own exit figure, the SAME number `vault_exits.declaredWithdrawableUsd` divides), `idle-floor` (uninvested cash, a LOWER BOUND used where the venue publishes no exit figure or where its figure equals the cash, and the sentence then begins \"at least\" and says which) or `queue-float` (the same cash on a vault whose exit we have READ as a queue: the arithmetic is identical and the claim is not, because nothing there can be withdrawn without first filing a request, so that sentence never says \"could be withdrawn\"). Until 2026-10-17 it read the floor unconditionally and described it as what could be withdrawn, which gave two answers to one question on 246 vaults holding $4.26bn. (This used to tell a caller to read `bandRange` first. That field is on the FULL vault document and this tool has never returned it, so the instruction could not be followed; the full document is one link away in `full`.)"
},
"says": {
"type": "string",
"description": "The finding in one sentence. Where we cannot say what a holder earned, it says that plainly rather than reporting the venue’s own figure as ours."
},
"slug": {
"type": "string",
"description": "The vault this answer is about, read back so it cannot be attached to the wrong one."
},
"earned": {
"type": [
"object",
"null"
],
"description": "Advertised against realised, with the ratio, the verdict and the window. TWO SEPARATE QUESTIONS live here and both must be read: `comparability` is whether the two rates can be set against each other at all, and `representativeness` is whether the result is worth planning around — `typical` where the movement is spread across the window, `event-dominated` where ONE step carries the return, `unknown` where we did not look at the shape. A vault can be `comparable` AND `event-dominated` at once: one was, with a single day accounting for 52% of the movement its return comes from, and a reader seeing only `comparable` planned around a figure that describes an event. `biggestStepShare` is that day’s share of total movement. Null where we have too few price points to say. `band` carries the exact boundaries behind `verdict` — below `short` of the advertised rate is short, above `beating` is beating, everything between is in-line — so a ratio of 0.70 reading `in-line` can be checked rather than guessed. AND `advertisedOverTheSamePeriod` IS THE ONE TO QUOTE WHERE IT IS POPULATED: it answers what the venue was advertising WHILE the return was being earned, time-weighted over the same days, which is a different and better question than what it advertises today. `benchmark` is that rate, `ratio` is the realised divided by it, and `coversWindow` says how much of the period the quotes reach. READ `excluded`: quotes whose reward composition is unread are counted there and NOT averaged in, because a share price carries the asset yield and nothing else, so a rate that may include incentives cannot be set against one. On one vault that distinction was the whole answer — 93 days of quotes were two series, 26 of ours averaging +1.12% with the composition read, and 67 backfilled from the venue averaging −0.20% with 61 negative and no reward reading at all; averaging them gave 0.17%, which is a number about neither. `benchmark` null means too little of the period is covered to average, and today’s rate is NOT a substitute — but READ `overCommonPeriod` there, which is offered exactly then: it shrinks the RETURN to the days the quotes cover so both sides span the same window, and says in its own sentence that it is the weaker of the two because a short window annualised magnifies whatever happened in it. Never both: one benchmark answering and the other explaining itself is a reading, two answering would be two numbers to choose between. No ratio is drawn where the benchmark is not positive. AND READ `sourcesDisagree` WHERE IT IS POPULATED: it says how far the venue and the aggregator are apart on the days both answer, and they are apart on every Morpho V2 vault measured — PayPal USD Main agreed on 1 of 26 days with a median gap of 0.51 percentage points, Galaxy USDC on 7 of 26 at 0.75, which on a rate near 2.85% is a fifth of the figure. Neither source is corrected into the other. \"What the venue advertises\" is not one number, and a caller comparing a realised return against an advertised rate must say which advertised rate it used. AND READ `quotedOverWindow` BEFORE THE RATIO: the ratio divides a realised rate measured over `overDays` by the rate the venue quotes TODAY, which are different periods. That object holds the quotes we recorded while we watched — `mean`, `low`, `high`, `daysHeld` of `ofWindowDays`, `coversWindow`, and `quoteSits` saying whether today’s quote is `typical`, `near-its-high` or `near-its-low` for this vault. One vault read 0.70 against today’s quote and 0.996 against the average it had actually been advertising. Null where we hold fewer than five days of its quotes. AND WHERE `comparability` IS `fixed-unit-price`, READ `whyFixed` BEFORE CONCLUDING ANYTHING: `unitBasis` of `one-to-one-by-construction` means the contract reports exactly as many assets as shares, so the price CANNOT move and a flat price is the design rather than a measurement of performance — 23 of the largest 25 such vaults, holding $1.202bn across Concrete, Euler and Morpho Vaults V2, are in that state. `flat-for-another-reason` means the two are NOT equal and we have not explained it, which is published as our open question rather than left to look like a vault that earns nothing. `assetsMoved` false, with `lookbackDays`, says the vault’s own holdings did not move at all in that window. `assetHeldShare` is where the capital actually is, from `totalAssets()` against the asset token’s `balanceOf(vault)`: 1 means every unit sits in the vault as cash with nothing borrowed from it, so there is no interest to earn and the flat price is the proof; 0 means every unit is deployed somewhere else, so whatever is earned is downstream of the contract and could never show in its price. Measured over the twenty largest such vaults the split was total: eleven Euler vaults holding $212M at 1, nine Concrete and Morpho V2 vaults holding $970M at 0, nothing in between. AND WHERE `assetHeldShare` IS 0, READ `deployedInto` AND `deployedIntoSays`: that is where a return would have to come from, since the vault’s own price cannot carry one. `into` is the contracts it deploys into, read from the vault itself and published by no venue; `end` is how far we got — `price-moves` means the next contract IS a vault whose unit price moves, so a return is readable one level down; `price-fixed-too` means it reports as many assets as shares as well, so the flatness is not one contract’s artefact; `not-a-vault` means it answered with an interface we do not recognise, and `stoppedAt`, `implementation` and `selectors` say exactly where OUR walk ended so a caller can continue it; `attested-accounting` is the one to read on the largest of them: the contract does NOT derive what it is worth from what it holds, a multisig POSTS the figure, and `accounting` carries who may post it, when they last did, how far one posting may move it and when a posting goes stale. Those bound the POSTING and check nothing about the number, so a flat unit price upstream is not evidence that nothing was earned and a moving one would not be evidence that anything was. Its interface was resolved selector by selector against a signature database holding their preimages, which is a lookup and not a guessed ABI. `unreachable` means nothing answered, which is an endpoint and not a fact about the vault. A stopped walk is never a finding that the vault earns nothing, and `deployedIntoSays` says so in words. Null where the vault names no strategy or the walk has not run for it yet. AND `delivery` SAYS WHICH COMPARISON THE HEADLINE RESTS ON: `basis` `same-period` (what was advertised across the window the return covers), `common-period` (both sides shrunk to the days our quotes of known composition cover, `days` says how many) or `none`, where no verdict against a promise is given and `currentQuoteContext` carries the ratio to today’s quote as context only. Quote `delivery.verdict`, not `verdict`, when saying whether a holder earned what was advertised."
},
"operator": {
"type": [
"object",
"null"
],
"description": "Who runs it, as the venue names them."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"cannotSee": {
"type": "array",
"items": {
"type": "string"
},
"description": "What we could not establish. Empty means every check ran."
},
"exitPaths": {
"type": [
"string",
"null"
],
"description": "ON A MORPHO V2 VAULT, ITS WAYS OUT AS READ: whether a liquidity adapter is set (without one an ordinary withdrawal pays from idle only), and each adapter’s forced-deallocation penalty. The report’s exit section prints the same sentence. Null where not V2 or not read."
},
"sameBlock": {
"type": [
"object",
"null"
],
"description": "CAPITAL AND POSITIONS AT ONE BLOCK, on Morpho vaults over $10M: `totalUsd` (the vault’s totalAssets) against `inMarketsUsd` (its positions, interest accrued to that block as the vault accrues it) plus `lostUsd` where the vault keeps losses on its books; `gapUsd` is what the positions do not explain at that moment and `reconciles` is true within a millionth. It confirms agreement at that block and nothing about readings taken at other times. `idleUsd` is on the vault’s own balance and outside its total. Null where the job has not read this vault."
},
"capitalUsd": {
"type": [
"number",
"null"
],
"description": "What the vault holds, in US dollars, at `capitalAsOf`. Null where we cannot price the underlying — unread, never zero."
},
"redemption": {
"type": [
"object",
"null"
],
"description": "HOW a holder gets out, read from the vault contract, and a DIFFERENT question from how much could be withdrawn. `mechanic` is `synchronous` (a redeem settles in the transaction that asks, subject to the vault having the money — NOT a promise of an instant exit), `request-and-claim` (ERC-7540: you ask and are paid when the vault settles it, and a liquidity figure cannot answer that), `request-via-queue` (the vault’s own teller carries no withdraw and no redeem at all: leaving means approving a SEPARATE queue contract and filing a request there for a filler to settle, so neither ERC-7540 getter is on this vault and where you stand is a question for the queue — ether.fi and Veda BoringVaults are this shape), or `unread` (our reading fell short, not a vault with no way out). `paused` is the contract’s own switch and is decisive when true. `feeBps` is what leaving costs, which nobody publishes beside a yield. `hours` is a notice period the contract STATES and is null where it states none — which is not zero. `queueReadable` is whether a holder can ask the contract where they stand: true where ERC-7540’s `pendingRedeemRequest` AND `claimableRedeemRequest` are both present, FALSE where a request can be filed and neither getter exists, null where there is no queue at all. Lagoon-pattern vaults carry the full set and Upshift’s carry neither, so two vaults reading `request-and-claim` can differ on the thing a holder feels most — once filed, whether anything will tell you what became of it. Found by lifting every selector out of the implementation bytecode rather than by guessing names."
},
"exitHistory": {
"type": [
"object",
"null"
],
"description": "HOW OFTEN THE DOOR WAS SHUT, from our own daily register rather than today’s figure. `readings` is how many daily readings we hold and `firstDay`/`lastDay` the span; `lockedDays` is how many sat under 1.25% of supply withdrawable, `lockedShare` that as a fraction, `longestSpell` the longest UNBROKEN run of such readings, and `worstExitShare` the least that could ever leave. Counted in READINGS and never in calendar days: a day missing from the register is a day nobody looked, not a day the vault was fine, and the sentence says so. Under fourteen readings it describes rather than summarises, because a share over four readings is a number that looks like a record and is not one. The register has run daily since 18 August and cannot be backfilled at any price, so this answer grows more valuable and no competitor can produce it retrospectively. Null where we hold no reading for this vault, which is our record rather than a verdict on it."
},
"measurement": {
"type": "object",
"description": "Whether we still measure this vault. `state` is `measured`; `late` where the capital was last read more than 36 hours ago and under a week, with `hoursSince` and `says` giving its age; or `stopped` where the one source it had has not answered for more than seven days: the figures are then the last ones measured, on `lastMeasuredAt`, and `says` names why in the sentence the vault page shows."
},
"changedHands": {
"type": [
"object",
"null"
],
"description": "WHETHER THOSE KEYS HAVE BEEN MOVING. `keys` says who controls the vault today; this says what has changed and when, which is the question an investment committee asks and a snapshot cannot answer. `changes` is newest first, each with `at`, `field`, `before`, `after` and one sentence in `says`; the fields watched are the owner key, the curator, the guardian, the authority, the proxy admin, the effective controller and its kind, the timelock delay, the multisig threshold and the pause. READ `watchedSince` BEFORE READING `count` AS A FACT ABOUT THE VAULT: a count of 0 means nothing moved since that date and NOT that the vault has never changed hands — anything earlier predates this platform holding control state and we do not reconstruct it. A failure to read a field is never filed as a removal, so `the owner key was removed` means we read an owner and then read its absence, not that an RPC timed out. Free, per vault; the population of it, every vault whose control moved this month, is on the paid screen."
},
"marketHistory": {
"type": [
"object",
"null"
],
"description": "THE FULLY BORROWED MARKET THIS VAULT LENDS INTO, READ OVER ITS HISTORY, where it holds a tenth of itself in a Morpho market at least 99.9% borrowed: `fullThroughout` and `ceilingThroughout` from the chain at blocks about thirty days apart (supplied, borrowed, the rate model’s rate at target), and `noRepaymentSince`, when anyone last borrowed or repaid, from the venue’s transaction index. Where nobody has repaid for months on a market at the rate ceiling, a rising share price is interest booked and not paid in. `observed` and `readAs` are the report’s own sentences. Null elsewhere."
},
"capitalAccount": {
"type": [
"object",
"null"
],
"description": "WHERE A LENDING VAULT’S CAPITAL IS, the same account the report and the vault page draw: `heldUsd` splits into `lentUsd` (lent in the markets we read), `notBorrowedUsd` (supplied to them and not borrowed) and `outsideUsd` (in no market we read: may be cash or another allocation, NOT verified as cash). `venueWithdrawableUsd` is the venue’s own figure and overlaps the others: never add it to them. `residualUsd` is how far the venue’s figure misses not-borrowed plus outside. Where two readings disagree, `gapUsd` names the difference and `lentIsEstimate` is true: lent is then capped and is an estimate, not a measurement at one moment. Null where the vault lends into no market we read."
},
"exitSimulation": {
"type": [
"object",
"null"
],
"description": "WITHDRAWALS SIMULATED AT ONE BLOCK, where the vault is a Morpho vault over $10M: `withdraw` run by eth_call as its largest depositor (nothing sent) for $1M, a tenth of the vault, the venue’s withdrawable figure and the holder’s whole position, each capped at what the holder owns. `tries[]` carries `usd`, `label`, `ok` and the contract’s revert `reason`; `says` is the report’s sentence. `maxWithdrawUsd` is the vault’s own maxWithdraw(holder) as a LIMIT and is null where that answer is not one (a 0 beside a withdrawal that passed: Morpho V2 answers 0 by design); the raw answer is `contractReportedMaxWithdrawUsd` and `maxWithdrawMeans` says which. One holder at one block, before anybody else takes the same liquidity. Null where not simulated."
},
"collateralCrowd": {
"type": [
"object",
"null"
],
"description": "WHO ELSE LENDS AGAINST THE SAME THING. `crowding` cannot be asked where the vault IS the lending market - in Euler’s EVK a share of its own market is 100% by construction - and this is the question those depositors do have. Each entry carries the collateral, what this vault supplied against it, how many vaults WE TRACK lend against it and what they hold between them, and `shareOfCrowd`. THE DENOMINATOR IS THE VAULTS WE TRACK and never a market: nobody publishes how much of an asset is pledged across all of lending, so this says who falls together, not how large the fall would be. A slice named `idle` is the absence of an exposure and joins no crowd. The member list is the paid half and is at /v1/collateral/{symbol}."
},
"depositorEarned": {
"type": [
"object",
"null"
],
"description": "WHAT DEPOSITORS ACTUALLY EARNED, on venues that publish no PnL. Hyperliquid hands us each follower’s PnL; Morpho hands us millions of equity snapshots and no PnL at all. An equity that moved is not a return - it moves because the share price moved, which is earning, and because somebody deposited or withdrew, which is not. Only intervals where a holder demonstrably neither added nor removed are attributed, and the share price move for each interval is the MEDIAN of what that vault’s own depositors did, because not one depositor snapshot shares an instant with a price reading. `measured` of `depositors`, `earnedUsd`, `spanCovered`, and the intervals DROPPED because money moved. Null where the job has not reached this vault, which is not the same as nobody earning anything."
},
"controlFreshness": {
"type": "object",
"description": "How old the control reading behind `keys` is, by the same rule the due-diligence report and the vault page use: `state` is `fresh` within `thresholdMinutes` (a day and two hours; control is read daily), `stale` past it, so a role may have changed since, or `unavailable` where control was never read. `ageMinutes` and `says` give the age in words."
}
}
}{
"title": "What the record shows about one vault",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}find_vaults — Find a vault by what it has done
Find a vault by what it has DONE rather than what it is called. Free. The filter worth knowing is `earned`: `short` returns the vaults where a holder earned LESS than the venue advertises — how many there are is in the answer, never here — and `beating` the ones where they earned more. It matches only vaults whose realised return is COMPARABLE with an advertised one, which is a SMALL part of the open vaults and the exact share is in `vaults_earned`, not in this sentence; a venue that quotes nothing or a share price that never moves is excluded rather than counted as in-line, so a short list here means few comparable and not few that performed. Call vault_check on a slug for the full answer.
{
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "Part of a name or an address."
},
"chain": {
"type": "string",
"description": "e.g. base, solana, hyperliquid."
},
"limit": {
"type": "number",
"description": "Default 25, capped at 100."
},
"earned": {
"type": "string",
"description": "in-line, beating or short — realised against advertised, comparable cases only."
},
"minTvl": {
"type": "number",
"description": "Floor on stated size, in US dollars."
},
"offset": {
"type": "number",
"description": "Where to start, for the next page: send the `nextOffset` of the previous answer. Default 0."
},
"maxRisk": {
"type": "number",
"description": "Ceiling on the published risk score. Scored for a part of the open vaults and not all of them; the unscored are EXCLUDED from a scored filter, which is not the same as scoring well, and the counts are on the answer rather than here."
},
"platform": {
"type": "string",
"description": "A platform id or its display name, e.g. morpho or Hyperliquid."
},
"minOwnShare": {
"type": "number",
"description": "Floor on the share of the vault its leader holds, 0 to 1. Readable on Hyperliquid and Drift only, so a floor on it excludes every other venue, where it is ABSENT and not zero. How many that is today is in the answer. Evidence of alignment, never proof: the holder can be a treasury or a custodian."
},
"agentManaged": {
"type": "boolean",
"description": "Only vaults a machine runs."
}
}
}{
"type": "object",
"required": [
"data"
],
"properties": {
"data": {
"type": "array",
"items": {
"type": "object"
},
"description": "Each with its slug, which vault_check takes."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"fields": {
"type": "string",
"description": "Which fields this tool kept from the endpoint’s own rows."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
}
}
}{
"title": "Find a vault by what it has done",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}operator_check — Who runs the money, and how much is theirs
The question that follows vault_check: who runs the money, and how much of it is their own. Free. Read `ownShare` with its limits, which travel inside it: the leader’s holding is readable on Hyperliquid and Drift ONLY, so elsewhere it is ABSENT and not zero, the median describes the strategies we can read rather than the manager, and a large own-share is evidence of alignment and never proof, because the address running a vault can be a treasury or a custodian holding for other people. `cannotSee` names whatever of that applies here. Take the slug from vault_check’s `operator`.
{
"type": "object",
"required": [
"slug"
],
"properties": {
"slug": {
"type": "string",
"description": "The operator’s slug, as vault_check returns it in `operator.slug`."
}
}
}{
"type": "object",
"required": [
"name",
"cannotSee",
"says"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"name": {
"type": "string",
"description": "The operator’s name as published. Their words, not an identity we verified."
},
"says": {
"type": "string",
"description": "The operator in one sentence, including what we cannot see about them — own-capital share is readable on two venues only."
},
"ownShare": {
"type": [
"object",
"null"
],
"description": "Median share of the vault its leader holds, the count it is taken over, and what it does and does not mean."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"cannotSee": {
"type": "array",
"items": {
"type": "string"
}
},
"capitalUsd": {
"type": [
"number",
"null"
],
"description": "Capital across every open strategy we read for this operator. Null where none of them can be priced."
},
"strategies": {
"type": "object",
"description": "How many open and closed, and on which platforms."
},
"agentManaged": {
"type": "integer",
"description": "How many of their open strategies we have reason to call agent-managed. A COUNT of strategies, never a share of the capital."
}
}
}{
"title": "Who runs the money, and how much is theirs",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}list_vaults — Find the vault you mean
The vaults we read, so a caller holding a name rather than a slug can find the one it means. Free. Absence from this list means we do not read that vault, never that it does not exist.
{
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "Part of a vault or platform name, matched as typed."
},
"limit": {
"type": "number",
"description": "Default 25, maximum 200."
},
"offset": {
"type": "number",
"description": "Where to start, for the next page: send the `nextOffset` of the previous answer. Default 0."
}
}
}{
"type": "object",
"required": [
"data"
],
"properties": {
"data": {
"type": "array",
"items": {
"type": "object"
},
"description": "Each with its slug, which is what vault_check takes."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"fields": {
"type": "string",
"description": "Which fields this tool kept from the endpoint’s own rows, and where the rest are."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
}
}
}{
"title": "Find the vault you mean",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}find_agents — Find agents by what the record shows
Find agents to hire by what the chain records rather than what they claim: filter every address ever paid on x402, Virtuals ACP, the Olas mech marketplace or Masumi by independent payers, dealings, ACP completion, whether its declared service answers, whether it pays its own payers back, how spread its own SPENDING is (`spreadSuppliers`), and how recently it was paid. Free. An address absent from the result was never paid on a rail we read, which is not the same as never having worked. EVERY ROW CARRIES `subject`, WHICH IS WHAT WE CAN POSITIVELY SAY IT IS, with the evidence under it. `agent` means we saw agent-protocol work (ACP, Olas mech, Masumi) or saw it HIRE as well as be hired. `marketplace` means many paid it and it paid many, both above a stated floor. `unknown` is the largest bucket and is a real answer, not a failure: it means the evidence does not separate an agent from something that merely sells. READ THIS BEFORE FILTERING: a catalogue listing does NOT make a subject an API service, and `api-service` is in the vocabulary but currently UNREACHABLE; `items` says why and gives the measured mix. Nothing is inferred from a zero and no self-description is read, so a listing calling itself an autonomous agent moves nothing.
{
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "A name fragment from its registrations. A claim, matched as typed."
},
"dir": {
"enum": [
"asc",
"desc"
],
"type": "string",
"description": "Sort direction. Default desc."
},
"rail": {
"type": "string",
"description": "x402, acp, mech or masumi."
},
"sort": {
"type": "string",
"description": "One named dimension: independent, dealings, usd, completion, recent, paidBack, largestShare, counterparties, serving, suppliers, largestSupplierShare or spent. The last three order by what an agent rests on rather than by who rests on it. There is no \"best\" — nothing here has earned the right to rank."
},
"limit": {
"type": "number",
"description": "Default 25, maximum 200."
},
"minUsd": {
"type": "number",
"description": "Fewest US dollars received on the rails we read, all time."
},
"offset": {
"type": "number",
"description": "Where to start, for the next page: send the `nextOffset` of the previous answer. Default 0."
},
"network": {
"type": "string",
"description": "Only agents seen on this network, e.g. base, ethereum, polygon, solana."
},
"service": {
"type": "string",
"description": "What it was paid for, in the subject’s own words: a service name the protocol recorded or a description it published. Matched as typed."
},
"serving": {
"type": "boolean",
"description": "Only agents whose declared service answered our last probe. An agent that declares none is excluded, not failed."
},
"callable": {
"type": "boolean",
"description": "Only agents that declare a callable endpoint and answered our probe."
},
"noTwoWay": {
"type": "boolean",
"description": "Exclude agents that also pay their own payers, which can be one owner moving money between their own addresses."
},
"activeDays": {
"type": "number",
"description": "Paid within this many days."
},
"registered": {
"type": "boolean",
"description": "Only agents holding a registration in a registry we read. A registration is a claim, never a verification."
},
"minCameBack": {
"type": "number",
"description": "0 to 1, the least share of its payers who paid it more than once. Excludes every seller with too few payers to have a rate."
},
"minDealings": {
"type": "number",
"description": "Fewest dealings on the rails we read, all time."
},
"minCompletion": {
"type": "number",
"description": "ACP jobs completed over those that ENDED, 0 to 1. It is a conditional rate: one agent reads 0.9776 on it while 38.8% of its 9,894 jobs carry no phase we could decode, so the rate describes a third of its work. Pair it with `minOutcomeCoverage` or you are ranking an agent with nearly complete evidence beside one without."
},
"minIndependent": {
"type": "number",
"description": "Fewest counterparties that paid it and were never paid back. The closest thing here to \"has real custom\"."
},
"supplierSpread": {
"enum": [
"diversified",
"concentrated",
"insufficient-data",
"none"
],
"type": "string",
"description": "The precise form of `spreadSuppliers`. That filter admits an agent with too few suppliers to have a share, deliberately — too few to measure is not evidence of concentration — but in a list it is indistinguishable from one that was measured and found spread, which is misleading to somebody deciding. Ask for `diversified` to get only the measured ones. `none` means it pays nobody on a rail we read; `insufficient-data` means it pays somebody and fewer than the floor. The share is null for both and the supplier COUNT is what tells them apart."
},
"notConcentrated": {
"type": "boolean",
"description": "No single counterparty above half of ten or more dealings."
},
"spreadSuppliers": {
"type": "boolean",
"description": "Only agents that PAY somebody and where no single party takes more than half of what the agent itself SPENDS. The other direction from the concentration of its custom: an agent with forty payers and one supplier looks diversified on every other figure here and is one supplier away from stopping. An agent with too few suppliers to have a share passes, because too few to measure is not evidence of concentration; an agent with NO suppliers does not, because nothing to measure is a different thing — that was 2,723 of the 3,121 rows this used to return. Use `supplierSpread=diversified` for the measured ones alone. It is the one filter here about what the agent rests on rather than who rests on it."
},
"minOutcomeCoverage": {
"type": "number",
"description": "The share of ALL this agent’s ACP jobs that reached an outcome at all, 0 to 1. What `minCompletion` COVERS. Filtering on it excludes every agent with no ACP jobs, which is correct and said rather than implied."
},
"contractedSuppliers": {
"type": "boolean",
"description": "At least one supplier the protocol names as a provider, not merely a payee."
}
}
}{
"type": "object",
"required": [
"items"
],
"properties": {
"asOf": {
"type": [
"string",
"null"
],
"format": "date-time",
"description": "When the record behind these rows was last rebuilt. Null means nothing in the answer carries a date, which is a gap in our reading rather than a fact about the agents."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"items": {
"type": "array",
"items": {
"type": "object"
},
"description": "Matching agents, ordered by the ONE dimension asked for, never a composite score. Each row carries `subject`: { type, evidence[], alsoShows[], says }. `evidence` names each thing we SAW and, in `supports`, which type it is evidence for — or null where the observation is real and separates nothing, which is the case for a catalogue listing. So `unknown` WITH evidence is the normal shape here and means \"we saw these things and they do not settle it\", never \"we saw nothing\". `alsoShows` carries the types a subject also shows evidence for, because they are not exclusive and dropping one would make the label more confident than the evidence. Measured once and not refreshed since, over all 3,077 paid addresses: **1,377 agent, 38 marketplace, 1,662 unknown**. Those are a PAST reading kept because they are the point — this contract once claimed `unknown` would be commonest before anything had been measured, and the measurement found it at zero, which is how a defect in the classifier was caught. Read the answer’s own denominators for today’s mix; these say what KIND of mix to expect. A catalogue listing does NOT make a subject an API service. Agents sell priced endpoints too (this platform sells four), so a listing says it sells over HTTP and nothing about which kind of seller. `api-service` is therefore in the vocabulary and is currently UNREACHABLE: separating it would need evidence that a subject does NOT do agent work, and that is an absence, since 86% of these addresses pay nobody on a rail we read and paying from a second wallet looks identical to buying nothing."
},
"filters": {
"type": "object",
"description": "What was applied, read back, including what a floor excluded."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
}
}
}{
"title": "Find agents by what the record shows",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}counterparty_history — One agent’s record day by day (paid)
How one agent’s record has moved: dealings, independent payers, concentration and delivery, day by day. PAID, one cent a call over x402 — the median price of this rail. Without payment the tool answers with the price and how to pay it, and the free check remains available. The history begins the day we started keeping it; it is a record we keep, not one the chain gives away. WHY readOnlyHint IS false: this tool reads and writes nothing in our store, but calling it WITH an x402 payment authorisation transfers USDC from the caller to us and records a receipt, so the call is not free of consequence and an agent must not make it speculatively. Called without an authorisation it changes nothing and answers with the price.
{
"type": "object",
"required": [
"address"
],
"properties": {
"days": {
"type": "number",
"description": "1 to 365, default 90. The history begins the day we started keeping it, so a longer window does not reach further back than that."
},
"address": {
"type": "string",
"description": "The agent whose record you want day by day. An EVM, Cardano or Solana address."
}
}
}{
"type": "object",
"required": [
"address"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"items": {
"type": "array",
"items": {
"type": "object"
},
"description": "One entry per day: dealings, independent payers, concentration, delivery."
},
"change": {
"type": [
"object",
"null"
],
"description": "What moved across the window, and over which dates."
},
"address": {
"type": "string",
"description": "The address this history is for, normalised and read back."
},
"payment": {
"type": "object",
"description": "What was paid and how it settled. Present because this call took money."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"keptSince": {
"type": "string",
"format": "date",
"description": "The day we began keeping this. Nothing before it exists, at any window."
}
}
}{
"title": "One agent’s record day by day (paid)",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}vaults_earned — What depositors kept, against what was advertised
What depositors actually KEPT, against what their vault advertised. READ `population` BEFORE QUOTING ANY SHARE FROM THIS. The comparison covers only the vaults where we hold BOTH an advertised rate and a realised one, which is a narrow and lopsided set, usually dominated by one venue: `population.largestVenue` names it and `population.concentration` says how much of the set it is. A share from here is a statement about those vaults, never about DeFi and never about this whole site. Not the vault’s return, which is what a unit held throughout would have earned: this is read from the depositor register, per vault, and set against the venue’s own quote scaled to how long those depositors have held. How many gave their depositors less, and by how much, is in each answer’s `population`, counted when it is served; quote it from there, never from this description. ADVERTISED RATES ARE DECIMAL FRACTIONS and some are in the hundreds of per cent: 0.042 is 4.2% and 4.2 is 420%. Those survive a stated band of -1 to 5 (-100% to 500%); `advertisedBand.refused` says how many readings the band threw out, and `advertisedIsHigh` marks each row above 100% so a remarkable figure cannot be mistaken for a misplaced decimal. TWO THINGS TO REPEAT WHENEVER QUOTING IT. A depositor who withdrew everything is not in the register and the ones who left after losing are exactly the ones a return figure most needs, so this reads BETTER than the truth rather than worse; and `gapPoints` is percentage POINTS, not per cent, because a depositor cannot lose more than they put in. The aggregate and the widest gaps are free; the rest of the list needs the earned.detail capability and `withheld` says how many are missing.
{
"type": "object",
"properties": {}
}{
"type": "object",
"required": [
"comparable",
"items",
"limits"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"items": {
"type": "array",
"items": {
"type": "object"
},
"description": "name, slug, venue, advertised, advertisedIsHigh, kept, gapPoints, medianDaysHeld. Widest gap first."
},
"limits": {
"type": "string",
"description": "What this cannot tell you, including the survivorship inside the register. Quote it with the numbers."
},
"byVenue": {
"type": [
"array",
"null"
],
"items": {
"type": "object"
},
"description": "FREE whatever tier you hold: coverage per protocol, largest basis first, each with `vaults`, `depositorsMeasured`, `basisUsd` and `comparable`. The population here is concentrated on the venues that publish a per-depositor profit, and reading the headline without this is reading a number about those venues as though it were about the market."
},
"returned": {
"type": "integer",
"description": "Rows in this answer."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"withheld": {
"type": "integer",
"description": "Rows the plan does not carry. Zero with earned.detail."
},
"comparable": {
"type": "integer",
"description": "Vaults where the two sides can be set against each other at all. This is the population every share here is over."
},
"population": {
"type": "object",
"description": "Who the comparison is actually about: listed, comparable, byVenue, largestVenue, concentration and isOneVenue. `listed` is how many survived the advertised band and are in `byVenue`; `comparable` is the job own count. They are different counts and must never be divided into one another. Where isOneVenue is true the figure is a reading about that one venue in a market-wide sentence."
},
"losingShare": {
"type": [
"number",
"null"
],
"description": "Measured depositors who are down, as a decimal fraction."
},
"alsoMeasured": {
"type": "object",
"description": "THE OTHER YIELD MEASURE, named so you do not quote one as the other. This tool answers what the depositors STILL IN a vault kept, read from the depositor register, which needs a per-depositor profit and so covers one venue. `performance.realised` on a vault answers what a unit held THROUGHOUT would have earned, read from the share price, and covers far more. A vault can deliver its advertised rate to a unit held all year while the people actually in it are down, because they arrived at different times."
},
"survivorship": {
"type": [
"object",
"null"
],
"description": "FREE. Every figure comes from depositors STILL IN the vault: `basis` is `current-holders-only`, `exitedDepositorsIncluded` is false, and `direction` is `reads-better-than-the-truth`, the likely direction and not a guarantee (`directionCertain` is false): some depositors leave after a gain. Somebody who withdrew everything is not in the register, and the ones who left after losing money are exactly the ones a return figure most needs. No size is published for the bias, because an unmeasured correction presented as a figure would be worse than the bias itself."
},
"advertisedBand": {
"type": "object",
"description": "minDecimal, maxDecimal, refused and means. Advertised rates are decimal fractions, so a value above 1 is above 100%; readings outside the band are refused rather than counted as zero."
},
"shortOfAdvertised": {
"type": [
"integer",
"null"
],
"description": "Of those, how many gave their depositors less."
},
"shortOfAdvertisedShare": {
"type": [
"number",
"null"
],
"description": "The headline share, as a decimal fraction. Never quote it without `population` beside it."
}
}
}{
"title": "What depositors kept, against what was advertised",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}agent_changes — What moved in the agent record
What moved in the agent record, across every agent, since a date. The counterparty check answers what is true about one address NOW; this answers what changed, for all of them, which is the question anybody holding a list asks next. One row per FIELD that moved, not per address: an agent whose dealings and concentration both moved is two facts, and folding them would make you diff the answer to work out what was meant. Roughly one agent in five moves on an ordinary day; how many are tracked is a denominator on the answer rather than a number frozen here. A day with no row for an address is a day it did not move, never a day nobody looked. The record began on a date and `from` says which: asking for anything earlier answers from there rather than pretending nothing happened. Needs a key and the agents.changes capability.
{
"type": "object",
"properties": {
"limit": {
"type": "number",
"description": "Default 1,000, maximum 5,000. Read `capped` before concluding you have it all."
},
"since": {
"type": "string",
"description": "An ISO date, such as 2026-10-01. The last seven days if you leave it out."
},
"offset": {
"type": "number",
"description": "Rows to skip, for the next page when `capped` is true: the previous offset plus the rows it returned. Default 0."
},
"address": {
"type": "string",
"description": "One address, where you want its movement rather than everybody’s."
}
}
}{
"type": "object",
"required": [
"since",
"count",
"items",
"limits"
],
"properties": {
"from": {
"type": [
"string",
"null"
],
"description": "The first day held at all. There is nothing before it because the feed began when it began."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"count": {
"type": "integer",
"description": "Rows IN THIS ANSWER, which is one per field that moved and not one per agent. Where `capped` is true it is the page size and not the total, so it is a floor on how much moved and never the whole of it."
},
"items": {
"type": "array",
"items": {
"type": "object"
},
"description": "address, day, field, was, now, and the path of that agent’s full check."
},
"since": {
"type": "string",
"description": "The day this answer starts from, read back so a clamp is visible."
},
"capped": {
"type": "boolean",
"description": "TRUE where the page came back full, so ask again with an `offset`; FALSE means nothing moved after these rows in the window asked for, so the feed is complete rather than merely begun. A cap is not a count."
},
"limits": {
"type": "string",
"description": "What this answer cannot tell you. Quote it with the numbers."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
}
}
}{
"title": "What moved in the agent record",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}counterparty_bulk — Check up to fifty addresses in one call (paid)
Check up to fifty addresses in one call, each with its observations, AND what the set shares underneath: the parties that two or more of them pay. That second part is the one answer no per-address row can contain, because the input is your own shortlist — four agents stopping together because they all pay one party is invisible in four separate clean records. PAID two ways and they buy different things: one cent a call over x402, priced per call rather than per address, buys the rows; the `shared` block needs the `dependency.shared` capability, on Analyst and above, and when it is missing the block says so rather than vanishing. A shared recipient is not proof of a shared dependency — several agents paying one address may be paying a relayer, a bridge or the same customer back — and an empty list is a finding about these addresses AND about our reach. WHY readOnlyHint IS false: this tool reads and writes nothing in our store, but calling it WITH an x402 payment authorisation transfers USDC from the caller to us and records a receipt, so the call is not free of consequence and an agent must not make it speculatively. Called without an authorisation it changes nothing and answers with the price.
{
"type": "object",
"required": [
"addresses"
],
"properties": {
"addresses": {
"type": "array",
"items": {
"type": "string"
},
"description": "One to fifty addresses, in any mix of forms: an EVM address in any case, a Solana base58 address, a Cardano bech32 one. Fifty-one or more is REFUSED with a 400 `too_many` rather than silently truncated, so a caller never reads a partial answer as a whole one. An address we have never seen paid comes back as a row saying so, not omitted — so the answer has one row per address you sent, in the order you sent them."
}
}
}{
"type": "object",
"required": [
"items"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"items": {
"type": "array",
"items": {
"type": "object"
},
"description": "One entry per address given, in the order given, each with its own observations and its own limits."
},
"limits": {
"type": "string",
"description": "What this answer cannot tell you."
},
"shared": {
"type": "object",
"description": "The parties two or more of the given addresses pay, most shared first, each with how many of yours pay it and which ones. `available: false` with a `capability` name where the plan does not carry it. An empty list means no party is paid by more than one of them on a rail we read, which is a finding about them and about our reach: a model, an endpoint or a host shared between them is a dependency no payment rail shows."
},
"checked": {
"type": "integer",
"description": "How many addresses were read."
},
"payment": {
"type": "object",
"description": "What was paid and how it settled. Priced per call, not per address."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
}
}
}{
"title": "Check up to fifty addresses in one call (paid)",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}agent_dependencies — What several agents rest on at once (paid)
If your firm relies on several external agents, what do they rest on AT ONCE? Give it two to twenty-five addresses and it finds the parties more than one of them pays. THE PRODUCT IS THE DISTINCTION, NOT THE JOIN: finding that three agents paid the same address tells you almost nothing — on our rails one address is paid by 71 agents across 4,100 payments and another by three agents once each, and calling both "shared" makes the second sound like the first. Four tests must ALL hold before the word dependency is used: repeated (more than one payment, because a single payment is an event), one-way (it has never paid them back, because money moving both directions is trade between peers and survives either of them leaving), sustained (spanning more than a day, so a burst of twenty in an afternoon is not a standing arrangement), and shared (more than one of the agents you named). All four are published on every party under `tests`, so you can draw the line somewhere else than we did. Anything short is a `shared-counterparty` with its failed test named and is reported as a LEAD. The concentration line counts dependencies only. A party just ONE of them pays is left out entirely — that is `counterparty_check`, free. Most shared infrastructure, a cloud region or a model provider on a card or one operator holding the keys, leaves no payment at all, so what this finds is a FLOOR on the concentration that exists and never a measure of it. PAID.
{
"type": "object",
"required": [
"addresses"
],
"properties": {
"addresses": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 25,
"minItems": 2,
"description": "The agents your firm relies on. At least two, because the question is what they rest on together; at most 25, because the join covers every payment each has made."
}
}
}{
"type": "object",
"required": [
"agents",
"shared",
"says",
"cannotSee",
"limits",
"read"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"read": {
"type": "object",
"description": "HOW CURRENT THE RAIL BEHIND THIS IS. Recomputed from the payments table on every call, never a stored snapshot — but only as current as the chain that has fallen FURTHEST BEHIND, which is the date given, not the chain most recently read. `furthestBehind` names it and `stalled` lists any chain reporting an error, which may be further behind still. A dependency formed after `railReadAt` is not here yet. Do not confuse it with `firstAt`/`lastAt` on each party: those are when payments HAPPENED."
},
"says": {
"type": "string",
"description": "What was found, with the floor stated."
},
"kinds": {
"type": "object",
"description": "What dependency and shared-counterparty each mean."
},
"agents": {
"type": "array",
"items": {
"type": "string"
},
"description": "The agents asked about, read back and de-duplicated."
},
"limits": {
"type": "string",
"description": "That the four tests are a JUDGEMENT about where a relationship becomes a reliance, published as one so you can draw the line elsewhere, and that a party only one of these agents pays is left out rather than listed."
},
"shared": {
"type": "array",
"items": {
"type": "object"
},
"description": "Parties paid by more than one of them, dependencies first. Each carries `kind`, `tests` with all four, `paidBy` with payments and span, and what it means."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"cannotSee": {
"type": "array",
"items": {
"type": "string"
},
"description": "Why this is a floor: most shared infrastructure leaves no payment at all."
},
"concentration": {
"type": [
"object",
"null"
],
"description": "The dependency reaching the most of your agents, or null. DEPENDENCIES ONLY: counting leads here would let three agents who each bought something once produce \"three of your five depend on this\"."
}
}
}{
"title": "What several agents rest on at once (paid)",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}agent_business — Does this agent have a business (paid)
Does this agent have a BUSINESS, and can any of it be demonstrated? Six questions an investor, a marketplace or an operator actually asks, each answered separately: are the addresses paying it really independent customers; is revenue growing or shrinking; how much is repeat custom; how many of those payments can be shown to have bought anything; what does it cost to run and to whom; and if it has several addresses, can the link be DEMONSTRATED or is it an unconfirmed assumption. THERE IS NO SCORE over the six, deliberately: a number would average "thousands of addresses paid this" against "we hold no delivery record at all", and the second is a statement about US. Four things the data invites and this refuses, explained in `limits`: an ADDRESS is not a customer; a PAYMENT is not a delivery; a SHARED FUNDER is not an identity; and a COST we cannot see is not a cost that is not there. It also carries `economicChain`: the four links an agent has to complete to be a working economic subject (a customer paid it, it ordered or did work, the result was demonstrably delivered, a customer came back), each `evidenced`, `unevidenced` (about the agent), `unread` (about US) or `unevidenceable` (about the rail), never folded together. NO SCORE there either: the worst link governs. For an x402-only agent the delivery link is unevidenceable, which is the rail and not the agent. PAID.
{
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"type": "string",
"description": "The agent’s address."
}
}
}{
"type": "object",
"required": [
"address",
"answers",
"coverage",
"says",
"limits"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "How much of the question we could answer, and which parts we could not."
},
"limits": {
"type": "string",
"description": "The four conclusions the data invites and this refuses. An ADDRESS is not a customer (one customer can hold twenty). A PAYMENT is not a delivery (the busiest agents settle on rails that carry money without carrying a job, so “no delivery record” there is our reach and not a finding). A SHARED FUNDER is not an identity (an exchange funds thousands of strangers), so a link is published as demonstrated or as merely co-occurring and the second is never promoted. A COST we cannot see is not a cost that is not there."
},
"address": {
"type": "string",
"description": "The agent this is about, read back."
},
"answers": {
"type": "array",
"items": {
"type": "object"
},
"description": "One per question: `asks` in a person’s words, `answered` as measured/partial/unmeasured, `says`, and the `figures` behind the sentence so you can do your own arithmetic. Measured once and not refreshed since, on two live agents: one takes $2,388 a month from 4,772 payers of whom 97% are repeat, the other $932,414 from 7,220 payers of whom 12% are — two completely different businesses that a counterparty count reports identically."
},
"commerce": {
"type": "object",
"description": "Settled, of that service-linked, of that delivery-evidenced, nested and never added; plus the payments whose rail cannot evidence delivery (x402). Read `says` before quoting any of them as revenue. SINCE 2026-10-28 `settledShownToBeX402` and `rails.x402.usdShownToBeX402` sit beside `settled` and `rails.x402.usd`, which count every authorised transfer: the ShownToBeX402 figures count only payments shown to be x402. Quote those as x402."
},
"coverage": {
"type": "object",
"description": "How many of the six are measured, partial and unmeasured. A count, never averaged into a grade."
},
"evidence": {
"type": "object",
"description": "The common payment record (TODO-07): `state: built` with `facilitator` (who submitted, never credited), `onChainRecipient`, `listedPayee`, `facilitatorIsRecipient`, `recipientIsListedPayee`, `settlement`, `delivery`, `deliveryEvidence`, `says`; or `state: not-applicable` with `says` naming why no single payment is described."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"recordAsOf": {
"type": [
"string",
"null"
],
"format": "date-time",
"description": "When the nightly record behind most figures was computed. The revenue months are read live."
},
"economicChain": {
"type": "object",
"description": "SINCE 2026-10-11. The four links of the chain an agent has to complete to be a working economic subject: a customer paid it, it ordered or did work, the result was demonstrably delivered, a customer came back. Each link is `evidenced`, `unevidenced` (we read the rail and found nothing — a statement about the agent), `unread` (THE RAIL RECORDS IT AND WE DO NOT READ IT — a statement about us, and the only one of the four that closes by our building something) or `unevidenceable` (the rail does not record it at all, so no reading by anybody will change it — a statement about the rail). Those three silences are never folded together, because they call for three different acts. Masumi settles work through an escrow whose release or refund is on chain, and SINCE 2026-10-11 WE READ IT: a release to the seller is the buyer letting the escrow go, which is the closest thing to an accepted delivery this chain can show, and it counts alongside an ACP completion. A CLOSURE does not count — over all 29,088 settled escrows at the time this was measured once, 5,819 refunded to the BUYER against 4,659 released, so counting closures would publish a refund as delivered work more often than not. An agent whose escrows the step reader has not walked yet is `unread` rather than `unevidenced`, per agent, because the chain carries the answer and we have not asked for that one. THE FOURTH STATE is the one to read carefully: x402 is 91% of the volume and records payment, never delivery, so for an x402-only agent link three is unevidenceable and reporting it as missing would blame the agent for the rail. NO SCORE: the worst link governs, because averaging lets a strong payment record carry an absent delivery record. Measured once and not refreshed since, over all 3,135 addresses we held then: paid 3,048, hires 432, delivered 684, returned 2,412, and **185 with all four**."
},
"answeredStates": {
"type": "object",
"description": "What measured, partial and unmeasured each mean."
}
}
}{
"title": "Does this agent have a business (paid)",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}agent_economic_profile — Agent economic profile (paid)
One agent, four readings, in the order a buyer actually asks them: what it EARNS and from whom, whether its contracted work FINISHED and who signed it off, who it RESTS ON as a supplier, and whether it is still TRADING and its endpoints answer. Those questions are answered singly elsewhere on this server; this is the one that answers them together, over the same nightly record, so the four cannot disagree with each other or with the endpoints they summarise. THERE IS NO SCORE ON TOP and there will not be one: a single figure would be the thing every reader quotes and the only one none of them can check, and it would average a measured quantity with an unmeasured one until neither is visible. Each block keeps its own counts and its own denominator. A block we have not read comes back NULL rather than as a row of zeros, because "it has no completed jobs" and "we have not read its job side" are different facts and a buyer choosing between agents deserves to know which one they are looking at; `blocksAnswered` says how many of the four this answer could fill. `cannotSee` names what no block here can reach at all — revenue that never touched a rail we read, dependencies that are not payments, and whether completed work was any GOOD, which we cannot see from who signed it off. PAID.
{
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"type": "string",
"description": "The agent’s address. EVM or Cardano; both resolve to the record key."
}
}
}{
"type": "object",
"required": [
"address",
"blocksAnswered",
"cannotSee",
"says"
],
"properties": {
"asOf": {
"type": [
"string",
"null"
],
"format": "date-time",
"description": "When the nightly record behind these figures was computed."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "How much of the profile is answered, and which parts are missing because we have not read them."
},
"format": {
"type": "object",
"description": "What kind of address this is and which rails were checked for it."
},
"health": {
"type": [
"object",
"null"
],
"description": "Endpoints listed and probed, what the unpaid handshake got, and how long it has been quiet."
},
"address": {
"type": "string",
"description": "The agent this is about, read back."
},
"revenue": {
"type": [
"object",
"null"
],
"description": "Dealings, buyers, repeat buyers, largest buyer share, independent vs two-way, and whether its payers share an obscure third party."
},
"delivery": {
"type": [
"object",
"null"
],
"description": "Of contracted work that reached an outcome: completed, who judged it, refunded, and the phases nobody read."
},
"evidence": {
"type": "object",
"description": "The common payment record (TODO-07): `state: built` with `facilitator` (who submitted, never credited), `onChainRecipient`, `listedPayee`, `facilitatorIsRecipient`, `recipientIsListedPayee`, `settlement`, `delivery`, `deliveryEvidence`, `says`; or `state: not-applicable` with `says` naming why no single payment is described."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"cannotSee": {
"type": "array",
"items": {
"type": "string"
},
"description": "What no block here can reach, named in the answer rather than in a footnote."
},
"wellKnown": {
"type": [
"object",
"null"
],
"description": "Set when the address is not a party at all — a burn address, a router — so a record against it is not a business."
},
"dependencies": {
"type": [
"object",
"null"
],
"description": "Who it pays, split into suppliers the protocol NAMES, those merely listed, and those with no evidence of supplying anything."
},
"blocksAnswered": {
"type": "number",
"description": "How many of the four this answer could fill. Read it as the denominator of everything above."
}
}
}{
"title": "Agent economic profile (paid)",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}exit_screen — Where has a size like mine actually cleared (paid)
YOU HAVE A SIZE AND YOU WANT TO KNOW WHERE IT HAS ACTUALLY CLEARED. Every other exit reading here is about one vault; this asks the same question across every vault whose withdrawal history we have read, which is the question an allocator with capital actually has. Send `amountUsd` and each vault comes back with one of THREE verdicts, and the middle one is the finding: `paid-in-one` means a single settled withdrawal at least that large has been recorded there; `paid-across-a-day` means that much has left in one day and never in one withdrawal, which is a different fact and not a weaker version of the first; `never-asked-this-size` means nothing that large has been withdrawn, which is NOT a refusal and NOT a failure, because most vaults have never been asked. It exists because an auditor screened a $6M exit from a vault declaring $15.10M withdrawable, got a supporting answer, and found the largest withdrawal ever observed there was $500K. Declared liquidity is a LEVEL at an instant; what has left is a FLOW; only the second is evidence about a size. The two are never divided by one another and no ratio between them is returned. `population` names how many vaults have no history at all, because those are UNREAD rather than untested and must not be counted as either answer. Nothing here is a forecast: a vault that paid this much last March can refuse today. PAID.
{
"type": "object",
"required": [
"amountUsd"
],
"properties": {
"limit": {
"type": "number",
"description": "How many vaults to list, 1 to 100. The counts cover the whole population either way."
},
"amountUsd": {
"type": "number",
"description": "The size you are weighing, in US dollars, e.g. 6000000. Required: \"can I get out\" has no answer without one."
}
}
}{
"type": "object",
"required": [
"amountUsd",
"population",
"counts",
"verdicts",
"cannotSee",
"limits",
"vaults"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"counts": {
"type": "object",
"description": "How many vaults fall in each of the three verdicts, over the whole population rather than over the listed rows."
},
"limits": {
"type": "string",
"description": "Why this is a record and not a forecast."
},
"vaults": {
"type": "array",
"items": {
"type": "object"
},
"description": "Each vault with its verdict, its largest single exit and when, its busiest day, how many withdrawals we have seen and how far back the history reaches."
},
"verdicts": {
"type": "object",
"description": "What each verdict means, beside the answer rather than in documentation you have to find."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"amountUsd": {
"type": "number",
"description": "The size asked about, read back."
},
"cannotSee": {
"type": "array",
"items": {
"type": "string"
},
"description": "What a record of settled withdrawals structurally cannot contain."
},
"population": {
"type": "object",
"description": "`withHistory` and `withoutHistory`: a vault with no withdrawal history is unread, never a vault nobody has withdrawn from."
}
}
}{
"title": "Where has a size like mine actually cleared (paid)",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}vault_exit_confidence — Could you get your money back out (paid)
Before you put capital in, ask what evidence there is that you could get a PARTICULAR amount back out. Every other reading about a vault is about return or about risk of loss; this is about the practical question a holder has, and it is always about a size: "can I get out" has no answer, "can I get $2m out of a vault with $400k free and 25,941 settled withdrawals behind it" has several. Six readings, each with its own verdict and the question it answers in a holder’s own words: has anybody actually got money out, is there anything in it now, could YOUR amount leave, is somebody queued ahead of you, how much of the capital is lent out, and can somebody change the terms under you. The answer is the WORST of them and never an average — a queue in front of you is not offset by a long history, it happens before your turn — and an `unknown` outranks a `supports`, because a gap we could not read must never be counted as a pass. `not-asked` marks a component whose input you did not send, and `not-applicable` one this venue does not have at all — only two venues have an exit queue to be behind. Neither is a gap in our record and neither lowers the answer. A withdrawable figure is a FLOOR on venues whose vaults lend into markets holding their own liquidity, and the component says so rather than reading it as a ceiling. NOTHING HERE IS A GUARANTEE of withdrawal and no sentence it emits contains the word: a vault that has paid everybody for a year can stop tomorrow. PAID.
{
"type": "object",
"required": [
"slug"
],
"properties": {
"slug": {
"type": "string",
"description": "The vault’s slug, or its contract address — either resolves."
},
"amountUsd": {
"type": "number",
"description": "What you are thinking of taking out, in dollars. Send it: without it the sizing component reports `not-asked` and you get the general picture rather than an answer about you."
}
}
}{
"type": "object",
"required": [
"slug",
"summary",
"says",
"components",
"cannotSee",
"limits"
],
"properties": {
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "The worst component in one sentence, with what it does and does not mean."
},
"slug": {
"type": "string",
"description": "The vault this is about, read back, so an answer cannot be attached to the wrong one."
},
"limits": {
"type": "string",
"description": "Why the answer is the worst component rather than an average, and why no sentence here contains the word guarantee."
},
"summary": {
"enum": [
"supports",
"against",
"unknown"
],
"type": "string",
"description": "The worst component. Never an average and never a score."
},
"verdicts": {
"type": "object",
"description": "What each verdict means, beside the answer rather than in documentation you have to find."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"amountUsd": {
"type": [
"number",
"null"
],
"description": "The amount asked about. Null means the sizing did not run."
},
"cannotSee": {
"type": "array",
"items": {
"type": "string"
},
"description": "What this structurally cannot contain, so a supporting answer is never read as an assured exit."
},
"components": {
"type": "array",
"items": {
"type": "object"
},
"description": "Each reading: `key`, the `asks` it answers, its `verdict` and what it read."
}
}
}{
"title": "Could you get your money back out (paid)",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}check_action — One check before you sign (paid)
CALL THIS BEFORE YOUR AGENT SIGNS ANYTHING THAT MOVES MONEY. One request instead of five: it reads the counterparty record, decodes the calldata you are about to sign, looks up the seller’s own catalogue listing, runs an unpaid 402 handshake with the endpoint, and applies a published policy — and then, which is the point, compares them WITH EACH OTHER. The losses an agent actually suffers live between those readings and no single one of them can see them: the bytes paying an address you never checked, so a spotless counterparty record is about a party this transaction does not pay; an UNLIMITED APPROVAL wearing the shape of a one-cent payment; the seller’s listing naming a payee the calldata does not; a policy allowing a payment for a path that is not served. Every cross-check names BOTH readings that disagree in `between`, and none of them is an accusation — the record can be wrong. Send `calldata` whenever you have it: it unlocks the strongest check there is. A cross-check can only make the verdict worse, never better. SEND `calldata`, `chain` AND `from` and it is also SIMULATED: one `eth_call` at the latest block, the node executes it and throws the result away, nothing is signed or spent. A revert is the strongest finding available, because every other reading argues about whether you should send it and this one is that it will not work. Not a state diff, run against the current block, and a node that refuses is reported as our gap rather than as a failure. `ran` says which parts did not run and why, because a check that did not happen must never look like one that passed. PAID; its five components are each free on their own tools and what is sold here is the comparison.
{
"type": "object",
"properties": {
"from": {
"type": "string",
"description": "The address the transaction would be sent FROM. Only used to SIMULATE it, and worth sending: without it the call runs as the zero address and reverts for a reason about our request rather than your transaction."
},
"asset": {
"type": "string",
"description": "The TOKEN you are paying in. Compared against the one the seller’s listing prices in: a payment in a token the seller does not credit leaves your side and arrives nowhere, and every other check passes on it."
},
"chain": {
"type": "string",
"description": "The chain it is about to be sent on, e.g. base."
},
"action": {
"enum": [
"pay",
"allocate_capital",
"connect_mcp",
"call_tool"
],
"type": "string",
"description": "What you are about to do, because the checks differ: `pay` weighs a recipient’s payment record, `allocate_capital` weighs a VAULT (pass its slug as `subject`) on exit, controller and evidence, `connect_mcp` and `call_tool` weigh a server or tool you are about to trust. Defaults to `pay`. Choosing the wrong one does not fail — it answers the wrong question, and `checked` in the reply names which checks actually ran."
},
"policy": {
"type": "string",
"description": "A policy slug or version from list_policies. Defaults to conservative."
},
"subject": {
"type": "string",
"description": "For allocate_capital: the vault slug."
},
"calldata": {
"type": "string",
"description": "The hex bytes you are about to sign. Left out, the transaction is reported as UNREAD rather than fine."
},
"resource": {
"type": "string",
"description": "The http(s) URL of the endpoint being bought, where there is one."
},
"valueWei": {
"type": "string",
"description": "Native value in wei, where the transaction carries any. Simulation only."
},
"amountUsd": {
"type": "number",
"description": "What is about to be sent, in dollars."
},
"recipient": {
"type": "string",
"description": "The address about to be paid."
}
}
}{
"type": "object",
"required": [
"verdict",
"says",
"crossChecks",
"parts",
"ran",
"limits"
],
"properties": {
"ran": {
"type": "object",
"description": "Which parts ran and why each one did not."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "The worst thing found, in one sentence."
},
"parts": {
"type": "object",
"description": "Each reading on its own: policy, transaction, listing, endpoint, settledOn."
},
"limits": {
"type": "string",
"description": "What this check structurally cannot contain: what the action is FOR, terms agreed privately, anything beyond a single `eth_call` at the latest block (no state diff, no fork), and calls the decoder refuses rather than guesses at. Read it before treating a clean answer as a safe one."
},
"verdict": {
"enum": [
"allow",
"review",
"abstain",
"unsupported"
],
"type": "string",
"description": "The policy’s decision, worsened by the cross-checks and never improved by them."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"crossChecks": {
"type": "array",
"items": {
"type": "object"
},
"description": "The comparisons between parts. Each names the TWO readings that disagree. The ones that AGREED are published too, because a comparison that ran and agreed is different from one that never ran. `tone` is `bad`, `warn`, `good` or `neutral`, and the fourth is the one to read carefully: a NEUTRAL check established nothing and can neither improve nor worsen the verdict. `the-price-has-moved-since-the-listing` compares the endpoint’s own quote with its catalogue entry in BASE UNITS after checking that both name the same token, so it is exact; where the tokens differ or either is unnamed it reports `the-two-prices-are-not-comparable` instead, because a 402 states an amount in the asset’s smallest unit and never its decimals. The same rule is applied by `preflight_payment` and `evaluate_action`."
}
}
}{
"title": "One check before you sign (paid)",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}inspect_payment — Read the transaction you are about to sign
Call this with the CALLDATA you are about to sign, before you sign it. Every other check here asks whether an address is worth dealing with; this asks whether the transaction is the one you think it is, and the two catch different losses. No amount of reputation makes the recipient in the bytes match the recipient on your screen, and a spotless counterparty record says nothing about an UNLIMITED APPROVAL granted to it, which is not a payment at all but a standing permission that outlives the transaction. It reads three calls — transferWithAuthorization, approve, transfer — and REFUSES to guess at any other: decoding unknown calldata without the ABI means guessing where each argument begins, and a confident wrong answer about where money goes is worse than none. Amounts are atomic units, never dollars, because the token owns its decimals. Free, needs no key, stores nothing.
{
"type": "object",
"required": [],
"properties": {
"to": {
"type": "string",
"description": "The contract being called, where you know it. On an ERC-20 payment this IS the token: the calldata carries a recipient and an amount and never the asset, so without `to` there is nothing to compare the endpoint’s quoted asset against."
},
"data": {
"type": "string",
"description": "The calldata, 0x-prefixed. Without it nothing about the transaction is read, and the answer says so rather than passing."
},
"expect": {
"type": "object",
"properties": {
"chain": {
"type": "string"
},
"payTo": {
"type": "string",
"description": "Who you mean to pay."
},
"validAfter": {
"type": "string",
"description": "Seconds since the epoch."
},
"validBefore": {
"type": "string",
"description": "Seconds since the epoch, as EIP-3009 writes them."
},
"amountAtomic": {
"type": "string",
"description": "The amount in ATOMIC units of the token, as a decimal string. Not dollars."
}
},
"description": "What you believe you are doing. Anything you leave out is not compared, and is reported as not compared rather than as agreeing."
},
"resource": {
"type": "string",
"description": "The http(s) URL of the endpoint you are paying, where there is one. Send it and the answer also compares the bytes with the amount and the payee that endpoint demanded in its own 402 — the check that catches a transaction which is exactly what you meant and is not what the seller asked for."
},
"refreshQuote": {
"type": "boolean",
"description": "Knock on `resource` NOW instead of comparing against a stored reading, and set what comes back against the reading it replaced. `againstEndpoint.refreshed.quoteStillMatches` is true, false, or null where the two cannot be compared, with `verdict` as match/changed/unknown and `differs` naming which of amount, asset, network or payee moved. A CHANGED PAYEE is the substitution this whole check exists to catch. Only for an endpoint already in the x402 catalogue we read — we do not fetch a URL because a request named it — and at most one live handshake a minute per resource, shared by every caller. A match is what the endpoint said a moment ago, not a promise about the next moment."
},
"maxQuoteAgeSeconds": {
"type": "number",
"description": "How old a quote you are willing to act on, in seconds. YOURS, not ours: four hours is alarming for a market quote and irrelevant for a tariff unchanged since March, so a threshold published here would be a number we invented wearing the clothes of a measurement. The answer comes back as `againstEndpoint.standing.freshness` — `within-your-limit` or `older-than-your-limit` — with `freshEnough` as the boolean and your limit echoed back. Without it the answer says `no-limit-given` rather than grading the age."
}
}
}{
"type": "object",
"required": [
"verdict",
"findings"
],
"properties": {
"call": {
"type": [
"string",
"null"
],
"description": "What the bytes actually are, or null where it is not a call this decodes."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"limits": {
"type": "string",
"description": "What decoding a payment establishes. It reads what the transaction SAYS it will do; it does not simulate it and cannot tell you the recipient is honest."
},
"spender": {
"type": [
"string",
"null"
],
"description": "Who would be allowed to move your tokens later. Only an approve() has one, and an approve() has nothing else."
},
"verdict": {
"enum": [
"read",
"mismatch",
"undecodable"
],
"type": "string",
"description": "`read` means every comparison you asked for agreed. `mismatch` means at least one did not. `undecodable` means the call is not one this reads and NOTHING was checked — it is not a pass."
},
"evidence": {
"type": "object",
"description": "The common payment record, the same object on every payment answer: `listedPayee` from the seller’s catalogue entry, `onChainRecipient` from the bytes, `recipientIsListedPayee` (false is a finding, null means one side is missing), `facilitator` (the technical route, never the seller), `settlement` (`unknown` here, because nothing has been sent), `delivery` (`unevidenceable` on x402, whose rail records the payment and never the delivery) and `says`."
},
"findings": {
"type": "array",
"items": {
"type": "object"
},
"description": "One per check, each carrying whether it ran: observation, mismatch, or unknown."
},
"selector": {
"type": [
"string",
"null"
],
"description": "The four-byte function selector the calldata begins with, where we could read one. Null means the payload is not a call we can decode, which is not evidence that it is bad."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"recipient": {
"type": [
"string",
"null"
],
"description": "Who RECEIVES the money. Null on an approve(), which pays nobody: branch on this rather than on the prose."
},
"amountAtomic": {
"type": [
"string",
"null"
],
"description": "Atomic units, as a decimal string. The token owns its decimals and this does not apply them."
},
"actualTransfer": {
"type": "boolean",
"description": "Whether tokens move in THIS transaction. False for an approval, where the amount is a ceiling on what somebody else may take later rather than a sum leaving now."
},
"againstEndpoint": {
"type": [
"object",
"null"
],
"description": "SENT `resource`? THEN THIS COMPARES THE BYTES WITH WHAT THE ENDPOINT ITSELF IS DEMANDING, which is the one thing `expect` cannot catch: bytes that are exactly what you meant and are not what the seller is asking for. `amount` is `exact-match`, `below-quote`, `above-quote` or `unknown`, in BASE UNITS with no scaling and only where the token the call is made against is the token the quote prices in. **`below-quote` is the expensive one**: the call is refused and the tokens have left. `payee` is `matches`, `differs` or `unknown` against the address the 402 named, and it reads the approved SPENDER on an approval, which pays nobody. `asked: false` means we have never probed that endpoint, which is our gap and not evidence about it. `verdict` above is UNCHANGED by any of this — that is the manifest against YOUR expectation, and the two questions are kept apart on purpose."
},
"unlimitedAllowance": {
"type": "boolean",
"description": "True where this grants permission to move every token of this kind you will ever hold."
}
}
}{
"title": "Read the transaction you are about to sign",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": false
}record_receipt — Record what you did, so it can be checked later
Call this AFTER you have paid an address or called a tool, to put what you did on the record. Until now this platform answered "should I" and never heard what happened, which is the difference between a lookup and a control layer: a receipt lets the next question about this subject be asked against a record instead of a guess, and lets a verdict we gave be read back against what followed. `reference` is YOUR evidence, a transaction hash or the digest of a signed payload, and we store it WITHOUT verifying it. It comes back `claimed` and within the hour is judged against the payments we read from the chain: verified, contradicted, not found after two days, or unverifiable. Read the outcome at GET /v1/receipts/{id}. A claim never raises an evidence status, so a receipt nobody has checked never looks like one that settled. Needs a key; a receipt is readable only by the key that wrote it. WHY readOnlyHint IS false: this call writes a row in our store on behalf of the caller, so a second identical call is not free of consequence.
{
"type": "object",
"required": [
"subject",
"action",
"reference"
],
"properties": {
"chain": {
"type": "string",
"description": "The chain it settled on, where it settled on one."
},
"action": {
"enum": [
"payment",
"tool_call"
],
"type": "string",
"description": "Which kind of thing you are recording: `payment` for money that left, `tool_call` for a call you made. It decides nothing about verification — both are stored as YOUR claim, with your `reference` as the evidence — but it decides what the receipt can later be reconciled against, and a payment filed as a tool call will never meet the chain reading that would confirm it."
},
"policy": {
"type": "string",
"description": "The policy you evaluated under, so a later disagreement can be read against what we said at the time."
},
"subject": {
"type": "string",
"description": "The address you acted on."
},
"verdict": {
"type": "string",
"description": "The decision we gave you before you acted, for the same reason."
},
"deadline": {
"type": "string",
"description": "ISO 8601 time you expected it by. A time already past is refused, because it is usually seconds where milliseconds were meant."
},
"expected": {
"type": "string",
"description": "What you expected to follow, in your words. Ours to store and yours to claim."
},
"amountUsd": {
"type": "number",
"description": "What you committed, in US dollars."
},
"reference": {
"type": "string",
"description": "Your evidence that it happened: a transaction hash, or the digest of a signed payload. Stored, not verified."
}
}
}{
"type": "object",
"required": [
"id",
"recordedAt"
],
"properties": {
"id": {
"type": "string",
"description": "The receipt, for asking about it later."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "What was and was not verified."
},
"outcome": {
"type": [
"string",
"null"
],
"description": "Always null here: the receipt is a claim when it is written. Within the hour it is judged against the payments we read from the chain; read the outcome with `receipt_status`."
},
"subject": {
"type": "string",
"description": "The address the receipt is about, read back, so a receipt cannot be filed against the wrong party."
},
"evidence": {
"type": "object",
"description": "The common payment record (TODO-07): `state: built` with `facilitator` (who submitted, never credited), `onChainRecipient`, `listedPayee`, `facilitatorIsRecipient`, `recipientIsListedPayee`, `settlement`, `delivery`, `deliveryEvidence`, `says`; or `state: not-applicable` with `says` naming why no single payment is described."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"recordedAt": {
"type": "string",
"description": "When we wrote it down. Not when your action happened."
}
}
}{
"title": "Record what you did, so it can be checked later",
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": false,
"destructiveHint": false
}receipt_status — What became of a receipt you recorded
What became of a receipt you recorded with `record_receipt`. We do not take a receipt on your word: each one is read against the payments WE read from the chain and judged `verified` (it paid the subject and the amount agrees), `contradicted` (which of the two did not), `not-found` (absent from what we read two days after it was filed, a statement about our reading and never that it did not happen) or `unverifiable` (the reference is not a transaction hash). Until judged it is `claimed`, and a claim raises no evidence status anywhere. Readable only by the key that wrote it. Free.
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "The id `record_receipt` returned."
}
}
}{
"type": "object",
"required": [
"id",
"status",
"says"
],
"properties": {
"id": {
"type": "string",
"description": "The receipt."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "What was found, or why it is still a claim."
},
"status": {
"enum": [
"claimed",
"verified",
"contradicted",
"not-found",
"unverifiable"
],
"type": "string",
"description": "What we found when we read it; `claimed` until then."
},
"subject": {
"type": "string",
"description": "The address it is about, read back."
},
"evidence": {
"type": "object",
"description": "The common payment record (TODO-07): `state: built` with `facilitator` (who submitted, never credited), `onChainRecipient`, `listedPayee`, `facilitatorIsRecipient`, `recipientIsListedPayee`, `settlement`, `delivery`, `deliveryEvidence`, `says`; or `state: not-applicable` with `says` naming why no single payment is described."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"outcomeAt": {
"type": [
"string",
"null"
],
"description": "When it was judged; null while it is still a claim."
},
"reference": {
"type": "string",
"description": "Your evidence, as you gave it."
}
}
}{
"title": "What became of a receipt you recorded",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}monitor_subject — Be told when the answer about an address changes
Ask to be told when the answer about an address CHANGES. The dimensions are the ones the counterparty check already answers, so this is that same reading on a schedule rather than a second opinion; what it adds is the comparison. The first run records a reading and reports nothing — there is nothing yet to compare against, and a first reading dressed up as news is the thing this avoids. Idempotent per subject: calling it again edits the watch rather than creating a second. An unknown dimension or policy is REFUSED rather than dropped, because a watch that quietly ignores half of what you asked for is worse than no watch. Needs a key. WHY readOnlyHint IS false: this call writes a row in our store on behalf of the caller, so a second identical call is not free of consequence.
{
"type": "object",
"required": [
"subject"
],
"properties": {
"policy": {
"type": "string",
"description": "The policy its verdict is read against. An unknown name is refused rather than replaced with a default."
},
"subject": {
"type": "string",
"description": "The address to watch."
},
"dimensions": {
"type": "array",
"items": {
"enum": [
"endpoint",
"counterparty",
"risk",
"vault"
],
"type": "string"
},
"description": "What to watch. All four if you leave it out. An unknown name is refused, not ignored."
}
}
}{
"type": "object",
"required": [
"id",
"subject",
"dimensions"
],
"properties": {
"id": {
"type": "string",
"description": "The watch, for changing or ending it later."
},
"meta": {
"type": "object",
"description": "How current this answer is and what it was read from: `freshness` and the ages behind it. Present on every answer from this server, whatever the tool."
},
"says": {
"type": "string",
"description": "What this watch will and will not tell you. It reports a CHANGE in what we hold, which is not the same as a change in the world."
},
"policy": {
"type": [
"string",
"null"
],
"description": "The policy its verdict is read against. Null where the watch names none and the default applies; read `says` for which that is."
},
"subject": {
"type": "string",
"description": "The address being watched, read back so a watch cannot be set on the wrong one."
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the server wants a caller to know about THIS answer before acting on it. Empty is the normal case and is not a guarantee."
},
"dimensions": {
"type": "array",
"items": {
"type": "string"
},
"description": "What is actually being watched, which is what you asked for or all four."
},
"firstReadingAt": {
"type": [
"string",
"null"
],
"description": "When the comparison baseline was taken. Null means no reading yet, so nothing can be reported as a change."
}
}
}{
"title": "Be told when the answer about an address changes",
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}Prompts
Not advertised
Resources
Not advertised
90-day uptime
A lightweight connection check about every six hours, sampled on hosts with many listings. It shows the server answered, not that its tools work.
- 2026-11-07: 1 of 1 checks answered
30-day availability
Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.
- Successful checks
- 1 of 1 (100%)
- Average latency
- 1838 ms
- 95th percentile latency
- 1838 ms
Maintenance history
Public check and source-refresh events. Editorial and ownership records remain private.
- · probe · passed · 1838 ms · metadata changed · 44 tool(s)
Connect from your client
Each snippet registers the same endpoint; pick the client you use.
Claude Code
claude mcp add --transport http aspern-mcp https://aspern.org/mcpCodex
codex mcp add aspern-mcp --url https://aspern.org/mcpCursor
{
"mcpServers": {
"aspern-mcp": {
"url": "https://aspern.org/mcp"
}
}
}Cline
{
"mcpServers": {
"aspern-mcp": {
"type": "streamableHttp",
"url": "https://aspern.org/mcp",
"disabled": false
}
}
}VS Code
{
"servers": {
"aspern-mcp": {
"type": "http",
"url": "https://aspern.org/mcp"
}
}
}Gemini CLI
gemini mcp add --transport http aspern-mcp https://aspern.org/mcpClaude Desktop
https://aspern.org/mcpWindsurf
{
"mcpServers": {
"aspern-mcp": {
"serverUrl": "https://aspern.org/mcp"
}
}
}Let your agent find its tools
Find servers like ASPERN MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.
Other ai and machine learning servers
Do you run ASPERN MCP?
Prove ownership with a DNS record, a well-known file, or your Official MCP Registry entry, and your edits publish immediately. Proofs are re-checked weekly; admins can still hide or remove a listing.