{
  "openapi": "3.1.0",
  "info": {
    "title": "Ghostprint API",
    "version": "1.0.0",
    "description": "Start Solana token scans and read how their earliest wallets connect on-chain. Shared funding or timing doesn't prove common ownership. Results are evidence, not financial advice."
  },
  "servers": [{ "url": "/api/v1" }],
  "security": [{ "apiKey": [] }],
  "paths": {
    "/scans": {
      "post": {
        "summary": "Start a scan, or join one already running",
        "description": "A token that already has a result returns immediately and uses none of your limit.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "type": "object", "required": ["mint"], "properties": { "mint": { "type": "string", "description": "Token mint address" } } }
            }
          }
        },
        "responses": {
          "200": { "description": "A result exists", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScanAccepted" } } } },
          "202": { "description": "Queued or running", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScanAccepted" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/scans/{mint}": {
      "get": {
        "summary": "Get a scan's progress or report",
        "parameters": [{ "name": "mint", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Progress or the finished report", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScanResult" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/me": {
      "get": {
        "summary": "Get your tier, usage and credit balance",
        "responses": {
          "200": {
            "description": "Your account",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "wallet": { "type": "string" },
                    "tier": { "type": "string", "enum": ["account", "holder"] },
                    "scans_per_hour": { "type": "integer" },
                    "scans_used_this_hour": { "type": "integer" },
                    "scan_credits": { "type": "integer" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": { "apiKey": { "type": "http", "scheme": "bearer" } },
    "responses": {
      "Error": {
        "description": "Error",
        "headers": { "Retry-After": { "schema": { "type": "integer" }, "description": "Seconds to wait, on 429 and 503" } },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": ["missing_api_key", "invalid_api_key", "too_many_requests", "invalid_mint", "scan_limit_reached", "queue_full", "free_capacity_reached", "not_scanned"]
              },
              "message": { "type": "string" }
            }
          }
        }
      },
      "ScanAccepted": {
        "type": "object",
        "properties": {
          "mint": { "type": "string" },
          "status": { "type": "string", "enum": ["done", "queued", "running"] },
          "used_scan_credit": { "type": "boolean" },
          "scans_remaining_this_hour": { "type": "integer" }
        }
      },
      "ScanResult": {
        "type": "object",
        "properties": {
          "mint": { "type": "string" },
          "status": { "type": "string", "enum": ["queued", "running", "done", "failed"] },
          "progress": {
            "type": "object",
            "properties": { "step": { "type": "string" }, "done": { "type": "integer" }, "total": { "type": "integer" } }
          },
          "error": { "type": "string" },
          "report": { "$ref": "#/components/schemas/Report" }
        }
      },
      "Report": {
        "type": "object",
        "description": "The full scan result, as described in the API reference.",
        "properties": {
          "mint": { "type": "string" },
          "token": { "type": "object" },
          "creator": { "type": ["string", "null"] },
          "launchedAt": { "type": ["integer", "null"] },
          "scannedAt": { "type": "integer" },
          "buyers": { "type": "array", "items": { "type": "object" } },
          "funders": { "type": "array", "items": { "type": "object" } },
          "excludedFunders": { "type": "array", "items": { "type": "object" } },
          "links": { "type": "array", "items": { "type": "object" } },
          "flags": { "type": "array", "items": { "type": "object" } },
          "clusters": { "type": "array", "items": { "type": "object" } },
          "coverage": { "type": "object" },
          "summary": { "type": "object" }
        }
      }
    }
  }
}
