{
  "openapi": "3.1.0",
  "info": {
    "title": "DoppelCart Public Read API",
    "version": "1.0.0",
    "description": "Search and inspect nebty's public evidence record for the DoppelCart fake-shop network. The API is read-only in this description."
  },
  "servers": [
    { "url": "https://investigations.nebty-id.com" }
  ],
  "paths": {
    "/doppelcart/api/rows": {
      "get": {
        "summary": "Search and list investigation rows",
        "description": "Returns a paginated list. Search covers domains, attributed brands, registrars and exposed contacts.",
        "parameters": [
          { "$ref": "#/components/parameters/query" },
          { "$ref": "#/components/parameters/page" },
          { "$ref": "#/components/parameters/size" },
          { "$ref": "#/components/parameters/sort" },
          { "$ref": "#/components/parameters/direction" },
          { "$ref": "#/components/parameters/brand" },
          { "$ref": "#/components/parameters/verdict" },
          { "$ref": "#/components/parameters/confidence" },
          { "$ref": "#/components/parameters/live" },
          { "$ref": "#/components/parameters/registrar" },
          { "$ref": "#/components/parameters/brandSource" },
          { "$ref": "#/components/parameters/named" },
          { "$ref": "#/components/parameters/hasContact" }
        ],
        "responses": {
          "200": {
            "description": "A page of matching rows",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/RowPage" }
              }
            }
          }
        }
      }
    },
    "/doppelcart/api/stats": {
      "get": {
        "summary": "Get current dataset statistics",
        "responses": {
          "200": {
            "description": "Live counts and the most recent capture date",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Stats" }
              }
            }
          }
        }
      }
    },
    "/doppelcart/api/facets": {
      "get": {
        "summary": "Get available filter values",
        "responses": {
          "200": {
            "description": "Filter values and their row counts",
            "content": {
              "application/json": {
                "schema": { "type": "object", "additionalProperties": true }
              }
            }
          }
        }
      }
    },
    "/doppelcart/api/detail/{domain}": {
      "get": {
        "summary": "Get the evidence record for one domain",
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "description": "Domain name exactly as stored in the dataset",
            "schema": { "type": "string", "format": "hostname" }
          }
        ],
        "responses": {
          "200": {
            "description": "The row, archived-page metadata and related domains",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Detail" }
              }
            }
          },
          "404": { "description": "Domain not present in the dataset" }
        }
      }
    },
    "/doppelcart/api/export": {
      "get": {
        "summary": "Export all rows attributed to one brand",
        "parameters": [
          {
            "name": "brand",
            "in": "query",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 5,000 rows as CSV",
            "content": {
              "text/csv": { "schema": { "type": "string" } }
            }
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "query": { "name": "q", "in": "query", "schema": { "type": "string", "maxLength": 120 } },
      "page": { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1, "default": 1 } },
      "size": { "name": "size", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 } },
      "sort": { "name": "sort", "in": "query", "schema": { "type": "string", "enum": ["domain", "brand", "registered_at", "verdict", "build_files_matched"], "default": "domain" } },
      "direction": { "name": "dir", "in": "query", "schema": { "type": "string", "enum": ["asc", "desc"], "default": "asc" } },
      "brand": { "name": "brand", "in": "query", "schema": { "type": "string" } },
      "verdict": { "name": "verdict", "in": "query", "schema": { "type": "string", "enum": ["fleet_confirmed", "fleet_likely", "dormant", "unrelated", "unverified_blocked"] } },
      "confidence": { "name": "confidence", "in": "query", "schema": { "type": "string" } },
      "live": { "name": "live", "in": "query", "schema": { "type": "string", "enum": ["yes", "probable", "no", "unknown"] } },
      "registrar": { "name": "registrar", "in": "query", "schema": { "type": "string" } },
      "brandSource": { "name": "brand_source", "in": "query", "schema": { "type": "string", "enum": ["title+support_email", "support_email", "page_title", "domain_string"] } },
      "named": { "name": "named", "in": "query", "description": "Set to 1 to return only rows with an attributed brand", "schema": { "type": "string", "enum": ["1"] } },
      "hasContact": { "name": "has_contact", "in": "query", "description": "Set to 1 to return only rows with an exposed contact", "schema": { "type": "string", "enum": ["1"] } }
    },
    "schemas": {
      "InvestigationRow": {
        "type": "object",
        "properties": {
          "domain": { "type": "string" },
          "live": { "type": ["string", "null"] },
          "http_status": { "type": ["integer", "null"] },
          "final_url": { "type": ["string", "null"] },
          "brand_impersonated": { "type": ["string", "null"] },
          "victim_contact_found": { "type": ["string", "null"] },
          "victim_contact_evidence": { "type": ["string", "null"] },
          "ip": { "type": ["string", "null"] },
          "asn": { "type": ["string", "null"] },
          "registrar": { "type": ["string", "null"] },
          "registered_at": { "type": ["string", "null"] },
          "nameservers": { "type": ["string", "null"] },
          "build_files_matched": { "type": ["integer", "null"] },
          "nebty_scan_url": { "type": ["string", "null"] },
          "urlscan_url": { "type": ["string", "null"] },
          "evidence_source": { "type": ["string", "null"] },
          "verdict": { "type": ["string", "null"] },
          "confidence": { "type": ["string", "null"] },
          "brand_source": { "type": ["string", "null"] },
          "notes": { "type": ["string", "null"] }
        },
        "required": ["domain"]
      },
      "RowPage": {
        "type": "object",
        "properties": {
          "rows": { "type": "array", "items": { "$ref": "#/components/schemas/InvestigationRow" } },
          "page": { "type": "integer" },
          "size": { "type": "integer" },
          "total": { "type": "integer" },
          "total_for_query": { "type": "integer" },
          "pages": { "type": "integer" },
          "out_of_range": { "type": "boolean" },
          "query_ignored": { "type": "boolean" }
        },
        "required": ["rows", "page", "size", "total", "pages"]
      },
      "Stats": {
        "type": "object",
        "properties": {
          "domains": { "type": "integer" },
          "confirmed": { "type": "integer" },
          "brands": { "type": "integer" },
          "pages": { "type": "integer" },
          "contacts": { "type": "integer" },
          "captured_through": { "type": ["string", "null"] }
        }
      },
      "Detail": {
        "type": "object",
        "properties": {
          "row": { "$ref": "#/components/schemas/InvestigationRow" },
          "siblings": { "type": "array", "items": { "type": "object", "additionalProperties": true } }
        },
        "required": ["row", "siblings"]
      }
    }
  }
}
