Wallet Verification API

machine-callable — no login, no dashboard

No platform wants a malicious wallet walking in the front door. ChainHound reads a wallet's real on-chain history and hands back a risk score — you decide the threshold. "Reject anything above 40" is a one-line check against a response field, not a research project.

Two verification depths, same riskScore / riskLabel shape both times — pick the one that matches how much you need to trust the answer. Both skip ENS name registration (a sequential on-chain write, and the slowest part of the pipeline) — a gate needs a fast verdict, not a pretty name. The web app at chain-hound-nu.vercel.app still registers every wallet as a real ENSv2 subname; these two endpoints just don't wait on it.

POST/api/agent/walletshallow · fast

Single-wallet verification. Risk-scores one address from its own transaction history alone — Uniswap V3 swaps, Aave V3 lending, on-chain fund flow. No counterparties followed. Use this when you just need "is this one address clean?" before an approval, deposit, or sign-up.

Request
{
  "walletAddress": "0x8f3a91c2b7e4a1f0d9c6b5e3a2f1d0c9b8a7e6f5"
}
Response
{
  "walletAddress": "0x8f3a...",
  "chain": "mainnet",
  "swaps": { "...": "raw Uniswap V3 result" },
  "lending": { "...": "raw Aave V3 result" },
  "fundFlow": { "sent": [...], "received": [...] },
  "riskAnalysis": {
    "riskScore": 62,
    "riskLabel": "Medium",
    "flags": [ { "severity": "Medium",
      "title": "..." } ],
    "positiveSignals": [ "..." ]
  }
}

If scoring fails, riskAnalysis is { error: string } instead — no riskScore. Check for .error first.

POST/api/agent/tracedeep · more secure

Trail verification. Verifies the wallet plus its most recent outgoing counterparties (one hop out), risk-scores each independently, and rolls the results into one deterministic pass/fail trail verdict — the trail is only as clean as its riskiest link. Use this for higher-stakes gates (large withdrawals, custody onboarding) where a clean wallet fed by a dirty one still shouldn't pass.

Request
{
  "walletAddress": "0x8f3a91c2b7e4a1f0d9c6b5e3a2f1d0c9b8a7e6f5"
}
Response
{
  "rootWallet": "0x8f3a...",
  "nodes": [
    { "wallet": "0x8f3a...", "depth": 0,
      "riskScore": 62, "riskLabel": "Medium",
      "isSink": false },
    { "wallet": "0x1a2b...", "depth": 1,
      "riskScore": 88, "riskLabel": "High",
      "isSink": false }
  ],
  "edges": [ { "from": "0x8f3a...", "to": "0x1a2b...",
    "amount": "1.4", "token": "ETH", "timestamp": 1234 } ],
  "stats": { "totalNodesAnalyzed": 2, "totalEdgesFound": 1 },
  "overallRisk": { "score": 88, "label": "High",
    "passed": false, "scoredNodes": 2, "totalNodes": 2,
    "reason": "Highest risk in this trail comes from
      a wallet 1 hop out (0x1a2b...): High (88/100)" }
}

If scoredNodes is 0, overallRisk.score is absent and passed is false (fails closed) — nothing in the trail could be scored, not "the trail is clean."

POST/api/agent/uniswap-activitysupporting · chainable

Live Uniswap V3 subgraph lookup (via The Graph's Subgraph MCP) for one wallet's recent swap activity — the same lookup the two verification endpoints above run internally, exposed standalone so it can be chained as its own step (e.g. pull this as supporting evidence after a wallet is flagged).

Request
{
  "walletAddress": "0x8f3a91c2b7e4a1f0d9c6b5e3a2f1d0c9b8a7e6f5"
}
Response
{
  "walletAddress": "0x8f3a...",
  "chain": "mainnet",
  "swaps": { "...": "raw Uniswap V3 subgraph result" }
}
integration

How a platform actually uses this — apply your own threshold to the score. Check for a failed/missing score first: riskScore > threshold silently evaluates tofalse when the score is missing, which fails open — exactly the wrong default for a gate meant to keep malicious wallets out.

const res = await fetch("https://chain-hound-production.up.railway.app/api/agent/wallet", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ walletAddress }),
});
const { riskAnalysis } = await res.json();

if ("error" in riskAnalysis || riskAnalysis.riskScore === undefined) {
  // couldn't be scored — fail closed (flag for manual review), don't wave it through
} else if (riskAnalysis.riskScore > 40) {
  // block, flag for review, or route to /api/agent/trace for a deeper check
}

Full OpenAPI 3.0 spec: /openapi/chainhound.json — paste this URL into Bazantic's Spec URL field, or any OpenAPI-compatible client, to wire these up as callable tools.