---
openapi: 3.0.0
info:
  description: GreyNoise is a cybersecurity company that collects and analyzes Internet-wide scan and attack traffic. Use GreyNoise to contextualize existing alerts, filter false-positives, identify compromised devices, and track emerging threats.
  version: "3.0.0"
  title: GreyNoise API
  contact:
    email: hello@greynoise.io
  license:
    name: Proprietary
    url: https://www.greynoise.io/terms
tags:
  - name: Community
    description: Endpoints for the community level users
  - name: IP Lookup
    description: Calls to identify whether or not an IP address is noise, or get more information about a given IP address.
  - name: GNQL
    description: Calls to interface with GNQL (GreyNoise Query Language).
  - name: IP Timeline
    description: |
      Noise data captures internet scanning activity against GreyNoise sensors
      deployed globally.
      The IP Timeline APIs allow temporal analysis and presents the user with a
      view of how this data has changed over the time.
  - name: Recall
    description: |
      Endpoint that are used for retrieving GNQL data over time. Allows users
      to view hourly snapshots of IP activity for IPs that return for any
      GNQL query.
  - name: Utility
    description: |
      Endpoints that are used for checking status or retrieving basic metadata
  - name: CVE
    description: |
      Endpoints that are used for retrieving information about Common Vulnerabilities and Exposures (CVEs).
  - name: Tags
    description: |
      Endpoints for retrieving tag information, metadata, and associated activity data.
  - name: Compare
    description: |
      Endpoints for comparing GNQL query results across workspaces. Compare IP
      presence and statistics between a source and target workspace to identify
      differences in observed activity.
  - name: Sessions
    description: |
      Endpoints for querying, analyzing, and exporting raw network session (PCAP)
      data captured by GreyNoise sensors. Use the `scope` parameter to control
      data access (workspace or demo). Required entitlements vary by scope.
  - name: Business Service Intelligence
    description: |
      Endpoints that surface aggregate statistics for the GreyNoise Business Service
      Intelligence catalog (formerly RIOT). Use `date=now` for current information
      or `date=YYYY-MM-DD` for historical information. All BSI
      endpoints require the proper entitlement.
  - name: Blocklists
    description: >
      Endpoints for managing enterprise blocklists. Blocklists are workspace-scoped
      GNQL-driven IP lists that automatically refresh. Requires the Blocklists
      entitlement.
  - name: Event Feeds
    description: |
      Consume and search workspace-scoped feed events retained for approximately
      90 days.

      Pull consumers keep their checkpoints on the server. Fetching is read-only;
      acknowledge the returned signed cursor only after processing the complete
      batch. Event search uses a separate stateless pagination cursor and never
      changes a pull consumer's checkpoint.

      Session events are webhook-only and are not retained, searchable, or
      available through the pull API. Access depends on the workspace's Feeds and
      Event Feed API entitlements.

      For an end-to-end integration workflow, see
      [Using the Feeds API to Retrieve Events](https://docs.greynoise.io/docs/using-the-feeds-api-to-retrieve-events).
  - name: Threat Briefs
    description: |
      Endpoints for browsing GreyNoise research articles (Threat Briefs) and
      subscribing to them as RSS feeds. Browsing requires workspace-level
      authentication and appropriate entitlements; the community RSS feed is public.
  - name: Tactics
    description: |
      Endpoints for querying Tactics detections — aggregated attacker sessions
      observed on your deployed sensors, labeled with MITRE ATT&CK
      techniques and tactics. Search detections in a workspace, retrieve a
      single detection's command timeline, list the destination IPs it
      contacted, and inspect the files it touched. Requires the Tactics
      entitlement.
security:
  - APIKeyHeaderAuth: []
paths:
  /v3/community/{ip}:
    get:
      operationId: getCommunityIP
      parameters:
        - '$ref': '#/components/parameters/ip'
      tags:
        - Community
      summary: Community API
      description: |
        The Community API provides community users with a free tool to
        query IPs in the GreyNoise dataset and retrieve a subset of the full
        IP context data returned by the IP Lookup API.
      responses:
        '200':
          description: Query was successful, Community API found either a RIOT or Noise record for submitted IP address.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/CommunityResponse'
        '400':
          description: |
            The IP address submitted is not a valid routable IPv4 address.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Request is not a valid routable IPv4 address
        '404':
          description: The community API was unable to find a record for the requested IP address.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ip:
                    type: string
                    example: 1.2.3.4
                  noise:
                    type: boolean
                    example: false
                  riot:
                    type: boolean
                    example: false
                  message:
                    type: string
                    example: IP not observed scanning the internet or contained in RIOT data set.
        '429':
          description: Predefined rate-limit has been reached
          content:
            application/json:
              schema:
                type: object
                properties:
                  plan:
                    type: string
                    example: unauthenticated
                  rate-limit:
                    type: string
                    example: 100-lookups/day
                  plan_url:
                    type: string
                    example: https://greynoise.io/pricing
                  message:
                    type: string
                    example: >
                      You have hit your daily rate limit of 100 requests per day. Please create a free account or upgrade your plan at https://greynoise.io/pricing.

        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/ip/{ip}:
    get:
      tags:
        - IP Lookup
      summary: IP Lookup
      operationId: V3IP
      description: |
        Get more information about a given IP address. Returns time ranges,
        IP metadata (network owner, ASN, reverse DNS pointer, country),
        associated actors, activity tags, and raw port scan and web
        request information.

        Use the `quick` parameter to return a subset of the response fields, for a faster response time.
      parameters:
        - '$ref': '#/components/parameters/ip'
        - in: query
          name: quick
          description: If true, the response will only include the IP address and the classification or trust level.
          required: false
          schema:
            type: boolean
            default: false
        - '$ref': '#/components/parameters/workspaceLabels'
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                oneOf:
                  - '$ref': '#/components/schemas/IPResponseV3'
                  - '$ref': '#/components/schemas/QuickIpProfile'
        '206':
          description: |
            Partial content - request partially successful.
            Due to plan limitations, your request only returned a subset of
            fields and/or data. Contact sales@greynoise.io to upgrade your
            plan and unlock full results.
          content:
            application/json:
              schema:
                oneOf:
                  - '$ref': '#/components/schemas/IPResponseV3'
                  - '$ref': '#/components/schemas/QuickIpProfile'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
  /v3/ip:
    post:
      tags:
        - IP Lookup
      summary: IP Lookup - Multi
      operationId: V3MultiIP
      description: |
        Retrieves information about the submitted set of IP addresses
        from the Internet Scanner and Business Service intelligence datasets
        (consolidated response based on subscription entitlements).
        Returns time ranges, IP metadata (network owner, ASN, reverse DNS pointer, country),
        associated actors, tags, raw port scan data, web request information,
        classification and/or trust level, and provider information.

        Use the `quick` parameter to return a subset of the response fields, for a faster response time.

        Can process up to 10,000 IPs per request.
      requestBody:
        '$ref': '#/components/requestBodies/MultiIpRequest'
      parameters:
        - in: query
          name: quick
          description: If true, the response will only include the IP address and the classification or trust level.
          required: false
          schema:
            type: boolean
            default: false
        - '$ref': '#/components/parameters/workspaceLabels'
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                oneOf:
                  - '$ref': '#/components/schemas/MultiIPResponseV3'
                  - '$ref': '#/components/schemas/QuickMultiIPResponseV3'
        '206':
          description: |
            Partial content - request partially successful.
            Due to plan limitations, your request only returned a subset of
            fields and/or data. Contact sales@greynoise.io to upgrade your
            plan and unlock full results.
          content:
            application/json:
              schema:
                oneOf:
                  - '$ref': '#/components/schemas/MultiIPResponseV3'
                  - '$ref': '#/components/schemas/QuickMultiIPResponseV3'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
  /v3/gnql:
    get:
      tags:
        - GNQL
      summary: GNQL V3 Query
      operationId: gnqlV3Query
      description: |
        GreyNoise Query Language
        GNQL (GreyNoise Query Language) is a domain-specific query language
        that uses Lucene deep under the hood. GNQL aims to enable GreyNoise
        Enterprise and Research users to make complex and one-off queries
        against the GreyNoise dataset as new business cases arise. GNQL is
        built with self-defeat and fully featured product lines in mind. If
        we do our job correctly, each individual GNQL query that brings our
        users and customers sufficient value will eventually be transitioned
        into it's own individual offering.
        _License: The `business_service_intelligence` response field
        requires the BSI Module. Without it, every result returns an empty
        `business_service_intelligence` object; all other fields are
        returned normally._
        Facets:
        * `ip` - The IP address of the scanning device IP
        * `classification` - Whether the device has been categorized as
        unknown, benign, or malicious
        * `first_seen` - The date the device was first observed by GreyNoise
        * `last_seen` - The date the device was most recently observed
        by GreyNoise
        * `actor` - The benign actor the device has been associated with,
        such as Shodan, Censys, GoogleBot, etc
        * `tags` - A list of the tags the device has been assigned over the
        past 90 days
        * `spoofable` - This IP address has been opportunistically scanning the
        Internet, however has failed to complete a full TCP connection. Any
        reported activity could be spoofed.
        * `vpn` - This IP is associated with a VPN service. Activity, malicious
        or otherwise, should not be attributed to the VPN service provider.
        * `vpn_service` - The VPN service the IP is associated with
        * `tor` - Whether or not the device is a known Tor exit node
        * `cve` - A list of CVEs that the device has been associated with
        * `single_destination` - A boolean parameter that filters source country
        IPs that have only been observed in a single destination country
        * `metadata.category` - Whether the device belongs to a business, isp,
        hosting, education, or mobile network
        * `metadata.carrier` - The Internet Service Provider (ISP) or telecommunications carrier
        associated with the source IP address
        * `metadata.country` - The full name of the country the device is
        geographically located in (This is the same data as
        `metadata.source_country`. `metadata.source_country` is preferred)
        * `metadata.country_code` - The two-character country code of the
        country the device is geographically located in (This is the same data
        as `metadata.source_country_code`. `metadata.source_country_code`
        is preferred)
        * `metadata.datacenter` - The datacenter or hosting provider from which the activity originates.
        This could indicate the use of cloud services, managed hosting,
        or enterprise datacenter infrastructure.
        * `metadata.domain` - The domain name associated with the source IP address
        * `metadata.sensor_hits` - The amount of unique data that has been recorded by the sensor
        * `metadata.sensor_count` - The number of sensors the IP Address has been observed on
        * `metadata.city` - The city the device is geographically located in
        * `metadata.region` - The region the device is geographically located in
        * `metadata.organization` - The organization that owns the network that
        the IP address belongs to
        * `metadata.rdns` - The reverse DNS pointer of the IP
        * `metadata.asn` - The autonomous system the IP address belongs to
        * `metadata.asn_subnet` - Exact match on the latest-observed ASN subnet for the IP in GreyNoise scan data
        * `metadata.destination_cities` - The city where the GreyNoise sensor is geographically located
        * `metadata.destination_asns` - The ASN associated with the destination IP address
        * `metadata.destination_countries` - The full country name where the GreyNoise
        sensors are physically located
        * `metadata.destination_country_codes` - The country code where the GreyNoise
        sensors are physically located
        * `metadata.destination_country` - The full country name where the GreyNoise
        sensors are physically located
        * `metadata.destination_country_code` - The country code where the GreyNoise
        sensors are physically located
        * `metadata.latitude` - The geographic latitude of the source IP address
        * `metadata.longitude` - The geographic longitude of the source IP address
        * `metadata.rdns_parent` - The parent domain retrieved through reverse DNS (RDNS)
        lookup of the source IP address
        * `metadata.rdns_validated` - A validation status that confirms whether the
        reverse DNS (RDNS) record correctly maps to the source domain
        * `metadata.source_country_code` - The two-character country code of the
        country the device is geographically located in
        * `metadata.source_country` - The full name of the country the device is
        geographically located in
        * `raw_data.scan.port` - The port being targeted on a GreyNoise sensor
        * `raw_data.scan.protocol` - The protocol of the port the device has
        been observed scanning
        * `raw_data.web.paths` - Any HTTP paths the device has been observed
        crawling the Internet for
        * `raw_data.web.useragents` - Any HTTP user-agents the device has been
        observed using while crawling the Internet
        * `raw_data.ja3.fingerprint` - The JA3 TLS/SSL fingerprint
        * `raw_data.ja3.port` - The corresponding TCP port for the given JA3
        fingerprint
        * `raw_data.hassh.fingerprint` - The HASSH fingerprint
        * `raw_data.hassh.port` - The corresponding TCP port for the given HASSH
        fingerprint
        * `raw_data.http.md5` - An MD5 hash of the body content. This compact,
        unique representation of the data allows for quick comparisons and
        deduplication of payloads without storing the raw content.
        * `raw_data.http.cookie_keys` - The keys or names of cookies exchanged in the
        communication. These can reveal session identifiers, tracking mechanisms,
        or other metadata used in web interactions,
        providing clues about application behavior or vulnerabilities.
        * `raw_data.http.request_authorization` - The contents of the Authorization header in a request,
        typically containing authentication credentials or tokens (e.g., Basic Auth, Bearer tokens).
        Analyzing this helps verify authorization mechanisms and detect credential misuse or token abuse.
        * `raw_data.http.request_cookie` - Key-value pairs stored in cookies sent with an HTTP request.
        These cookies often contain session identifiers, user preferences, or tracking data,
        which can be analyzed to detect unauthorized access or manipulation.
        * `raw_data.http.request_header` - Request Headers are the keys (names) of HTTP headers that a
        client sends to a server.
        * `raw_data.http.request_method` - The HTTP method used in the request, such as GET, POST, PUT, or DELETE.
        Analyzing methods can reveal the intent of the request, such as retrieving or modifying resources,
        and identify unexpected or suspicious activity.
        * `raw_data.http.request_origin` - Indicates the origin of the request, typically used in
        cross-origin resource sharing (CORS) to specify where the request originated.
        This helps identify unauthorized or potentially malicious cross-origin requests.
        * `raw_data.tls.cipher` - The encryption algorithm or cipher suite used during
        the secure communication. Identifying the cipher helps assess the
        security of the connection, particularly in TLS/SSL traffic.
        * `raw_data.tls.ja4` - JA4 TLS fingerprint. JA4 captures distinctive
        characteristics of TLS client behavior, useful for identifying and
        clustering malicious or anomalous clients.
        * `raw_data.http.ja4h` - JA4H HTTP client fingerprint. Captures
        characteristics of HTTP client behavior including method, headers,
        and cookie fields, useful for identifying and tracking HTTP clients.
        * `raw_data.ssh.ja4ssh` - JA4SSH fingerprint. Captures SSH traffic
        patterns including packet lengths and directions, useful for
        identifying SSH client behavior and detecting anomalous sessions.
        * `raw_data.tcp.ja4t` - JA4T TCP fingerprint. Captures TCP
        connection characteristics such as window size, options, and MSS,
        useful for OS fingerprinting and identifying network stacks.
        * `raw_data.tcp.ja4l` - JA4L light distance/latency fingerprint.
        Captures TCP TTL and window size characteristics, useful for
        estimating client-server distance and identifying proxied connections.
        Behavior:
        * `raw_data.ssh.key` - This is the SSH key used.
        * You can subtract facets by prefacing the query with a minus character
        * Numeric facets such as `raw_data.scan.port` take inclusive ranges and
        lists: `raw_data.scan.port:[1-1024]` (or `[1 TO 1024]`),
        `raw_data.scan.port:[1024 TO *]`, `raw_data.scan.port:[22,23,2323]`
        and `raw_data.scan.port:[22, 80-90]`. For example,
        `raw_data.scan.port:502 -raw_data.scan.port:[0-501] -raw_data.scan.port:[503-65535]`
        returns IPs that scanned port 502 and no other port
        * The data that this endpoint queries refreshes once per hour
        Shortcuts:
        * You can find interesting hosts by using the GNQL query term
        `interesting`
        * You can use the keyword `today` in the `first_seen` and
        `last_seen` parameters: `last_seen:today` or `first_seen:today`
        Examples:
        * `last_seen:today` - Returns all IPs scanning/crawling the
        Internet today
        * `tags:Mirai` - Returns all devices with the "Mirai" tag
        * `tags:"RDP Scanner"` - Returns all devices with the "RDP
        Scanner" tag
        * `classification:malicious metadata.country:Belgium`
        - Returns all compromised devices located in Belgium
        * `classification:malicious metadata.rdns:*.gov*` - Returns
        all compromised devices that include .gov in their reverse DNS records
        * `metadata.organization:Microsoft classification:malicious`
        - Returns all compromised devices that belong to Microsoft
        * `(raw_data.scan.port:445 and raw_data.scan.protocol:TCP)
        metadata.os:Windows*` - Return all devices scanning the Internet
        for port 445/TCP running Windows operating systems
        (Conficker/EternalBlue/WannaCry)
        * `raw_data.scan.port:554` - Returns all devices scanning the
        Internet for port 554
        * `-metadata.organization:Google raw_data.web.useragents:GoogleBot`
        - Returns all devices crawling the Internet with "GoogleBot" in
        their useragent from a network that does NOT belong to Google
        * `tags:"Siemens PLC Scanner" -classification:benign` - Returns
        all devices scanning the Internet for SCADA devices who ARE
        NOT tagged by GreyNoise as "benign"
        (Shodan/Project Sonar/Censys/Google/Bing/etc)
        * `classification:benign` - Returns all "good guys" scanning
        the Internet
        * `raw_data.ja3.fingerprint:795bc7ce13f60d61e9ac03611dd36d90`
        - Returns all devices crawling the Internet with a matching
        client JA3 TLS/SSL fingerprint
        * `raw_data.hassh.fingerprint:51cba57125523ce4b9db67714a90bf6e`
        - Returns all devices crawling the Internet with a matching
        client HASSH fingerprint
        * `raw_data.tls.ja4:t13d1516h2_8daaf6152771_02713d6af862`
        - Returns all devices with a matching JA4 TLS fingerprint
        * `raw_data.http.ja4h:ge11cn060000_4e59edc1297a_4da5efaf0cbd`
        - Returns all devices with a matching JA4H HTTP fingerprint
        * `raw_data.ssh.ja4ssh:c76s76_c71s59_c0s0`
        - Returns all devices with a matching JA4SSH fingerprint
        * `raw_data.tcp.ja4t:64240_2-1-3-1-1-4_1460_8`
        - Returns all devices with a matching JA4T TCP fingerprint
        * `raw_data.tcp.ja4l:1460_64`
        - Returns all devices with a matching JA4L light
        distance/latency fingerprint
        * `raw_data.web.paths:"/HNAP1/"` -Returns all devices crawling
        the Internet for the HTTP path "/HNAP1/"
        * `8.0.0.0/8` - Returns all devices scanning the Internet from
        the CIDR block 8.0.0.0/8
        * `cve:CVE-2021-30461` - Returns all devices associated with the
        supplied CVE
        * `source_country:Iran` - Returns all results originating from Iran
        * `destination_country:Ukraine single_destination:true`
        - Returns all results scanning in only Ukraine
      parameters:
        - '$ref': '#/components/parameters/query'
        - in: query
          name: size
          description: The number of results provided per page for paginating through all results of a query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 10000
            default: 10000
        - in: query
          name: scroll
          description: Scroll token to paginate through results. Incompatible with `format=csv`.
          required: false
          schema:
            type: string
        - in: query
          name: quick
          description: If true, the response will only include the IP address and the classification or trust level.
          required: false
          schema:
            type: boolean
            default: false
        - in: query
          name: format
          description: Specifies the desired format of the results. Must be either csv or json.
          required: false
          schema:
            type: string
            enum: [csv, json]
            default: json
        - in: query
          name: exclude
          description: |
            Comma-separated list of fields to exclude from the response.
            Recognized top-level response fields (e.g. `tags`, `cves`, `vpn`, `tor`, `raw_data`, `metadata`),
            `metadata.<subfield>` paths (e.g. `metadata.organization`, `metadata.source_country`,
            `metadata.destination_countries`), and `raw_data.<subfield>` paths (e.g. `raw_data.ja3`,
            `raw_data.http.useragent`) are accepted. The special value `tags.details` preserves tag
            identity (id, slug) and strips only the enriched details. Unknown field names return 400.
          required: false
          schema:
            type: string
            example: "metadata.organization,metadata.city,raw_data.ja3"
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                oneOf:
                  - '$ref': '#/components/schemas/GNQLV3Response'
                  - '$ref': '#/components/schemas/QuickGNQLV3Response'
        '206':
          description: |
            Partial content - request partially successful.
            Due to plan limitations, your request only returned a subset of
            fields and/or data. Contact sales@greynoise.io to upgrade your
            plan and unlock full results.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/GNQLV3Response'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
  /v3/gnql/stats:
    get:
      tags:
        - GNQL
      summary: GNQL V3 Stats
      operationId: gnqlV3Stats
      description: |
        Get aggregate statistics for the top organizations, actors, tags,
        ASNs, countries, classifications, and operating systems of all the
        results of a given GNQL query.
      parameters:
        - '$ref': '#/components/parameters/query'
        - in: query
          name: count
          description: Number of top aggregates to grab
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 10000
            default: 1000
      responses:
        '200':
          description: Query successful.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/GNQLStats'
        '206':
          description: |
            Partial content - request partially successful.
            Due to plan limitations, your request only returned a subset of
            fields and/or data. The `adjusted_query` field in the response
            indicates how the original query was modified. Contact
            sales@greynoise.io to upgrade your plan and unlock full results.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/GNQLStats'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
  /v3/gnql/count:
    get:
      tags:
        - GNQL
      summary: GNQL V3 Count
      operationId: gnqlV3Count
      description: |
        Return the total number of IPs matching a GNQL query, without
        returning the IPs themselves. Use this to size a result set before
        paging through it with `GET /v3/gnql` or exporting it with
        `GET /v3/gnql/ips`.

        Entitlement, data-reach, and restricted-field handling match the
        search endpoint: if your plan reduces the query's reach or restricts a
        field it references, the query is rewritten before it runs, the
        response is a `206`, and `adjusted_query` reports the query that was
        actually executed.
      parameters:
        - '$ref': '#/components/parameters/query'
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/GNQLCountResponse'
        '206':
          description: |
            Partial content - request partially successful.
            Due to plan limitations, the submitted query was rewritten before
            it was counted. The `adjusted_query` field in the response
            indicates how the original query was modified. Contact
            sales@greynoise.io to upgrade your plan and unlock full results.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/GNQLCountResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/QueryEngineAtCapacity'
        '500':
          $ref: '#/components/responses/UnexpectedError'
        '504':
          $ref: '#/components/responses/QueryTimeout'
  /v3/gnql/ips:
    get:
      tags:
        - GNQL
      summary: GNQL V3 IP Export
      operationId: gnqlV3ExportIPs
      description: |
        Stream every IP address matching a GNQL query as a single JSON
        document: `{"ips": [...], "request_metadata": {...}}`. Unlike
        `GET /v3/gnql`, this endpoint returns IP addresses only — no metadata,
        tags, or raw data — and is not paginated: the full result set is
        streamed in one response, so no `scroll` token is involved.

        IPs are ordered most recently seen first (by `last_seen` descending,
        ties broken by ascending IP address). A client that reads only the
        beginning of the stream therefore receives the most recently observed
        IPs rather than an arbitrary slice of the match set.

        Because a large export can take a while to produce its first bytes,
        the response is kept alive with whitespace between chunks. Whitespace
        between JSON tokens is insignificant, so any standard JSON parser
        handles it; a parser reading the body incrementally should expect it.

        Entitlement, data-reach, and restricted-field handling match the
        search endpoint. When plan limitations rewrite the query, the response
        is a `206` if nothing has been written yet; on an export that has
        already begun streaming the status is committed as `200`, so treat a
        non-empty `request_metadata.adjusted_query` as the authoritative
        signal that the query was modified.
      parameters:
        - '$ref': '#/components/parameters/query'
        - in: query
          name: exclude_riot
          description: |
            When `true`, omit Business Service Intelligence (BSI, formerly
            RIOT) IPs at trust levels 1 and 2 — the tiers a blocklist should
            never emit. Requires the BSI Module; without it the parameter is
            accepted and the result set is returned unfiltered.
            `request_metadata.count` then reports the number of IPs actually
            returned rather than the pre-filter total.
          required: false
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/GNQLIPExportResponse'
        '206':
          description: |
            Partial content - request partially successful.
            Due to plan limitations, the submitted query was rewritten before
            it was executed. The `adjusted_query` field in
            `request_metadata` indicates how the original query was modified.
            Contact sales@greynoise.io to upgrade your plan and unlock full
            results.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/GNQLIPExportResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/QueryEngineAtCapacity'
        '500':
          $ref: '#/components/responses/UnexpectedError'
        '504':
          $ref: '#/components/responses/QueryTimeout'
  /v3/gnql/validate:
    post:
      tags:
        - GNQL
      summary: Validate GNQL Query
      operationId: gnqlV3Validate
      description: |
        Report whether a GNQL query would be accepted, without running it.

        This endpoint runs no query against the dataset and reports no search
        usage, so it is safe to call on every keystroke while a user composes
        a query. It also returns *every* reason a query was rejected, not just
        the first, with a character position for syntax errors so a client can
        place a caret on the offending token.

        A rejected query is not an error: it is a `200` with `valid` set to
        `false`. The HTTP status describes whether the query could be checked,
        so a caller can distinguish "your query is wrong" from "we could not
        tell you". A `4xx` or `5xx` therefore always means the request itself
        was malformed, unauthorized, or could not be serviced.

        Validation is gated on the same entitlement as search, and applies
        your plan's data reach and field restrictions: a query that is valid
        but would be rewritten before execution returns `valid: true` with
        `is_query_adjusted: true` and the rewritten query in `adjusted_query`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - query
              properties:
                query:
                  type: string
                  description: |
                    The GNQL query to validate. Leading and trailing
                    whitespace is trimmed; an empty query is a `400`.
                  maxLength: 4096
                  example: 'classification:malicious last_seen:1d'
      responses:
        '200':
          description: |
            The query was checked. `valid` carries the verdict — a rejected
            query is reported here, not as a `4xx`.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/GNQLValidateResponse'
              examples:
                valid:
                  summary: Query is accepted as submitted
                  value:
                    valid: true
                    query: 'classification:malicious last_seen:1d'
                adjusted:
                  summary: Query is valid but plan limitations rewrite it
                  value:
                    valid: true
                    query: 'classification:malicious last_seen:90d'
                    adjusted_query: 'classification:malicious last_seen:7d'
                    is_query_adjusted: true
                    message: 'The requested data reach of 90 days exceeds your plan; 7 days was used.'
                invalid:
                  summary: Query is rejected, with every reason listed
                  value:
                    valid: false
                    query: '(classification:malicious AND'
                    message: "Expected a search term after 'AND' but found end of query; Expected ')' but found end of query"
                    errors:
                      - message: "Expected a search term after 'AND' but found end of query"
                        position: 29
                        expected: 'search term'
                        found: 'end of query'
                        code: SYNTAX_ERROR
                      - message: "Expected ')' but found end of query"
                        position: 29
                        expected: "')'"
                        found: 'end of query'
                        code: SYNTAX_ERROR
        '400':
          description: |
            Bad request - the body was not valid JSON, `query` was missing or
            empty, or the query exceeded 4096 characters.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorMessage'
              example:
                message: query is required
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '413':
          description: |
            Request entity too large - the request body exceeded 64 KiB.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorMessage'
              example:
                message: request body is too large
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/gnql/timeseries:
    get:
      tags:
        - Recall
      summary: GNQL V3 Recall
      operationId: gnqlTimeSeries
      description: |
        Get hourly GNQL records for a given time range.
      parameters:
        - '$ref': '#/components/parameters/query'
        - in: query
          name: start
          description: Start date for the desired time range
          required: false
          schema:
            type: string
            format: date-time
          example: "2025-01-01T00:00:00Z"
        - in: query
          name: end
          description: End date for the desired time range
          required: false
          schema:
            type: string
            format: date-time
          example: "2025-01-07T23:59:59Z"
        - in: query
          name: format
          description: Specifies the desired format of the results. Must be either csv or json.
          required: false
          schema:
            type: string
            enum: [csv, json]
            default: json
        - in: query
          name: limit
          description: Specifies the number of records desired from the backend query. For example, if you specify a limit of 100, you will get the first 100 records for your query, divided up by hour.
          required: false
          schema:
            type: integer
        - in: query
          name: offset
          description: Specifies the offset at which to apply the limit. With limit, can be used to paginate through a large response. For example, if you specify a limit of 100 and and an offset of 200, you will get the next 100 records starting at the 200th record.
          required: false
          schema:
            type: integer
      responses:
        '200':
          description: Query successful.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/TimeSeriesResponse'
        '206':
          description: |
            Partial content - request partially successful.
            Due to plan limitations, your request only returned a subset of
            fields and/or data. Contact sales@greynoise.io to upgrade your
            plan and unlock full results.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/TimeSeriesResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/ExceededLimit'
  /v3/gnql/timeseries/stats:
    get:
      tags:
        - Recall
      summary: GNQL V3 Recall Stats
      operationId: gnqlTimeSeriesStats
      description: |
        Get the number of unique IPs that match a GNQL query
        per hour/day over a given time range.
      parameters:
        - '$ref': '#/components/parameters/query'
        - in: query
          name: start
          description: Start date for the desired time range
          required: false
          schema:
            type: string
            format: date-time
          example: "2025-01-01T00:00:00Z"
        - in: query
          name: end
          description: End date for the desired time range
          required: false
          schema:
            type: string
            format: date-time
          example: "2025-01-07T23:59:59Z"
        - in: query
          name: format
          description: Specifies the desired format of the results. Must be either csv or json.
          required: false
          schema:
            type: string
            enum: [csv, json]
            default: json
        - in: query
          name: interval
          description: Specifies the time interval over which to aggregate unique IPs that return the given GNQL query.
          required: true
          schema:
            type: string
            enum: [hour, day]
      responses:
        '200':
          description: Query successful.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/TimeSeriesStatsResponse'
        '206':
          description: |
            Partial content - request partially successful.
            Due to plan limitations, your request only returned a subset of
            fields and/or data. Contact sales@greynoise.io to upgrade your
            plan and unlock full results.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/TimeSeriesStatsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/ExceededLimit'
  /v3/gnql/metadata:
    get:
      tags:
        - GNQL
      summary: GNQL V3 Metadata Query
      operationId: gnqlV3MetadataQuery
      description: |
        GreyNoise Query Language Metadata Endpoint
        This endpoint provides the same functionality as the main GNQL endpoint
        but with additional field filtering capabilities. It automatically excludes
        raw data from responses and allows you to specify additional fields to exclude.

        The metadata endpoint is designed for use cases where you need to retrieve
        IP intelligence data without the raw scan data, making it more efficient
        for metadata-focused queries.

        _License: The `business_service_intelligence` response field
        requires the BSI Module. Without it, every result returns an empty
        `business_service_intelligence` object; all other fields are
        returned normally._
      parameters:
        - '$ref': '#/components/parameters/query'
        - in: query
          name: size
          description: The number of results provided per page for paginating through all results of a query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 10000
            default: 10000
        - in: query
          name: scroll
          description: Scroll token to paginate through results
          required: false
          schema:
            type: string
        - in: query
          name: quick
          description: If true, the response will only include the IP address and the classification or trust level.
          required: false
          schema:
            type: boolean
            default: false
        - in: query
          name: exclude
          description: |
            Comma-separated list of additional fields to exclude from the response.
            `raw_data` is always excluded by this endpoint; specifying it is redundant.
            Recognized top-level response fields (e.g. `tags`, `cves`, `vpn`, `tor`, `metadata`)
            and `metadata.<subfield>` paths (e.g. `metadata.organization`, `metadata.source_country`,
            `metadata.destination_countries`) are accepted. The special value `tags.details`
            preserves tag identity (id, slug) and strips only the enriched details. Unknown
            field names return 400.
          required: false
          schema:
            type: string
            example: "metadata.organization,metadata.city,metadata.rdns"
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                oneOf:
                  - '$ref': '#/components/schemas/GNQLV3Response'
                  - '$ref': '#/components/schemas/QuickGNQLV3Response'
        '206':
          description: |
            Partial content - request partially successful.
            Due to plan limitations, your request only returned a subset of
            fields and/or data. Contact sales@greynoise.io to upgrade your
            plan and unlock full results.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/GNQLV3Response'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
  /v3/noise/ips/{ip}/timeline:
    parameters:
      - in: path
        name: ip
        description: |
          IPv4 address to look up Noise time series activity timeline for
        schema:
          type: string
          example: 36.32.2.102
        required: true
      - in: query
        name: days
        description: |
          Number of days to show data for. `source_asn_subnet` is limited to
          90 days.
        schema:
          type: string
          example: "7"
          default: "1"
        required: false
      - in: query
        name: field
        description: Field over which to show activity breakdown
        schema:
          type: string
          enum:
            - destination_port
            - http_path
            - http_user_agent
            - source_asn
            - source_asn_subnet
            - source_org
            - source_rdns
            - tag_ids
            - classification
          example: classification
          default: classification
        required: true
      - in: query
        name: granularity
        description: |
          Granularity of activity date ranges. This can be in hours (e.g. Xh)
          or days (Xd).
          Valid hours are between 1 and 24. Valid days are between 1 and 90.
          `source_asn_subnet` supports daily granularity only (`1d` or `24h`).
        schema:
          type: string
          example: 8h
          default: 1d
        required: false
      - '$ref': '#/components/parameters/workspaceLabels'
    get:
      operationId: getIPTimelineFieldSummary
      tags:
        - IP Timeline
      summary: IP Timeline Field Summary
      description: |
        Retrieve an IP address' summary of noise activity for a specific field.

        _License: This endpoint requires an additional subscription
        license to use._
      responses:
        '200':
          description: Success - returns activity data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IPTimelineResponse'
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/tags:
    get:
      operationId: listTags
      tags:
        - Tags
      summary: List Tags
      description: |
        Retrieve a list of tags with their metadata. Supports filtering by name, slug, and CVE.

        This endpoint returns tag information including ID, name, slug, category, intention,
        description, references, CVEs, and related tags.
      parameters:
        - in: query
          name: name
          description: Filter tags by name (partial match)
          required: false
          schema:
            type: string
            example: Mirai
        - in: query
          name: slug
          description: Filter tags by slug (exact match)
          required: false
          schema:
            type: string
            example: mirai
        - in: query
          name: cve
          description: Filter tags by associated CVE
          required: false
          schema:
            type: string
            example: CVE-2020-1234
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/TagsMetadata'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/tags/{id}/activity:
    get:
      tags:
        - Tags
      summary: Get Tag Activity
      description: |
        Retrieve a single tag's activity over time as a series of time buckets, each
        reporting how many distinct IP addresses were seen scanning for that tag.

        The series is returned under `activity`, keyed by the tag's intention
        (for example `malicious`). Buckets are contiguous and half-open: a bucket
        covers `start` inclusive to `end` exclusive. Buckets with no activity are
        still returned, with `active_ips` of `0`.

        Set `include_ips=true` to additionally receive the IP addresses behind each
        bucket. This is opt-in because it is substantially more expensive than the
        counts alone, and the per-bucket list is capped — see `ips_truncated` below.
      parameters:
        - in: path
          name: id
          description: |
            The UUID of the tag, as returned in the `id` field of `GET /v3/tags`.

            Must be a well-formed UUID. A malformed value returns `400`.
          required: true
          schema:
            type: string
            format: uuid
            example: ef0cc90d-d80c-436f-92c5-3d8f8665c9ac
        - in: query
          name: days
          description: |
            How many days back to retrieve activity for.

            The requested value is additionally clamped by the data reach granted to
            your workspace by your plan. If you ask for more days than your plan
            allows, the request succeeds against the shorter window rather than
            failing — check `metadata.start_date` to see the window actually used.
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 90
            default: 1
            example: 7
        - in: query
          name: granularity
          description: |
            The width of each time bucket, as a duration string.

            Specify the width in whole hours with the `h` suffix, such as `1h`, `4h`,
            `24h` or `168h`. The duration must be between 1 hour and 1 week
            (`168h`) inclusive; anything outside that range returns `400`.
          required: false
          schema:
            type: string
            default: 24h
            example: 24h
        - in: query
          name: include_ips
          description: |
            When `true`, each bucket additionally carries the `ips` array of distinct
            source IP addresses observed in that bucket, and `metadata` reports
            `ips_included`, `ips_per_bucket` and `ips_truncated`.

            Defaults to `false`. Values are parsed as booleans (`true`/`false`, `1`/`0`);
            any other value returns `400` before the query runs.

            Requesting IPs is significantly slower than requesting counts alone, and
            each bucket's list is capped at 1,000 addresses. Prefer leaving this off
            when you only need the activity counts.
          required: false
          schema:
            type: boolean
            default: false
            example: true
        - '$ref': '#/components/parameters/workspaceLabels'
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/TagActivity'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/cves:
    post:
      operationId: bulkCVELookup
      tags:
        - CVE
      summary: Bulk CVE Lookup
      description: |
        Retrieve information about multiple CVEs in a single request. Supports up to 10,000 CVEs per request.

        This endpoint requires an Enterprise trial or paid plan with the appropriate entitlement.
        Response type depends on user entitlements (minimal, basic, or advanced).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - cves
              properties:
                cves:
                  type: array
                  items:
                    type: string
                    pattern: '^CVE-\d{4}-\d+$'
                  description: Array of CVE IDs to lookup
                  example:
                    - CVE-2024-12345
                    - CVE-2023-45678
                  maxItems: 10000
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                type: array
                items:
                  oneOf:
                    - '$ref': '#/components/schemas/CVEAdvancedResponse'
                    - '$ref': '#/components/schemas/CVEBasicResponse'
                    - '$ref': '#/components/schemas/CVEMinimalResponse'
        '400':
          description: |
            Bad request - invalid CVE format or too many CVEs requested.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: "Maximum number of CVEs per request is 10000"
        '403':
          description: |
            Forbidden - bulk CVE search requires an Enterprise trial or paid plan.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: "Bulk CVE search requires an Enterprise trial or paid plan. Start a trial or upgrade your plan to continue."
        '429':
          description: Too many requests. You've hit the rate-limit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /ping:
    get:
      operationId: ping
      tags:
        - Utility
      summary: Ping
      description: |
        Provides a simple endpoint to check GreyNoise status and GreyNoise
        API access
      responses:
        '200':
          description: Ping successful
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: pong
                  expiration:
                    type: string
                    description: API key expiration date
                    example: "2026-12-31"
                  offering:
                    type: string
                    description: Compatibility offering value. Currently always enterprise for authenticated requests.
                    example: enterprise
                  address:
                    type: string
                    description: Client IP address
                    example: "3.215.138.152"
                  plan:
                    type: string
                    description: Active subscription plan name
                    example: Elite
                  modules:
                    type: string
                    description: Comma-separated list of active add-on module names
                    example: "Hunt,Vulnerability Prioritization"
        '401':
          $ref: '#/components/responses/Unauthorized'
  /v1/cve/{cve_id}:
    get:
      operationId: getCVE
      tags:
        - CVE
      summary: Retrieve CVE Information
      description: Retrieve details about a specific Common Vulnerabilities and Exposures (CVE).
      parameters:
        - name: cve_id
          in: path
          description: The CVE ID to query (e.g., CVE-2024-12345)
          required: true
          schema:
            type: string
            pattern: '^CVE-\d{4}-\d+$'
      responses:
        '200':
          description: Successful response with CVE details based on entitlements.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/CVEAdvancedResponse'
                  - $ref: '#/components/schemas/CVEBasicResponse'
                  - $ref: '#/components/schemas/CVEMinimalResponse'
        '400':
          description: Invalid CVE ID format or missing CVE ID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: "bad query: invalid CVE format"
        '404':
          description: CVE not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: "CVE not found"
        '500':
          description: Internal server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: "internal server error"
  /v3/cves/list:
    get:
      operationId: listCVEs
      tags:
        - CVE
      summary: List CVEs
      description: |
        Return CVE records tracked by GreyNoise, ordered by publication date,
        last-update date, or CVSS score. Default cap is 100 records, configurable
        up to 1000 via the `limit` query parameter.

        This endpoint requires the `feature-search-cves-bulk` entitlement.
        Response payload is shaped according to the caller's CVE Insights tier
        (minimal, basic, or advanced).
      parameters:
        - in: query
          name: sort
          required: false
          schema:
            type: string
            enum: [published, updated, cvss]
            default: published
          description: Field to sort by.
        - in: query
          name: order
          required: false
          schema:
            type: string
            enum: [desc, asc]
            default: desc
          description: Sort direction.
        - in: query
          name: exploitable_only
          required: false
          schema:
            type: boolean
            default: false
          description: When true, restrict to CVEs with attack_vector="Network".
        - in: query
          name: limit
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 100
          description: Maximum number of CVE records to return.
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                type: object
                properties:
                  cves:
                    type: array
                    items:
                      oneOf:
                        - '$ref': '#/components/schemas/CVEAdvancedResponse'
                        - '$ref': '#/components/schemas/CVEBasicResponse'
                        - '$ref': '#/components/schemas/CVEMinimalResponse'
                  generated_at:
                    type: string
                    format: date-time
                    description: Timestamp when the response was produced.
        '400':
          description: Bad request — unknown sort/order, bad limit, or invalid exploitable_only.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden — caller lacks the bulk CVE search entitlement.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/workspaces/diff:
    post:
      tags:
        - Compare
      summary: Workspace Diff
      operationId: WorkspaceDiff
      description: |
        Compare GNQL query results between two workspaces. Returns per-IP
        diff results showing which IPs exist in each workspace and the
        field-level differences between them.

        Workspace IDs can be copied from **Plan details** in the [GreyNoise Visualizer](https://viz.greynoise.io): click the user menu in the top right, then select **Plan details**. The workspace ID is shown under the workspace name.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkspaceDiffRequest'
      responses:
        '200':
          description: OK - diff results returned successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceDiffResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/workspaces/stats-diff:
    post:
      tags:
        - Compare
      summary: Workspace Stats Diff
      operationId: WorkspaceStatsDiff
      description: |
        Compare GNQL query statistics between two workspaces. Returns
        aggregated stats for each workspace and a diff highlighting
        values unique to each workspace or shared between them.

        Workspace IDs can be copied from **Plan details** in the [GreyNoise Visualizer](https://viz.greynoise.io): click the user menu in the top right, then select **Plan details**. The workspace ID is shown under the workspace name.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StatsDiffRequest'
      responses:
        '200':
          description: OK - stats diff returned successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatsDiffResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/workspaces/unique-ips:
    post:
      tags:
        - Compare
      summary: Start Unique IPs Job
      operationId: StartUniqueIPsJob
      description: |
        Start an asynchronous job to discover IPs that are unique to the
        source workspace (present in source but not in target) for a given
        GNQL query.

        Returns a job ID that can be polled for status and results.

        Workspace IDs can be copied from **Plan details** in the [GreyNoise Visualizer](https://viz.greynoise.io): click the user menu in the top right, then select **Plan details**. The workspace ID is shown under the workspace name.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UniqueIPsStartRequest'
      responses:
        '202':
          description: Accepted - job started successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UniqueIPsStartResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/workspaces/unique-ips/{job_id}:
    get:
      tags:
        - Compare
      summary: Get Unique IPs Job Status
      operationId: GetUniqueIPsJobStatus
      description: |
        Poll the status of a unique IPs discovery job. When the job is
        complete, the response includes the list of unique IPs found.

        Results are paginated via `limit` and `offset` query parameters.
      parameters:
        - name: job_id
          in: path
          description: The job ID returned by the Start Unique IPs Job endpoint.
          required: true
          schema:
            type: string
        - name: source_workspace
          in: query
          description: |
            The source workspace. Defaults to the workspace associated with the
            request's API key when omitted. Accepts either a workspace UUID or
            one of the aliases `greynoise`, `community`, or `personal`.
          required: false
          schema:
            type: string
        - name: limit
          in: query
          description: Maximum number of unique IPs to return. Defaults to 10000.
          required: false
          schema:
            type: integer
            default: 10000
        - name: offset
          in: query
          description: Number of IPs to skip for pagination. Defaults to 0.
          required: false
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: OK - job status and results returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UniqueIPsStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/sessions:
    get:
      tags:
        - Sessions
      summary: Get Sessions
      operationId: getSessions
      description: |
        Returns a paginated list of network sessions matching the query criteria.
        Sessions represent individual network connections captured by GreyNoise sensors.
      parameters:
        - '$ref': '#/components/parameters/scope'
        - in: query
          name: start_time
          description: Start time for the query range (ISO 8601 format).
          required: true
          schema:
            type: string
            format: date-time
          example: "2025-01-01T00:00:00Z"
        - in: query
          name: end_time
          description: End time for the query range (ISO 8601 format).
          required: true
          schema:
            type: string
            format: date-time
          example: "2025-01-07T23:59:59Z"
        - in: query
          name: query
          description: Lucene query string to filter sessions.
          required: false
          schema:
            type: string
        - in: query
          name: page
          description: Page number for pagination.
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        - in: query
          name: page_size
          description: Number of results per page.
          required: false
          schema:
            type: integer
            default: 25
            minimum: 1
            maximum: 100
        - in: query
          name: sort_by
          description: Field to sort results by.
          required: false
          schema:
            type: string
            default: lastPacket
        - in: query
          name: sort_desc
          description: Whether to sort in descending order.
          required: false
          schema:
            type: string
            default: "true"
            enum: ["true", "false"]
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/SessionsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/sessions/fields:
    get:
      tags:
        - Sessions
      summary: Get Session Fields
      operationId: getSessionFields
      description: |
        Returns the list of available session fields for querying and display.
        Use this to discover which fields can be used in queries, sorting, and aggregations.
      parameters:
        - '$ref': '#/components/parameters/scope'
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/SessionFieldsResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/sessions/counts:
    get:
      tags:
        - Sessions
      summary: Get Session Counts
      operationId: getSessionCounts
      description: |
        Returns aggregated counts of sessions grouped by the specified fields.
        Useful for building dashboards and understanding traffic distribution.
      parameters:
        - '$ref': '#/components/parameters/scope'
        - in: query
          name: start_time
          description: Start time for the query range (ISO 8601 format).
          required: true
          schema:
            type: string
            format: date-time
          example: "2025-01-01T00:00:00Z"
        - in: query
          name: end_time
          description: End time for the query range (ISO 8601 format).
          required: true
          schema:
            type: string
            format: date-time
          example: "2025-01-07T23:59:59Z"
        - in: query
          name: fields
          description: Comma-separated list of fields to aggregate on.
          required: true
          schema:
            type: string
          example: "source.ip,destination.port"
        - in: query
          name: query
          description: Lucene query string to filter sessions.
          required: false
          schema:
            type: string
        - in: query
          name: size
          description: Number of buckets per aggregation level.
          required: false
          schema:
            type: integer
            default: 10
            minimum: 1
            maximum: 100
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/SessionCountsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/sessions/connections:
    get:
      tags:
        - Sessions
      summary: Get Session Connections
      operationId: getSessionConnections
      description: |
        Returns a graph of connections between source and destination fields.
        Useful for visualizing network relationships and communication patterns.
      parameters:
        - '$ref': '#/components/parameters/scope'
        - in: query
          name: start_time
          description: Start time for the query range (ISO 8601 format).
          required: true
          schema:
            type: string
            format: date-time
          example: "2025-01-01T00:00:00Z"
        - in: query
          name: end_time
          description: End time for the query range (ISO 8601 format).
          required: true
          schema:
            type: string
            format: date-time
          example: "2025-01-07T23:59:59Z"
        - in: query
          name: query
          description: Lucene query string to filter sessions.
          required: false
          schema:
            type: string
        - in: query
          name: src_field
          description: Source field to aggregate on.
          required: false
          schema:
            type: string
            default: source.ip
        - in: query
          name: dest_field
          description: Destination field to aggregate on.
          required: false
          schema:
            type: string
            default: destination.ip
        - in: query
          name: max_nodes
          description: Maximum number of nodes to return.
          required: false
          schema:
            type: integer
            default: 100
            minimum: 1
            maximum: 10000
        - in: query
          name: min_connections
          description: Minimum number of connections to include a node.
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/SessionConnectionsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/sessions/timeseries:
    get:
      tags:
        - Sessions
      summary: Get Session Timeseries
      operationId: getSessionTimeseries
      description: |
        Returns timeseries data for sessions, optionally grouped by a field.
        Useful for visualizing session volume over time and identifying trends.
      parameters:
        - '$ref': '#/components/parameters/scope'
        - in: query
          name: start_time
          description: Start time for the query range (ISO 8601 format).
          required: true
          schema:
            type: string
            format: date-time
          example: "2025-01-01T00:00:00Z"
        - in: query
          name: end_time
          description: End time for the query range (ISO 8601 format).
          required: true
          schema:
            type: string
            format: date-time
          example: "2025-01-07T23:59:59Z"
        - in: query
          name: query
          description: Lucene query string to filter sessions.
          required: false
          schema:
            type: string
        - in: query
          name: field
          description: Field to group timeseries by.
          required: false
          schema:
            type: string
        - in: query
          name: size
          description: Number of groups to return when field is provided.
          required: false
          schema:
            type: integer
            default: 10
            minimum: 1
            maximum: 100
        - in: query
          name: interval
          description: Time interval for bucketing.
          required: false
          schema:
            type: string
            default: auto
            enum: [auto, 1s, 1m, 1h, 1d]
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/SessionTimeseriesResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/sessions/unique:
    get:
      tags:
        - Sessions
      summary: Get Unique Field Values
      operationId: getSessionUniqueValues
      description: |
        Returns unique values for a session field as a CSV download, optionally with counts.
        Useful for extracting distinct IPs, ports, or other field values matching a query.
      parameters:
        - '$ref': '#/components/parameters/scope'
        - in: query
          name: start_time
          description: Start time for the query range (ISO 8601 format).
          required: true
          schema:
            type: string
            format: date-time
          example: "2025-01-01T00:00:00Z"
        - in: query
          name: end_time
          description: End time for the query range (ISO 8601 format).
          required: true
          schema:
            type: string
            format: date-time
          example: "2025-01-07T23:59:59Z"
        - in: query
          name: field
          description: Field to get unique values for.
          required: true
          schema:
            type: string
          example: "source.ip"
        - in: query
          name: query
          description: Lucene query string to filter sessions.
          required: false
          schema:
            type: string
        - in: query
          name: include_counts
          description: Whether to include counts in the output.
          required: false
          schema:
            type: string
            default: "false"
            enum: ["true", "false"]
      responses:
        '200':
          description: CSV file with unique values.
          content:
            text/csv:
              schema:
                type: string
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/sessions/export:
    get:
      tags:
        - Sessions
      summary: Export PCAP for Multiple Sessions
      operationId: exportSessionsPcap
      description: |
        Returns a PCAP file containing packets from sessions matching the query criteria.
        The response is a binary PCAP file suitable for analysis with tools like Wireshark.

        Not available when `scope=demo` (returns 403).
      parameters:
        - '$ref': '#/components/parameters/scope'
        - in: query
          name: start_time
          description: Start time for the query range (ISO 8601 format).
          required: true
          schema:
            type: string
            format: date-time
          example: "2025-01-01T00:00:00Z"
        - in: query
          name: end_time
          description: End time for the query range (ISO 8601 format).
          required: true
          schema:
            type: string
            format: date-time
          example: "2025-01-07T23:59:59Z"
        - in: query
          name: query
          description: Lucene query string to filter sessions.
          required: false
          schema:
            type: string
        - in: query
          name: mode
          description: |
            Export selection mode.
            - `page`: Export a single page of results (use with `page` and `page_size`). This is the default.
            - `all`: Export all sessions matching the query, up to `page_size` results.
          required: false
          schema:
            type: string
            default: page
            enum: [page, all]
        - in: query
          name: page
          description: Page number to export when `mode=page`.
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        - in: query
          name: page_size
          description: |
            Number of sessions per page when `mode=page`, or the maximum number of
            sessions to export when `mode=all`. The legacy `size` parameter is
            accepted as an alias.
          required: false
          schema:
            type: integer
            default: 100
            minimum: 1
            maximum: 1000
        - in: query
          name: sort_by
          description: Field to sort results by.
          required: false
          schema:
            type: string
            default: lastPacket
        - in: query
          name: sort_desc
          description: Whether to sort in descending order.
          required: false
          schema:
            type: string
            default: "true"
            enum: ["true", "false"]
      responses:
        '200':
          description: PCAP file containing matching session packets.
          content:
            application/vnd.tcpdump.pcap:
              schema:
                type: string
                format: binary
          headers:
            Content-Disposition:
              schema:
                type: string
              example: 'attachment; filename="sessions.pcap"'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/sessions/{session_id}:
    get:
      tags:
        - Sessions
      summary: Get Session by ID
      operationId: getSessionById
      description: |
        Returns a single session by its ID, including full session metadata
        and connection details.
      parameters:
        - '$ref': '#/components/parameters/scope'
        - in: path
          name: session_id
          description: The unique session identifier.
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/Session'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/sessions/{session_id}/frames:
    get:
      tags:
        - Sessions
      summary: Get Session PCAP
      operationId: getSessionPcap
      description: |
        Returns raw PCAP bytes for a single session. The response is a binary
        PCAP file suitable for analysis with tools like Wireshark.
      parameters:
        - '$ref': '#/components/parameters/scope'
        - in: path
          name: session_id
          description: The unique session identifier.
          required: true
          schema:
            type: string
      responses:
        '200':
          description: PCAP file for the requested session.
          content:
            application/vnd.tcpdump.pcap:
              schema:
                type: string
                format: binary
          headers:
            Content-Disposition:
              schema:
                type: string
              example: 'attachment; filename="session-{session_id}.pcap"'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/sessions/{session_id}/export:
    get:
      tags:
        - Sessions
      summary: Export Session Data
      operationId: exportSessionData
      description: |
        Downloads session data as PCAP or raw payload for a single session.
        Use the `type` parameter to select the export format.

        Not available when `scope=demo` (returns 403).
      parameters:
        - '$ref': '#/components/parameters/scope'
        - in: path
          name: session_id
          description: The unique session identifier.
          required: true
          schema:
            type: string
        - in: query
          name: type
          description: Export format type.
          required: false
          schema:
            type: string
            default: pcap
            enum: [pcap, rawSource, rawDestination]
      responses:
        '200':
          description: Session data file in the requested format.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/bsi/trust:
    get:
      tags:
        - Business Service Intelligence
      summary: BSI Trust-Level Stats
      operationId: getBSITrust
      description: |
        Returns counts of BSI IPs and CIDRs grouped by trust level.
        Use `date=now` for current information or
        `date=YYYY-MM-DD` for historical information.

        This endpoint requires the proper BSI entitlements.
      parameters:
        - $ref: '#/components/parameters/bsiDate'
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BSITrustResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/bsi/company:
    get:
      tags:
        - Business Service Intelligence
      summary: BSI Company Stats
      operationId: getBSICompany
      description: |
        Returns counts of BSI IPs and CIDRs grouped by company name.
        Use `date=now` for current information or
        `date=YYYY-MM-DD` for historical information.

        This endpoint requires the proper BSI entitlements.
      parameters:
        - $ref: '#/components/parameters/bsiDate'
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BSICompanyResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/bsi/category:
    get:
      tags:
        - Business Service Intelligence
      summary: BSI Category Stats
      operationId: getBSICategory
      description: |
        Returns counts of BSI IPs and CIDRs grouped by category.
        Use `date=now` for current information or
        `date=YYYY-MM-DD` for historical information.

        This endpoint requires the proper BSI entitlements.
      parameters:
        - $ref: '#/components/parameters/bsiDate'
      responses:
        '200':
          description: OK - request successful.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BSICategoryResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/bsi/download:
    get:
      tags:
        - Business Service Intelligence
      summary: BSI Bulk Data Download
      operationId: getBSIDownload
      description: |
        Returns the newest BSI bulk-data file for the requested date as a
        gzipped, newline-delimited JSON (`.json.gz`) stream. Each line is a
        single record with provider metadata (`name`, `category`,
        `trust_level`, etc.), a CIDR (`ip_cidr`), and a scan timestamp.

        Use `date=now` for today's snapshot (resolved server-side to today's
        UTC date) or `date=YYYY-MM-DD` for a specific historical partition.

        _License: This endpoint requires an additional subscription
        license to use._
      parameters:
        - $ref: '#/components/parameters/bsiDate'
      responses:
        '200':
          description: OK — gzipped JSONL stream of the BSI bulk-data file for the requested date.
          headers:
            Content-Disposition:
              description: |
                Suggested filename for saving the response. The date in the
                filename reflects the resolved date (e.g. `now` resolves to
                today's UTC date).
              schema:
                type: string
                example: attachment; filename="riot-bulk-2026-05-14.json.gz"
          content:
            application/gzip:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v1/psychic:
    post:
      tags:
        - Psychic
      summary: Psychic Model Download
      operationId: postPsychicModelDownload
      description: |
        Downloads a Psychic model file. The request body selects the model
        (`1`, `2`, `3`, `m1`, `m2`, `m3`, `model1`, `model2`, or `model3`).
        Models 1, 2, and 3 may set `date` to `latest` or a `YYYY-MM-DD`
        date. When `date` is omitted, the latest daily model is used. Models 1,
        2, and 3 may also request an inclusive date range with `start_date` and
        `end_date`; ranges are limited to 30 days and return one combined
        download.
        Set `format` to `mmdb` to download a MaxMind DB file. When `format`
        is omitted, the binary model format is used.

        _License: This endpoint requires the Psychic model meter entitlement
        for the requested model. Historical dates also require that model's
        Psychic lookback-days entitlement to cover the requested date or range
        start date._
      requestBody:
        required: true
        description: Psychic model selector, optional daily model date or range, and optional file format.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PsychicDownloadRequest'
      responses:
        '200':
          description: OK - binary Psychic model file.
          headers:
            Content-Disposition:
              description: Suggested filename for saving the response.
              schema:
                type: string
                example: attachment; filename="m2-2026-06-04.bin"
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
            application/vnd.maxmind.maxmind-db:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/PsychicBadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/PsychicForbidden'
        '404':
          $ref: '#/components/responses/PsychicNotFound'
        '413':
          $ref: '#/components/responses/PsychicRequestEntityTooLarge'
        '429':
          $ref: '#/components/responses/PsychicTooManyRequests'
        '500':
          $ref: '#/components/responses/PsychicUnexpectedError'
  /v1/psychic/snapshots:
    post:
      tags:
        - Psychic
      summary: Psychic Snapshot Download
      operationId: postPsychicSnapshotDownload
      description: |
        Downloads a precomputed Psychic snapshot artifact. Snapshots are fixed,
        already-materialized files for common rolling windows.
        This endpoint does not generate missing ranges or convert missing MMDBs.

        Models 1, 2, and 3 require `days` and currently support only `7` or
        `30`.
        `date`, `start_date`, and `end_date` are not supported on this endpoint.

        A successful response streams the snapshot file. Transfer time still
        depends on file size and client connection speed. A `404` response means
        the requested snapshot artifact is not available.

        _License: This endpoint requires the Psychic model meter entitlement
        for the requested model. Models 1, 2, and 3 also require that model's
        Psychic lookback-days entitlement to cover the resolved snapshot start
        date._
      requestBody:
        required: true
        description: Psychic snapshot selector and optional file format.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PsychicSnapshotRequest'
            examples:
              model3SevenDayBin:
                summary: Model 3 seven-day binary snapshot
                value:
                  model: '3'
                  days: 7
                  format: bin
              model3ThirtyDayMMDB:
                summary: Model 3 thirty-day MMDB snapshot
                value:
                  model: '3'
                  days: 30
                  format: mmdb
      responses:
        '200':
          description: OK - binary Psychic snapshot file.
          headers:
            Content-Disposition:
              description: Suggested filename for saving the response.
              schema:
                type: string
                example: attachment; filename="m3-2026-06-17-to-2026-06-23.mmdb"
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
            application/vnd.maxmind.maxmind-db:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/PsychicBadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/PsychicForbidden'
        '404':
          $ref: '#/components/responses/PsychicNotFound'
        '413':
          $ref: '#/components/responses/PsychicRequestEntityTooLarge'
        '429':
          $ref: '#/components/responses/PsychicTooManyRequests'
        '500':
          $ref: '#/components/responses/PsychicUnexpectedError'
  /v3/bsi/lookup:
    get:
      tags:
        - Business Service Intelligence
      summary: BSI Single-IP Lookup
      operationId: getBSILookup
      description: |
        Returns every BSI provider whose CIDR(s) contain the queried IPv4
        address, ordered by ascending precedence (lower precedence = higher
        priority). An empty `matches` array means the IP is not in BSI.

        IPv6 addresses are rejected with HTTP 400. Lookups are served from
        an in-memory index that refreshes from the daily bulk-data
        partition; staleness may be up to ~30 minutes after a partition
        rolls.

        _License: This endpoint requires an additional subscription
        license to use._
      parameters:
        - name: ip
          in: query
          description: The IPv4 address to look up.
          required: true
          schema:
            type: string
            example: 8.8.8.8
      responses:
        '200':
          description: OK — request successful.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BSILookupResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          description: Service Unavailable — BSI index not yet built.
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/bsi/bulk:
    post:
      tags:
        - Business Service Intelligence
      summary: BSI Bulk IP Lookup
      operationId: getBSIBulkLookup
      description: |
        Accepts up to 1,000 IPv4 addresses and returns BSI provider matches
        for each, preserving request order. A given IP's `matches` entry is
        an empty array if it is not in BSI. IPv6 addresses are rejected
        with HTTP 400 (the whole request fails; callers should re-issue
        without the offending entry).

        _License: This endpoint requires an additional subscription
        license to use._
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BSIBulkRequest'
      responses:
        '200':
          description: OK — request successful.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BSIBulkResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          description: Service Unavailable — BSI index not yet built.
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v1/callback/ip/{ip}:
    get:
      tags:
        - Callback
      summary: Callback IP Lookup
      operationId: CallbackGetIP
      description: |
        Retrieve detailed information about a specific callback IP, including
        attack stage, scanner associations, and downloaded malware files.
      parameters:
        - name: ip
          in: path
          description: The callback IP address to look up.
          required: true
          schema:
            type: string
            example: 198.51.100.42
      responses:
        '200':
          description: OK - callback IP details returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallbackIPDetailResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v1/callback/ips:
    post:
      tags:
        - Callback
      summary: List Callback IPs
      operationId: CallbackListIPs
      description: |
        Retrieve a paginated list of callback IPs with filtering by attack
        stage, date ranges, file attributes, and scanner associations.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CallbackListIPsRequest'
      responses:
        '200':
          description: OK - paginated list of callback IPs returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallbackListIPsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v1/callback/export-ips:
    post:
      tags:
        - Callback
      summary: Export Callback IPs
      operationId: CallbackExportIPs
      description: |
        Export callback IPs matching the given filters as a newline-delimited
        plain text list. Supports the same filter parameters as the List
        Callback IPs endpoint.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CallbackFilterFields'
      responses:
        '200':
          description: OK - newline-delimited list of callback IPs.
          content:
            text/plain:
              schema:
                type: string
                example: |
                  198.51.100.42
                  203.0.113.7
                  192.0.2.99
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v1/callback/overview:
    post:
      tags:
        - Callback
      summary: Callback Overview Statistics
      operationId: CallbackOverview
      description: |
        Retrieve aggregate statistics for callback IPs including counts by
        attack stage, file analysis status, scanner associations, and top
        threat names.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CallbackFilterFields'
      responses:
        '200':
          description: OK - overview statistics returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallbackOverviewResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/feeds/events/search:
    post:
      tags:
        - Event Feeds
      operationId: SearchFeedEvents
      summary: Search retained feed events
      description: |
        Search the authenticated workspace's retained feed-event history without
        reading or changing any pull-consumer checkpoint. Results are ordered
        newest first.

        The simplest request is an empty JSON object. It returns events from all
        currently enabled, non-session feeds over the previous hour. Set `from`
        to a relative duration such as `6h` or `7d`, a UTC date, or an RFC 3339
        timestamp. `to` accepts a UTC date or RFC 3339 timestamp and defaults to
        the time the request is received.

        For narrower searches, use `feed_ids` and the advanced exact-match fields
        `event_types`, `ip`, `cve`, `tag`, and `classification`. Explicitly named
        disabled feeds remain searchable while their events are retained.

        This endpoint uses stateless keyset pagination. When `has_more` is true,
        send a new request containing only the returned `cursor` and, optionally,
        `limit`. The signed cursor restores the original workspace, feed scope,
        filters, and absolute time window. It is unrelated to the cursor returned
        by the pull endpoint for acknowledgement.

        Retained history is approximately 90 days. Session events are not stored
        and cannot be searched.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EventFeedSearchRequest'
            examples:
              default:
                summary: Search all enabled feeds over the previous hour
                value: {}
              feedHistory:
                summary: Search one feed over the previous day
                value:
                  feed_ids:
                    - 5575f06f-97fa-435a-a176-7682a4fbc92f
                  from: 1d
                  limit: 100
              absoluteWindow:
                summary: Search an explicit UTC date range
                value:
                  from: '2026-08-01'
                  to: '2026-08-08'
              maliciousIPs:
                summary: Advanced exact-match filters
                value:
                  from: 6h
                  event_types:
                    - ip-classification-change
                  ip: 198.51.100.42
                  classification: malicious
                  limit: 100
              nextPage:
                summary: Continue a search using its signed cursor
                value:
                  cursor: eyJ2IjoyLCJwIjoic2VhcmNoIiwiLi4uIn0.signature
                  limit: 100
      responses:
        '200':
          description: A newest-first page of matching retained events.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventFeedSearchResponse'
        '400':
          $ref: '#/components/responses/EventFeedBadRequest'
        '401':
          $ref: '#/components/responses/EventFeedUnauthorized'
        '403':
          $ref: '#/components/responses/EventFeedForbidden'
        '404':
          $ref: '#/components/responses/EventFeedNotFound'
        '408':
          $ref: '#/components/responses/EventFeedRequestTimeout'
        '410':
          $ref: '#/components/responses/EventFeedGone'
        '500':
          $ref: '#/components/responses/EventFeedUnexpectedError'
        '502':
          $ref: '#/components/responses/EventFeedBadGateway'
        '504':
          $ref: '#/components/responses/EventFeedGatewayTimeout'
  /v3/feeds/{feed_id}/events:
    get:
      tags:
        - Event Feeds
      operationId: FetchFeedEvents
      summary: Fetch the next feed-event batch
      description: |
        Return events after the named consumer's server-held checkpoint. If the
        consumer has no checkpoint, reading begins at the oldest event still
        inside retention. Omitting `consumer` uses the `default` consumer.

        Fetching is read-only. Repeating this request before acknowledgement can
        return the same events. Process the complete batch, then send the response
        `cursor` to the acknowledge endpoint with the same consumer name. The
        next scheduled fetch does not need to retain or submit that cursor.

        If a stored checkpoint has fallen outside retention, reading resumes at
        the oldest retained event and `retention.gap_detected` is true. Session
        events are webhook-only and are never returned.
      parameters:
        - $ref: '#/components/parameters/eventFeedId'
        - in: query
          name: consumer
          required: false
          description: Name of the independent server-held checkpoint.
          schema:
            allOf:
              - $ref: '#/components/schemas/EventFeedConsumerName'
            default: default
        - in: query
          name: limit
          required: false
          description: Maximum number of events to return.
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 100
      responses:
        '200':
          description: The next event batch for the named consumer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventFeedFetchResponse'
        '400':
          $ref: '#/components/responses/EventFeedBadRequest'
        '401':
          $ref: '#/components/responses/EventFeedUnauthorized'
        '403':
          $ref: '#/components/responses/EventFeedForbidden'
        '404':
          $ref: '#/components/responses/EventFeedNotFound'
        '408':
          $ref: '#/components/responses/EventFeedRequestTimeout'
        '500':
          $ref: '#/components/responses/EventFeedUnexpectedError'
        '502':
          $ref: '#/components/responses/EventFeedBadGateway'
        '504':
          $ref: '#/components/responses/EventFeedGatewayTimeout'
  /v3/feeds/{feed_id}/events/ack:
    post:
      tags:
        - Event Feeds
      operationId: AcknowledgeFeedEvents
      summary: Acknowledge a processed feed-event batch
      description: |
        Advance the named consumer's server-held checkpoint to the signed cursor
        returned by the fetch endpoint. Acknowledge only after the complete batch
        has been processed successfully.

        Acknowledgement is the only operation that advances pull state. Repeating
        an acknowledgement or acknowledging an older cursor is a safe no-op and
        never moves the checkpoint backward. The `advanced` response field reports
        whether this request moved it forward.
      parameters:
        - $ref: '#/components/parameters/eventFeedId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EventFeedAcknowledgeRequest'
            example:
              consumer: scheduled-lambda
              cursor: eyJ2IjoxLCJ3IjoiLi4uIn0.signature
      responses:
        '200':
          description: The consumer's current checkpoint after acknowledgement.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventFeedAcknowledgeResponse'
        '400':
          $ref: '#/components/responses/EventFeedBadRequest'
        '401':
          $ref: '#/components/responses/EventFeedUnauthorized'
        '403':
          $ref: '#/components/responses/EventFeedForbidden'
        '404':
          $ref: '#/components/responses/EventFeedNotFound'
        '408':
          $ref: '#/components/responses/EventFeedRequestTimeout'
        '409':
          $ref: '#/components/responses/EventFeedConflict'
        '410':
          $ref: '#/components/responses/EventFeedGone'
        '500':
          $ref: '#/components/responses/EventFeedUnexpectedError'
        '502':
          $ref: '#/components/responses/EventFeedBadGateway'
        '504':
          $ref: '#/components/responses/EventFeedGatewayTimeout'
  /v3/feeds/{feed_id}/consumers:
    get:
      tags:
        - Event Feeds
      operationId: ListEventFeedConsumers
      summary: List feed-event consumers
      description: |
        List the feed's durable named checkpoints and their approximate lag. Each
        consumer advances independently, so multiple applications can process the
        same feed on different schedules.

        `active` means the checkpoint advanced within the last 24 hours, `idle`
        means it has not advanced for 24 hours, and `expired` means it points to an
        event outside retention. Approximate lag is an inexpensive upper-bound
        sequence estimate and may include interleaved events for other feeds.
      parameters:
        - $ref: '#/components/parameters/eventFeedId'
      responses:
        '200':
          description: The feed's managed consumers and current retention state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventFeedConsumersResponse'
        '400':
          $ref: '#/components/responses/EventFeedBadRequest'
        '401':
          $ref: '#/components/responses/EventFeedUnauthorized'
        '403':
          $ref: '#/components/responses/EventFeedForbidden'
        '404':
          $ref: '#/components/responses/EventFeedNotFound'
        '408':
          $ref: '#/components/responses/EventFeedRequestTimeout'
        '500':
          $ref: '#/components/responses/EventFeedUnexpectedError'
        '502':
          $ref: '#/components/responses/EventFeedBadGateway'
        '504':
          $ref: '#/components/responses/EventFeedGatewayTimeout'
    post:
      tags:
        - Event Feeds
      operationId: CreateEventFeedConsumer
      summary: Create a feed-event consumer
      description: |
        Create an independent server-held checkpoint at the beginning of the
        feed's retained history. A feed supports at most 25 durable consumers.
      parameters:
        - $ref: '#/components/parameters/eventFeedId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EventFeedConsumerCreateRequest'
            example:
              consumer: scheduled-lambda
      responses:
        '201':
          description: Consumer created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventFeedConsumer'
        '400':
          $ref: '#/components/responses/EventFeedBadRequest'
        '401':
          $ref: '#/components/responses/EventFeedUnauthorized'
        '403':
          $ref: '#/components/responses/EventFeedForbidden'
        '404':
          $ref: '#/components/responses/EventFeedNotFound'
        '408':
          $ref: '#/components/responses/EventFeedRequestTimeout'
        '409':
          $ref: '#/components/responses/EventFeedConflict'
        '500':
          $ref: '#/components/responses/EventFeedUnexpectedError'
        '502':
          $ref: '#/components/responses/EventFeedBadGateway'
        '504':
          $ref: '#/components/responses/EventFeedGatewayTimeout'
  /v3/feeds/{feed_id}/consumers/{consumer}/reset:
    post:
      tags:
        - Event Feeds
      operationId: ResetEventFeedConsumer
      summary: Reset a feed-event consumer
      description: |
        Return an existing consumer checkpoint to the beginning of currently
        retained feed history. The next fetch can redeliver retained events that
        the consumer processed previously.
      parameters:
        - $ref: '#/components/parameters/eventFeedId'
        - $ref: '#/components/parameters/eventFeedConsumer'
      responses:
        '200':
          description: Consumer reset.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventFeedConsumer'
        '400':
          $ref: '#/components/responses/EventFeedBadRequest'
        '401':
          $ref: '#/components/responses/EventFeedUnauthorized'
        '403':
          $ref: '#/components/responses/EventFeedForbidden'
        '404':
          $ref: '#/components/responses/EventFeedNotFound'
        '408':
          $ref: '#/components/responses/EventFeedRequestTimeout'
        '500':
          $ref: '#/components/responses/EventFeedUnexpectedError'
        '502':
          $ref: '#/components/responses/EventFeedBadGateway'
        '504':
          $ref: '#/components/responses/EventFeedGatewayTimeout'
  /v3/feeds/{feed_id}/consumers/{consumer}:
    delete:
      tags:
        - Event Feeds
      operationId: DeleteEventFeedConsumer
      summary: Delete a feed-event consumer
      description: Delete one independent server-held checkpoint.
      parameters:
        - $ref: '#/components/parameters/eventFeedId'
        - $ref: '#/components/parameters/eventFeedConsumer'
      responses:
        '204':
          description: Consumer deleted.
        '400':
          $ref: '#/components/responses/EventFeedBadRequest'
        '401':
          $ref: '#/components/responses/EventFeedUnauthorized'
        '403':
          $ref: '#/components/responses/EventFeedForbidden'
        '404':
          $ref: '#/components/responses/EventFeedNotFound'
        '408':
          $ref: '#/components/responses/EventFeedRequestTimeout'
        '500':
          $ref: '#/components/responses/EventFeedUnexpectedError'
        '502':
          $ref: '#/components/responses/EventFeedBadGateway'
        '504':
          $ref: '#/components/responses/EventFeedGatewayTimeout'
  /v3/workspaces/{workspace_id}/blocklists:
    post:
      tags:
        - Blocklists
      summary: Create Blocklist
      operationId: CreateBlocklist
      description: >
        Create a new blocklist in the specified workspace. The blocklist
        is defined by a GNQL query and will automatically refresh its IP list.
      parameters:
        - $ref: '#/components/parameters/workspaceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateBlocklistRequest'
      responses:
        '201':
          description: Created - blocklist successfully created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BlocklistResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/UnexpectedError'
    get:
      tags:
        - Blocklists
      summary: List Blocklists
      operationId: ListBlocklists
      description: >
        List all blocklists for a workspace. Results are paginated via
        `limit` and `offset` query parameters.
      parameters:
        - $ref: '#/components/parameters/workspaceId'
        - in: query
          name: limit
          description: Maximum number of blocklists to return (1-100).
          required: false
          schema:
            type: integer
            default: 10
            minimum: 1
            maximum: 100
        - in: query
          name: offset
          description: Number of blocklists to skip for pagination.
          required: false
          schema:
            type: integer
            default: 0
            minimum: 0
      responses:
        '200':
          description: OK - blocklists returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListBlocklistsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/workspaces/{workspace_id}/blocklists/{blocklist_id}:
    put:
      tags:
        - Blocklists
      summary: Update Blocklist
      operationId: UpdateBlocklist
      description: >
        Update an existing blocklist. The GNQL query, name, IP limit, and
        enabled status can be changed.
      parameters:
        - $ref: '#/components/parameters/workspaceId'
        - $ref: '#/components/parameters/blocklistId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateBlocklistRequest'
      responses:
        '200':
          description: OK - blocklist updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BlocklistResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/UnexpectedError'
    get:
      tags:
        - Blocklists
      summary: Get Blocklist
      operationId: GetBlocklist
      description: >
        Retrieve a single blocklist by ID.
      parameters:
        - $ref: '#/components/parameters/workspaceId'
        - $ref: '#/components/parameters/blocklistId'
      responses:
        '200':
          description: OK - blocklist returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BlocklistResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/UnexpectedError'
    delete:
      tags:
        - Blocklists
      summary: Delete Blocklist
      operationId: DeleteBlocklist
      description: >
        Delete a blocklist by ID.
      parameters:
        - $ref: '#/components/parameters/workspaceId'
        - $ref: '#/components/parameters/blocklistId'
      responses:
        '204':
          description: No Content - blocklist deleted.
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/workspaces/{workspace_id}/blocklists/{blocklist_id}/ips:
    get:
      tags:
        - Blocklists
      summary: Get Blocklist IPs
      operationId: GetBlocklistIPs
      description: >
        Retrieve the list of IP addresses currently matching the blocklist's
        GNQL query.
      parameters:
        - $ref: '#/components/parameters/workspaceId'
        - $ref: '#/components/parameters/blocklistId'
        - in: query
          name: size
          description: Maximum number of IPs to return. When omitted or 0, the blocklist's configured `ip_limit` is used.
          required: false
          schema:
            type: integer
            minimum: 0
      responses:
        '200':
          description: OK - IP list returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BlocklistIPsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/UnexpectedError'
        '503':
          description: Blocklist data is being prepared. Retry after a short delay.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /v3/articles:
    get:
      tags:
        - Threat Briefs
      summary: List Articles
      operationId: listArticles
      description: |
        Returns a paginated list of published articles (Threat Briefs). Supports
        filtering by category, search term, and pagination parameters.
      parameters:
        - in: query
          name: page
          description: Page number for pagination (1-indexed).
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        - in: query
          name: size
          description: Number of articles per page.
          required: false
          schema:
            type: integer
        - in: query
          name: search
          description: Case-insensitive search across title, subtitle, and description.
          required: false
          schema:
            type: string
        - in: query
          name: category
          description: Filter articles by category value.
          required: false
          schema:
            type: string
        - in: query
          name: sort_by
          description: Field to sort results by.
          required: false
          schema:
            type: string
            default: published_at
        - in: query
          name: sort_desc
          description: Sort in descending order. Set to `false` for ascending.
          required: false
          schema:
            type: string
            default: "true"
            enum: ["true", "false"]
      responses:
        '200':
          description: A paginated list of published articles.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListArticlesResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/articles/categories:
    get:
      tags:
        - Threat Briefs
      summary: List Categories
      operationId: listArticleCategories
      description: |
        Returns the list of available article categories.
      responses:
        '200':
          description: List of article categories.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ArticleCategory'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/articles/{id}:
    get:
      tags:
        - Threat Briefs
      summary: Get Article
      operationId: getArticle
      description: |
        Returns a single published article by its ID.
      parameters:
        - in: path
          name: id
          required: true
          description: Unique article identifier.
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: The requested article.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Article'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/articles/rss-token:
    get:
      tags:
        - Threat Briefs
      summary: Get RSS Feed URL
      operationId: getRSSFeedToken
      description: |
        Returns the calling workspace's private RSS feed URL, issuing one on first
        access if the workspace does not have one yet, so `feed_url` is always
        populated on success. Requires an entitlement granting access beyond the
        public `community` category.
      responses:
        '200':
          description: The workspace's RSS feed URLs.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RSSFeedResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
    post:
      tags:
        - Threat Briefs
      summary: Generate RSS Feed URL
      operationId: generateRSSFeedToken
      description: |
        Rotates the calling workspace's private RSS feed URL: issues a new URL and
        invalidates the previous one. Use this to revoke a leaked feed URL. Requires
        an entitlement granting access beyond the public `community` category.
      responses:
        '200':
          description: The workspace's RSS feed URLs.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RSSFeedResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/ExceededLimit'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/articles/rss:
    get:
      tags:
        - Threat Briefs
      summary: Public RSS Feed
      operationId: getPublicRSSFeed
      description: |
        Returns an RSS 2.0 feed of published community Threat Briefs. This feed is
        public and unauthenticated, and exposes only the `community` category.
      security: []
      responses:
        '200':
          description: RSS 2.0 XML feed.
          content:
            application/rss+xml:
              schema:
                type: string
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/articles/rss/{token}:
    get:
      tags:
        - Threat Briefs
      summary: Private RSS Feed
      operationId: getPrivateRSSFeed
      description: |
        Returns a workspace's entitlement-filtered RSS 2.0 feed. The feed URL
        (obtained from `GET`/`POST /v3/articles/rss-token`) embeds an opaque token
        that is the only credential, so RSS readers need no API key. The feed
        contents follow the workspace's live entitlements on every fetch.
      security: []
      parameters:
        - in: path
          name: token
          required: true
          description: The opaque feed token from the issued feed URL.
          schema:
            type: string
      responses:
        '200':
          description: RSS 2.0 XML feed.
          content:
            application/rss+xml:
              schema:
                type: string
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/UnexpectedError'
  /v3/workspaces/{workspace_id}/tactics:
    post:
      tags:
        - Tactics
      summary: Search Tactics Detections
      operationId: SearchTacticsDetections
      description: |
        Search the Tactics detections recorded for a workspace.

        A detection is an aggregated view of a single attacker session observed
        on one of your deployed sensors: the commands that ran, the MITRE ATT&CK
        techniques and tactics they map to, the files the session touched, and
        the hosts it contacted. Only sessions carrying at least one ATT&CK
        technique are retained as detections, so activity that never matched a
        technique is not returned by this endpoint.

        Filters supplied in the request body are combined with AND, with one
        exception: `techniques` and `tactics` combine with each other using OR
        (a detection matches if it carries any listed technique **or** any
        listed tactic), and that bundle is then ANDed with the remaining
        filters. A body is required; send `{}` to apply no filters.

        Results are ordered newest first and paged with an opaque cursor: pass
        the `next_cursor` from a response back as the `cursor` query parameter
        to fetch the following page. `total_count`, `stats`, and `tactic_stats`
        are computed over the whole filtered result set and are returned only
        on the first page (when no `cursor` is supplied).

        Each detection in `sessions` carries a **bounded** `commands` preview —
        at most 40 of the session's distinct commands, in no particular order.
        Fetch the detection by ID for the complete, ordered command timeline.
      parameters:
        - $ref: '#/components/parameters/workspaceId'
        - in: query
          name: page_size
          description: Number of detections to return per page.
          required: false
          schema:
            type: integer
            default: 100
            minimum: 1
            maximum: 10000
        - in: query
          name: cursor
          description: >
            Opaque pagination cursor taken from a previous response's
            `next_cursor`. Omit to request the first page.
          required: false
          schema:
            type: string
        - in: query
          name: exclude_non_interactive
          description: >
            When `true`, return only interactive sessions (those with a shell
            ancestor). Non-interactive sessions are included by default.
          required: false
          schema:
            type: boolean
            default: false
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TacticsSearchRequest'
      responses:
        '200':
          description: OK - matching detections returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TacticsSearchResponse'
        '400':
          $ref: '#/components/responses/TacticsBadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/TacticsForbidden'
        '500':
          $ref: '#/components/responses/TacticsUnexpectedError'
  /v3/workspaces/{workspace_id}/tactics/{detection_id}:
    get:
      tags:
        - Tactics
      summary: Get Tactics Detection
      operationId: GetTacticsDetection
      description: |
        Retrieve a single Tactics detection, including its complete ordered
        command timeline, its full set of touched file paths, and a
        per-protocol summary of the unique destination IPs the session
        contacted.

        Commands and file paths are paginated independently, 500 per page:

        - `commands_page` slices `commands` and `command_events` together -
          they are parallel views of one sequence. `command_count` is the total
          across every page and `commands_has_more` signals a further page.
        - `artifacts_page` slices `artifacts`, sorted by path for stable
          paging. `artifact_count` is the total across every page and
          `artifacts_has_more` signals a further page.

        Advancing one does not affect the other. The list of destination IPs is
        served by its own endpoint; this response carries only their per-
        protocol counts in `dest_ips`.
      parameters:
        - $ref: '#/components/parameters/workspaceId'
        - $ref: '#/components/parameters/detectionId'
        - in: query
          name: commands_page
          description: >
            1-based page of the detection's command sequence, 500 commands per
            page. Slices `commands` and `command_events` together.
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        - in: query
          name: artifacts_page
          description: >
            1-based page of the detection's file paths, 500 per page. Advances
            independently of `commands_page`.
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
      responses:
        '200':
          description: OK - detection returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TacticsDetection'
        '400':
          $ref: '#/components/responses/TacticsBadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/TacticsForbidden'
        '404':
          $ref: '#/components/responses/TacticsNotFound'
        '500':
          $ref: '#/components/responses/TacticsUnexpectedError'
  /v3/workspaces/{workspace_id}/tactics/{detection_id}/dest-ips:
    get:
      tags:
        - Tactics
      summary: List Tactics Detection Destination IPs
      operationId: ListTacticsDetectionDestinationIPs
      description: |
        List the unique remote IP addresses a detection's session contacted,
        sorted for stable pagination.

        Internet-routable addresses are returned by default; this includes
        link-local (cloud instance metadata, `169.254.0.0/16`) and multicast
        addresses, which are deliberately kept visible. Set
        `include_lateral=true` to additionally return RFC 1918 and loopback
        addresses — traffic aimed at the sensor's own neighborhood rather than
        the internet.
      parameters:
        - $ref: '#/components/parameters/workspaceId'
        - $ref: '#/components/parameters/detectionId'
        - in: query
          name: page
          description: 1-based page number.
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        - in: query
          name: page_size
          description: Number of IP addresses to return per page.
          required: false
          schema:
            type: integer
            default: 100
            minimum: 1
            maximum: 10000
        - in: query
          name: include_lateral
          description: >
            When `true`, also return RFC 1918 and loopback (lateral)
            addresses. Defaults to internet-side addresses only.
          required: false
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: OK - destination IP page returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TacticsDestinationIPsResponse'
        '400':
          $ref: '#/components/responses/TacticsBadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/TacticsForbidden'
        '500':
          $ref: '#/components/responses/TacticsUnexpectedError'
  /v3/workspaces/{workspace_id}/tactics/{detection_id}/files:
    post:
      tags:
        - Tactics
      summary: List Tactics Detection Files
      operationId: ListTacticsDetectionFiles
      description: |
        List the files observed during a detection's session, with each file's
        path, size, type, and hashes.

        Set `mutations_only=true` to restrict the response to files the session
        actually created or modified, rather than every file it touched.

        This endpoint returns metadata only - it never carries file bytes.
        Retrieve a file's contents with the host artifact content endpoint,
        which is gated on its own entitlement.

        This endpoint takes no request body.
      parameters:
        - $ref: '#/components/parameters/workspaceId'
        - $ref: '#/components/parameters/detectionId'
        - in: query
          name: mutations_only
          description: >
            When `true`, return only files the session created or modified.
          required: false
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: >
            OK - files returned. `null` is returned rather than an empty array
            when the detection ID is unknown, or when `mutations_only=true` and
            the session modified no files.
          content:
            application/json:
              schema:
                type: array
                nullable: true
                items:
                  $ref: '#/components/schemas/TacticsDetectionFile'
        '400':
          $ref: '#/components/responses/TacticsBadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/TacticsForbidden'
        '500':
          $ref: '#/components/responses/TacticsUnexpectedError'
  /v3/workspaces/{workspace_id}/host-artifact/content:
    post:
      tags:
        - Tactics
      summary: Get Host Artifact Content
      operationId: GetTacticsHostArtifactContent
      description: |
        Fetch the contents of a single file observed on one of your deployed
        sensors, identified by its path within the workspace. The most
        recently observed version of the file at that path is returned.

        Small files are returned inline as base64 (`type: "inline"`); larger
        files are returned as a short-lived presigned download URL
        (`type: "link"`). Exactly one of `contents` or `url` is populated.

        This endpoint requires the Tactics file-content entitlement, which is
        separate from the entitlement gating the rest of the Tactics
        endpoints.
      parameters:
        - $ref: '#/components/parameters/workspaceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TacticsHostArtifactContentRequest'
      responses:
        '200':
          description: OK - file content returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TacticsHostArtifactContent'
        '400':
          $ref: '#/components/responses/TacticsBadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/TacticsForbidden'
        '404':
          $ref: '#/components/responses/TacticsNotFound'
        '500':
          $ref: '#/components/responses/TacticsUnexpectedError'
servers:
  - url: https://api.greynoise.io
  - url: http://api.greynoise.io
components:
  securitySchemes:
    APIKeyHeaderAuth:
      type: apiKey
      in: header
      name: key
  responses:
    EventFeedBadRequest:
      description: A path parameter, query parameter, request body, filter, or cursor is invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EventFeedError'
          example:
            error: bad request
            code: 400
    EventFeedUnauthorized:
      description: The API key or authenticated user is missing or invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EventFeedError'
          example:
            error: unauthorized
            code: 401
    EventFeedForbidden:
      description: The caller cannot access the workspace or the required entitlement is not enabled.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EventFeedError'
          example:
            error: event feed API entitlement required
            code: 403
    EventFeedNotFound:
      description: The workspace-scoped feed or consumer was not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EventFeedError'
          example:
            error: not found
            code: 404
    EventFeedRequestTimeout:
      description: The request was canceled before it completed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EventFeedError'
          example:
            error: request timeout
            code: 408
    EventFeedConflict:
      description: |
        The requested consumer conflicts with existing state, or the feed has
        reached its durable-consumer limit.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EventFeedError'
          example:
            error: conflict
            code: 409
    EventFeedGone:
      description: |
        The cursor points outside the retained event window. Follow the returned
        recovery action to fetch from the current retention boundary.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EventFeedError'
          examples:
            acknowledgeCursorExpired:
              summary: An acknowledgement cursor has fallen outside retention
              value:
                error: gone
                code: 410
                recovery:
                  action: fetch_without_cursor
            searchCursorExpired:
              summary: A search cursor has fallen outside retention
              value:
                error: gone
                code: 410
                recovery:
                  action: restart_search
    EventFeedUnexpectedError:
      description: An unexpected error occurred while processing the request.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EventFeedError'
          example:
            error: internal server error
            code: 500
    EventFeedBadGateway:
      description: The Event Feed service was unavailable or returned an invalid response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EventFeedError'
          example:
            error: bad gateway
            code: 502
    EventFeedGatewayTimeout:
      description: The Event Feed service did not complete the request before its deadline.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EventFeedError'
          example:
            error: gateway timeout
            code: 504
    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorMessage'
          example:
            message: IP not found
    BadRequest:
      description: |
        Bad request - request syntax is invalid for the specified endpoint.
        Verify request syntax and try again.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorMessage'
          example:
            message: Invalid parameter
    PsychicBadRequest:
      description: |
        Bad request - request syntax is invalid for the Psychic download endpoint.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PsychicError'
          examples:
            missingModel:
              value:
                error: model is required
            invalidDate:
              value:
                error: date must be latest or YYYY-MM-DD
    Unauthorized:
      description: Unauthorized. Please check your API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorMessage'
          example:
            message: Unauthorized
    Forbidden:
      description: |
        Forbidden - request is not authorized due to an invalid API key or plan limitations.
        If due to plan limitations, contact sales@greynoise.io to upgrade your plan and unlock full results.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorMessage'
          example:
            message: Forbidden
    PsychicForbidden:
      description: |
        Forbidden - request is not authorized due to API-key access, Psychic entitlement, or Psychic lookback limitations.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/PsychicError'
              - $ref: '#/components/schemas/ErrorMessage'
          examples:
            missingEntitlement:
              value:
                error: feature not allowed
            lookbackExceeded:
              value:
                error: lookback date exceeds entitlement
    PsychicNotFound:
      description: Psychic model file not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PsychicError'
          example:
            error: not found
    PsychicRequestEntityTooLarge:
      description: Request body too large. BI request-body inspection is limited to 32 KiB.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PsychicError'
          example:
            error: request body too large
    PsychicTooManyRequests:
      description: |
        Too many requests - either too many concurrent Psychic range downloads are
        being generated, or the requested Psychic model meter has no remaining
        usage. Retry later for concurrent-generation limits, or contact
        sales@greynoise.io to adjust model usage entitlements.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PsychicError'
          examples:
            activeRangeGenerations:
              value:
                error: too many active range generations
            usageLimitExceeded:
              value:
                error: usage limit exceeded
    PsychicUnexpectedError:
      description: Unexpected Psychic download error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PsychicError'
          example:
            error: internal error
    QueryEngineAtCapacity:
      description: |
        Too many requests - the query engine is shedding load. This is
        retryable: a `Retry-After` header carries the suggested delay when the
        engine supplies one.
      headers:
        Retry-After:
          description: Seconds to wait before retrying, when supplied by the query engine.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorMessage'
          example:
            message: The query engine is at capacity. Please retry shortly.
    QueryTimeout:
      description: |
        Gateway timeout - the query took too long to execute and was
        cancelled. Narrow the query (a shorter data reach, a more selective
        field) and retry.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorMessage'
          example:
            message: The query took too long to execute. Narrow the query and retry.
    UnexpectedError:
      description: Unexpected error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorMessage'
          example:
            message: Encountered error while performing request
    ExceededLimit:
      description: Too many requests. You've hit the rate-limit.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorMessage'
          example:
            message: You've hit the rate limit for this endpoint.
    TacticsBadRequest:
      description: |
        Bad request - a path parameter, query parameter, or request body was
        invalid. Validation errors raised by the Tactics backend are returned
        as a plain-text message; errors raised by the API gateway use the JSON
        `{"message": ...}` shape.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/ErrorMessage'
              - type: string
          examples:
            invalidQueryParameter:
              value: page_size must be an integer
            invalidRequestBody:
              value: 'invalid request body: unexpected EOF'
            gatewayError:
              value:
                message: 'bad request: multiple workspaces provided'
    TacticsForbidden:
      description: |
        Forbidden - the workspace is not entitled to Tactics (or, for the host
        artifact content endpoint, not entitled to file contents), or the
        authenticated user does not have access to the requested workspace.
        Contact sales@greynoise.io to enable Tactics for your workspace.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/TacticsError'
              - $ref: '#/components/schemas/ErrorMessage'
          examples:
            missingEntitlement:
              value:
                error: feature not enabled for this workspace
            workspaceAccessDenied:
              value:
                message: forbidden
    TacticsNotFound:
      description: |
        Not found - no detection or file matched the request. Returned as a
        plain-text message by the Tactics backend.
      content:
        application/json:
          schema:
            oneOf:
              - type: string
              - $ref: '#/components/schemas/ErrorMessage'
          examples:
            detectionNotFound:
              value: session not found
            artifactNotFound:
              value: host artifact not found
    TacticsUnexpectedError:
      description: |
        Unexpected error while serving the request. Errors raised by the
        Tactics backend are returned as a plain-text message; errors raised by
        the API gateway use the JSON `{"message": ...}` shape.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/ErrorMessage'
              - type: string
          examples:
            gatewayError:
              value:
                message: internal server error
            serviceError:
              value: Internal Server Error
  parameters:
    ip:
      name: ip
      in: path
      description: IP address to query
      required: true
      schema:
        type: string
    query:
      name: query
      in: query
      description: GNQL query string
      required: true
      schema:
        type: string
    scope:
      name: scope
      in: query
      description: |
        Controls the data scope for the query.
        - `workspace`: Query data from the current workspace (default). Requires the Sensors entitlement.
        - `demo`: Query demo/sample data. Requires the Swarm entitlement. Not available on export endpoints.
      required: false
      schema:
        type: string
        default: workspace
        enum: [workspace, demo]
    workspaceLabels:
      name: workspace_labels
      in: query
      description: |
        Comma-separated list of dataset scopes to include in the query.
        When omitted, only the default GreyNoise global dataset is queried.

        Allowed values:
        - `greynoise`: GreyNoise's global dataset.
        - `community`: Aggregated community-contributed data.
        - `personal`: The authenticated caller's own workspace data.
          Requires an authenticated workspace.

        Enforcement varies by endpoint; see each operation's response codes:
        - `GET /v3/ip/{ip}` and `POST /v3/ip` return `403 Forbidden` when any
          value is supplied without the Community Dataset entitlement. Values
          are not validated server-side; unrecognized values yield empty
          results rather than an error.
        - `GET /v3/noise/ips/{ip}/timeline` returns `400 Bad Request` for
          unrecognized values and for `personal` without an authenticated
          workspace. The Community Dataset entitlement is not enforced on
          this endpoint.
        - `GET /v3/tags/{id}/activity` returns `400 Bad Request` for unrecognized
          values, and `403 Forbidden` for recognized values supplied without the
          Community Dataset entitlement.
      required: false
      schema:
        type: string
        example: greynoise,community
    workspaceId:
      name: workspace_id
      in: path
      description: Workspace UUID.
      required: true
      schema:
        type: string
        format: uuid
    blocklistId:
      name: blocklist_id
      in: path
      description: Blocklist UUID.
      required: true
      schema:
        type: string
        format: uuid
    eventFeedId:
      name: feed_id
      in: path
      description: Feed UUID. The feed must belong to the authenticated workspace.
      required: true
      schema:
        type: string
        format: uuid
    eventFeedConsumer:
      name: consumer
      in: path
      description: Name of the feed's independent server-held checkpoint.
      required: true
      schema:
        $ref: '#/components/schemas/EventFeedConsumerName'
    detectionId:
      name: detection_id
      in: path
      description: >
        Tactics detection identifier. This is the value returned as
        `session_id` in detection payloads.
      required: true
      schema:
        type: string
        format: uuid
    bsiDate:
      name: date
      in: query
      description: |
        Snapshot date for the requested stats. `now` (the default) reads
        current data from the BSI IP database. A value of the form
        `YYYY-MM-DD` reads historical data for that day; returns
        `404` if no data is available.
      required: false
      schema:
        type: string
        default: now
        example: 2026-05-12
  requestBodies:
    MultiIpRequest:
      required: true
      content:
        application/json:
          schema:
            '$ref': '#/components/schemas/MultiIpRequest'
      description: Ipv4 addresses to perform noise lookup.
  schemas:
    Article:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Unique article identifier.
          example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        title:
          type: string
          description: Article title.
          example: "Understanding Internet Background Noise"
        subtitle:
          type: string
          description: Article subtitle.
          example: "A deep dive into scan traffic"
        description:
          type: string
          description: Article description or body text.
        category:
          $ref: '#/components/schemas/ArticleCategory'
        author:
          type: string
          description: Name of the article author.
          example: "GreyNoise Research"
        s3_url:
          type: string
          description: |
            Presigned URL to the article PDF. Empty for a linked article, which carries an
            external `link` instead. Exactly one of `s3_url` and `link` is populated.
        link:
          type: string
          format: uri
          description: |
            External URL for a linked article, such as a blog post. Empty for a PDF brief.
            Exactly one of `s3_url` and `link` is populated.
          example: "https://www.greynoise.io/blog/example-post"
        thumbnail_url:
          type: string
          description: URL to the article thumbnail image.
        created_at:
          type: string
          format: date-time
          description: Timestamp when the article was created.
        updated_at:
          type: string
          format: date-time
          description: Timestamp when the article was last updated.
        published_at:
          type: string
          format: date-time
          description: Timestamp when the article was published. Omitted if unpublished.
        is_published:
          type: boolean
          description: Whether the article is currently published.
    ArticleCategory:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Unique category identifier.
        label:
          type: string
          description: Display label for the category.
          example: "Threat Intelligence"
        value:
          type: string
          description: Machine-readable category value.
          example: "threat-intelligence"
        color:
          type: string
          description: Hex color code for the category.
          example: "#FF5733"
    ListArticlesResponse:
      type: object
      properties:
        metadata:
          $ref: '#/components/schemas/ArticleListMetadata'
        data:
          type: array
          items:
            $ref: '#/components/schemas/Article'
    ArticleListMetadata:
      type: object
      properties:
        total_results:
          type: integer
          description: Total number of articles matching the query.
          example: 42
        current_page:
          type: integer
          description: Current page number.
          example: 1
        count:
          type: integer
          description: Number of articles returned in this page.
          example: 10
    RSSFeedResponse:
      type: object
      properties:
        feed_url:
          type: string
          description: |
            The workspace's private RSS feed URL. Treat this URL as a secret;
            anyone with it can read the workspace's entitlement-filtered feed.
          example: "https://api.greynoise.io/v3/articles/rss/uJ8x9...redacted"
        public_feed_url:
          type: string
          description: The public community RSS feed URL.
          example: "https://api.greynoise.io/v3/articles/rss"
    Error:
      type: object
      properties:
        error:
          type: string
      required:
        - error
    ErrorMessage:
      type: object
      properties:
        message:
          type: string
      required:
        - message
    EventFeedError:
      type: object
      additionalProperties: false
      required:
        - error
        - code
      properties:
        error:
          type: string
          description: Safe, human-readable error message.
        code:
          type: integer
          description: HTTP status code.
        recovery:
          $ref: '#/components/schemas/EventFeedRecovery'
    EventFeedRecovery:
      type: object
      additionalProperties: false
      required:
        - action
      properties:
        action:
          type: string
          description: Recovery action for a cursor that has fallen outside retention.
          enum:
            - fetch_without_cursor
            - restart_search
    EventFeedEventType:
      type: string
      description: |
        Retained event type. `session-received` and `new-tag-activity` events are
        not retained and therefore are not available to pull consumers or search.
      enum:
        - ip-classification-change
        - cve-status-change
        - cve-activity-spike
        - vendor-cve-spike
        - tag-spike
        - new-callback-ip
        - new-callback-file
        - credential-observed
    EventFeedEvent:
      type: object
      additionalProperties: false
      required:
        - event_type
        - payload
        - created_at
      properties:
        event_type:
          $ref: '#/components/schemas/EventFeedEventType'
        payload:
          type: object
          additionalProperties: true
          description: Event-type-specific JSON payload.
        created_at:
          type: string
          format: date-time
          description: Time the event entered the retained feed log.
    EventFeedRetention:
      type: object
      additionalProperties: false
      required:
        - oldest_available_at
        - gap_detected
      properties:
        oldest_available_at:
          type: string
          format: date-time
          description: Beginning of the currently retained event window.
        gap_detected:
          type: boolean
          description: |
            True when a stale consumer checkpoint was automatically resumed at
            the oldest retained event, meaning some events are no longer available.
    EventFeedFetchResponse:
      type: object
      additionalProperties: false
      required:
        - events
        - has_more
        - consumer
        - retention
      properties:
        events:
          type: array
          items:
            $ref: '#/components/schemas/EventFeedEvent'
        cursor:
          type: string
          maxLength: 2048
          description: |
            Opaque signed position for acknowledging the last event in this batch.
            Omitted when the response has no acknowledgeable event.
        has_more:
          type: boolean
          description: Whether more events currently follow this batch.
        consumer:
          $ref: '#/components/schemas/EventFeedConsumerName'
        retention:
          $ref: '#/components/schemas/EventFeedRetention'
      example:
        events:
          - event_type: ip-classification-change
            payload:
              ip: 198.51.100.42
              old_classification: unknown
              new_classification: malicious
            created_at: '2026-08-07T14:21:18Z'
        cursor: eyJ2IjoxLCJ3IjoiLi4uIn0.signature
        has_more: false
        consumer: scheduled-lambda
        retention:
          oldest_available_at: '2026-05-09T00:00:00Z'
          gap_detected: false
    EventFeedAcknowledgeRequest:
      type: object
      additionalProperties: false
      required:
        - cursor
      properties:
        cursor:
          type: string
          minLength: 1
          maxLength: 2048
          description: Signed cursor returned by the feed-event fetch endpoint.
        consumer:
          allOf:
            - $ref: '#/components/schemas/EventFeedConsumerName'
          default: default
          description: Must identify the same consumer used to fetch the batch.
    EventFeedAcknowledgeResponse:
      type: object
      additionalProperties: false
      required:
        - consumer
        - cursor
        - acknowledged_at
        - updated_at
        - advanced
      properties:
        consumer:
          $ref: '#/components/schemas/EventFeedConsumerName'
        cursor:
          type: string
          maxLength: 2048
          description: Signed representation of the consumer's current checkpoint.
        acknowledged_at:
          type: string
          format: date-time
          description: Creation time of the event at the current checkpoint.
        updated_at:
          type: string
          format: date-time
          description: Time the server-held checkpoint was last advanced.
        advanced:
          type: boolean
          description: Whether this request moved the checkpoint forward.
    EventFeedConsumerName:
      type: string
      minLength: 1
      maxLength: 128
      pattern: '^[A-Za-z0-9][A-Za-z0-9._-]*$'
      description: |
        Stable consumer name beginning with an alphanumeric character and
        containing only alphanumerics, period, underscore, or hyphen.
      example: scheduled-lambda
    EventFeedConsumerCreateRequest:
      type: object
      additionalProperties: false
      required:
        - consumer
      properties:
        consumer:
          $ref: '#/components/schemas/EventFeedConsumerName'
    EventFeedConsumer:
      type: object
      additionalProperties: false
      required:
        - name
        - has_acknowledged
        - acknowledged_sequence
        - acknowledged_at
        - updated_at
        - status
        - approximate_lag
      properties:
        name:
          $ref: '#/components/schemas/EventFeedConsumerName'
        has_acknowledged:
          type: boolean
          description: Whether this consumer has acknowledged an event.
        acknowledged_sequence:
          type: integer
          format: int64
          minimum: 0
          description: Internal monotonic sequence at the current checkpoint.
        acknowledged_at:
          type: string
          format: date-time
          description: Creation time of the event at the current checkpoint.
        updated_at:
          type: string
          format: date-time
          description: Time the checkpoint was created or last advanced.
        status:
          type: string
          enum:
            - active
            - idle
            - expired
        approximate_lag:
          type: integer
          format: int64
          minimum: 0
          description: |
            Upper-bound sequence distance from current ingestion. Interleaved
            events for other feeds can contribute to this value.
    EventFeedConsumersResponse:
      type: object
      additionalProperties: false
      required:
        - consumers
        - retention
      properties:
        consumers:
          type: array
          maxItems: 25
          items:
            $ref: '#/components/schemas/EventFeedConsumer'
        retention:
          $ref: '#/components/schemas/EventFeedRetention'
    EventFeedSearchRequest:
      type: object
      additionalProperties: false
      description: |
        For the first page, omit `cursor` and provide any search criteria. An
        empty object searches all enabled feeds over the previous hour.

        For each subsequent page, send `cursor` and optional `limit` only. Search
        criteria cannot be combined with a cursor because the cursor already
        contains the original normalized scope and absolute time window.
      properties:
        feed_ids:
          type: array
          maxItems: 100
          description: |
            Feed UUIDs to include. Omit to search all currently enabled,
            non-session feeds in the workspace. Explicit IDs may refer to disabled
            feeds, but deleted feeds return `404` and session feeds return `400`.
          items:
            type: string
            format: uuid
        event_types:
          type: array
          maxItems: 8
          description: Advanced filter for exact retained event types.
          items:
            $ref: '#/components/schemas/EventFeedEventType'
        ip:
          type: string
          maxLength: 45
          description: Advanced filter for an exact IP in the indexed event dimensions.
          example: 198.51.100.42
        cve:
          type: string
          maxLength: 128
          description: Advanced filter for an exact CVE in the indexed event dimensions.
          example: CVE-2026-12345
        tag:
          type: string
          maxLength: 256
          description: Advanced filter for an exact tag in the indexed event dimensions.
          example: Mirai Variant
        classification:
          type: string
          maxLength: 128
          description: Advanced filter for an exact classification in the indexed event dimensions.
          example: malicious
        from:
          description: |
            Inclusive lower event-time bound. Accepts a positive whole-number
            duration in hours or days relative to `to` (for example `1h`, `6h`,
            `1d`, or `7d`), a UTC date (`YYYY-MM-DD`), or an RFC 3339 timestamp.
            Defaults to one hour before `to`.
          anyOf:
            - type: string
              pattern: '^[1-9][0-9]*(h|d)$'
            - type: string
              format: date
              pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'
            - type: string
              format: date-time
              pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|[+-][0-9]{2}:[0-9]{2})$'
          example: 1h
        to:
          description: |
            Exclusive upper event-time bound as a UTC date (`YYYY-MM-DD`) or RFC
            3339 timestamp. Defaults to the time the request is received and must
            be later than the resolved `from` value.
          anyOf:
            - type: string
              format: date
              pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'
            - type: string
              format: date-time
              pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|[+-][0-9]{2}:[0-9]{2})$'
          example: '2026-08-17T12:00:00Z'
        cursor:
          type: string
          maxLength: 8192
          description: |
            Opaque signed cursor returned by the preceding search page. A
            continuation request may contain only this field and optional `limit`.
        limit:
          type: integer
          minimum: 1
          maximum: 1000
          default: 100
          description: Maximum number of events to return.
    EventFeedSearchResult:
      type: object
      additionalProperties: false
      required:
        - feed_id
        - event_type
        - payload
        - created_at
      properties:
        feed_id:
          type: string
          format: uuid
          description: Feed that matched the retained event.
        event_type:
          $ref: '#/components/schemas/EventFeedEventType'
        payload:
          type: object
          additionalProperties: true
          description: Event-type-specific JSON payload.
        created_at:
          type: string
          format: date-time
          description: Time the event entered the retained feed log.
    EventFeedSearchRetention:
      type: object
      additionalProperties: false
      required:
        - oldest_available_at
      properties:
        oldest_available_at:
          type: string
          format: date-time
          description: Beginning of the currently retained event window.
    EventFeedSearchResponse:
      type: object
      additionalProperties: false
      required:
        - events
        - has_more
        - retention
      properties:
        events:
          type: array
          items:
            $ref: '#/components/schemas/EventFeedSearchResult'
        cursor:
          type: string
          maxLength: 8192
          description: Opaque cursor for the next page. Omitted on the final page.
        has_more:
          type: boolean
        retention:
          $ref: '#/components/schemas/EventFeedSearchRetention'
    PsychicError:
      type: object
      properties:
        error:
          type: string
      required:
        - error
    PsychicDownloadDate:
      type: string
      description: "`latest` or a date in `YYYY-MM-DD` format. Future dates are rejected."
      example: latest
    PsychicModel:
      type: string
      description: Psychic model selector. Accepted selectors identify models 1, 2, and 3.
      enum:
        - '1'
        - '2'
        - '3'
        - m1
        - m2
        - m3
        - model1
        - model2
        - model3
      example: model2
    PsychicDownloadFormat:
      type: string
      description: Download file format. Defaults to `bin`.
      enum:
        - bin
        - mmdb
      default: bin
      example: bin
    PsychicDownloadRequest:
      type: object
      required:
        - model
      properties:
        model:
          $ref: '#/components/schemas/PsychicModel'
        date:
          allOf:
            - $ref: '#/components/schemas/PsychicDownloadDate'
          description: When omitted, the latest daily model is used.
        start_date:
          allOf:
            - $ref: '#/components/schemas/PsychicDownloadDate'
          description: Inclusive range start date. Must be sent with `end_date`, cannot be combined with `date`, and is supported only for models 1, 2, and 3.
        end_date:
          allOf:
            - $ref: '#/components/schemas/PsychicDownloadDate'
          description: Inclusive range end date. Must be sent with `start_date`, cannot be combined with `date`, and ranges cannot exceed 30 days.
        format:
          $ref: '#/components/schemas/PsychicDownloadFormat'
    PsychicSnapshotRequest:
      type: object
      required:
        - model
      properties:
        model:
          $ref: '#/components/schemas/PsychicModel'
        days:
          type: integer
          description: Required for models 1, 2, and 3.
          enum:
            - 7
            - 30
          example: 30
        format:
          $ref: '#/components/schemas/PsychicDownloadFormat'
      additionalProperties: false
      description: |
        Snapshot downloads use precomputed artifacts only. Models 1, 2, and 3
        require `days` with a value of `7` or `30`. Sending `date`, `start_date`,
        or `end_date` returns `400`.
    MetadataV3:
      properties:
        mobile:
          type: boolean
          description: Defines if the IP is part of a known cellular network.
          example: false
        source_country:
          type: string
          description: Country where the IP address is registered or operates.
          example: United States
        source_country_code:
          type: string
          description: Country code of the IP address based on ISO 3166-1 alpha-2.
          example: US
        source_city:
          type: string
          description: The city where the device is geographically located.
          example: Seattle
        region:
          type: string
          description: The region where the device is geographically located.
          example: Seattle
        organization:
          type: string
          description: The name of organization that owns the IP address.
          example: DigitalOcean, LLC
        rdns:
          type: string
          description: The reverse DNS pointer.
          example: crawl-66-249-79-17.googlebot.com
        asn:
          type: string
          description: The autonomous system identification number.
          example: AS521
        asn_subnet:
          type: string
          description: The latest-observed ASN subnet for the IP in GreyNoise scan data, not authoritative current BGP state.
          example: 203.0.113.0/24
        category:
          type: string
          description: The subset of network types the IP address belongs to.
          enum:
            - isp
            - business
            - hosting
            - mobile
            - education
          example: education
        os:
          type: string
          description: An approximate guess of the operating system of the device, based on the TCP stack fingerprint.
          example: Windows 7/8
        destination_countries:
          type: array
          items:
            type: string
            description: |
              The full name or country code where GreyNoise sensor
              is physically located.
            example: Germany
        destination_country_codes:
          type: array
          items:
            type: string
            description: |
              The country codes where GreyNoise sensor is
              physically located.
            example: Germany
        destination_cities:
          type: array
          items:
            type: string
            description: |
              The city where the GreyNoise sensor is geographically located.
            example: Berlin
        destination_asns:
          type: array
          items:
            type: string
            description: |
              The ASN associated with the destination IP address.
            example: AS1234
        single_destination:
          type: boolean
          description: |
            A Boolean parameter indicating whether the source IP address
            has only been observed in a single destination country.
          example: true
        carrier:
          type: string
          description: |
            The Internet Service Provider (ISP) or telecommunications
            carrier associated with the source IP address.
          example: AIS
        datacenter:
          type: string
          description: |
            The datacenter or hosting provider from which the activity originates.
            This could indicate the use of cloud services,
            managed hosting, or enterprise datacenter infrastructure.
          example: us-west-1
        domain:
          type: string
          description: |
            The domain name associated with the source IP address.
          example: example.com
        rdns_parent:
          type: string
          description: |
            The parent domain retrieved through reverse DNS (RDNS)
            lookup of the source IP address.
          example: example.com
        rdns_validated:
          type: boolean
          description: |
            A validation status that confirms whether the reverse DNS (RDNS)
            record correctly maps to the source domain.
          example: true
        latitude:
          type: number
          description: |
            The geographic latitude of the source IP address.
          example: 37.7749
        longitude:
          type: number
          description: |
            The geographic longitude of the source IP address.
          example: -122.4194
        sensor_count:
          type: integer
          description: |
            Number of sensors with events observed.
          example: 10
        sensor_hits:
          type: integer
          description: |
            Number of scanning events observed.
          example: 10
    GNQLIPContextV3:
      properties:
        ip:
          type: string
          description: IP address that the information is about.
          example: 71.6.135.131
        internet_scanner_intelligence:
          '$ref': '#/components/schemas/InternetScannerIntelligence'
        business_service_intelligence:
          '$ref': '#/components/schemas/BusinessServiceIntelligence'
    InternetScannerIntelligence:
      properties:
        classification:
          type: string
          description: The classification of the IP address, either "benign", "malicious", or "unknown", based on the activity observed by GreyNoise.
          enum:
            - benign
            - malicious
            - unknown
          example: benign
        first_seen:
          type: string
          description: The earliest date GreyNoise observed any activity from this IP.
          format: date
          example: '2018-01-28'
        last_seen:
          type: string
          description: The most recent date GreyNoise observed any activity from this IP.
          format: date
          example: '2018-02-28'
        last_seen_timestamp:
          type: string
          description: The timestamp of the last observed activity from this IP.
          format: date-time
          example: '2025-01-15T12:30:45Z'
        last_seen_malicious:
          type: string
          format: date-time
          description: Timestamp of the most recent activity classified as malicious. Empty if never observed.
          example: '2026-08-14T18:45:30Z'
        last_seen_suspicious:
          type: string
          format: date-time
          description: Timestamp of the most recent activity classified as suspicious. Empty if never observed.
          example: '2026-08-14T19:23:31Z'
        last_seen_benign:
          type: string
          format: date-time
          description: Timestamp of the most recent activity classified as benign. Empty if never observed.
          example: '2026-08-14T19:24:57Z'
        found:
          type: boolean
          description: Indicates if the IP was observed scanning the GreyNoise sensor network. Also referred to as 'noise'.
          example: true
        actor:
          type: string
          description: The overt actor this IP is associated with.
          example: Shodan.io
        spoofable:
          type: boolean
          description: This IP address has been opportunistically scanning the Internet, however has failed to complete a full TCP connection. Any reported activity could be spoofed.
          example: true
        cves:
          type: array
          items:
            type: string
          description: A list of CVEs associate with this IP.
          example:
            - CVE-2020-1234
            - CVE-2021-2345
        callback_ips:
          type: array
          items:
            type: string
          description: IPs observed as callback destinations for this IP's activity.
        source_workspaces:
          type: array
          items:
            type: string
            enum: [greynoise, community, personal]
          description: Which datasets this IP was found in, matching the workspace_labels selected on the request. Omitted when no workspace_labels were selected.
          example:
            - greynoise
            - community
        tor:
          type: boolean
          description: Whether or not the device is a known Tor exit node.
          example: false
        vpn:
          type: boolean
          description: This IP is associated with a VPN service. Activity, malicious or otherwise, should not be attributed to the VPN service provider.
          example: true
        vpn_service:
          type: string
          description: Name of associated VPN Service.
          example: IPVANISH_VPN
        metadata:
          '$ref': '#/components/schemas/MetadataV3'
        tags:
          type: array
          description: Activity tags associated with this IP.
          items:
            '$ref': '#/components/schemas/IPResponseV3Tags'
        tag_volumes:
          type: array
          description: Per-tag session counts observed for this IP. Omitted when no tag activity has been recorded.
          items:
            type: object
            properties:
              tag_id:
                type: string
                format: uuid
                description: The ID of the tag.
              session_count:
                type: integer
                format: int64
                description: Number of sessions associated with this tag for this IP.
        raw_data:
          type: object
          description: Raw data observed directly by GreyNoise.
          properties:
            scan:
              type: array
              items:
                type: object
                properties:
                  port:
                    type: integer
                    description: Port number
                    example: 80
                  protocol:
                    type: string
                    description: Protocol
                    example: TCP
            ja3:
              type: array
              items:
                type: object
                properties:
                  fingerprint:
                    type: string
                    example: c3a6cf0bf2e690ac8e1ecf6081f17a50
                    description: JA3 hash fingerprint string
                  port:
                    type: integer
                    example: 443
                    description: TCP port connection that the SSL/TLS communication occurred over
            hassh:
              type: array
              items:
                type: object
                properties:
                  fingerprint:
                    type: string
                    example: 51cba57125523ce4b9db67714a90bf6e
                    description: HASSH hash fingerprint string
                  port:
                    type: integer
                    example: 2222
                    description: |
                      TCP port connection where the HASSH hash was identified
            http:
              type: object
              properties:
                md5:
                  type: string
                  description: |
                    An MD5 hash of the body content. This compact,
                    unique representation of the data allows for quick
                    comparisons and deduplication of payloads without
                    storing the raw content.
                  example: 9764955b67107eeb9edfae76f429e783
                cookie_keys:
                  type: array
                  description: |
                    The keys or names of cookies exchanged in the communication.
                    These can reveal session identifiers, tracking mechanisms,
                    or other metadata used in web interactions,
                    providing clues about application behavior or vulnerabilities.
                  example:
                    - expremotekey
                  items:
                    type: string
                request_authorization:
                  type: array
                  description: |
                    The contents of the Authorization header in a request,
                    which can include credentials, tokens, or other authentication
                    information.
                  example:
                    - Bearer exampletoken
                    - Basic username:password
                  items:
                    type: string
                request_cookies:
                  type: array
                  description: |
                    Key-value pairs stored in cookies sent with an HTTP request.
                    These cookies often contain session identifiers, user preferences,
                    or tracking data, which can be analyzed to detect unauthorized
                    access or manipulation.
                  example:
                    - session_id=1234567890
                  items:
                    type: string
                request_header:
                  type: array
                  description: |
                    Request Headers are the keys (names) of HTTP headers
                    that a client sends to a server.
                  example:
                    - "Content-Type: application/json"
                    - "Accept: application/json"
                  items:
                    type: string
                method:
                  type: array
                  description: |
                    The HTTP method used in the request, such as GET, POST, PUT, or DELETE.
                    Analyzing methods can reveal the intent of the request,
                    such as retrieving or modifying resources,
                    and identify unexpected or suspicious activity.
                  example:
                    - GET
                    - POST
                    - PUT
                    - DELETE
                  items:
                    type: string
                request_origin:
                  type: array
                  description: |
                    Indicates the origin of the request,
                    typically used in cross-origin resource sharing (CORS)
                    to specify where the request originated.
                    This helps identify unauthorized or potentially
                    malicious cross-origin requests.
                  example:
                    - 111.111.1.1
                  items:
                    type: string
                host:
                  type: array
                  description: |
                    The host of the request, which can include the domain name
                    and port number. These values can provide insight into the services or
                    endpoints the actor may have been attempting to interact with.
                  example:
                    - example.com
                    - example.com:8080
                  items:
                    type: string
                uri:
                  type: array
                  items:
                    type: string
                    description: |
                      The URI of the request, which can include the path and query parameters.
                      This can provide insight into the specific resource or data being requested.
                path:
                  type: array
                  items:
                    type: string
                    description: Observed scanning activity traversed this web path.
                    example: '/robots.txt'
                useragent:
                  type: array
                  items:
                    type: string
                    description: Observed scanning activity used these user agents.
                    example: >
                      Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)

                ja4h:
                  type: array
                  items:
                    type: string
                    description: |
                      List of JA4H HTTP client fingerprints observed in network traffic from the IP.
                      JA4H captures characteristics of HTTP client behavior including method,
                      headers, and cookie fields, useful for identifying and tracking HTTP clients.
                  example:
                    - ge11cn060000_4e59edc1297a_4da5efaf0cbd
            tls:
              type: object
              properties:
                cipher:
                  type: string
                  description: |
                    The encryption algorithm or cipher suite used during the
                    secure communication. Identifying the cipher helps assess
                    the security of the connection, particularly in TLS/SSL traffic.
                  example: TLS_AES_128_GCM_SHA256
                ja4:
                  type: array
                  items:
                    type: string
                    description: |
                      List of JA4 TLS fingerprints observed in network traffic from the IP.
                      JA4 is a modern fingerprinting method that captures distinctive
                      characteristics of TLS client behavior,
                      useful for identifying and clustering malicious or anomalous clients.
                  example:
                    - t13d1516h2_8daaf6152771_02713d6af862
            ssh:
              type: object
              properties:
                key:
                  type: array
                  description: |
                    This is the SSH key used.
                  example:
                    - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1234567890
                  items:
                    type: string
                ja4ssh:
                  type: array
                  items:
                    type: string
                    description: |
                      List of JA4SSH fingerprints observed in network traffic from the IP.
                      JA4SSH captures SSH traffic patterns including packet lengths and
                      directions, useful for identifying SSH client behavior and detecting
                      anomalous sessions.
                  example:
                    - c76s76_c71s59_c0s0
            tcp:
              type: object
              properties:
                ja4t:
                  type: array
                  items:
                    type: string
                    description: |
                      List of JA4T TCP fingerprints observed in network traffic from the IP.
                      JA4T captures TCP connection characteristics such as window size,
                      options, and MSS, useful for OS fingerprinting and identifying
                      network stacks.
                  example:
                    - 64240_2-1-3-1-1-4_1460_8
                ja4l:
                  type: string
                  description: |
                    JA4L light distance/latency fingerprint observed in network traffic
                    from the IP. Captures TCP TTL and window size characteristics, useful
                    for estimating client-server distance and identifying proxied
                    connections.
                  example: "1460_64"
            source:
              properties:
                bytes:
                  type: integer
                  description: |
                    The total amount of data transferred (in bytes) during the observed session or connection.
                  example: 1024
    BusinessServiceIntelligence:
      properties:
        found:
          type: boolean
          description: |
            Indicates if an IP is part of the RIOT dataset or not.
          example: true
        category:
          type: string
          description: |
            RIOT category the provider belongs to, identifying the type of service provided.
          example: hosting
        name:
          type: string
          description: |
            The name of the provider and/or service.
          example: example.com
        description:
          type: string
          description: |
            A description of the provider and what they do.
          example: example.com
        explanation:
          type: string
          description: |
            An explanation of the category type and what may be expected from this provider and category.
        last_updated:
          type: string
          description: |
            Date and time when this record was last updated from its source (format: YYYY-MM-DDTHH:MM:SSZ).
          example: '2025-01-15T12:30:45Z'
        reference:
          type: string
          description: |
            Reference URL for information about this provider and/or service.
          example: https://example.com
        trust_level:
          type: string
          description: |
            Trust level assigned to this IP/provider. One of:
              - "1" — high trust; broadly used legitimate provider where end-user attribution is high.
              - "2" — moderate trust; common business service infrastructure where end-user attribution is limited.
              - "3" — label only; cloud compute or bulk hosting provider with no inherent trust signal.
          example: "1"
    QuickBusinessServiceIntelligence:
      type: object
      properties:
        found:
          type: boolean
          description: |
            Indicates if an IP is part of the RIOT dataset or not.
        trust_level:
          type: string
          description: |
            Trust level assigned to this IP/provider. One of:
              - "1" — high trust; broadly used legitimate provider where end-user attribution is high.
              - "2" — moderate trust; common business service infrastructure where end-user attribution is limited.
              - "3" — label only; cloud compute or bulk hosting provider with no inherent trust signal.
          example: "1"
    QuickInternetScannerIntelligence:
      type: object
      properties:
        found:
          type: boolean
          description: |
            Indicates if the IP was observed scanning the GreyNoise sensor network. Also referred to as 'noise'.
        classification:
          type: string
          description: |
            The classification of the IP address, either "benign", "malicious",
            or "unknown", based on the activity observed by GreyNoise.
          enum:
            - benign
            - malicious
            - unknown
          example: benign
    QuickMultiIPResponseV3:
      type: object
      properties:
        data:
          type: array
          items:
            type: object
            properties:
              ip:
                type: string
                example: 8.8.8.8
                description: IP address that the information is about.
              business_service_intelligence:
                '$ref': '#/components/schemas/QuickBusinessServiceIntelligence'
              internet_scanner_intelligence:
                '$ref': '#/components/schemas/QuickInternetScannerIntelligence'
        request_metadata:
          '$ref': '#/components/schemas/IpResponseMetadataV3'
    MultiIPResponseV3:
      type: object
      properties:
        data:
          type: array
          items:
            type: object
            properties:
              ip:
                type: string
                example: 8.8.8.8
                description: IP address that the information is about.
              business_service_intelligence:
                '$ref': '#/components/schemas/BusinessServiceIntelligence'
              internet_scanner_intelligence:
                '$ref': '#/components/schemas/InternetScannerIntelligence'
        request_metadata:
          '$ref': '#/components/schemas/IpResponseMetadataV3'
    IpResponseMetadataV3:
      type: object
      properties:
        restricted_fields:
          type: array
          description: |
            The fields that were restricted due to plan limitations.
          example:
            - 'ip'
            - 'cve'
            - 'destination_cities'
          items:
            type: string
        message:
          type: string
          example: ok
          description: A status message indicating if there were issues with the request
        ips_not_found:
          type: array
          description: |
            The list of IPs not found
          items:
            type: string
        invalid_ips:
          type: array
          description: |
            The list of submitted values that were not valid IP addresses.
          items:
            type: string
    IPResponseV3:
      type: object
      properties:
        ip:
          type: string
          example: 8.8.8.8
          description: |
            IP address that the information is about.
        business_service_intelligence:
          '$ref': '#/components/schemas/BusinessServiceIntelligence'
        internet_scanner_intelligence:
          '$ref': '#/components/schemas/InternetScannerIntelligence'
        request_metadata:
          type: object
          properties:
            restricted_fields:
              type: array
              description: |
                The fields that were restricted due to plan limitations.
              example:
                - 'ip'
                - 'cve'
                - 'destination_cities'
              items:
                type: string
    QuickIpProfile:
      type: object
      properties:
        ip:
          type: string
          example: 8.8.8.8
          description: IP address that the information is about.
        business_service_intelligence:
          '$ref': '#/components/schemas/QuickBusinessServiceIntelligence'
        internet_scanner_intelligence:
          '$ref': '#/components/schemas/QuickInternetScannerIntelligence'
    MultiIpRequest:
      type: object
      required:
        - ips
      properties:
        ips:
          type: array
          items:
            type: string
            example: 8.8.8.8
    CommunityResponse:
      type: object
      properties:
        ip:
          type: string
          example: 1.2.3.4
        noise:
          type: boolean
          example: false
        riot:
          type: boolean
          example: true
        classification:
          type: string
          example: benign
        name:
          type: string
          example: Cloudflare
        link:
          type: string
          example: https://viz.greynoise.io/riot/1.2.3.4
        last_seen:
          type: string
          example: '2020-01-01'
        message:
          type: string
          example: Success
    TagsMetadata:
      type: object
      properties:
        tags:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                example: ef0cc90d-d80c-436f-92c5-3d8f8665c9ac
              label:
                type: string
                example: MIRAI
              slug:
                type: string
                example: mirai
              name:
                type: string
                example: Mirai
              category:
                type: string
                example: worm
              intention:
                type: string
                example: malicious
              description:
                type: string
                example: This IP address exhibits behavior that indicates it is infected with Mirai or a Mirai-like variant of malware.
              references:
                type: array
                items:
                  type: string
                  example: https://en.wikipedia.org/wiki/Mirai_(malware)
              recommend_block:
                type: boolean
                example: false
              cves:
                type: array
                items:
                  type: string
                  example: CVE-2020-1234
              created_at:
                type: string
                example: '2020-04-07'
              related_tags:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                      example: da78f2eb-fe6b-4e84-913c-cdab4ff067c2
                    name:
                      type: string
                      example: Oracle WebLogic RCE CVE-2020-14882
                    intention:
                      type: string
                      example: malicious
                    category:
                      type: string
                      example: activity
                    slug:
                      type: string
                      example: oracle-weblogic-rce-cve-2020-14882
    TagActivity:
      type: object
      properties:
        slug:
          type: string
          description: Human-friendly identifier for the tag the activity belongs to.
          example: apache-log4j-rce-attempt
        activity:
          type: object
          description: |
            The activity series, keyed by the tag's intention (for example `malicious`,
            `benign` or `unknown`). Each value is the list of time buckets covering the
            requested window, in ascending order.
          additionalProperties:
            type: array
            items:
              '$ref': '#/components/schemas/TagActivitySlice'
          example:
            malicious:
              - start: '2026-01-24T10:00:00Z'
                end: '2026-01-24T11:00:00Z'
                include_start: true
                include_end: false
                timestamp: '2026-01-24T10:00:00Z'
                active_ips: 14
              - start: '2026-01-24T11:00:00Z'
                end: '2026-01-24T12:00:00Z'
                include_start: true
                include_end: false
                timestamp: '2026-01-24T11:00:00Z'
                active_ips: 10
        aggregations:
          type: object
          description: Totals across the whole requested window, not per bucket.
          properties:
            total_ips:
              type: integer
              description: |
                Approximate count of distinct IP addresses seen for this tag across the
                whole window. This is a distinct-count estimate, so it is not guaranteed
                to equal the size of a de-duplicated union of the per-bucket `ips` lists.
              example: 115
            classification:
              type: object
              description: The same total broken down by the tag's intention.
              additionalProperties:
                type: integer
              example:
                malicious: 115
        metadata:
          '$ref': '#/components/schemas/TagActivityMetadata'
    TagActivitySlice:
      type: object
      description: A single time bucket of tag activity.
      properties:
        start:
          type: string
          format: date-time
          description: ISO8601 UTC timestamp for the start of the bucket. Inclusive.
          example: '2026-01-24T10:00:00Z'
        end:
          type: string
          format: date-time
          description: ISO8601 UTC timestamp for the end of the bucket. Exclusive.
          example: '2026-01-24T11:00:00Z'
        include_start:
          type: boolean
          description: Whether `start` is inclusive of the bucket. Always `true`.
          example: true
        include_end:
          type: boolean
          description: Whether `end` is inclusive of the bucket. Always `false`.
          example: false
        timestamp:
          type: string
          format: date-time
          description: |
            Duplicate of `start`, retained for backward compatibility with clients
            written before `start` and `end` existed. Prefer `start` and `end`.
          deprecated: true
          example: '2026-01-24T10:00:00Z'
        active_ips:
          type: integer
          description: |
            Approximate count of distinct IP addresses seen scanning for this tag
            during the bucket.

            This is always the full count for the bucket. It is **not** reduced to
            match `ips` when that list has been truncated, so `active_ips` and
            `len(ips)` legitimately disagree whenever `ips_truncated` is `true`.
          example: 14
        ips:
          type: array
          description: |
            The distinct source IP addresses observed in this bucket. Present only when
            the request set `include_ips=true`, and omitted otherwise.

            Capped at 1,000 addresses per bucket. When the bucket held more than that,
            `ips_truncated` is `true` and this list is a biased subset, not a sample —
            see `ips_truncated`.
          items:
            type: string
            example: 1.2.3.4
        ips_truncated:
          type: boolean
          description: |
            `true` when this bucket held more than 1,000 distinct IP addresses and `ips`
            therefore contains only some of them. Omitted when `false`.

            **Treat a truncated list as a partial, non-random subset.** The cap is
            applied by taking the addresses with the most matching records in the
            bucket, so what survives truncation is skewed toward the noisiest, most
            persistently-seen IPs; the quieter ones are exactly the ones dropped. It is
            not a random sample and must not be used to estimate proportions,
            distributions or coverage of the bucket.

            The gap can be large. On the busiest tag measured, a bucket returned on the
            order of 1% of the addresses it actually held. Because `active_ips` beside
            it keeps reporting the full count, a client that ignores this flag will
            silently mistake a 1% prefix for the complete list.

            To enumerate every IP for a tag rather than the per-bucket top slice, use
            `GET /v3/tags/{id}/ips`.
          example: true
    TagActivityMetadata:
      type: object
      description: Describes the window and the options the response was produced under.
      properties:
        start_date:
          type: string
          format: date-time
          description: |
            ISO8601 UTC start of the window actually queried. This reflects the `days`
            value after it was clamped to your plan's data reach, so it may cover a
            shorter period than you requested.
          example: '2026-01-21T10:00:00Z'
        end_date:
          type: string
          format: date-time
          description: ISO8601 UTC end of the window actually queried.
          example: '2026-01-24T10:00:00Z'
        granularity:
          type: string
          description: The bucket width used, matching the requested `granularity`.
          example: 24h
        ips_included:
          type: boolean
          description: |
            Echoes whether `include_ips` was set, so a client can tell an empty `ips`
            list apart from one that was never requested. Omitted when `false`.
          example: true
        ips_per_bucket:
          type: integer
          description: |
            The per-bucket cap that was applied to `ips`. Fixed server-side at 1,000;
            it is not a request parameter and cannot be raised. Omitted when
            `include_ips` was not set.
          example: 1000
        ips_truncated:
          type: boolean
          description: |
            `true` when **any** bucket in the response hit the cap. A convenience
            roll-up so a client can detect truncation without scanning every bucket;
            check each bucket's own `ips_truncated` to find which ones. Omitted when
            `false`.
          example: true
    IPResponseV3Tags:
      type: object
      properties:
        id:
          type: string
          example: ef0cc90d-d80c-436f-92c5-3d8f8665c9ac
          description: |
            The unique identifier for the tag.
        slug:
          type: string
          example: mirai
          description: |
            The slugified version of the tag name.
        name:
          type: string
          example: Mirai
          description: |
            The human-readable name for the tag.
        category:
          type: string
          example: worm
          description: |
            Category of the IP address such as hosting or ISP.
        intention:
          type: string
          example: malicious
          description: |
            The intent of the tag, either suspicious, malicious, benign, or unknown.
        description:
          type: string
          example: This IP address exhibits behavior that indicates it is infected with Mirai or a Mirai-like variant of malware.
          description: |
            A detailed description of the tag, including the observed activity
            and any relevant context or details.
        references:
          type: array
          items:
            type: string
            example: https://en.wikipedia.org/wiki/Mirai_(malware)
            description: |
              A list of URLs or references that provide additional information
              about the tag and its associated activity.
        recommend_block:
          type: boolean
          example: false
          description: |
            A boolean value indicating whether the tag should be recommended
            for blocking or filtering purposes.
        cves:
          type: array
          items:
            type: string
            example: CVE-2020-1234
            description: |
              A list of CVEs associated with the tag.
        created:
          type: string
          example: '2020-04-07'
          description: |
            The date the tag was created. May be empty.
        updated_at:
          type: string
          format: date-time
          example: '2026-02-10T15:59:49Z'
          description: |
            The timestamp when the tag was last updated.
    QuickGNQLV3Response:
      type: object
      properties:
        request_metadata:
          '$ref': '#/components/schemas/GNQLV3ResponseMetadata'
        data:
          type: array
          description: The relevant IP records requested by the user
          items:
            '$ref': '#/components/schemas/QuickIpProfile'
    GNQLV3Response:
      type: object
      properties:
        request_metadata:
          '$ref': '#/components/schemas/GNQLV3ResponseMetadata'
        data:
          type: array
          description: The relevant IP records requested by the user
          items:
            '$ref': '#/components/schemas/GNQLIPContextV3'
    GNQLV3ResponseMetadata:
      type: object
      properties:
        complete:
          type: boolean
          example: false
          description: Whether all records have been delivered or not. `false` means there's another page
        scroll:
          type: string
          example: >
            DnF1ZXJ5VGhlbkZldGNoBQAAAAAAeygtFkFKSExEdUc4VEtta2syaGg2R3kzNGcAAAAAAH soLhZBSkhMRHVHOFRLbWtrMmhoNkd5MzRnAAAAAAB7KC8WQUpITER1RzhUS21razJoaDZH eTM0ZwAAAAAAeygxFkFKSExEdUc4VEtta2syaGg2R3kzNGcAAAAAAHsoMBZBSkhMRHVHOF RLbWtrMmhoNkd5MzRn

          description: Scroll token to use for pagination
        query:
          type: string
          example: last_seen:2019-07-28 classification:malicious
          description: The GNQL query string the requester queried
        adjusted_query:
          type: string
          example: last_seen:2019-07-28 classification:malicious
          description: |
            When certain query parameters are not specified or incompatible
            with your current plan, GreyNoise automatically adjusts params on your
            query prior to execution.
        count:
          type: integer
          example: 1
          description: The number of total results for the given GNQL query
        message:
          type: string
          example: ok
          description: A status message indicating if there were issues with the request
        restricted_fields:
          type: array
          description: The fields that were restricted due to plan limitations
          example:
            - 'ip'
            - 'asn'
            - 'organization'
            - 'country'
            - 'city'
            - 'region'
          items:
            type: string
    GNQLCountResponse:
      type: object
      properties:
        count:
          type: integer
          format: int64
          description: The number of IPs matching the query
          example: 50000
        query:
          type: string
          description: The GNQL query string the requester submitted
          example: classification:malicious last_seen:1d
        adjusted_query:
          type: string
          description: |
            The query that was actually executed, when plan limitations
            rewrote the submitted query. Absent when the query ran unchanged.
          example: classification:malicious last_seen:1d last_seen:7d
        message:
          type: string
          description: A status message indicating if there were issues with the request
          example: The requested data reach exceeds your plan.
        restricted_fields:
          type: array
          description: The fields that were restricted due to plan limitations
          items:
            type: string
          example:
            - metadata.organization
            - raw_data.ja3
      required:
        - count
        - query
    GNQLIPExportResponse:
      type: object
      properties:
        ips:
          type: array
          description: |
            Every IP address matching the query, ordered most recently seen
            first (`last_seen` descending, ties broken by ascending IP address).
          items:
            type: string
          example:
            - 1.2.3.4
            - 5.6.7.8
        request_metadata:
          '$ref': '#/components/schemas/GNQLIPExportMetadata'
      required:
        - ips
        - request_metadata
    GNQLIPExportMetadata:
      type: object
      properties:
        count:
          type: integer
          format: int64
          description: |
            The number of IPs matching the query. When `exclude_riot=true`
            this is the number of IPs actually returned, after the BSI
            exclusion, rather than the pre-filter total.
          example: 50000
        query:
          type: string
          description: The GNQL query string the requester submitted
          example: classification:malicious last_seen:1d
        adjusted_query:
          type: string
          description: |
            The query that was actually executed, when plan limitations
            rewrote the submitted query. Absent when the query ran unchanged.
          example: classification:malicious last_seen:1d last_seen:7d
        message:
          type: string
          description: A status message indicating if there were issues with the request
          example: No results found.
        restricted_fields:
          type: array
          description: The fields that were restricted due to plan limitations
          items:
            type: string
          example:
            - metadata.organization
      required:
        - count
        - query
    GNQLValidateResponse:
      type: object
      properties:
        valid:
          type: boolean
          description: |
            Whether the query would be accepted. `false` is a verdict, not an
            error - it is returned with HTTP 200.
          example: true
        query:
          type: string
          description: The query that was validated, after whitespace trimming
          example: classification:malicious last_seen:1d
        errors:
          type: array
          description: |
            Every reason the query was rejected, not only the first. Absent
            when `valid` is `true`.
          items:
            '$ref': '#/components/schemas/GNQLQueryError'
        message:
          type: string
          description: |
            The rejection reason when `valid` is `false`, or a data-reach or
            field-restriction notice when it is `true`.
          example: invalid query
        adjusted_query:
          type: string
          description: |
            The query that would actually be executed, when plan limitations
            would rewrite the submitted query. Absent when the query would run
            unchanged.
          example: classification:malicious last_seen:7d
        is_query_adjusted:
          type: boolean
          description: |
            Whether `adjusted_query` differs from the submitted query, so a
            client can show what will actually run.
          example: false
      required:
        - valid
        - query
    GNQLQueryError:
      type: object
      description: |
        One machine-readable reason a query was rejected. This mirrors a
        `details[]` entry on a search `400`, so a client can render both the
        same way.
      properties:
        message:
          type: string
          description: Human-readable description of the problem
          example: unexpected end of query
        position:
          type: integer
          description: |
            Zero-based character offset in the query the error points at.
            Absent when the error is not tied to a position. Note that `0` is
            a real offset - a query beginning with an operator fails at
            offset 0 - so distinguish absent from zero.
          example: 26
        expected:
          type: string
          description: What the parser expected at `position`
          example: a search term
        found:
          type: string
          description: What the parser found at `position` instead
          example: <end of input>
        code:
          type: string
          description: |
            Machine-readable error code. Current values include
            `SYNTAX_ERROR`, `FIELD_NOT_FOUND`, `UNSUPPORTED_FIELD`,
            `DISCONTINUED_FIELD`, `INVALID_VALUE_TYPE`, `UNSUPPORTED_KEYWORD`,
            `INVALID_TIME_INTERVAL`, `INVALID_DATE_FORMAT`,
            `QUERY_TOO_COMPLEX`, `UNSUPPORTED_BARE_TERM`,
            `RECENCY_REQUIRES_VALUE`, `UNSUPPORTED_DATE_RESOLUTION` and
            `INVALID_QUERY`. New codes may be added, so treat an unrecognized
            code as a generic rejection and fall back to `message`.
          example: SYNTAX_ERROR
        field:
          type: string
          description: The GNQL field the error concerns, when it is field-specific
          example: classification
      required:
        - message
    GNQLStats:
      type: object
      properties:
        query:
          type: string
          description: The GNQL query string the requester queried
          example: last_seen:2019-07-28 classification:malicious
        count:
          type: integer
          description: The number of total results for the given GNQL query
          example: 50000
        adjusted_query:
          type: string
          description: |
            If the original query was adjusted due to plan limitations (for
            example, the requested data reach was reduced), this field
            contains the query that was actually executed. Empty when the
            original query was run unchanged.
          example: last_seen:2019-07-28 classification:malicious last_seen:7d
        stats:
          type: object
          properties:
            classifications:
              type: array
              description: Most common classifications
              items:
                type: object
                properties:
                  classification:
                    type: string
                    example: malicious
                  count:
                    type: integer
                    example: 5000
            spoofable:
              type: array
              description: Count of which are spoofable
              items:
                type: object
                properties:
                  spoofable:
                    type: boolean
                    example: false
                  count:
                    type: integer
                    example: 5000
            organizations:
              type: array
              description: Most common organizations
              items:
                type: object
                properties:
                  organization:
                    type: string
                    example: DigitalOcean, LLC
                  count:
                    type: integer
                    example: 5000
            actors:
              type: array
              description: Most common actors
              items:
                type: object
                properties:
                  actor:
                    type: string
                    example: Shodan.io
                  count:
                    type: integer
                    example: 5000
            countries:
              type: array
              description: |
                Most common countries (Same data as
                metadata.source_countries. source_countries is preferred)
              items:
                type: object
                properties:
                  country:
                    type: string
                    example: United States
                  count:
                    type: integer
                    example: 5000
            source_countries:
              type: array
              description: Most common source countries
              items:
                type: object
                properties:
                  country:
                    type: string
                    example: United States
                  count:
                    type: integer
                    example: 5000
            destination_countries:
              type: array
              description: Most common destination countries
              items:
                type: object
                properties:
                  country:
                    type: string
                    example: United States
                  count:
                    type: integer
                    example: 5000
            tags:
              type: array
              description: Most common tags
              items:
                type: object
                properties:
                  tag:
                    type: string
                    example: SSH Bruteforcer
                  id:
                    type: string
                    format: uuid
                    description: The unique identifier for the tag
                    example: 4c076d9c-be48-4bd1-bec4-6005e06c0f89
                  count:
                    type: integer
                    example: 5000
            operating_systems:
              type: array
              description: Most common operating systems
              items:
                type: object
                properties:
                  operating_system:
                    type: string
                    example: Windows 7/8
                  count:
                    type: integer
                    example: 5000
            categories:
              type: array
              description: Most common categories
              items:
                type: object
                properties:
                  category:
                    type: string
                    example: education
                  count:
                    type: integer
                    example: 5000
            asns:
              type: array
              description: Most common ASNs
              items:
                type: object
                properties:
                  asn:
                    type: string
                    example: AS4134
                  count:
                    type: integer
                    example: 5000
    IPTimelineResponse:
      type: object
      properties:
        results:
          type: array
          items:
            type: object
            properties:
              data:
                type: integer
                description: Hourly activity count for the field value in this time bucket
                example: 1
              label:
                type: string
                description: |
                  A label that corresponds to a distinct value for the
                  given field
                example: 'unknown'
              timestamp:
                type: string
                description: |
                  Time range bucket based on granularity - the timestamp
                  represents the start of the bucket
                format: date-time
                example: '2023-01-23T00:00:00Z'
        metadata:
          type: object
          properties:
            ip:
              type: string
              description: IP queried
              example: '36.32.2.102'
            field:
              type: string
              description: Field over which to show change
              example: 'classification'
            first_seen:
              type: string
              description: |
                The earliest date GreyNoise observed any activity from this IP.
              example: '2022-06-15'
            start:
              type: string
              format: date-time
              description: Start of time range for data
              example: '2023-01-18T00:00:00Z'
            end:
              type: string
              format: date-time
              description: End of time range for data
              example: '2023-01-25T21:55:18.486036894Z'
            granularity:
              type: string
              description: Granularity at which to show data
              example: '1d'
            metric:
              type: string
              description: The metric used within the data field
              example: 'count'
    CVEAdvancedResponse:
      type: object
      properties:
        id:
          type: string
          description: The CVE identifier.
          example: CVE-2024-12345
        details:
          $ref: '#/components/schemas/CVEDetails'
        timeline:
          $ref: '#/components/schemas/CVETimeline'
        exploitation_details:
          $ref: '#/components/schemas/CVEExploitationDetails'
        exploitation_stats:
          $ref: '#/components/schemas/CVEExploitationStats'
        exploitation_activity:
          $ref: '#/components/schemas/CVEExploitationActivity'
    CVEBasicResponse:
      type: object
      properties:
        id:
          type: string
          description: The CVE identifier.
          example: CVE-2024-12345
        details:
          $ref: '#/components/schemas/CVEDetails'
        timeline:
          $ref: '#/components/schemas/CVETimeline'
        exploitation_details:
          $ref: '#/components/schemas/CVEExploitationDetails'
    CVEMinimalResponse:
      type: object
      properties:
        id:
          type: string
          description: The CVE identifier.
          example: CVE-2024-12345
        details:
          $ref: '#/components/schemas/CVEDetails'
    CVEDetails:
      type: object
      properties:
        vulnerability_name:
          type: string
          description: The name of the vulnerability.
          example: Sample Vulnerability
        vulnerability_description:
          type: string
          description: Description of the vulnerability.
          example: This vulnerability allows remote attackers to execute arbitrary code.
        cve_cvss_score:
          type: number
          description: The CVSS score of the CVE.
          example: 7.5
        product:
          type: string
          description: The product affected by the vulnerability.
          example: Sample Product
        vendor:
          type: string
          description: The vendor of the affected product.
          example: Sample Vendor
        published_to_nist_nvd:
          type: boolean
          description: Whether the CVE is published to the NIST National Vulnerability Database.
          example: true
    CVETimeline:
      type: object
      properties:
        cve_published_date:
          type: string
          format: date
          description: The date the CVE was published.
          example: '2024-01-01'
        cve_last_updated_date:
          type: string
          format: date
          description: The date the CVE was last updated.
          example: '2024-01-02'
        first_known_published_date:
          type: string
          format: date
          description: The first known published date of the CVE.
          example: '2024-01-01'
        cisa_kev_date_added:
          type: string
          format: date
          description: The date the CVE was added to the CISA KEV list.
          example: '2024-01-03'
    CVEExploitationDetails:
      type: object
      properties:
        attack_vector:
          type: string
          description: The attack vector for the CVE.
          example: Network
        exploit_found:
          type: boolean
          description: Whether an exploit has been found for this CVE.
          example: true
        exploitation_registered_in_kev:
          type: boolean
          description: Whether the exploitation is registered in KEV.
          example: true
        epss_score:
          type: number
          description: The EPSS score for the CVE.
          example: 0.8
    CVEExploitationStats:
      type: object
      properties:
        number_of_available_exploits:
          type: integer
          description: The number of available exploits for the CVE.
          example: 5
        number_of_threat_actors_exploiting_vulnerability:
          type: integer
          description: The number of threat actors exploiting the vulnerability.
          example: 3
        number_of_botnets_exploiting_vulnerability:
          type: integer
          description: The number of botnets exploiting the vulnerability.
          example: 2
    CVEExploitationActivity:
      type: object
      properties:
        activity_seen:
          type: boolean
          description: Whether exploitation activity has been observed.
          example: true
        benign_ip_count_1d:
          type: integer
          description: The count of benign IPs in the last day.
          example: 100
        benign_ip_count_10d:
          type: integer
          description: The count of benign IPs in the last 10 days.
          example: 500
        benign_ip_count_30d:
          type: integer
          description: The count of benign IPs in the last 30 days.
          example: 1000
        threat_ip_count_1d:
          type: integer
          description: The count of threat IPs in the last day.
          example: 10
        threat_ip_count_10d:
          type: integer
          description: The count of threat IPs in the last 10 days.
          example: 50
        threat_ip_count_30d:
          type: integer
          description: The count of threat IPs in the last 30 days.
          example: 100
    TimeSeriesResponse:
      type: object
      description: |
        Response object for the timeseries endpoint. The response is a map
        where keys are time intervals and values are arrays of IP records
        observed in that interval.

        Time interval keys are formatted as YYYY-MM-DD-HH (e.g., "2025-01-15-14")
      additionalProperties:
        description: |
          Time interval key (format: YYYY-MM-DD-HH)
        type: array
        items:
          $ref: '#/components/schemas/TimeSeriesRecord'
      example:
        "2025-11-10-14":
          - ip: "203.0.113.45"
            internet_scanner_intelligence:
              first_seen: '2018-01-28'
              last_seen: '2018-02-28'
              found: true
              tags:
                - Mirai
                - Telnet Worm
              actor: Shodan.io
              spoofable: true
              classification: benign
              cves:
                - CVE-2020-1234
                - CVE-2021-2345
              vpn: true
              vpn_service: IPVANISH_VPN
              tor: false
              last_seen_timestamp: '2025-01-15T12:30:45Z'
              metadata:
                asn: "AS13335"
                source_country: "United States"
                source_country_code: "US"
                organization: "Example Hosting"
              raw_data:
                scan:
                  - port: 22
                    protocol: "tcp"
    TimeSeriesRecord:
      type: object
      description: A single IP record in a timeseries response
      properties:
        ip:
          type: string
          description: The IP address
          example: "203.0.113.45"
        internet_scanner_intelligence:
          $ref: '#/components/schemas/TimeSeriesIntelligence'
    TimeSeriesStatsResponse:
      type: object
      description: |
        Response object for the timeseries stats/aggregation endpoint.
        This provides aggregated IP counts over time intervals matching
        a GNQL query.
      properties:
        count:
          type: integer
          description: The sum of all IP counts across all time intervals
          example: 1500
        min:
          type: integer
          description: The minimum IP count observed in any single time interval
          example: 10
        max:
          type: integer
          description: The maximum IP count observed in any single time interval
          example: 250
        data:
          type: array
          items:
            $ref: '#/components/schemas/TimeSeriesStatsRecord'
          description: Array of aggregated data points, one per time interval
      example:
        count: 1500
        min: 10
        max: 250
        data:
          - date: "2025-11-10 14:00:00.000"
            count: 125
          - date: "2025-11-10 15:00:00.000"
            count: 250
          - date: "2025-11-10 16:00:00.000"
            count: 180
    TimeSeriesStatsRecord:
      type: object
      description: A single aggregated data point in a timeseries stats response
      properties:
        date:
          type: string
          description: |
            The starting timestamp for this time interval in format
            'YYYY-MM-DD HH:mm:ss.SSS'.
            For hourly intervals, the time component represents the hour.
            For daily intervals, the time will be 00:00:00.000.
          example: "2025-11-10 14:00:00.000"
        count:
          type: integer
          description: |
            The number of unique IPs matching the query in this time
            interval
          example: 125
    TimeSeriesIntelligence:
      type: object
      description: Intelligence data for an IP in the timeseries dataset
      properties:
        first_seen:
          type: string
          description: The earliest date GreyNoise observed any activity from this IP.
          format: date
          example: '2018-01-28'
        last_seen:
          type: string
          description: The most recent date GreyNoise observed any activity from this IP.
          format: date
          example: '2018-02-28'
        found:
          type: boolean
          description: Indicates if the IP was observed scanning the GreyNoise sensor network. Also referred to as 'noise'.
          example: true
        tags:
          type: array
          items:
            type: string
          description: |
            A list of activity/malware tags GreyNoise has applied to this
            IP.
          example:
            - Mirai
            - Telnet Worm
        actor:
          type: string
          description: The overt actor this IP is associated with.
          example: Shodan.io
        spoofable:
          type: boolean
          description: This IP address has been opportunistically scanning the Internet, however has failed to complete a full TCP connection. Any reported activity could be spoofed.
          example: true
        classification:
          type: string
          description: The classification of the IP address, either "benign", "malicious", or "unknown", based on the activity observed by GreyNoise.
          enum:
            - benign
            - malicious
            - unknown
          example: benign
        cves:
          type: array
          items:
            type: string
          description: A list of CVEs associate with this IP.
          example:
            - CVE-2020-1234
            - CVE-2021-2345
        vpn:
          type: boolean
          description: This IP is associated with a VPN service. Activity, malicious or otherwise, should not be attributed to the VPN service provider.
          example: true
        vpn_service:
          type: string
          description: Name of associated VPN Service.
          example: IPVANISH_VPN
        tor:
          type: boolean
          description: Whether or not the device is a known Tor exit node.
          example: false
        last_seen_timestamp:
          type: string
          description: The timestamp of the last observed activity from this IP.
          format: date-time
          example: '2025-01-15T12:30:45Z'
        metadata:
          $ref: '#/components/schemas/MetadataV3'
        raw_data:
          $ref: '#/components/schemas/TimeSeriesRawData'
    TimeSeriesRawData:
      type: object
      description: Raw data collected about the IP's scanning activity
      properties:
        scan:
          type: array
          items:
            $ref: '#/components/schemas/TimeSeriesScanEntry'
          description: Observed scan activity
        ja3:
          type: array
          items:
            $ref: '#/components/schemas/TimeSeriesJA3Entry'
          description: JA3 TLS fingerprints observed
        hassh:
          type: array
          items:
            $ref: '#/components/schemas/TimeSeriesHASSHEntry'
          description: HASSH SSH fingerprints observed
        http:
          $ref: '#/components/schemas/TimeSeriesHTTPData'
        source:
          $ref: '#/components/schemas/TimeSeriesSourceData'
        tls:
          $ref: '#/components/schemas/TimeSeriesTLSData'
        ssh:
          $ref: '#/components/schemas/TimeSeriesSSHData'
        tcp:
          $ref: '#/components/schemas/TimeSeriesTCPData'
    TimeSeriesScanEntry:
      type: object
      properties:
        port:
          type: integer
          description: Port number
          example: 80
        protocol:
          type: string
          description: Protocol
          example: TCP
    TimeSeriesJA3Entry:
      type: object
      properties:
        fingerprint:
          type: string
          example: c3a6cf0bf2e690ac8e1ecf6081f17a50
          description: JA3 hash fingerprint string
        port:
          type: integer
          example: 443
          description: TCP port connection that the SSL/TLS communication occurred over
    TimeSeriesHASSHEntry:
      type: object
      properties:
        fingerprint:
          type: string
          example: 51cba57125523ce4b9db67714a90bf6e
          description: HASSH hash fingerprint string
        port:
          type: integer
          example: 2222
          description: |
            TCP port connection where the HASSH hash was identified
    TimeSeriesHTTPData:
      type: object
      description: HTTP-related data observed
      properties:
        md5:
          type: array
          items:
            type: string
          description: |
            MD5 hashes of the body content. These compact,
            unique representations of the data allow for quick
            comparisons and deduplication of payloads without
            storing the raw content.
          example:
            - 9764955b67107eeb9edfae76f429e783
        cookie_keys:
          type: array
          items:
            type: string
          description: |
            The keys or names of cookies exchanged in the communication.
            These can reveal session identifiers, tracking mechanisms,
            or other metadata used in web interactions,
            providing clues about application behavior or vulnerabilities.
          example:
            - expremotekey
        request_authorization:
          type: array
          items:
            type: string
          description: |
            The contents of the Authorization header in a request,
            which can include credentials, tokens, or other authentication
            information.
          example:
            - Bearer exampletoken
            - Basic username:password
        request_cookies:
          type: array
          items:
            type: string
          description: |
            Key-value pairs stored in cookies sent with an HTTP request.
            These cookies often contain session identifiers, user preferences,
            or tracking data, which can be analyzed to detect unauthorized
            access or manipulation.
          example:
            - session_id=1234567890
        request_header:
          type: array
          items:
            type: string
          description: |
            Request Headers are the keys (names) of HTTP headers
            that a client sends to a server.
          example:
            - "Content-Type: application/json"
            - "Accept: application/json"
        method:
          type: array
          items:
            type: string
          description: |
            The HTTP method used in the request, such as GET, POST, PUT, or DELETE.
            Analyzing methods can reveal the intent of the request,
            such as retrieving or modifying resources,
            and identify unexpected or suspicious activity.
          example:
            - GET
            - POST
            - PUT
            - DELETE
        path:
          type: array
          items:
            type: string
          description: Observed scanning activity traversed this web path.
          example:
            - '/robots.txt'
        request_origin:
          type: array
          items:
            type: string
          description: |
            Indicates the origin of the request,
            typically used in cross-origin resource sharing (CORS)
            to specify where the request originated.
            This helps identify unauthorized or potentially
            malicious cross-origin requests.
          example:
            - 111.111.1.1
        useragent:
          type: array
          items:
            type: string
          description: Observed scanning activity used these user agents.
          example:
            - >
              Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)

        ja4h:
          type: array
          items:
            type: string
          description: |
            List of JA4H HTTP client fingerprints observed in network traffic from the IP.
            JA4H captures characteristics of HTTP client behavior including method,
            headers, and cookie fields, useful for identifying and tracking HTTP clients.
          example:
            - ge11cn060000_4e59edc1297a_4da5efaf0cbd
    TimeSeriesSourceData:
      type: object
      description: Source traffic metadata
      properties:
        bytes:
          type: integer
          format: int64
          description: |
            The total amount of data transferred (in bytes) during the
            observed session or connection.
          example: 1024
    TimeSeriesTLSData:
      type: object
      description: TLS-related data
      properties:
        cipher:
          type: array
          items:
            type: string
          description: |
            The encryption algorithms or cipher suites used during secure
            communication. Identifying the ciphers helps assess the security
            of the connection, particularly in TLS/SSL traffic.
          example:
            - TLS_AES_128_GCM_SHA256
            - TLS_AES_256_GCM_SHA384
        ja4:
          type: array
          items:
            type: string
          description: |
            List of JA4 TLS fingerprints observed in network traffic from the IP.
            JA4 is a modern fingerprinting method that captures distinctive
            characteristics of TLS client behavior,
            useful for identifying and clustering malicious or anomalous clients.
          example:
            - t13d1516h2_8daaf6152771_02713d6af862
    TimeSeriesSSHData:
      type: object
      description: SSH-related data
      properties:
        key:
          type: array
          items:
            type: string
          description: |
            This is the SSH key used.
          example:
            - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1234567890
        ja4ssh:
          type: array
          items:
            type: string
          description: |
            List of JA4SSH fingerprints observed in network traffic from the IP.
            JA4SSH captures SSH traffic patterns including packet lengths and
            directions, useful for identifying SSH client behavior and detecting
            anomalous sessions.
          example:
            - c76s76_c71s59_c0s0
    TimeSeriesTCPData:
      type: object
      description: TCP-related data
      properties:
        ja4t:
          type: array
          items:
            type: string
          description: |
            List of JA4T TCP fingerprints observed in network traffic from the IP.
            JA4T captures TCP connection characteristics such as window size,
            options, and MSS, useful for OS fingerprinting and identifying
            network stacks.
          example:
            - 64240_2-1-3-1-1-4_1460_8
        ja4l:
          type: string
          description: |
            JA4L light distance/latency fingerprint observed in network traffic
            from the IP. Captures TCP TTL and window size characteristics, useful
            for estimating client-server distance and identifying proxied
            connections.
          example: "1460_64"
    WorkspaceDiffRequest:
      type: object
      required:
        - query
      properties:
        source_workspace:
          type: string
          description: |
            The source workspace to compare from. Defaults to the workspace
            associated with the request's API key when omitted. Accepts either
            a workspace UUID or one of the aliases `greynoise` (GreyNoise
            production / GoG), `community` (community dataset; requires the
            community dataset entitlement), or `personal` (the caller's
            workspace).
        target_workspace:
          type: string
          description: |
            The target workspace to compare against. Accepts a workspace UUID or
            one of the aliases `greynoise`, `community`, or `personal`. Defaults
            to the GreyNoise production (GoG) workspace when omitted.
        query:
          type: string
          description: GNQL query string to filter IPs in both workspaces.
          example: "classification:malicious"
        size:
          type: integer
          description: |
            Number of IP diff results to return per page. Defaults to 10, maximum
            1000. Larger result sets are retrieved by paging with `next_token`.
          default: 10
          minimum: 1
          maximum: 1000
        next_token:
          type: string
          description: Pagination token from a previous response to fetch the next page of results.
        require_both_workspaces:
          type: boolean
          description: When true, only return IPs that exist in both workspaces.
          default: false
        ips_from_source:
          type: boolean
          description: When true, only return IPs sourced from the source workspace.
          default: false
    WorkspaceDiffResult:
      type: object
      properties:
        ip_diffs:
          type: array
          items:
            $ref: '#/components/schemas/IPDiffResult'
        source_workspace_name:
          type: string
          description: Display name of the source workspace.
        target_workspace_name:
          type: string
          description: Display name of the target workspace.
        count:
          type: integer
          description: Total number of IPs compared.
        next_token:
          type: string
          description: Token for paginating through additional results.
    IPDiffResult:
      type: object
      properties:
        ip:
          type: string
          description: The IP address being compared.
          example: "1.2.3.4"
        in_source_workspace:
          type: boolean
          description: Whether the IP exists in the source workspace.
        in_target_workspace:
          type: boolean
          description: Whether the IP exists in the target workspace.
        source_workspace_name:
          type: string
          description: Display name of the source workspace.
        target_workspace_name:
          type: string
          description: Display name of the target workspace.
        diff_result:
          $ref: '#/components/schemas/DiffResult'
    DiffResult:
      type: object
      description: Field-level differences between the IP record in each workspace.
      properties:
        differences:
          type: array
          items:
            $ref: '#/components/schemas/Difference'
        identical:
          type: boolean
          description: True if the records are identical across both workspaces.
        truncated:
          type: array
          description: |
            Paths whose `differences` are a prefix rather than the complete set.
            A path is capped at 1000 entries per operation; the comparison
            itself is exact, only the listing is cut. Absent when nothing was
            truncated. Use the per-IP diff to retrieve a capped path in full.
          items:
            $ref: '#/components/schemas/DiffTruncation'
    DiffTruncation:
      type: object
      properties:
        path:
          type: string
          description: JSON path whose differences were capped.
          example: "raw_data.scan"
        operation:
          type: string
          enum:
            - added
            - removed
            - modified
          description: The operation whose entries were capped for this path.
        returned:
          type: integer
          description: Number of entries present in `differences` for this path and operation.
          example: 1000
        total:
          type: integer
          description: Total number of differing entries found for this path and operation.
          example: 65534
    Difference:
      type: object
      properties:
        path:
          type: string
          description: JSON path of the differing field.
          example: "metadata.organization"
        operation:
          type: string
          enum:
            - added
            - removed
            - modified
          description: The type of change detected.
        source_value:
          description: The value in the source workspace (present for removed and modified operations).
        target_value:
          description: The value in the target workspace (present for added and modified operations).
    StatsDiffRequest:
      type: object
      required:
        - query
      properties:
        source_workspace:
          type: string
          description: |
            The source workspace. Defaults to the workspace associated with the
            request's API key when omitted. Accepts either a workspace UUID or
            one of the aliases `greynoise` (GreyNoise production / GoG),
            `community` (community dataset; requires the community dataset
            entitlement), or `personal` (the caller's workspace).
        target_workspace:
          type: string
          description: |
            The target workspace. Accepts a workspace UUID or one of the aliases
            `greynoise`, `community`, or `personal`. Defaults to the GreyNoise
            production (GoG) workspace when omitted.
        query:
          type: string
          description: GNQL query string to filter IPs in both workspaces.
          example: "classification:malicious"
        count:
          type: integer
          description: Number of items to return per stats category. Defaults to 10.
          default: 10
    StatsDiffResult:
      type: object
      properties:
        source_workspace_name:
          type: string
          description: Display name of the source workspace.
        target_workspace_name:
          type: string
          description: Display name of the target workspace.
        source_stats:
          $ref: '#/components/schemas/CompareStats'
        target_stats:
          $ref: '#/components/schemas/CompareStats'
        diff:
          $ref: '#/components/schemas/StatsDiff'
        source_count:
          type: integer
          format: int64
          description: Total number of IPs matching the query in the source workspace.
        target_count:
          type: integer
          format: int64
          description: Total number of IPs matching the query in the target workspace.
    CompareStats:
      type: object
      description: Aggregated statistics for a workspace query result.
      properties:
        classifications:
          type: array
          items:
            $ref: '#/components/schemas/StatsClassificationItem'
        spoofable:
          type: array
          items:
            $ref: '#/components/schemas/StatsSpoofableItem'
        organizations:
          type: array
          items:
            $ref: '#/components/schemas/StatsOrganizationItem'
        actors:
          type: array
          items:
            $ref: '#/components/schemas/StatsActorItem'
        countries:
          type: array
          items:
            $ref: '#/components/schemas/StatsCountryItem'
        source_countries:
          type: array
          items:
            $ref: '#/components/schemas/StatsCountryItem'
        tags:
          type: array
          items:
            $ref: '#/components/schemas/StatsTagItem'
        operating_systems:
          type: array
          items:
            $ref: '#/components/schemas/StatsOperatingSystemItem'
        categories:
          type: array
          items:
            $ref: '#/components/schemas/StatsCategoryItem'
        asns:
          type: array
          items:
            $ref: '#/components/schemas/StatsASNItem'
    StatsDiff:
      type: object
      description: Diff of stats between source and target workspaces.
      properties:
        classifications:
          $ref: '#/components/schemas/StatsDiffCategory'
        spoofable:
          $ref: '#/components/schemas/StatsDiffCategory'
        organizations:
          $ref: '#/components/schemas/StatsDiffCategory'
        actors:
          $ref: '#/components/schemas/StatsDiffCategory'
        countries:
          $ref: '#/components/schemas/StatsDiffCategory'
        source_countries:
          $ref: '#/components/schemas/StatsDiffCategory'
        tags:
          $ref: '#/components/schemas/StatsDiffCategory'
        categories:
          $ref: '#/components/schemas/StatsDiffCategory'
        asns:
          $ref: '#/components/schemas/StatsDiffCategory'
    StatsDiffCategory:
      type: object
      properties:
        in_both:
          type: array
          items:
            $ref: '#/components/schemas/StatsDiffItem'
        unique_to_source:
          type: array
          description: Items that only appear in the source workspace results.
          items:
            type: object
        unique_to_target:
          type: array
          description: Items that only appear in the target workspace results.
          items:
            type: object
    StatsDiffItem:
      type: object
      properties:
        key:
          type: string
          description: The category value (e.g., country name, organization name).
        source_count:
          type: integer
          description: Count in the source workspace.
        target_count:
          type: integer
          description: Count in the target workspace.
        delta:
          type: integer
          description: Difference between source and target counts (source - target).
    StatsClassificationItem:
      type: object
      properties:
        classification:
          type: string
        count:
          type: integer
    StatsSpoofableItem:
      type: object
      properties:
        spoofable:
          type: boolean
        count:
          type: integer
    StatsOrganizationItem:
      type: object
      properties:
        organization:
          type: string
        count:
          type: integer
    StatsActorItem:
      type: object
      properties:
        actor:
          type: string
        count:
          type: integer
    StatsCountryItem:
      type: object
      properties:
        country:
          type: string
        count:
          type: integer
    StatsTagItem:
      type: object
      properties:
        tag:
          type: string
        id:
          type: string
          format: uuid
        count:
          type: integer
    StatsOperatingSystemItem:
      type: object
      properties:
        operating_system:
          type: string
        count:
          type: integer
    StatsCategoryItem:
      type: object
      properties:
        category:
          type: string
        count:
          type: integer
    StatsASNItem:
      type: object
      properties:
        asn:
          type: string
        count:
          type: integer
    UniqueIPsStartRequest:
      type: object
      required:
        - query
      properties:
        source_workspace:
          type: string
          description: |
            The source workspace. IPs unique to this workspace will be
            discovered. Defaults to the workspace associated with the request's
            API key when omitted. Accepts either a workspace UUID or one of the
            aliases `greynoise` (GreyNoise production / GoG), `community`
            (community dataset; requires the community dataset entitlement), or
            `personal` (the caller's workspace).
        target_workspace:
          type: string
          description: |
            The target workspace to compare against. Accepts a workspace UUID or
            one of the aliases `greynoise`, `community`, or `personal`. Defaults
            to the GreyNoise production (GoG) workspace when omitted.
        query:
          type: string
          description: GNQL query string to filter IPs.
          example: "classification:malicious"
    UniqueIPsStartResponse:
      type: object
      properties:
        job_id:
          type: string
          description: The ID of the started job. Use this to poll for status.
    UniqueIPsStatusResponse:
      type: object
      properties:
        job_id:
          type: string
          description: The job ID.
        status:
          type: string
          enum:
            - pending
            - running
            - complete
            - error
          description: Current status of the job.
        processed:
          type: integer
          description: Number of IPs processed so far.
        total:
          type: integer
          description: Total number of IPs to process.
        found:
          type: integer
          description: Number of unique IPs found so far.
        unique_ips:
          type: array
          items:
            type: string
          description: List of unique IPs found (present when job is complete).
        has_more:
          type: boolean
          description: Whether more results are available beyond the current page.
        limit:
          type: integer
          description: The limit used for this response page.
        offset:
          type: integer
          description: The offset used for this response page.
        error:
          type: string
          description: Error message if the job failed.
    CallbackFilterFields:
      type: object
      description: Common filter fields for callback IP queries.
      properties:
        is_stage_1:
          type: boolean
          description: |
            Filter by stage 1 status. true = file downloaded from this IP.
        is_stage_2:
          type: boolean
          description: |
            Filter by stage 2 status. true = suspected C2 based on VT/sandbox analysis.
        first_seen_after:
          type: string
          format: date
          description: Only include IPs first seen after this date (YYYY-MM-DD).
        first_seen_before:
          type: string
          format: date
          description: Only include IPs first seen before this date (YYYY-MM-DD).
        last_seen_after:
          type: string
          format: date
          description: Only include IPs last seen after this date (YYYY-MM-DD).
        last_seen_before:
          type: string
          format: date
          description: Only include IPs last seen before this date (YYYY-MM-DD).
        has_files:
          type: boolean
          description: If true, only include IPs with associated malware files. If false, only IPs without files.
        file_type:
          type: string
          description: Filter by file MIME type (e.g. "application/x-executable").
        file_name:
          type: string
          description: Filter by file name substring match.
        file_hash:
          type: string
          description: Filter by file SHA256 hash.
        scanner_ips:
          type: array
          items:
            type: string
          description: Filter to IPs associated with these scanner IPs.
        ips:
          type: array
          items:
            type: string
          description: Filter to this specific set of callback IPs.
    CallbackListIPsRequest:
      allOf:
        - $ref: '#/components/schemas/CallbackFilterFields'
        - type: object
          properties:
            page:
              type: integer
              minimum: 0
              default: 0
              description: Zero-indexed page number.
            page_size:
              type: integer
              minimum: 1
              maximum: 100
              default: 20
              description: Number of results per page (1-100).
    CallbackFileResponse:
      type: object
      description: Malware file associated with a callback IP.
      properties:
        sha256:
          type: string
          description: SHA-256 hash of the file.
          example: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
        md5:
          type: string
          description: MD5 hash of the file.
        sha1:
          type: string
          description: SHA-1 hash of the file.
        threat_name:
          type: string
          description: VirusTotal threat name, if available.
          example: Trojan.GenericKD.46542
        vt_detection_count:
          type: integer
          description: Number of VirusTotal engines that flagged the file as malicious.
          example: 42
        vt_engine_count:
          type: integer
          description: Total number of VirusTotal engines that scanned the file.
          example: 71
        file_name:
          type: string
          description: Original file name, if known.
          example: payload.bin
        size:
          type: integer
          format: int64
          description: File size in bytes.
          example: 24576
        type:
          type: string
          description: File MIME type.
          example: application/x-executable
    CallbackFileSummary:
      type: object
      description: Lightweight file reference returned with IP list results.
      properties:
        sha256:
          type: string
          description: SHA-256 hash of the file.
        file_name:
          type: string
          description: Original file name.
        type:
          type: string
          description: File MIME type.
        vt_threat_name:
          type: string
          description: VirusTotal threat name.
        vt_detection_count:
          type: integer
          description: Number of VirusTotal engines that flagged the file.
    CallbackIPDetailResponse:
      type: object
      description: Detailed information about a single callback IP.
      properties:
        ip:
          type: string
          description: The callback IP address.
          example: 198.51.100.42
        source_workspaces:
          type: array
          items:
            type: string
          description: |
            Labeled workspace sources where this IP was observed.
            Values are "GreyNoise", "Personal", or "Community".
          example: ["GreyNoise", "Personal"]
        attack_stage:
          type: integer
          minimum: 0
          maximum: 2
          nullable: true
          deprecated: true
          description: Deprecated. Use is_stage_1 / is_stage_2 instead.
        is_stage_1:
          type: boolean
          description: Whether a file was successfully downloaded from this IP (stage 1).
        is_stage_2:
          type: boolean
          description: Whether this IP is suspected C2 based on VT/sandbox analysis (stage 2).
        is_riot:
          type: boolean
          description: Whether this IP belongs to a known benign service (RIOT).
        riot_trust_level:
          type: integer
          minimum: 1
          maximum: 3
          description: RIOT trust level (1-3), present only for RIOT IPs.
        first_seen:
          type: string
          nullable: true
          description: ISO 8601 timestamp of when this IP was first observed.
          example: "2025-03-01T00:00:00Z"
        last_seen:
          type: string
          nullable: true
          description: ISO 8601 timestamp of when this IP was most recently observed.
          example: "2025-03-15T12:30:00Z"
        scanner_ips:
          type: array
          items:
            type: string
          description: Scanner IPs that delivered payloads referencing this callback IP.
          example: ["203.0.113.7", "192.0.2.99"]
        scanner_count:
          type: integer
          description: Number of distinct scanners associated with this IP.
          example: 5
        file_count:
          type: integer
          description: Number of malware files associated with this IP.
          example: 3
        active_files:
          type: array
          items:
            $ref: '#/components/schemas/CallbackFileResponse'
          description: Malware files associated with this callback IP.
        enrichment:
          $ref: '#/components/schemas/CallbackIPEnrichment'
    CallbackIPEnrichment:
      type: object
      description: Geolocation and network enrichment for a callback IP.
      properties:
        asn:
          type: string
          example: AS4837
        org:
          type: string
          example: CHINA UNICOM China169 Backbone
        city:
          type: string
          example: Qingdao
        region:
          type: string
          example: Shandong
        country:
          type: string
          example: China
        country_code:
          type: string
          example: CN
        latitude:
          type: number
          example: 36.0649
        longitude:
          type: number
          example: 120.3804
        is_tor:
          type: boolean
        route:
          type: string
          example: 119.176.0.0/12
        type:
          type: string
          example: isp
        domain:
          type: string
          example: chinaunicom.cn
        rdns:
          type: string
    CallbackIPSummary:
      type: object
      description: Summary representation of a callback IP in list results.
      properties:
        ip:
          type: string
          example: 198.51.100.42
        source_workspaces:
          type: array
          items:
            type: string
          example: ["GreyNoise"]
        attack_stage:
          type: integer
          minimum: 0
          maximum: 2
          nullable: true
          deprecated: true
          description: Deprecated. Use is_stage_1 / is_stage_2 instead.
        is_stage_1:
          type: boolean
        is_stage_2:
          type: boolean
        is_riot:
          type: boolean
        riot_trust_level:
          type: integer
          minimum: 1
          maximum: 3
          description: RIOT trust level (1-3), present only for RIOT IPs.
        first_seen:
          type: string
          nullable: true
        last_seen:
          type: string
          nullable: true
        scanner_ips:
          type: array
          items:
            type: string
        scanner_count:
          type: integer
        file_count:
          type: integer
        files:
          type: array
          items:
            $ref: '#/components/schemas/CallbackFileSummary'
          description: Lightweight file references for this IP.
        enrichment:
          $ref: '#/components/schemas/CallbackIPEnrichment'
    CallbackListIPsResponse:
      type: object
      description: Paginated list of callback IPs.
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/CallbackIPSummary'
        total:
          type: integer
          description: Total number of matching callback IPs.
          example: 142
        page:
          type: integer
          description: Current page number (zero-indexed).
          example: 0
        page_size:
          type: integer
          description: Number of results per page.
          example: 20
    CallbackThreatNameStat:
      type: object
      properties:
        threat_name:
          type: string
          description: VirusTotal threat name.
          example: Trojan.GenericKD.46542
        file_count:
          type: integer
          description: Number of files with this threat name.
          example: 12
        ip_count:
          type: integer
          description: Number of IPs associated with files of this threat name.
          example: 8
    CallbackOverviewResponse:
      type: object
      description: Aggregate statistics for callback IPs matching the given filters.
      properties:
        total_ips:
          type: integer
          description: Total number of callback IPs.
          example: 142
        stage_1_ips:
          type: integer
          description: Number of stage 1 (initial payload delivery) IPs.
          example: 98
        stage_2_ips:
          type: integer
          description: Number of stage 2 (post-exploitation callback) IPs.
          example: 44
        unconfirmed_ips:
          type: integer
          description: Number of IPs not yet confirmed as stage 1 or stage 2.
          example: 0
        total_files:
          type: integer
          description: Total number of associated malware files.
          example: 230
        files_with_vt:
          type: integer
          description: Files that have been analyzed by VirusTotal.
          example: 180
        files_without_vt:
          type: integer
          description: Files pending VirusTotal analysis.
          example: 50
        total_cross_refs:
          type: integer
          description: Total IP-to-file associations.
          example: 312
        total_scanner_links:
          type: integer
          description: Total scanner-to-callback-IP associations.
          example: 456
        ips_with_files:
          type: integer
          description: Number of IPs that have associated files.
          example: 110
        ips_without_files:
          type: integer
          description: Number of IPs with no associated files.
          example: 32
        ips_with_scanners:
          type: integer
          description: Number of IPs with known scanner associations.
          example: 130
        ips_without_scanners:
          type: integer
          description: Number of IPs with no known scanner associations.
          example: 12
        distinct_scanners:
          type: integer
          description: Total number of unique scanner IPs.
          example: 89
        riot_level_1_ips:
          type: integer
          description: Number of IPs at RIOT trust level 1.
        riot_level_2_ips:
          type: integer
          description: Number of IPs at RIOT trust level 2.
        riot_level_3_ips:
          type: integer
          description: Number of IPs at RIOT trust level 3.
        not_riot_ips:
          type: integer
          description: Number of IPs not classified as RIOT.
        top_threat_names:
          type: array
          items:
            $ref: '#/components/schemas/CallbackThreatNameStat'
          description: Top VirusTotal threat names by file count.
    Session:
      type: object
      description: |
        A network session captured by GreyNoise sensors. Sessions contain network
        flow data, protocol details, and enrichment metadata. The full set of fields
        is dynamic and can be discovered via the `/v3/sessions/fields` endpoint.
      additionalProperties: true
      properties:
        _id:
          type: string
          description: Unique session identifier.
          example: "2505-abcdef123456"
        firstPacket:
          type: string
          format: date-time
          description: Timestamp of the first packet in the session.
          example: "2025-01-15T10:30:00Z"
        lastPacket:
          type: string
          format: date-time
          description: Timestamp of the last packet in the session.
          example: "2025-01-15T10:30:05Z"
        source.ip:
          type: string
          description: Source IP address.
          example: "203.0.113.45"
        source.port:
          type: integer
          description: Source port number.
          example: 54321
        destination.ip:
          type: string
          description: Destination IP address.
          example: "198.51.100.10"
        destination.port:
          type: integer
          description: Destination port number.
          example: 443
        source.bytes:
          type: integer
          description: Total bytes sent from source.
          example: 1024
        source.packets:
          type: integer
          description: Total packets sent from source.
          example: 10
        destination.bytes:
          type: integer
          description: Total bytes sent from destination.
          example: 2048
        destination.packets:
          type: integer
          description: Total packets sent from destination.
          example: 8
        classification:
          type: string
          description: GreyNoise classification of the source IP.
          example: "malicious"
    SessionsResponse:
      type: object
      properties:
        sessions:
          type: array
          description: Array of session objects matching the query.
          items:
            '$ref': '#/components/schemas/Session'
        total:
          type: integer
          description: Total number of sessions matching the query.
          example: 150
        pagination:
          $ref: '#/components/schemas/SessionPagination'
        request_metadata:
          $ref: '#/components/schemas/SessionRequestMetadata'
    SessionPagination:
      type: object
      properties:
        page:
          type: integer
          description: Current page number.
          example: 1
        page_size:
          type: integer
          description: Number of results per page.
          example: 25
        sort_by:
          type: string
          description: Field used for sorting.
          example: lastPacket
        sort_desc:
          type: boolean
          description: Whether results are sorted in descending order.
          example: true
    SessionRequestMetadata:
      type: object
      properties:
        start_time:
          type: string
          description: Start time of the query range.
          example: "2025-01-01T00:00:00Z"
        end_time:
          type: string
          description: End time of the query range.
          example: "2025-01-07T23:59:59Z"
        query:
          type: string
          description: The Lucene query string used.
    SessionFieldsResponse:
      type: object
      properties:
        fields:
          type: array
          description: List of available session fields.
          items:
            $ref: '#/components/schemas/SessionField'
    SessionField:
      type: object
      properties:
        value:
          type: string
          description: The field identifier used in queries.
          example: source.ip
        label:
          type: string
          description: Human-readable label for the field.
          example: Source IP
        description:
          type: string
          description: Description of the field.
          example: The source IP address of the session.
        type:
          type: string
          description: Data type of the field.
          example: ip
        group:
          type: string
          description: Logical grouping of the field.
          example: source
        sortable:
          type: boolean
          description: Whether the field can be used for sorting.
          example: true
    SessionCountsResponse:
      type: object
      properties:
        items:
          type: array
          description: Aggregated count items.
          items:
            $ref: '#/components/schemas/SessionCountItem'
        total:
          type: integer
          description: Total number of sessions matching the query.
          example: 5000
        request_metadata:
          type: object
          properties:
            start_time:
              type: string
            end_time:
              type: string
            query:
              type: string
            fields:
              type: array
              items:
                type: string
    SessionCountItem:
      type: object
      properties:
        label:
          type: string
          description: The value of the aggregated field.
          example: "192.168.1.1"
        count:
          type: integer
          description: Number of sessions for this value.
          example: 42
        children:
          type: array
          description: Nested aggregation results for multi-field queries.
          items:
            $ref: '#/components/schemas/SessionCountItem'
    SessionConnectionsResponse:
      type: object
      properties:
        nodes:
          type: array
          description: Network nodes in the connection graph.
          items:
            $ref: '#/components/schemas/SessionConnectionNode'
        links:
          type: array
          description: Connections between nodes.
          items:
            $ref: '#/components/schemas/SessionConnectionLink'
        total:
          type: integer
          description: Total number of connections.
          example: 250
        request_metadata:
          type: object
          properties:
            start_time:
              type: string
            end_time:
              type: string
            query:
              type: string
            src_field:
              type: string
            dest_field:
              type: string
            max_nodes:
              type: integer
            min_connections:
              type: integer
    SessionConnectionNode:
      type: object
      properties:
        id:
          type: string
          description: Node identifier (field value).
          example: "192.168.1.1"
        type:
          type: string
          description: Whether this is a source or destination node.
          example: source
    SessionConnectionLink:
      type: object
      properties:
        source:
          type: string
          description: Source node identifier.
          example: "192.168.1.1"
        target:
          type: string
          description: Target node identifier.
          example: "10.0.0.1"
        value:
          type: integer
          description: Number of connections between source and target.
          example: 15
    SessionTimeseriesResponse:
      type: object
      properties:
        timeseries:
          type: array
          description: Timeseries data points (when no field grouping is used).
          items:
            $ref: '#/components/schemas/SessionTimeseriesPoint'
        items:
          type: array
          description: Grouped timeseries data (when field grouping is used).
          items:
            $ref: '#/components/schemas/SessionTimeseriesItem'
        total:
          type: integer
          description: Total number of sessions in the time range.
          example: 1000
        request_metadata:
          type: object
          properties:
            start_time:
              type: string
            end_time:
              type: string
            query:
              type: string
            field:
              type: string
            interval:
              type: string
    SessionTimeseriesPoint:
      type: object
      properties:
        timestamp:
          type: string
          description: Timestamp for this data point.
          example: "2025-01-01T00:00:00Z"
        count:
          type: integer
          description: Number of sessions in this time bucket.
          example: 42
    SessionTimeseriesItem:
      type: object
      properties:
        label:
          type: string
          description: The field value for this group.
          example: "192.168.1.1"
        count:
          type: integer
          description: Total sessions for this group.
          example: 150
        timeseries:
          type: array
          description: Timeseries data for this group.
          items:
            $ref: '#/components/schemas/SessionTimeseriesPoint'
    BSITrustResponse:
      type: object
      properties:
        date:
          type: string
          description: |
            The snapshot date the response was rendered against. Echoes the
            `date` query parameter when supplied, or the literal string `now`
            for live queries.
          example: now
        source:
          type: string
          description: |
            Where the data was sourced from. `db` indicates current BSI
            IP data; `s3` indicates historical data.
          example: db
        stats:
          type: object
          properties:
            trust_levels:
              type: array
              items:
                type: object
                properties:
                  trust_level:
                    type: string
                    example: "1"
                  ip_count:
                    type: integer
                    format: int64
                    example: 42315
                  cidr_count:
                    type: integer
                    format: int64
                    example: 218
    BSICompanyResponse:
      type: object
      properties:
        date:
          type: string
          example: now
        source:
          type: string
          example: db
        stats:
          type: object
          properties:
            companies:
              type: array
              items:
                type: object
                properties:
                  name:
                    type: string
                    example: Cloudflare
                  category:
                    type: string
                    example: cdn
                  trust_level:
                    type: string
                    example: "1"
                  ip_count:
                    type: integer
                    format: int64
                    example: 18402
                  cidr_count:
                    type: integer
                    format: int64
                    example: 91
    BSICategoryResponse:
      type: object
      properties:
        date:
          type: string
          example: now
        source:
          type: string
          example: db
        stats:
          type: object
          properties:
            categories:
              type: array
              items:
                type: object
                properties:
                  category:
                    type: string
                    example: cdn
                  ip_count:
                    type: integer
                    format: int64
                    example: 73221
                  cidr_count:
                    type: integer
                    format: int64
                    example: 327
    BSILookupMatch:
      type: object
      properties:
        cidr:
          type: string
          example: 8.8.8.0/24
        name:
          type: string
          example: Google
        category:
          type: string
          example: search_engine
        trust_level:
          type: string
          example: "1"
        precedence:
          type: integer
          example: 10
    BSILookupResponse:
      type: object
      properties:
        ip:
          type: string
          example: 8.8.8.8
        matches:
          type: array
          description: |
            Providers whose CIDR(s) contain the queried IP, in ascending
            precedence order. Empty if the IP is not in BSI.
          items:
            $ref: '#/components/schemas/BSILookupMatch'
    BSIBulkRequest:
      type: object
      required:
        - ips
      properties:
        ips:
          type: array
          maxItems: 1000
          items:
            type: string
            example: 8.8.8.8
    BSIBulkResponse:
      type: object
      properties:
        results:
          type: array
          description: |
            One entry per requested IP, in request order. Each entry
            mirrors the single-lookup response shape.
          items:
            type: object
            properties:
              ip:
                type: string
                example: 8.8.8.8
              matches:
                type: array
                items:
                  $ref: '#/components/schemas/BSILookupMatch'
    BlocklistResponse:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Blocklist ID.
          example: 550e8400-e29b-41d4-a716-446655440000
        workspace_id:
          type: string
          format: uuid
          description: Owning workspace ID.
          example: 7c9e6679-7425-40de-944b-e07fc1f90ae7
        query:
          type: string
          description: GNQL query that defines the blocklist.
          example: "classification:malicious last_seen:1d"
        query_hash:
          type: string
          description: Hash of the GNQL query.
        name:
          type: string
          nullable: true
          description: Human-readable name for the blocklist.
          example: Malicious scanners
        ip_limit:
          type: integer
          nullable: true
          description: Maximum number of IPs the blocklist will return.
          example: 1000
        entitlement_level:
          type: string
          description: The entitlement level used when the blocklist was created.
        enabled:
          type: boolean
          description: Whether the blocklist is active.
          example: true
        last_ip_count:
          type: integer
          description: Number of IPs returned on last refresh.
          example: 427
        query_workspace_id:
          type: string
          format: uuid
          description: Workspace whose data the query runs against, when different from the owning workspace.
        token:
          type: string
          description: Opaque token for unauthenticated blocklist access. Returned on list, create, and single-get operations.
        created_at:
          type: string
          format: date-time
          description: Creation timestamp.
        updated_at:
          type: string
          format: date-time
          description: Last update timestamp.
    CreateBlocklistRequest:
      type: object
      required:
        - query
      properties:
        query:
          type: string
          description: GNQL query that defines the blocklist.
          example: "classification:malicious last_seen:1d"
        name:
          type: string
          description: Human-readable name for the blocklist.
          example: Malicious scanners
        ip_limit:
          type: integer
          nullable: true
          minimum: 1
          description: Maximum number of IPs to return. Must be greater than 0 when provided.
          example: 1000
        enabled:
          type: boolean
          description: Whether the blocklist should be active. Defaults to true if omitted.
          default: true
        query_workspace_id:
          type: string
          format: uuid
          description: Workspace whose data the query runs against. Requires the GNQL Diff entitlement.
    UpdateBlocklistRequest:
      type: object
      required:
        - query
      properties:
        query:
          type: string
          description: GNQL query that defines the blocklist.
          example: "classification:malicious last_seen:1d"
        name:
          type: string
          description: Human-readable name for the blocklist.
          example: Malicious scanners (updated)
        ip_limit:
          type: integer
          nullable: true
          minimum: 1
          description: Maximum number of IPs to return. Must be greater than 0 when provided.
          example: 500
        enabled:
          type: boolean
          description: Whether the blocklist should be active.
        query_workspace_id:
          type: string
          format: uuid
          description: Workspace whose data the query runs against. Requires the GNQL Diff entitlement.
    ListBlocklistsResponse:
      type: object
      properties:
        blocklists:
          type: array
          items:
            $ref: '#/components/schemas/BlocklistResponse'
        total:
          type: integer
          description: Total number of blocklists in the workspace.
          example: 3
        limit:
          type: integer
          description: Page size used for the request.
          example: 10
        offset:
          type: integer
          description: Offset used for the request.
          example: 0
    BlocklistIPsResponse:
      type: object
      properties:
        ips:
          type: array
          description: IP addresses matching the blocklist query.
          items:
            type: string
            example: 198.51.100.42
    TacticsError:
      type: object
      properties:
        error:
          type: string
      required:
        - error
    TacticsSearchRequest:
      type: object
      description: >
        Detection search filters. Every field is optional; send `{}` to match
        all detections in the workspace. Fields combine with AND, except
        `techniques` and `tactics`, which combine with each other using OR.
      properties:
        start_time:
          type: string
          format: date-time
          description: >
            Only return detections active at or after this time. When omitted
            no lower bound is applied and the search spans all retained
            history; supplying a bound is strongly recommended.
          example: '2026-07-29T00:00:00Z'
        end_time:
          type: string
          format: date-time
          description: >
            Only return detections active at or before this time. Defaults to
            the current time.
          example: '2026-07-30T00:00:00Z'
        sensor_id:
          type: string
          format: uuid
          description: Only return detections observed on this sensor.
        profile_id:
          type: string
          format: uuid
          description: Only return detections observed on this profile.
        profile_instance_id:
          type: string
          format: uuid
          description: Only return detections observed on this profile instance.
        rules:
          type: array
          description: >
            Only return detections that triggered at least one of these
            sensor rules.
          items:
            type: string
          example:
            - command_run
            - internet_access
        techniques:
          type: array
          description: >
            Only return detections carrying at least one of these MITRE ATT&CK
            technique IDs. Combines with `tactics` using OR.
          items:
            type: string
          example:
            - T1082
            - T1059.004
        tactics:
          type: array
          description: >
            Only return detections carrying at least one of these MITRE ATT&CK
            tactic IDs. Combines with `techniques` using OR.
          items:
            type: string
          example:
            - TA0007
            - TA0002
        process_name:
          type: string
          description: >
            Only return detections containing an event whose process has this
            name.
          example: curl
        process_parent:
          type: string
          description: >
            Only return detections containing an event whose parent process has
            this name.
          example: bash
        process_grandparent:
          type: string
          description: >
            Only return detections containing an event whose grandparent
            process has this name.
        process_great_grandparent:
          type: string
          description: >
            Only return detections containing an event whose great-grandparent
            process has this name.
        any_process:
          type: string
          description: >
            Only return detections containing an event where this name appears
            at any level of the process ancestry - the process itself, its
            parent, grandparent, or great-grandparent.
          example: wget
        ip:
          type: string
          description: >
            Only return detections whose session contacted this destination
            address. IPv4 only.
          example: 198.51.100.42
    TacticsSearchResponse:
      type: object
      properties:
        sessions:
          type: array
          description: >
            The page of matching detections, ordered newest first. Named
            `sessions` because a detection is an aggregated attacker session.
          items:
            $ref: '#/components/schemas/TacticsDetectionSummary'
        has_more:
          type: boolean
          description: Whether a further page of detections is available.
          example: true
        next_cursor:
          type: string
          description: >
            Opaque cursor for the next page. Pass it back as the `cursor` query
            parameter. Omitted when `has_more` is false.
          example: eyJ0IjoiMjAyNi0wNy0yOVQxNDoxMjowM1oiLCJzIjoiMDE5MWQ4YzQtOTk0Yy04ZmMyLWEwNzMtM2ZlM2NjYjRhMDFmIn0=
        total_count:
          type: integer
          format: int64
          description: >
            Total number of detections matching the filter, across all pages.
            Returned on the first page only (when no `cursor` was supplied).
          example: 248
        stats:
          type: object
          description: |
            Per-day MITRE ATT&CK technique histogram over the full filtered
            result set. Keys are UTC calendar dates (`YYYY-MM-DD`); each value
            maps a technique ID to the number of distinct detections in which
            that technique appeared on that day. Returned on the first page
            only.
          additionalProperties:
            type: object
            additionalProperties:
              type: integer
              format: int64
          example:
            '2026-07-29':
              T1082: 14
              T1059.004: 9
        tactic_stats:
          type: object
          description: |
            Per-day MITRE ATT&CK tactic histogram, in the same shape as
            `stats`. A detection carrying several techniques that map to the
            same tactic is counted once. Returned on the first page only.
          additionalProperties:
            type: object
            additionalProperties:
              type: integer
              format: int64
          example:
            '2026-07-29':
              TA0007: 21
              TA0002: 11
    TacticsDetectionSummary:
      type: object
      description: >
        An aggregated attacker session observed on one of your deployed
        sensors, as returned by the detection search endpoint.
      properties:
        session_id:
          type: string
          format: uuid
          description: >
            Detection identifier. Pass this value as `detection_id` when
            fetching the detection, its destination IPs, or its files.
          example: 0191d8c4-994c-8fc2-a073-3fe3ccb4a01f
        workspace_id:
          type: string
          format: uuid
          description: Workspace the detection belongs to.
        sensor_id:
          type: string
          format: uuid
          description: Sensor that observed the session.
        profile_id:
          type: string
          format: uuid
          description: Profile the session ran against.
        profile_instance_id:
          type: string
          format: uuid
          description: Specific profile instance the session ran against.
        start_time:
          type: string
          format: date-time
          description: Time of the first event in the session.
          example: '2026-07-29T14:12:03Z'
        end_time:
          type: string
          format: date-time
          description: Time of the last event in the session.
          example: '2026-07-29T14:31:47Z'
        initial_process_name:
          type: string
          description: Name of the first process observed in the session.
          example: bash
        initial_process_parent:
          type: string
          description: Parent of the first process observed in the session.
          example: sshd
        rules:
          type: array
          description: Distinct sensor rules the session triggered.
          items:
            type: string
          example:
            - command_run
            - internet_access
        techniques:
          type: array
          description: Distinct MITRE ATT&CK technique IDs observed in the session.
          items:
            type: string
          example:
            - T1082
            - T1059.004
        tactics:
          type: array
          description: >
            Distinct MITRE ATT&CK tactic IDs observed in the session, derived
            from its techniques.
          items:
            type: string
          example:
            - TA0007
            - TA0002
        commands:
          type: array
          description: |
            Commands run during the session.

            On the search endpoint this is a bounded preview: at most 40 of the
            session's **distinct** commands, in no particular order and not
            necessarily the earliest ones. Fetch the detection by ID for the
            complete sequence in chronological order, including repeats.
          items:
            type: string
          example:
            - uname -a
            - cat /etc/passwd
        executable_paths:
          type: array
          description: |
            Distinct paths of executables run during the session.

            On the search endpoint this is a bounded preview: at most 40 paths,
            an arbitrary subset with no total. Fetch the detection by ID for
            the complete set.
          items:
            type: string
          example:
            - /usr/bin/curl
        artifacts:
          type: array
          description: |
            Files the session created or modified.

            On the search endpoint this is a bounded preview: at most 40 files,
            an arbitrary subset with no total. On the get-detection endpoint the
            set is complete and paged by `artifacts_page` rather than truncated,
            so every path is reachable.
          items:
            $ref: '#/components/schemas/TacticsDetectionArtifact'
        interactive:
          type: boolean
          description: >
            Whether the session had a shell ancestor - a hands-on-keyboard
            session rather than automated activity.
          example: true
    TacticsDetection:
      description: >
        A single detection with its full command timeline and destination-IP
        summary, as returned by the get-detection endpoint.
      allOf:
        - $ref: '#/components/schemas/TacticsDetectionSummary'
        - type: object
          properties:
            command_count:
              type: integer
              format: int64
              description: >
                Total number of commands in the session, across every page of
                `commands`.
              example: 213
            commands_page:
              type: integer
              description: The 1-based command page contained in this response.
              example: 1
            commands_page_size:
              type: integer
              description: Number of commands per page.
              example: 500
            commands_has_more:
              type: boolean
              description: Whether a further page of commands is available.
              example: false
            command_events:
              type: array
              description: >
                Chronological command timeline for this page - the same slice
                as `commands`, annotated with each command's timestamp and the
                techniques it triggered. Not populated for sessions recorded
                before command-level technique labeling; those fall back to the
                flat `commands` list.
              items:
                $ref: '#/components/schemas/TacticsCommandEvent'
            artifact_count:
              type: integer
              format: int64
              description: >
                Total number of file paths touched by the session, across every
                page of `artifacts`.
              example: 37
            artifacts_page:
              type: integer
              description: The 1-based artifact page contained in this response.
              example: 1
            artifacts_page_size:
              type: integer
              description: Number of file paths per page.
              example: 500
            artifacts_has_more:
              type: boolean
              description: Whether a further page of file paths is available.
              example: false
            dest_ips:
              $ref: '#/components/schemas/TacticsDestinationIPCounts'
    TacticsCommandEvent:
      type: object
      description: One command in a detection's chronological timeline.
      properties:
        timestamp:
          type: string
          format: date-time
          description: When the command ran.
          example: '2026-07-29T14:12:09Z'
        command:
          type: string
          description: The command line that ran.
          example: cat /etc/passwd
        techniques:
          type: array
          description: MITRE ATT&CK technique IDs this command triggered.
          items:
            type: string
          example:
            - T1003.008
    TacticsDetectionArtifact:
      type: object
      description: A file the detection's session created or modified.
      properties:
        path:
          type: string
          description: Absolute path of the file on the profile instance.
          example: /tmp/.x/miner
        sha256:
          type: string
          description: >
            Reserved. Not currently populated on this surface - use the
            detection files endpoint for a file's hashes, size, and type.
          example: ''
    TacticsDestinationIPCounts:
      type: object
      description: >
        Counts of the unique remote IP addresses the session contacted, keyed
        by protocol and split by whether the address is internet-routable or
        lateral (RFC 1918 and loopback).
      properties:
        internet:
          type: object
          description: >
            Unique internet-routable destination IPs per protocol. Includes
            link-local (instance metadata) and multicast addresses.
          additionalProperties:
            type: integer
            format: int64
          example:
            tcp: 12
            udp: 3
        lateral:
          type: object
          description: >
            Unique RFC 1918 and loopback destination IPs per protocol.
          additionalProperties:
            type: integer
            format: int64
          example:
            tcp: 4
    TacticsDestinationIPsResponse:
      type: object
      properties:
        session_id:
          type: string
          format: uuid
          description: The detection the addresses belong to.
          example: 0191d8c4-994c-8fc2-a073-3fe3ccb4a01f
        ips:
          type: array
          description: One page of unique destination IP addresses.
          items:
            type: string
            example: 198.51.100.42
        page:
          type: integer
          description: The 1-based page contained in this response.
          example: 1
        more:
          type: boolean
          description: Whether a further page of addresses is available.
          example: false
    TacticsDetectionFile:
      type: object
      description: A file observed during a detection's session.
      properties:
        workspace_id:
          type: string
          format: uuid
          description: Workspace the file was observed in.
        sensor_id:
          type: string
          format: uuid
          description: Sensor that observed the file.
        profile_id:
          type: string
          format: uuid
          description: Profile the file was observed on.
        profile_instance_id:
          type: string
          format: uuid
          description: Profile instance the file was observed on.
        timestamp:
          type: string
          format: date-time
          description: When the file event was observed.
          example: '2026-07-29T14:18:22Z'
        path:
          type: string
          description: Absolute path of the file on the profile instance.
          example: /tmp/.x/miner
        operations:
          type: array
          description: File operations observed against this path.
          items:
            type: string
          example:
            - create
            - write
        file_type:
          type: string
          description: Detected file type.
          example: ELF 64-bit LSB executable
        file_size:
          type: integer
          format: int64
          description: File size in bytes.
          example: 1843200
        tlsh_hash:
          type: string
          description: TLSH fuzzy hash of the file contents.
        md5_hash:
          type: string
          description: MD5 hash of the file contents.
        sha256_hash:
          type: string
          description: SHA-256 hash of the file contents.
        storage_type:
          type: string
          description: >
            Where the file contents are stored. `inline` contents are returned
            directly by the host artifact content endpoint; `s3` contents are
            returned as a presigned link.
          enum:
            - inline
            - s3
        container_id:
          type: string
          nullable: true
          description: Container the file was observed in, when applicable.
        container_name:
          type: string
          nullable: true
          description: Container name, when applicable.
        container_image:
          type: string
          nullable: true
          description: Container image, when applicable.
        host_session_uid:
          type: string
          format: uuid
          description: >
            The detection this file belongs to - the same value as the
            `detection_id` used to request it.
        process_uid:
          type: string
          format: uuid
          description: Identifier of the process that touched the file.
        ref_id:
          type: string
          format: uuid
          nullable: true
          description: Internal reference identifier for the stored file.
        correlated:
          type: boolean
          description: >
            Whether the file event was successfully correlated to the process
            that produced it.
          example: true
        enrichment_status:
          type: string
          description: >
            File-intelligence enrichment status. Always empty on this endpoint;
            enrichment is not returned with a detection's file list.
          example: ''
    TacticsHostArtifactContentRequest:
      type: object
      properties:
        path:
          type: string
          description: >
            Absolute path of the file on the profile instance. The most
            recently observed version of the file at this path is returned.
          example: /tmp/.x/miner
      required:
        - path
    TacticsHostArtifactContent:
      type: object
      description: >
        The contents of a single observed file. Exactly one of `contents` (when
        `type` is `inline`) or `url` (when `type` is `link`) is populated.
      properties:
        type:
          type: string
          description: How the contents are delivered.
          enum:
            - inline
            - link
          example: inline
        contents:
          type: string
          format: byte
          description: >
            Base64-encoded file contents. Present when `type` is `inline`.
        url:
          type: string
          description: >
            Short-lived presigned download URL. Present when `type` is `link`.
        expires_at:
          type: string
          format: date-time
          description: When the presigned URL expires. Present when `type` is `link`.
        path:
          type: string
          description: Absolute path of the file on the profile instance.
          example: /tmp/.x/miner
        sha256_hash:
          type: string
          description: SHA-256 hash of the file contents.
        file_size:
          type: integer
          format: int64
          description: File size in bytes.
          example: 1843200
