{
  "openapi": "3.0.3",
  "info": {
    "title": "Verdix",
    "version": "0.1.0",
    "description": "Pre-transfer risk check for EVM addresses on Base. Each POST path below is paid per request via x402 (USDC on Base mainnet, settled through Coinbase's CDP facilitator); an unpaid POST, or any GET on the same path, returns the 402 payment preview instead of running a check. Prefer not to handle x402 payments directly? See the verdix-mcp MCP server or the verdix-ai-sdk Vercel AI SDK tool at https://api.verdixapi.com/ai-sdk.",
    "termsOfService": "https://api.verdixapi.com/terms",
    "contact": { "url": "https://github.com/verdixapi" },
    "x-guidance": "Call Verdix before your agent sends funds to, approves, or pays an EVM address on Base, including a new x402 payTo. POST {\"address\": \"0x...\"} (42-char hex) as JSON to one tier URL and pay its single x402 price (USDC on Base, eip155:8453). Pick the tier by what is at stake: /risk/address/lite ($0.01) for list screening only (OFAC, scam/phishing lists, address-poisoning lookalikes, burn addresses, phishing tokens, flagged deployers); /risk/address/quick ($0.02) for small routine transfers; /risk/address/standard ($0.10) as the default for anything that matters; /risk/address/deep ($0.50) for large or first-time transfers. The answer's verdict is safe, caution or danger. Lite never says safe: its clean answer is no_known_risk (limited_checks: true), which means nothing matched its lists, not that the address is safe; call quick or higher when you need safe. Act on the verdict: danger means do not send; caution means stop and ask the user; safe means no risk signal was found, not a guarantee. A 503 answer is caution and is never charged. GET on the same URLs only returns the 402 price preview and never runs a check. The older POST /risk/address offers three prices in one 402; use the tier URLs instead."
  },
  "servers": [
    { "url": "https://api.verdixapi.com" }
  ],
  "paths": {
    "/risk/address": {
      "post": {
        "summary": "Risk check (legacy combined route)",
        "description": "Offers all three tier prices in one 402 (accepts[], tagged extra.tier). Whichever price is actually paid decides the tier that runs - never the body's \"tier\" field. A generic x402 client that always pays accepts[0] gets quick. New integrations should call /risk/address/{quick,standard,deep} instead, which each offer a single price.",
        "operationId": "riskAddressLegacy",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RiskRequest" } } }
        },
        "responses": {
          "200": { "description": "Verdict for the paid tier.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RiskResponse" } } } },
          "400": { "description": "Malformed request body or address.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidationError" } } } },
          "402": { "description": "Payment required. Body carries x402 payment requirements: three accepts entries (quick $0.02, standard $0.10, deep $0.50), each with scheme, network, payTo and price.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentRequired" } } } },
          "503": { "description": "A required upstream data source was unavailable; verdict is \"caution\". Payment is never settled for this response.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RiskResponse" } } } }
        }
      }
    },
    "/risk/address/lite": {
      "post": {
        "summary": "Lite tier risk check — $0.01",
        "description": "Cheapest screen, for when cost matters most. Checks OFAC sanctions, scam/phishing lists, address poisoning, burn addresses, phishing tokens and flagged deployers; skips address age and the caution-only contract signals. Never returns \"safe\": a clean answer is \"no_known_risk\" with limited_checks: true. For a verdict that can say safe, call /risk/address/quick. Single fixed price - see /pricing.",
        "operationId": "riskAddressLite",
        "x-payment-info": {
          "price": { "mode": "fixed", "amount": "0.01", "currency": "USD" },
          "protocols": [ { "x402": { "network": "eip155:8453", "asset": "USDC" } } ]
        },
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TierRiskRequest" } } }
        },
        "responses": {
          "200": { "description": "Verdict: no_known_risk, caution or danger (never safe).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LiteRiskResponse" } } } },
          "400": { "description": "Malformed request body or address.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidationError" } } } },
          "402": { "description": "Payment required: a single $0.01 accepts entry for this tier.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentRequired" } } } },
          "503": { "description": "A required list source was unavailable; verdict is \"caution\". Payment is never settled for this response.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LiteRiskResponse" } } } }
        }
      },
      "get": {
        "summary": "Lite tier payment preview (unpaid)",
        "description": "Always returns the 402 payment preview for the lite tier ($0.01). Never runs a check and never verifies or settles a payment; paid checks are POST only.",
        "operationId": "riskAddressLiteQuote",
        "responses": {
          "402": { "description": "Payment required: a single $0.01 accepts entry for this tier.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentRequired" } } } }
        }
      }
    },
    "/risk/address/quick": {
      "post": {
        "summary": "Quick tier risk check — $0.02",
        "description": "Fast screen for small, routine transfers. Checks OFAC sanctions, scam/phishing/exploit lists, address-poisoning lookalikes, burn addresses, contract/deployer signals, plus a fast on-chain address-age read. Single fixed price - see /pricing.",
        "operationId": "riskAddressQuick",
        "x-payment-info": {
          "price": { "mode": "fixed", "amount": "0.02", "currency": "USD" },
          "protocols": [ { "x402": { "network": "eip155:8453", "asset": "USDC" } } ]
        },
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TierRiskRequest" } } }
        },
        "responses": {
          "200": { "description": "Verdict.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RiskResponse" } } } },
          "400": { "description": "Malformed request body or address.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidationError" } } } },
          "402": { "description": "Payment required: a single $0.02 accepts entry for this tier.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentRequired" } } } },
          "503": { "description": "A required upstream data source was unavailable; verdict is \"caution\". Payment is never settled for this response.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RiskResponse" } } } }
        }
      }
    },
    "/risk/address/standard": {
      "post": {
        "summary": "Standard tier risk check — $0.10",
        "description": "Recommended default. Everything quick checks, but replaces the address-age read with deeper on-chain behaviour analysis (transfer history, activity patterns). Single fixed price - see /pricing.",
        "operationId": "riskAddressStandard",
        "x-payment-info": {
          "price": { "mode": "fixed", "amount": "0.10", "currency": "USD" },
          "protocols": [ { "x402": { "network": "eip155:8453", "asset": "USDC" } } ]
        },
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TierRiskRequest" } } }
        },
        "responses": {
          "200": { "description": "Verdict.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RiskResponse" } } } },
          "400": { "description": "Malformed request body or address.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidationError" } } } },
          "402": { "description": "Payment required: a single $0.10 accepts entry for this tier.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentRequired" } } } },
          "503": { "description": "A required upstream data source was unavailable; verdict is \"caution\". Payment is never settled for this response.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RiskResponse" } } } }
        }
      }
    },
    "/risk/address/deep": {
      "post": {
        "summary": "Deep tier risk check — $0.50",
        "description": "For large transfers, first-time counterparties, or contract interactions. Runs the same check set as standard today (reserved for further counterparty/contract checks as they ship). Single fixed price - see /pricing.",
        "operationId": "riskAddressDeep",
        "x-payment-info": {
          "price": { "mode": "fixed", "amount": "0.50", "currency": "USD" },
          "protocols": [ { "x402": { "network": "eip155:8453", "asset": "USDC" } } ]
        },
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TierRiskRequest" } } }
        },
        "responses": {
          "200": { "description": "Verdict.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RiskResponse" } } } },
          "400": { "description": "Malformed request body or address.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidationError" } } } },
          "402": { "description": "Payment required: a single $0.50 accepts entry for this tier.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentRequired" } } } },
          "503": { "description": "A required upstream data source was unavailable; verdict is \"caution\". Payment is never settled for this response.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RiskResponse" } } } }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "RiskRequest": {
        "type": "object",
        "required": ["address"],
        "properties": {
          "address": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$", "example": "0x71b749F58f63008672eEd55396151DC0C7493A78", "description": "EVM address to screen." },
          "chain": { "type": "string", "enum": ["base"], "default": "base" },
          "tier": { "type": "string", "enum": ["quick", "standard", "deep"], "default": "standard", "description": "Only used to shape the unpaid 402 preview on this route. Once a payment is verified, the tier actually paid for runs - this field is never trusted after that." }
        }
      },
      "TierRiskRequest": {
        "type": "object",
        "required": ["address"],
        "properties": {
          "address": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$", "example": "0x71b749F58f63008672eEd55396151DC0C7493A78", "description": "EVM address to screen." },
          "chain": { "type": "string", "enum": ["base"], "default": "base" }
        },
        "description": "No \"tier\" field: the URL already picks the tier, and any \"tier\" sent in the body is ignored."
      },
      "RiskResponse": {
        "type": "object",
        "properties": {
          "address": { "type": "string" },
          "chain": { "type": "string" },
          "tier": { "type": "string", "enum": ["quick", "standard", "deep"] },
          "price_usd": { "type": "number" },
          "risk_score": { "type": "integer", "minimum": 0, "maximum": 100 },
          "verdict": {
            "type": "string",
            "enum": ["safe", "caution", "danger"],
            "description": "safe = no risk signal found by these checks (not a guarantee); caution = not enough evidence, or a data source was unavailable; danger = a risk signal matched (see reasons)."
          },
          "reasons": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Why the verdict is not safe. Contract signals: unverified_contract = the address is a contract whose source code is not verified (on Blockscout or Sourcify), so what it does can't be read; unverified_dormant_contract = a contract not verified on Blockscout that fewer than 25 transactions ever touched (exploit and phishing contracts are often used once and dropped). Both mean caution, not danger: many legitimate contracts are unverified, and neither says the contract is malicious, only that Verdix can't vouch for it. recipient_is_token_contract = the address is an ERC-20 token contract (e.g. the USDC contract itself); sending a token there almost always loses it, so it is caution, never safe. Scam-list signal: scam_list_indirect_flag = a well-known official contract or exchange wallet on Base (e.g. the WETH predeploy or a Coinbase hot wallet) that the scam list (GoPlus) only marks as near malicious activity (it created, funded or traded with honeypot or malicious contracts); caution, never danger or safe, in every tier; any other scam-list flag on the same address is still danger. data_unavailable:<check> = that check could not complete (HTTP 503, not charged)."
          },
          "checked": { "type": "array", "items": { "type": "string" } },
          "as_of": { "type": "string", "format": "date-time" }
        },
        "example": {
          "address": "0x71b749F58f63008672eEd55396151DC0C7493A78",
          "chain": "base",
          "tier": "quick",
          "price_usd": 0.02,
          "risk_score": 5,
          "verdict": "safe",
          "reasons": [],
          "checked": ["ofac", "scam_lists", "onchain_age"],
          "as_of": "2026-09-24T10:00:00+00:00"
        }
      },
      "LiteRiskResponse": {
        "type": "object",
        "properties": {
          "address": { "type": "string" },
          "chain": { "type": "string" },
          "tier": { "type": "string", "enum": ["lite"] },
          "price_usd": { "type": "number" },
          "risk_score": { "type": "integer", "minimum": 0, "maximum": 100 },
          "verdict": {
            "type": "string",
            "enum": ["no_known_risk", "caution", "danger"],
            "description": "Never \"safe\". no_known_risk = no match on the lists lite checks (not a safety verdict); caution = not enough evidence, or a data source was unavailable; danger = a risk signal matched (see reasons)."
          },
          "reasons": { "type": "array", "items": { "type": "string" } },
          "checked": { "type": "array", "items": { "type": "string" } },
          "as_of": { "type": "string", "format": "date-time" },
          "limited_checks": { "type": "boolean", "enum": [true] },
          "checks_performed": { "type": "array", "items": { "type": "string" }, "description": "Checks that completed for this answer." },
          "not_checked": { "type": "array", "items": { "type": "string" }, "description": "Checks lite skips, plus any best-effort check that could not finish." },
          "full_check": { "type": "string", "description": "Pointer to /risk/address/quick for a full check." }
        },
        "example": {
          "address": "0x71b749F58f63008672eEd55396151DC0C7493A78",
          "chain": "base",
          "tier": "lite",
          "price_usd": 0.01,
          "risk_score": 40,
          "verdict": "no_known_risk",
          "reasons": ["lite_no_match:ofac,scam_lists,poisoning_watch,burn_list,phishing_token,deployer_flagged", "not_checked_in_lite:address_age,flash_loan_contracts,unverified_contracts"],
          "checked": ["ofac", "scam_lists", "poisoning_watch", "burn_list", "phishing_token", "deployer_flagged"],
          "as_of": "2026-10-06T10:00:00+00:00",
          "limited_checks": true,
          "checks_performed": ["ofac", "scam_lists", "poisoning_watch", "burn_list", "phishing_token", "deployer_flagged"],
          "not_checked": ["address_age", "flash_loan_contracts", "unverified_contracts"],
          "full_check": "Limited checks only: no known risk on these lists is not a safety verdict. For a full check that can return \"safe\", use POST /risk/address/quick."
        }
      },
      "ValidationError": {
        "type": "object",
        "properties": {
          "detail": { "type": "array", "items": { "type": "object" } }
        }
      },
      "PaymentRequired": {
        "type": "object",
        "description": "x402 payment-required body. Structure and field names follow the x402 protocol (https://www.x402.org/); Verdix does not extend it beyond the extra.tier tag on each accepts entry.",
        "properties": {
          "x402Version": { "type": "integer" },
          "accepts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "scheme": { "type": "string", "example": "exact" },
                "network": { "type": "string", "example": "eip155:8453" },
                "maxAmountRequired": { "type": "string" },
                "payTo": { "type": "string" },
                "resource": { "type": "string" },
                "extra": {
                  "type": "object",
                  "properties": { "tier": { "type": "string", "enum": ["lite", "quick", "standard", "deep"] } }
                }
              }
            }
          }
        }
      }
    }
  }
}
