{
 "openapi": "3.1.0",
 "info": {
  "title": "Roach Agent API",
  "version": "0.2.0",
  "description": "Solana token holder analysis and Bag Watch alerts for agents. REST + MCP share one credit ledger. Webhook deliveries are signed per Standard Webhooks (webhook-id = alert_id, webhook-timestamp, webhook-signature v1 HMAC-SHA256); reject timestamps older than 5 minutes."
 },
 "servers": [
  {
   "url": "https://api.stillroach.com",
   "description": "Roach Agent API"
  }
 ],
 "security": [
  {
   "bearer": []
  }
 ],
 "paths": {
  "/v1/health": {
   "get": {
    "summary": "Liveness",
    "security": [],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/llms.txt": {
   "get": {
    "summary": "LLM-readable overview of this API",
    "security": [],
    "responses": {
     "200": {
      "description": "text/plain"
     }
    }
   }
  },
  "/openapi.json": {
   "get": {
    "summary": "This document",
    "security": [],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/docs/mcp-setup.md": {
   "get": {
    "summary": "MCP connector setup guide",
    "security": [],
    "responses": {
     "200": {
      "description": "text/markdown"
     }
    }
   }
  },
  "/mcp": {
   "post": {
    "summary": "MCP (Streamable HTTP, stateless JSON-RPC). Tools: check_solana_token_basic, analyze_solana_token, watch_report, alerts_since, get_quota, get_credit_packs",
    "security": [
     {
      "bearer": []
     },
     {}
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "202": {
      "description": "notification accepted"
     },
     "429": {
      "description": "rate_limited / quota_exhausted (with pack_url, plan_url, x402_url) / key_daily_cap; honour Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/keys/nonce": {
   "post": {
    "summary": "Get a one-time Sign-In-With-Solana message for key creation",
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "wallet": {
          "type": "string"
         }
        },
        "required": [
         "wallet"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "400": {
      "description": "bad wallet",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "rate_limited / quota_exhausted (with pack_url, plan_url, x402_url) / key_daily_cap; honour Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/keys": {
   "post": {
    "summary": "Create an API key (wallet-signed; shown once, stored hashed; replaces any previous key)",
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "wallet": {
          "type": "string"
         },
         "message": {
          "type": "string"
         },
         "signature": {
          "type": "string",
          "description": "base58 ed25519 signature of message"
         }
        },
        "required": [
         "wallet",
         "message",
         "signature"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "key created",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "400": {
      "description": "bad input",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "bad signature / nonce",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "key mint caps",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/keys/current": {
   "delete": {
    "summary": "Revoke the calling key",
    "security": [
     {
      "bearer": []
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "missing or revoked key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "rate_limited / quota_exhausted (with pack_url, plan_url, x402_url) / key_daily_cap; honour Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/quota": {
   "get": {
    "summary": "Credits left: monthly pool (free/Watch/Roach tier, refills every 30 days from your start date; pool_resets_at = next refill, epoch seconds) + pack credits",
    "security": [
     {
      "bearer": []
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "missing or revoked key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "rate_limited / quota_exhausted (with pack_url, plan_url, x402_url) / key_daily_cap; honour Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/token-analysis": {
   "post": {
    "summary": "Token holder analysis. depth=basic is free (cached only); depth=full costs 1 credit for a fresh scan, 0 if cached",
    "security": [
     {
      "bearer": []
     },
     {}
    ],
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "schema": {
       "type": "string",
       "maxLength": 128
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "mint": {
          "type": "string",
          "pattern": "^[1-9A-HJ-NP-Za-km-z]{32,44}$",
          "description": "Solana token mint (base58)"
         },
         "depth": {
          "type": "string",
          "enum": [
           "basic",
           "lite",
           "full"
          ],
          "default": "full",
          "description": "basic: free cached summary. lite: 3 fresh/day free, then 1 credit (cached 0). full: 1 credit fresh, 0 cached"
         },
         "max_data_age_seconds": {
          "type": "integer",
          "minimum": 0,
          "maximum": 3600,
          "default": 600
         },
         "include_ai": {
          "type": "boolean",
          "description": "Optional AI read, depth=full only (+1 credit keyed, +$0.04 x402). When the AI step fails, keyed calls return the holder result with ai.status=ai_unavailable and the AI credit refunded; x402 calls settle nothing and return 503 ai_unavailable."
         }
        },
        "required": [
         "mint"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Analysis"
        }
       }
      }
     },
     "400": {
      "description": "bad input",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "409": {
      "description": "in_progress / ai_unavailable",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "422": {
      "description": "idempotency_mismatch",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "scan_failed (no credit charged)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "503": {
      "description": "busy / capacity",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "missing or revoked key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "rate_limited / quota_exhausted (with pack_url, plan_url, x402_url) / key_daily_cap; honour Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/watch/report/{mint}": {
   "get": {
    "summary": "Bag Watch report for a watched coin (Watch plan)",
    "security": [
     {
      "bearer": []
     }
    ],
    "parameters": [
     {
      "name": "mint",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^[1-9A-HJ-NP-Za-km-z]{32,44}$",
       "description": "Solana token mint (base58)"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "402": {
      "description": "watch_required",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "not_watched",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "missing or revoked key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "rate_limited / quota_exhausted (with pack_url, plan_url, x402_url) / key_daily_cap; honour Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/alerts": {
   "get": {
    "summary": "Bag Watch alerts since a cursor, plus webhook dead letters (Watch plan)",
    "security": [
     {
      "bearer": []
     }
    ],
    "parameters": [
     {
      "name": "since",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 200
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "402": {
      "description": "watch_required",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "missing or revoked key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "rate_limited / quota_exhausted (with pack_url, plan_url, x402_url) / key_daily_cap; honour Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/packs": {
   "get": {
    "summary": "Credit pack prices (SOL, USDC, $STILL at 20% off; Solana only, no cards)",
    "security": [],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/v1/packs/quote": {
   "post": {
    "summary": "Quote one 100-credit pack: exact amount, recipient and memo, valid 15 min",
    "security": [
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "kind": {
          "type": "string",
          "enum": [
           "sol",
           "usdc",
           "still"
          ]
         }
        },
        "required": [
         "kind"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "400": {
      "description": "bad kind",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "503": {
      "description": "price_unavailable",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "missing or revoked key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "too_many_quotes / rate_limited"
     }
    }
   }
  },
  "/v1/packs/verify": {
   "post": {
    "summary": "Verify the on-chain payment for a quote and add the credits (each transaction is single-use)",
    "security": [
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "quote_id": {
          "type": "string"
         },
         "signature": {
          "type": "string",
          "description": "transaction signature (base58)"
         }
        },
        "required": [
         "quote_id",
         "signature"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "402": {
      "description": "payment_not_verified",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "quote_not_found",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "409": {
      "description": "receipt_used / quote_paid",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "missing or revoked key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "rate_limited / quota_exhausted (with pack_url, plan_url, x402_url) / key_daily_cap; honour Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/x402/v1/token-analysis": {
   "post": {
    "summary": "Keyless pay-per-call (x402 v2, USDC on Solana): lite 0.02; full 0.08 fresh, 0.02 cached; AI +0.04 (full only). Settled only after a successful scan. No payment header -> 402 with PAYMENT-REQUIRED",
    "security": [],
    "parameters": [
     {
      "name": "PAYMENT-SIGNATURE",
      "in": "header",
      "schema": {
       "type": "string"
      },
      "description": "base64 x402 v2 PaymentPayload"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "mint": {
          "type": "string",
          "pattern": "^[1-9A-HJ-NP-Za-km-z]{32,44}$",
          "description": "Solana token mint (base58)"
         },
         "max_data_age_seconds": {
          "type": "integer",
          "minimum": 0,
          "maximum": 3600,
          "default": 600
         },
         "include_ai": {
          "type": "boolean",
          "description": "Optional AI read, depth=full only (+1 credit keyed, +$0.04 x402). When the AI step fails, keyed calls return the holder result with ai.status=ai_unavailable and the AI credit refunded; x402 calls settle nothing and return 503 ai_unavailable."
         },
         "depth": {
          "type": "string",
          "enum": [
           "lite",
           "full"
          ],
          "default": "full",
          "description": "lite: bagcheck.lite.v1 (top-10 holders, dev stake, launch buy / dev sold, KOL wallets, basic flags). full: bagcheck.v1"
         }
        },
        "required": [
         "mint"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "paid result; PAYMENT-RESPONSE header carries the settlement",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Analysis"
        }
       }
      }
     },
     "402": {
      "description": "payment required (x402 PaymentRequired body + PAYMENT-REQUIRED header), or settle_failed",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "409": {
      "description": "in_progress / payment_for_another_request / challenge_used / receipt_used / ai_unavailable",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "rate_limited (20/min per payer)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "scan_failed: not settled, nothing charged",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "503": {
      "description": "facilitator_unavailable / capacity / ai_unavailable (nothing settled)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "400": {
      "description": "bad_input (include_ai with depth=lite, bad mint, bad depth); nothing charged",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/webhooks": {
   "get": {
    "summary": "List alert webhooks (Watch plan)",
    "security": [
     {
      "bearer": []
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "402": {
      "description": "watch_required",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "missing or revoked key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "rate_limited / quota_exhausted (with pack_url, plan_url, x402_url) / key_daily_cap; honour Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   },
   "post": {
    "summary": "Add an alert webhook (max 3). Returns the whsec_ signing secret once",
    "security": [
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "url": {
          "type": "string",
          "format": "uri"
         }
        },
        "required": [
         "url"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "400": {
      "description": "bad_url",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "watch_required",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "409": {
      "description": "webhook_limit",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "missing or revoked key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "rate_limited / quota_exhausted (with pack_url, plan_url, x402_url) / key_daily_cap; honour Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/webhooks/{id}": {
   "delete": {
    "summary": "Remove a webhook (pending deliveries cancelled)",
    "security": [
     {
      "bearer": []
     }
    ],
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^wh_[0-9a-f]{16}$"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "404": {
      "description": "not_found",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "missing or revoked key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "rate_limited / quota_exhausted (with pack_url, plan_url, x402_url) / key_daily_cap; honour Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/webhooks/{id}/deliveries": {
   "get": {
    "summary": "Recent deliveries: status pending/sent/dead/cancelled, attempts, last HTTP code",
    "security": [
     {
      "bearer": []
     }
    ],
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^wh_[0-9a-f]{16}$"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "404": {
      "description": "not_found",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "missing or revoked key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "rate_limited / quota_exhausted (with pack_url, plan_url, x402_url) / key_daily_cap; honour Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/webhooks/{id}/test": {
   "post": {
    "summary": "Queue a signed test event to this webhook",
    "security": [
     {
      "bearer": []
     }
    ],
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^wh_[0-9a-f]{16}$"
      }
     }
    ],
    "responses": {
     "202": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "404": {
      "description": "not_found",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "missing or revoked key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "rate_limited / quota_exhausted (with pack_url, plan_url, x402_url) / key_daily_cap; honour Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/packs/blockhash": {
   "get": {
    "summary": "Recent blockhash for building the pack payment transaction",
    "security": [
     {
      "bearer": []
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "503": {
      "description": "rpc_unavailable"
     },
     "401": {
      "description": "key_required"
     },
     "429": {
      "description": "rate_limited"
     }
    }
   }
  },
  "/.well-known/x402": {
   "get": {
    "summary": "x402 discovery manifest: resources, input schema and USDC prices for the pay-per-call endpoint",
    "security": [],
    "responses": {
     "200": {
      "description": "manifest",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "securitySchemes": {
   "bearer": {
    "type": "http",
    "scheme": "bearer",
    "description": "rk_live_... key from https://app.stillroach.com/watch#keys (or X-Api-Key header)"
   }
  },
  "schemas": {
   "Error": {
    "type": "object",
    "properties": {
     "error": {
      "type": "string"
     },
     "message": {
      "type": "string"
     },
     "retry_after": {
      "type": "integer"
     }
    },
    "required": [
     "error"
    ]
   },
   "Analysis": {
    "type": "object",
    "properties": {
     "schema": {
      "const": "roach.analysis.v1"
     },
     "request_id": {
      "type": "string"
     },
     "mint": {
      "type": "string"
     },
     "depth": {
      "type": "string"
     },
     "data_as_of": {
      "type": [
       "integer",
       "null"
      ]
     },
     "cached": {
      "type": "boolean"
     },
     "credits": {
      "type": "object"
     },
     "result": {
      "type": "object",
      "description": "bagcheck.v1 result. Full depth adds holders.bundles.operators (wallets -> people, top_operator_pct_supply in % of supply), checks.dump_impact (price_change_pct if the top operator sold everything at once; venue curve|amm high confidence, approx low), checks.wallet_memory (repeat dumpers from coins scanned before) and verdict.info_codes (informational, never change the label)."
     },
     "ai": {
      "type": "object",
      "description": "Present only when include_ai was requested and the add-on is enabled",
      "properties": {
       "status": {
        "type": "string",
        "enum": [
         "ok",
         "ai_unavailable"
        ]
       },
       "refunded_credits": {
        "type": "integer"
       },
       "message": {
        "type": "string"
       }
      }
     }
    }
   }
  }
 },
 "webhooks": {
  "watch.alert": {
   "post": {
    "summary": "Bag Watch alert (facts only)",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "type": {
          "const": "watch.alert"
         },
         "alert_id": {
          "type": "string"
         },
         "ts": {
          "type": "integer"
         },
         "data": {
          "type": "object",
          "description": "bagwatch.alert.v1 event"
         },
         "report_url": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "2XX": {
      "description": "delivered; anything else is retried after 1m, 5m, 30m, 2h, 6h, 24h, then dead-lettered"
     }
    }
   }
  }
 }
}
