{
  "openapi": "3.1.0",
  "info": {
    "title": "dry.0tx.io",
    "version": "0.1.0",
    "description": "Checker for x402 endpoints on Solana. POST an endpoint URL; dry requests it five times the way a buyer would, stops before paying, and reports what would prevent a real buyer from completing the payment. Never pays, signs or holds a key. Free, no account. Full instructions: https://dry.0tx.io/llms.txt. Only the paths in this spec plus /, /llms.txt and /SKILL.md exist.",
    "contact": { "name": "@0sumTX on X", "url": "https://x.com/0sumTX" }
  },
  "servers": [{ "url": "https://dry.0tx.io" }],
  "paths": {
    "/check": {
      "post": {
        "summary": "Check an x402 endpoint on Solana without paying",
        "operationId": "check",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["url"],
                "properties": {
                  "url": { "type": "string", "format": "uri", "description": "The x402 endpoint. https:// only; private, loopback and local addresses are refused." },
                  "method": { "type": "string", "enum": ["GET", "POST"], "default": "GET" },
                  "body": { "description": "Optional JSON sent with a POST, as a buyer would send it. 16 KB max." }
                }
              },
              "example": { "url": "https://drip.0tx.io/v1/sol", "method": "POST", "body": { "devnet_address": "BwN9CVUyyQoxtHzm21bzjLxJHzLTpmebbRiLRCD26Rgm" } }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The check ran (whatever its verdict).",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckResult" } } }
          },
          "400": { "description": "invalid_url, url_refused, host_opted_out, invalid_method or invalid_body.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "rate_limited: more than 6 checks per minute from this IP. Wait a minute.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error", "message"],
        "properties": { "error": { "type": "string" }, "message": { "type": "string" }, "docs": { "type": "string" } }
      },
      "Check": {
        "type": "object",
        "required": ["name", "status"],
        "properties": {
          "name": { "type": "string", "enum": ["returns_402", "has_solana_option", "network_matches_token", "payto_can_receive", "requirements_stable", "fee_payer_funded"] },
          "status": { "type": "string", "enum": ["pass", "fail", "warn", "skipped"] },
          "detail": { "type": "string", "description": "Plain-language explanation. Always present on fail and warn." },
          "fix": { "type": "string", "description": "What to do about it. Always present on fail and warn." }
        }
      },
      "CheckResult": {
        "type": "object",
        "required": ["spec", "id", "url", "method", "checked_at", "verdict", "x402_version", "responses", "endpoint_checks", "options"],
        "properties": {
          "spec": { "type": "string", "const": "dry/0" },
          "id": { "type": "string", "example": "chk_3fA9xQ2mP0lK7wZc" },
          "url": { "type": "string" },
          "method": { "type": "string", "enum": ["GET", "POST"] },
          "checked_at": { "type": "string", "format": "date-time" },
          "verdict": { "type": "string", "enum": ["no_problems_found", "problems_found", "not_testable"], "description": "no_problems_found: every check passed or warned (not proof of payability; dry did not pay). problems_found: at least one fail. not_testable: no 402, or no Solana mainnet/devnet option." },
          "x402_version": { "type": ["integer", "null"], "description": "2 when requirements came in the PAYMENT-REQUIRED header, 1 when in the JSON body, null when no 402 was read." },
          "responses": { "type": "array", "description": "One per unpaid request, in order sent.", "items": { "type": "object", "properties": { "status": { "type": "integer" }, "error": { "type": "string", "enum": ["timeout", "refused", "too_many_redirects", "network"] } } } },
          "endpoint_checks": { "type": "array", "items": { "$ref": "#/components/schemas/Check" } },
          "options": {
            "type": "array",
            "description": "One per payment option on Solana mainnet or devnet.",
            "items": {
              "type": "object",
              "properties": {
                "network": { "type": "string", "description": "CAIP-2 id dry evaluated against." },
                "network_seen": { "type": "string", "description": "Network id as the endpoint sent it." },
                "asset": { "type": "string" },
                "pay_to": { "type": "string" },
                "amount": { "type": "string" },
                "fee_payer": { "type": ["string", "null"] },
                "checks": { "type": "array", "items": { "$ref": "#/components/schemas/Check" } }
              }
            }
          }
        }
      }
    }
  }
}
