{
  "openapi": "3.1.0",
  "info": {
    "title": "HoneyLabs",
    "version": "1.1.0",
    "summary": "Primary honeypot sensor observations, queryable.",
    "description": "HoneyLabs publishes what its own internet-facing honeypot sensors observe. Every record is first-hand.\n\n**If you are an AI agent, prefer the MCP server** at `https://mcp.honeylabs.net/mcp` (server card: `https://mcp.honeylabs.net/mcp/server-card`). The tools aggregate for you, so one call replaces a client you would otherwise write and maintain.\n\n**Prefer list endpoints over loops.** `/api/cves` returns every CVE currently being probed in one request; requesting them one id at a time costs far more for both sides and will hit limits quickly.\n\nKeys are free and issued at once at https://honeylabs.net/dashboard. Plans, prices and limits are in `/pricing.json`; every interface is listed in `/.well-known/api-catalog` (RFC 9727).",
    "contact": {
      "name": "HoneyLabs",
      "url": "https://honeylabs.net"
    },
    "license": {
      "name": "HoneyLabs terms of service",
      "url": "https://honeylabs.net/terms"
    },
    "termsOfService": "https://honeylabs.net/terms"
  },
  "servers": [
    {
      "url": "https://honeylabs.net"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "externalDocs": {
    "description": "Docs with worked examples",
    "url": "https://honeylabs.net/docs"
  },
  "tags": [
    {
      "name": "reports",
      "description": "One address, ASN, port or CVE."
    },
    {
      "name": "lists",
      "description": "Cheaper. Ask for many things at once."
    },
    {
      "name": "bulk",
      "description": "Submit a list of addresses."
    },
    {
      "name": "service",
      "description": "About the service itself: plans and limits."
    }
  ],
  "paths": {
    "/lookup/{ip}": {
      "get": {
        "tags": [
          "reports"
        ],
        "operationId": "getIpReport",
        "summary": "Full report for one address",
        "description": "For more than a handful of addresses use /api/enrich instead.",
        "parameters": [
          {
            "name": "ip",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "45.13.191.120"
          },
          {
            "name": "format",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "json"
              ]
            },
            "description": "Without this the response is HTML."
          }
        ],
        "responses": {
          "200": {
            "description": "Report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IpReport"
                }
              }
            }
          },
          "401": {
            "description": "No account. The body names the free signup path and the exact header to send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          },
          "429": {
            "description": "Limit reached. The body names a cheaper endpoint for the same question; Retry-After is accurate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/cves": {
      "get": {
        "tags": [
          "lists"
        ],
        "operationId": "listCves",
        "summary": "Every CVE currently observed being probed",
        "description": "Requires a key. This is the endpoint to use instead of looping over /api/cves/{id}. Computed once per cache period; while a first computation is still running the answer is 503 with Retry-After, never an empty list.",
        "parameters": [
          {
            "name": "window",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1h",
                "24h",
                "7d",
                "30d",
                "90d"
              ],
              "default": "7d"
            },
            "description": "Clamped to the plan's maximum. A shortened response carries window_notice."
          }
        ],
        "responses": {
          "200": {
            "description": "CVEs with counts and first/last seen",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CveList"
                }
              }
            }
          },
          "401": {
            "description": "No account. The body names the free signup path and the exact header to send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          },
          "429": {
            "description": "Limit reached. The body names a cheaper endpoint for the same question; Retry-After is accurate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          },
          "503": {
            "description": "The list is being computed and no earlier copy exists. Retry after the Retry-After seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/cves/{id}": {
      "get": {
        "tags": [
          "reports"
        ],
        "operationId": "getCve",
        "summary": "One CVE and who is probing it",
        "description": "Requires a key. Prefer /api/cves when you want more than one.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^CVE-\\d{4}-\\d{3,8}$"
            },
            "example": "CVE-2024-4577"
          },
          {
            "name": "window",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1h",
                "24h",
                "7d",
                "30d",
                "90d"
              ],
              "default": "90d"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CVE detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CveDetail"
                }
              }
            }
          },
          "404": {
            "description": "No signature for this CVE and the interactive sensors never saw it probed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          },
          "401": {
            "description": "No account. The body names the free signup path and the exact header to send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          },
          "429": {
            "description": "Limit reached. The body names a cheaper endpoint for the same question; Retry-After is accurate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/asns": {
      "get": {
        "tags": [
          "lists"
        ],
        "operationId": "listAsns",
        "summary": "Most active autonomous systems",
        "responses": {
          "200": {
            "description": "ASNs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AsnList"
                }
              }
            }
          },
          "401": {
            "description": "No account. The body names the free signup path and the exact header to send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          },
          "429": {
            "description": "Limit reached. The body names a cheaper endpoint for the same question; Retry-After is accurate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/asn/{asn}": {
      "get": {
        "tags": [
          "reports"
        ],
        "operationId": "getAsn",
        "summary": "One autonomous system",
        "parameters": [
          {
            "name": "asn",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "example": 14061
          }
        ],
        "responses": {
          "200": {
            "description": "ASN report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AsnReport"
                }
              }
            }
          },
          "401": {
            "description": "No account. The body names the free signup path and the exact header to send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          },
          "429": {
            "description": "Limit reached. The body names a cheaper endpoint for the same question; Retry-After is accurate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/ports": {
      "get": {
        "tags": [
          "lists"
        ],
        "operationId": "listPorts",
        "summary": "Most probed ports",
        "responses": {
          "200": {
            "description": "Ports",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortList"
                }
              }
            }
          },
          "401": {
            "description": "No account. The body names the free signup path and the exact header to send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          },
          "429": {
            "description": "Limit reached. The body names a cheaper endpoint for the same question; Retry-After is accurate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/port/{port}": {
      "get": {
        "tags": [
          "reports"
        ],
        "operationId": "getPort",
        "summary": "One port",
        "parameters": [
          {
            "name": "port",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 65535
            },
            "example": 22
          }
        ],
        "responses": {
          "200": {
            "description": "Port report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortReport"
                }
              }
            }
          },
          "401": {
            "description": "No account. The body names the free signup path and the exact header to send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          },
          "429": {
            "description": "Limit reached. The body names a cheaper endpoint for the same question; Retry-After is accurate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/top-actors": {
      "get": {
        "tags": [
          "lists"
        ],
        "operationId": "listTopActors",
        "summary": "Most active source addresses",
        "responses": {
          "200": {
            "description": "Actors",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActorList"
                }
              }
            }
          },
          "401": {
            "description": "No account. The body names the free signup path and the exact header to send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          },
          "429": {
            "description": "Limit reached. The body names a cheaper endpoint for the same question; Retry-After is accurate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/user-agents": {
      "get": {
        "tags": [
          "lists"
        ],
        "operationId": "listUserAgents",
        "summary": "Most seen user-agents",
        "responses": {
          "200": {
            "description": "User agents",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserAgentList"
                }
              }
            }
          },
          "401": {
            "description": "No account. The body names the free signup path and the exact header to send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          },
          "429": {
            "description": "Limit reached. The body names a cheaper endpoint for the same question; Retry-After is accurate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/enrich/preview": {
      "post": {
        "tags": [
          "bulk"
        ],
        "operationId": "previewEnrich",
        "summary": "Cost and validity of a submission, before spending credits",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string",
                    "description": "Free text; addresses are extracted from it."
                  }
                },
                "required": [
                  "text"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cost estimate",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EnrichPreview"
                }
              }
            }
          },
          "401": {
            "description": "No account. The body names the free signup path and the exact header to send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          },
          "429": {
            "description": "Limit reached. The body names a cheaper endpoint for the same question; Retry-After is accurate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/enrich": {
      "post": {
        "tags": [
          "bulk"
        ],
        "operationId": "enrich",
        "summary": "Verdicts for a list of addresses in one submission",
        "description": "10 addresses anonymously, 250 on free, 1,000 on Pro. Streamed.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string"
                  }
                },
                "required": [
                  "text"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Server-sent stream of enriched rows"
          },
          "401": {
            "description": "No account. The body names the free signup path and the exact header to send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          },
          "429": {
            "description": "Limit reached. The body names a cheaper endpoint for the same question; Retry-After is accurate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/stats": {
      "get": {
        "tags": [
          "lists"
        ],
        "operationId": "getStats",
        "summary": "Platform counters",
        "security": [],
        "description": "Unmetered and needs no key. Event counts (total_events, events_24h, events_1h) are first contact only. Address, network and country counts are distinct over first contact and interactive sessions together.",
        "responses": {
          "200": {
            "description": "Counters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Stats"
                }
              }
            }
          },
          "503": {
            "description": "Data backend briefly unavailable. Retry-After is set.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          },
          "429": {
            "description": "Per-address request rate exceeded. Unmetered does not mean unlimited.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refusal"
                }
              }
            }
          }
        }
      }
    },
    "/pricing.json": {
      "get": {
        "tags": [
          "service"
        ],
        "operationId": "getPlans",
        "summary": "Plans, prices and limits",
        "security": [],
        "description": "Every plan with its price (EUR, excluding VAT) and the limits the platform enforces, generated from the same registry the quota checks read. Needs no key and is not metered.",
        "responses": {
          "200": {
            "description": "The plan catalogue",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanCatalog"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Free key from https://honeylabs.net/dashboard"
      }
    },
    "schemas": {
      "Refusal": {
        "type": "object",
        "description": "Every refusal names what to do instead. Read it before retrying.",
        "properties": {
          "error": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          },
          "how_to_fix": {
            "type": "string",
            "example": "Authorization: Bearer <your-api-key>"
          },
          "signup": {
            "type": "string",
            "format": "uri"
          },
          "docs": {
            "type": "string",
            "format": "uri"
          },
          "cheaper_route": {
            "type": "object",
            "properties": {
              "instead_of": {
                "type": "string"
              },
              "use": {
                "type": "string"
              },
              "why": {
                "type": "string"
              }
            }
          },
          "alternatives": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "when": {
                  "type": "string"
                },
                "use": {
                  "type": "string"
                },
                "why": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "Engagement": {
        "type": "object",
        "description": "How much the actor actually sent, and whether it stayed.",
        "properties": {
          "level": {
            "type": "string",
            "enum": [
              "touch",
              "request",
              "payload"
            ],
            "description": "touch <64B, request 64B-4KB, payload >=4KB"
          },
          "label": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          },
          "bytes_sent": {
            "type": "integer"
          },
          "session_seconds": {
            "type": "integer"
          },
          "persistent": {
            "type": "boolean",
            "description": "Session held open 60s or more"
          }
        }
      },
      "Verdict": {
        "type": "object",
        "properties": {
          "verdict": {
            "type": "string",
            "enum": [
              "scanner",
              "tor",
              "malicious",
              "scanning",
              "probing",
              "none"
            ],
            "description": "Stable set, safe to switch on. 'none' means not observed, which is about our coverage rather than the address being clean."
          },
          "label": {
            "type": "string"
          },
          "detail": {
            "type": [
              "string",
              "null"
            ]
          },
          "confidence": {
            "type": "string",
            "enum": [
              "none",
              "low",
              "medium",
              "high"
            ]
          },
          "network_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "why": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The signals that decided it. Show these; a label without reasoning is an assertion."
          },
          "engagement": {
            "$ref": "#/components/schemas/Engagement"
          },
          "basis": {
            "type": "string",
            "enum": [
              "first_contact",
              "interactive",
              "combined"
            ],
            "description": "What decided the verdict: first-packet signals alone, the interactive sessions, or the first-packet classifier with the sessions counted in."
          }
        }
      },
      "WindowNotice": {
        "type": "object",
        "description": "Present when the requested window exceeded the plan and the response was shortened.",
        "properties": {
          "requested_days": {
            "type": "integer"
          },
          "returned_days": {
            "type": "integer"
          },
          "reason": {
            "type": "string"
          },
          "upgrade": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "IpReport": {
        "type": "object",
        "properties": {
          "ip": {
            "type": "string"
          },
          "observed": {
            "type": "boolean",
            "description": "True when a first-packet event or an interactive TCP session reached a sensor."
          },
          "total_events": {
            "type": "integer",
            "description": "First-packet events only."
          },
          "verdict": {
            "$ref": "#/components/schemas/Verdict"
          },
          "first_seen": {
            "type": [
              "string",
              "null"
            ],
            "description": "Earliest of either source."
          },
          "last_seen": {
            "type": [
              "string",
              "null"
            ],
            "description": "Latest of either source."
          },
          "events_24h": {
            "type": "integer"
          },
          "events_7d": {
            "type": "integer"
          },
          "interactive_sessions": {
            "type": "integer",
            "description": "Interactive TCP sessions in the last 90 days. UDP sessions are left out of every overview field (a UDP source can be forged) and stay in interaction."
          },
          "interactive_sessions_24h": {
            "type": "integer"
          },
          "interactive_sessions_7d": {
            "type": "integer"
          },
          "interactive_protocols": {
            "type": "array",
            "description": "Protocols of those sessions, most used first.",
            "items": {
              "type": "object",
              "properties": {
                "protocol": {
                  "type": "string"
                },
                "sessions": {
                  "type": "integer"
                }
              }
            }
          },
          "distinct_ports_total": {
            "type": "integer",
            "description": "TCP ports across both sources, each port once."
          },
          "geo": {
            "type": "object",
            "description": "country_code, country_name, region, city, lat, lon, asn and org; they fall back to the interactive sessions' values where the first-packet data has none.",
            "properties": {
              "country_code": {
                "type": "string"
              },
              "country_name": {
                "type": "string"
              },
              "region": {
                "type": "string"
              },
              "city": {
                "type": "string"
              },
              "lat": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "lon": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "asn": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "org": {
                "type": "string"
              }
            }
          },
          "fingerprints": {
            "type": "object"
          },
          "recent_events": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "source_domain": {
            "type": [
              "string",
              "null"
            ],
            "description": "rDNS, falling back to the sessions' rDNS."
          },
          "top_ports": {
            "type": "array",
            "description": "At most 15, ranked by events and sessions together.",
            "items": {
              "type": "object",
              "properties": {
                "port": {
                  "type": "integer"
                },
                "proto": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "count": {
                  "type": "integer",
                  "description": "First-packet events."
                },
                "sessions": {
                  "type": "integer",
                  "description": "Interactive sessions on that port."
                }
              }
            }
          },
          "timeline": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string"
                },
                "count": {
                  "type": "integer",
                  "description": "First-packet events."
                },
                "sessions": {
                  "type": "integer",
                  "description": "Interactive sessions."
                }
              }
            }
          },
          "cve_matches": {
            "type": "array",
            "description": "CVE signatures matched by this address's requests. A CVE whose payload an interactive session carried is a match with match_field \"interactive_session\" and confirmed_by \"interactive_session\". Each match carries actively_exploited (on the CISA KEV catalogue, or flagged by its signatures), kev_date_added (YYYY-MM-DD or null) and a title, taken the same way as on /api/cves.",
            "items": {
              "type": "object"
            }
          },
          "interaction": {
            "type": [
              "object",
              "null"
            ],
            "description": "The Interaction group: sessions, logins, commands, downloads and files from the interactive sensors over 90 days. null when the address held no session."
          }
        }
      },
      "CveList": {
        "type": "object",
        "properties": {
          "window": {
            "type": "string"
          },
          "pro": {
            "type": "boolean"
          },
          "window_notice": {
            "$ref": "#/components/schemas/WindowNotice"
          },
          "cves": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "tag_id": {
                  "type": "string"
                },
                "title": {
                  "type": "string",
                  "description": "The detection signature's title, else the KEV catalogue's vulnerabilityName, else a title HoneyLabs writes for an id the interactive sensors record that neither names. Empty when none applies."
                },
                "severity": {
                  "type": "string"
                },
                "actively_exploited": {
                  "type": "boolean",
                  "description": "On the CISA Known Exploited Vulnerabilities catalogue, or flagged as listed by the CVE's detection signatures. The catalogue is the first source and covers CVEs without a signature."
                },
                "events": {
                  "type": "integer",
                  "description": "Probes in the window on both sensor types: first_contact_events plus interactive_requests."
                },
                "unique_ips": {
                  "type": "integer",
                  "description": "Distinct source addresses over both sensor types. An address both saw counts once, so this is not a sum."
                },
                "interaction_sessions": {
                  "type": "integer",
                  "description": "Sessions on interactive sensors that sent the exploit in the window, on any transport. 0 when none."
                },
                "first_contact_events": {
                  "type": "integer",
                  "description": "Requests matching the CVE's detection signatures."
                },
                "first_contact_ips": {
                  "type": "integer",
                  "description": "Distinct addresses behind first_contact_events."
                },
                "interactive_requests": {
                  "type": "integer",
                  "description": "Requests to the interactive sensors that count as a probe for the CVE, plus one per session whose payload proved it without a counted request. 0 when none."
                },
                "interactive_sources": {
                  "type": "integer",
                  "description": "Distinct addresses behind interactive_requests."
                },
                "last_seen": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Date (YYYY-MM-DD, UTC) of the latest probe on either sensor type."
                },
                "published": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "NVD publication date when cached."
                },
                "kev_date_added": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "The date CISA added the CVE to the KEV catalogue (YYYY-MM-DD, its dateAdded). null when the catalogue does not list it."
                }
              }
            }
          }
        },
        "description": "Counts cover both sensor types: first contact (requests matching the CVE's detection signatures) and the requests the interactive sensors answered that count as a probe for the CVE (the sensor's rule for that CVE was the only one the request matched, on a path first contact's exclusions accept, or the request or its session carried the exploit). A request that matched several CVE rules counts for none. UDP is left out on both sides. A CVE only the interactive sensors saw is a row like any other; its title, severity and KEV flag come from probe_tags or NVD when either has them, and are empty otherwise."
      },
      "CveDetail": {
        "type": "object",
        "properties": {
          "cve_id": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "description": "The detection signature's title, else the KEV catalogue's vulnerabilityName, else a title HoneyLabs writes for an id the interactive sensors record that neither names. Empty when none applies."
          },
          "severity": {
            "type": "string"
          },
          "actively_exploited": {
            "type": "boolean",
            "description": "On the CISA Known Exploited Vulnerabilities catalogue, or flagged as listed by the CVE's detection signatures. The catalogue is the first source and covers CVEs without a signature."
          },
          "events": {
            "type": "integer",
            "description": "first_contact_events plus interactive_requests."
          },
          "unique_ips": {
            "type": "integer",
            "description": "Distinct addresses over both sensor types, each once."
          },
          "first_contact_events": {
            "type": "integer"
          },
          "first_contact_ips": {
            "type": "integer"
          },
          "interactive_requests": {
            "type": "integer"
          },
          "interactive_sources": {
            "type": "integer"
          },
          "patterns": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "actors": {
            "type": "array",
            "description": "Top sources over both sensor types, most events first.",
            "items": {
              "type": "object",
              "properties": {
                "ip": {
                  "type": "string"
                },
                "events": {
                  "type": "integer"
                },
                "first_contact_events": {
                  "type": "integer"
                },
                "interactive_requests": {
                  "type": "integer",
                  "description": "Requests the interactive sensors answered from this address; 0 when none."
                },
                "country_code": {
                  "type": "string"
                },
                "country": {
                  "type": "string"
                },
                "org": {
                  "type": "string"
                },
                "rdns": {
                  "type": "string"
                },
                "scanner_tag": {
                  "type": [
                    "object",
                    "null"
                  ],
                  "description": "The address's classification chip, hosting providers included; `scanner` says whether it is a recognised scanner."
                },
                "scanner": {
                  "type": [
                    "object",
                    "null"
                  ],
                  "description": "Set when the platform recognises the address as a scanner, by the signals the address report's \"Recognized scanner\" verdict reads: a research, commercial or vendor rule matching its reverse DNS or network name, or its address inside a known scanner range. null for every other address, hosting providers included.",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "The scanner's name in words, such as Censys. Empty when the range list names only a kind (university research scanners)."
                    },
                    "category": {
                      "type": "string",
                      "enum": [
                        "research",
                        "commercial",
                        "vendor",
                        ""
                      ],
                      "description": "The kind of scanner; empty when no rule or list entry says."
                    }
                  }
                }
              }
            }
          },
          "window_notice": {
            "$ref": "#/components/schemas/WindowNotice"
          },
          "interaction": {
            "$ref": "#/components/schemas/CveInteraction"
          },
          "known": {
            "type": "boolean",
            "description": "probe_tags holds the CVE, or the interactive sensors saw it probed in the last 90 days. A known CVE with no activity in the window answers 200 with zeros."
          },
          "body_patterns": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "top_asns": {
            "type": "array",
            "description": "At most 10, both sensor types.",
            "items": {
              "type": "object",
              "properties": {
                "asn": {
                  "type": "integer"
                },
                "org": {
                  "type": "string"
                },
                "events": {
                  "type": "integer"
                },
                "ips": {
                  "type": "integer"
                },
                "interactive_requests": {
                  "type": "integer"
                }
              }
            }
          },
          "top_countries": {
            "type": "array",
            "description": "At most 10, both sensor types.",
            "items": {
              "type": "object",
              "properties": {
                "country_code": {
                  "type": "string"
                },
                "country": {
                  "type": "string"
                },
                "events": {
                  "type": "integer"
                },
                "ips": {
                  "type": "integer"
                },
                "interactive_requests": {
                  "type": "integer"
                }
              }
            }
          },
          "fingerprints": {
            "type": "object",
            "properties": {
              "ja4": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "akin": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "sample_paths": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "First-contact request paths, at most 10."
          },
          "timeline": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "day": {
                  "type": "string",
                  "description": "YYYY-MM-DD, UTC"
                },
                "events": {
                  "type": "integer"
                },
                "ips": {
                  "type": "integer",
                  "description": "Distinct addresses that day over both sensor types."
                },
                "first_contact_events": {
                  "type": "integer"
                },
                "interactive_requests": {
                  "type": "integer"
                }
              }
            },
            "description": "One row per day either sensor type saw."
          },
          "interactive": {
            "$ref": "#/components/schemas/CveInteractive"
          },
          "window_hours": {
            "type": "integer"
          },
          "kev_date_added": {
            "type": [
              "string",
              "null"
            ],
            "description": "The date CISA added the CVE to the KEV catalogue (YYYY-MM-DD, its dateAdded). null when the catalogue does not list it."
          },
          "kev": {
            "type": [
              "object",
              "null"
            ],
            "description": "The CVE's entry in the CISA KEV catalogue; null when the catalogue does not list it.",
            "properties": {
              "date_added": {
                "type": "string",
                "description": "YYYY-MM-DD"
              },
              "vulnerability_name": {
                "type": "string"
              },
              "vendor_project": {
                "type": "string"
              },
              "product": {
                "type": "string"
              },
              "known_ransomware_campaign_use": {
                "type": "string",
                "description": "CISA's value: Known or Unknown."
              }
            }
          }
        },
        "description": "Counts cover both sensor types: first contact (requests matching the CVE's detection signatures) and the requests the interactive sensors answered that count as a probe for the CVE (the sensor's rule for that CVE was the only one the request matched, on a path first contact's exclusions accept, or the request or its session carried the exploit). A request that matched several CVE rules counts for none. UDP is left out on both sides."
      },
      "AsnList": {
        "type": "object",
        "properties": {
          "asns": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "AsnReport": {
        "type": "object",
        "properties": {
          "asn": {
            "type": "integer"
          },
          "org": {
            "type": "string"
          },
          "events": {
            "type": "integer"
          },
          "unique_ips": {
            "type": "integer"
          },
          "actors": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "ports": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "PortList": {
        "type": "object",
        "properties": {
          "ports": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "PortReport": {
        "type": "object",
        "properties": {
          "port": {
            "type": "integer"
          },
          "events": {
            "type": "integer"
          },
          "actors": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "ActorList": {
        "type": "object",
        "properties": {
          "actors": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "UserAgentList": {
        "type": "object",
        "properties": {
          "window": {
            "type": "string"
          },
          "user_agents": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "EnrichPreview": {
        "type": "object",
        "properties": {
          "ip_count": {
            "type": "integer"
          },
          "invalid": {
            "type": "integer"
          },
          "dupes": {
            "type": "integer"
          },
          "cost_credits": {
            "type": "integer"
          },
          "allowed": {
            "type": "boolean"
          },
          "flags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Stats": {
        "type": "object"
      },
      "CveInteraction": {
        "type": [
          "object",
          "null"
        ],
        "description": "What the interactive sensors recorded for this CVE: sessions in which a client went on to send the exploit after the sensor answered as the vulnerable service. null when there were none in the window. Hosts are defanged; render them as text, never as links.",
        "properties": {
          "window_days": {
            "type": "integer"
          },
          "sessions": {
            "type": "integer"
          },
          "sources": {
            "type": "integer",
            "description": "Distinct source addresses behind the sessions."
          },
          "top_asns": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "asn": {
                  "type": "integer"
                },
                "org": {
                  "type": "string"
                },
                "sessions": {
                  "type": "integer"
                }
              }
            }
          },
          "followed_by": {
            "type": "object",
            "description": "What the client did after sending the exploit.",
            "properties": {
              "downloads": {
                "type": "integer"
              },
              "commands": {
                "type": "integer"
              },
              "uploads": {
                "type": "integer",
                "description": "Sessions that handed the sensor a file other than the exploit's own body."
              }
            }
          },
          "download_hosts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "host": {
                  "type": "string",
                  "description": "Defanged, e.g. 203[.]0[.]113[.]9."
                },
                "sessions": {
                  "type": "integer"
                }
              }
            }
          },
          "recent": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "ts": {
                  "type": "string"
                },
                "country_code": {
                  "type": "string"
                },
                "asn_org": {
                  "type": "string"
                },
                "protocol": {
                  "type": "string"
                },
                "dest_port": {
                  "type": "integer"
                },
                "did": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "What the session did in words: logged in, tried logins, ran commands, named a download, uploaded a file, sent an exploit. The sensor's own labels are never published."
                }
              }
            }
          }
        }
      },
      "CveInteractive": {
        "type": [
          "object",
          "null"
        ],
        "description": "What the interactive sensors recorded of the probes for this CVE in the window. null when they answered none. Paths, user agents and payload excerpts are attacker text with every URL and address defanged; render them as text, never as links.",
        "properties": {
          "requests": {
            "type": "integer",
            "description": "Counted requests (as interactive_requests)."
          },
          "sources": {
            "type": "integer",
            "description": "Distinct source addresses behind them."
          },
          "payload_sources": {
            "type": "integer",
            "description": "How many of those sources sent the exploit payload."
          },
          "payload_sessions": {
            "type": "integer",
            "description": "Sessions whose payload proved the CVE, any transport."
          },
          "first_seen": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_seen": {
            "type": [
              "string",
              "null"
            ]
          },
          "top_sources": {
            "type": "array",
            "description": "At most 25, most requests first.",
            "items": {
              "type": "object",
              "properties": {
                "ip": {
                  "type": "string"
                },
                "requests": {
                  "type": "integer"
                },
                "payload": {
                  "type": "boolean",
                  "description": "Sent the exploit payload."
                },
                "country_code": {
                  "type": "string"
                },
                "country": {
                  "type": "string"
                },
                "asn": {
                  "type": "integer"
                },
                "org": {
                  "type": "string"
                },
                "first_seen": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "last_seen": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "scanner": {
                  "type": [
                    "object",
                    "null"
                  ],
                  "description": "Set when the platform recognises the address as a scanner, by the signals the address report's \"Recognized scanner\" verdict reads: a research, commercial or vendor rule matching its reverse DNS or network name, or its address inside a known scanner range. null for every other address, hosting providers included.",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "The scanner's name in words, such as Censys. Empty when the range list names only a kind (university research scanners)."
                    },
                    "category": {
                      "type": "string",
                      "enum": [
                        "research",
                        "commercial",
                        "vendor",
                        ""
                      ],
                      "description": "The kind of scanner; empty when no rule or list entry says."
                    }
                  }
                }
              }
            }
          },
          "paths": {
            "type": "array",
            "description": "Requests by method and path, at most 25.",
            "items": {
              "type": "object",
              "properties": {
                "method": {
                  "type": "string"
                },
                "path": {
                  "type": "string"
                },
                "requests": {
                  "type": "integer"
                },
                "sources": {
                  "type": "integer"
                }
              }
            }
          },
          "user_agents": {
            "type": "array",
            "description": "At most 10.",
            "items": {
              "type": "object",
              "properties": {
                "user_agent": {
                  "type": "string"
                },
                "requests": {
                  "type": "integer"
                }
              }
            }
          },
          "payloads": {
            "type": "array",
            "description": "Request bodies that carried the exploit, one per distinct body, with one of the paths it was sent to and how many.",
            "items": {
              "type": "object",
              "properties": {
                "method": {
                  "type": "string"
                },
                "path": {
                  "type": "string"
                },
                "paths": {
                  "type": "integer"
                },
                "excerpt": {
                  "type": "string",
                  "description": "The body as sent, the first 4,000 characters, URLs and addresses defanged."
                },
                "requests": {
                  "type": "integer"
                },
                "decoded": {
                  "type": "string",
                  "description": "An encoded stage in the body (PHP base64_decode('...'), echo ... | base64 -d) decoded to text, URLs and addresses defanged; empty when there is none."
                }
              }
            }
          },
          "bodies": {
            "type": "array",
            "description": "Other bodies of counted requests, same shape as payloads.",
            "items": {
              "type": "object"
            }
          },
          "after": {
            "type": "array",
            "description": "What these sources did on the interactive sensors from their first probe on (TCP sessions, any service), for sources that did more than probe. At most 25.",
            "items": {
              "type": "object",
              "properties": {
                "ip": {
                  "type": "string"
                },
                "sessions": {
                  "type": "integer"
                },
                "logged_in": {
                  "type": "integer",
                  "description": "Sessions that logged in."
                },
                "commands": {
                  "type": "integer",
                  "description": "Sessions that ran commands."
                },
                "downloads": {
                  "type": "integer",
                  "description": "Sessions that named a download."
                },
                "uploads": {
                  "type": "integer",
                  "description": "Sessions that uploaded a file."
                },
                "last_seen": {
                  "type": "string"
                },
                "did": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "The same in words: logged in, ran commands, named a download, uploaded a file."
                }
              }
            }
          },
          "scanner_sources": {
            "type": "integer",
            "description": "How many of `sources` the platform recognises as scanners, counted over every source, not only the listed ones."
          },
          "payload_scanner_sources": {
            "type": "integer",
            "description": "How many of the recognised scanners sent the exploit payload."
          },
          "scanner_category": {
            "type": "string",
            "enum": [
              "research",
              "commercial",
              "vendor",
              ""
            ],
            "description": "The kind every recognised scanner among the sources shares; empty when they differ, none is known, or there are none."
          }
        }
      },
      "PlanCatalog": {
        "type": "object",
        "required": [
          "currency",
          "prices_include_vat",
          "plans"
        ],
        "properties": {
          "currency": {
            "type": "string",
            "const": "EUR"
          },
          "prices_include_vat": {
            "type": "boolean"
          },
          "credit": {
            "type": "string",
            "description": "What one credit buys."
          },
          "included_in_every_plan": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "plans": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "name",
                "price",
                "limits"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "enum": [
                    "free",
                    "solo",
                    "pro",
                    "research",
                    "team"
                  ]
                },
                "name": {
                  "type": "string"
                },
                "summary": {
                  "type": "string"
                },
                "price": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string"
                    },
                    "vat_included": {
                      "type": "boolean"
                    },
                    "monthly": {
                      "type": "integer"
                    },
                    "annual": {
                      "type": "integer"
                    },
                    "free": {
                      "type": "boolean"
                    }
                  }
                },
                "how_to_get": {
                  "type": "object",
                  "properties": {
                    "method": {
                      "type": "string",
                      "enum": [
                        "sign_up",
                        "checkout",
                        "application",
                        "request"
                      ]
                    },
                    "url": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                },
                "limits": {
                  "type": "object",
                  "properties": {
                    "daily_credits": {
                      "type": "integer"
                    },
                    "requests_per_minute": {
                      "type": "integer"
                    },
                    "burst_requests_per_second": {
                      "type": "integer"
                    },
                    "query_window_days": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "query_window": {
                      "type": "string"
                    },
                    "full_history": {
                      "type": "boolean"
                    },
                    "enrich_addresses_per_request": {
                      "type": "integer"
                    },
                    "watchlists": {
                      "type": "integer"
                    },
                    "feed_tokens_per_watchlist": {
                      "type": "integer"
                    },
                    "alert_subscriptions": {
                      "type": "integer"
                    },
                    "fastest_alert_cadence_minutes": {
                      "type": "integer"
                    },
                    "fastest_feed_digest": {
                      "type": "string"
                    }
                  }
                },
                "support": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  }
}
