{
  "openapi": "3.0.3",
  "info": {
    "title": "Signalis IOC Feed API",
    "version": "1.0.0",
    "description": "Machine-readable feed of attacker-role indicators extracted from public CTI reporting. One endpoint, cursor-paginated, API-key authenticated. Indicators are returned refanged (usable) — the examples in this document are defanged for safe display. Requires a Pro or Team plan (real-time); the free tier is web-only and 7-day delayed. Keys: https://signalis.watch/keys · Docs: https://signalis.watch/docs/api"
  },
  "servers": [
    { "url": "https://api.signalis.watch", "description": "Production" }
  ],
  "security": [{ "bearerAuth": [] }, { "apiKeyHeader": [] }],
  "paths": {
    "/api/feed/v1/iocs": {
      "get": {
        "summary": "List attacker-role IOCs",
        "operationId": "listIocs",
        "description": "Returns attacker-role indicators, oldest-first, cursor-paginated. Only role=attacker is emitted (victim/example/incidental rows are liability or noise in a machine feed). Values are refanged and safe to import directly into a TIP/blocklist.",
        "parameters": [
          { "name": "limit", "in": "query", "required": false, "description": "Page size.", "schema": { "type": "integer", "minimum": 1, "maximum": 1000, "default": 100 } },
          { "name": "type", "in": "query", "required": false, "description": "Filter to a single IOC type.", "schema": { "type": "string", "enum": ["ip_v4", "ip_v6", "domain", "url", "email", "hash_md5", "hash_sha1", "hash_sha256", "hash_sha512", "cve", "registry_key", "file_path", "filename", "mutex", "asn", "user_agent", "yara_rule", "bitcoin_address", "monero_address", "ethereum_address"] } },
          { "name": "since", "in": "query", "required": false, "description": "Only indicators first seen at or after this RFC 3339 timestamp.", "schema": { "type": "string", "format": "date-time", "example": "2026-06-01T00:00:00Z" } },
          { "name": "cursor", "in": "query", "required": false, "description": "Opaque forward-pagination cursor. Pass the next_cursor from the previous page; do not construct it yourself.", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "A page of indicators.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IocPage" }, "example": {
              "data": [
                { "id": 84213, "value": "malicious-c2[.]example", "type": "domain", "role": "attacker", "report_id": 5012, "report_title": "Threat actor infrastructure roundup", "source_name": "Example CTI Vendor", "published_at": "2026-06-14T09:00:00Z", "severity_level": "high", "created_at": "2026-06-15T02:11:00Z" },
                { "id": 84214, "value": "hxxps://malicious-c2[.]example/gate[.]php", "type": "url", "role": "attacker", "report_id": 5012, "report_title": "Threat actor infrastructure roundup", "source_name": "Example CTI Vendor", "published_at": "2026-06-14T09:00:00Z", "severity_level": "high", "created_at": "2026-06-15T02:11:00Z" }
              ],
              "next_cursor": "eyJ0cyI6IjIwMjYtMDYtMTVUMDI6MTE6MDBaIiwiaWQiOjg0MjE0fQ",
              "has_more": true
            } } }
          },
          "401": { "description": "Missing or invalid API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "detail": "Invalid API key." } } } },
          "403": { "description": "The key's plan does not include API access, or the key is missing the feed:read scope.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "detail": "API access not included in your plan." } } } },
          "422": { "description": "Invalid parameter (e.g. a malformed cursor).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "detail": "Invalid cursor." } } } },
          "429": {
            "description": "Rate limit or monthly quota exceeded. Per-second limits include a Retry-After: 1 header; monthly-quota exhaustion returns 429 with no Retry-After (resets at the start of the month).",
            "headers": { "Retry-After": { "description": "Seconds to wait before retrying (per-second rate limit only).", "schema": { "type": "integer" } } },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "detail": "Rate limit exceeded." } } }
          },
          "503": {
            "description": "The feed is temporarily paused (operator kill-switch, e.g. a data-quality incident). Honor Retry-After.",
            "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer" } } },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "detail": "The IOC feed is temporarily paused." } } }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "description": "Send the key as: Authorization: Bearer sig_live_..." },
      "apiKeyHeader": { "type": "apiKey", "in": "header", "name": "X-API-Key", "description": "Alternatively: X-API-Key: sig_live_..." }
    },
    "schemas": {
      "Ioc": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "description": "Stable indicator id." },
          "value": { "type": "string", "description": "The indicator, refanged (usable). Defanged in these docs for display only." },
          "type": { "type": "string", "description": "One of the IOC types (see the type parameter enum)." },
          "role": { "type": "string", "enum": ["attacker"], "description": "Always 'attacker' in v1." },
          "report_id": { "type": "integer", "description": "Source report id (report detail is a member feature)." },
          "report_title": { "type": "string", "nullable": true },
          "source_name": { "type": "string", "description": "Publisher of the source report." },
          "published_at": { "type": "string", "format": "date-time", "nullable": true, "description": "When the source published." },
          "severity_level": { "type": "string", "nullable": true, "description": "Model-assessed severity of the source report." },
          "created_at": { "type": "string", "format": "date-time", "description": "When Signalis first saw this indicator (the pagination sort key)." }
        },
        "required": ["id", "value", "type", "role", "report_id", "source_name", "created_at"]
      },
      "IocPage": {
        "type": "object",
        "properties": {
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/Ioc" } },
          "next_cursor": { "type": "string", "nullable": true, "description": "Pass to ?cursor= for the next page. null when has_more is false." },
          "has_more": { "type": "boolean", "description": "True if another page exists." }
        },
        "required": ["data", "next_cursor", "has_more"]
      },
      "Error": {
        "type": "object",
        "properties": { "detail": { "type": "string" } },
        "required": ["detail"]
      }
    }
  }
}
