{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "Infervo CLI API",
    "version": "1.0.0",
    "summary": "Deterministic martech stack detection over website runtime signals and public DNS.",
    "description": "Infervo scans a company website in a headless browser, fingerprints marketing, analytics, CRM/ESP, ads, ecommerce, payments, consent, and server-side tagging signals, then attributes email infrastructure from public DNS. The scan result path is deterministic and does not use an LLM.",
    "contact": {
      "name": "Infervo",
      "email": "hello@infervo.app",
      "url": "https://staging.infervo.app/agents"
    }
  },
  "servers": [
    {
      "url": "https://staging.infervo.app/api/cli",
      "description": "Staging CLI API"
    }
  ],
  "security": [
    {
      "bearerToken": []
    }
  ],
  "tags": [
    {
      "name": "Auth",
      "description": "Token verification."
    },
    {
      "name": "Projects",
      "description": "Project creation and project-scoped history."
    },
    {
      "name": "Scans",
      "description": "Single scans, reports, diffs, memos, and exports."
    },
    {
      "name": "Batches",
      "description": "Asynchronous CSV/list scan batches."
    },
    {
      "name": "Watches",
      "description": "Scheduled change alerts with signed webhooks."
    },
    {
      "name": "Compare",
      "description": "Latest-scan stack comparison matrices."
    }
  ],
  "paths": {
    "/whoami": {
      "get": {
        "tags": [
          "Auth"
        ],
        "operationId": "whoami",
        "summary": "Verify a personal access token",
        "description": "Returns the authenticated user, organization, and token scopes. Requires read scope.",
        "responses": {
          "200": {
            "description": "Authenticated principal.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Whoami"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/projects": {
      "get": {
        "tags": [
          "Projects"
        ],
        "operationId": "listProjects",
        "summary": "List projects",
        "description": "Lists projects in the token's organization. Requires read scope.",
        "responses": {
          "200": {
            "description": "Project list.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "required": [
                            "projects"
                          ],
                          "properties": {
                            "projects": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Project"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Projects"
        ],
        "operationId": "createProject",
        "summary": "Create or reuse a project",
        "description": "Creates a project, or returns the existing project when the slug already exists. Requires write scope.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "examples": [
                      "Competitors"
                    ]
                  },
                  "description": {
                    "type": "string",
                    "examples": [
                      "Stacks I track"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Existing project reused.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/ProjectMutation"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "201": {
            "description": "Project created.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/ProjectMutation"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/projects/{slug}/scans": {
      "get": {
        "tags": [
          "Projects",
          "Scans"
        ],
        "operationId": "listProjectScans",
        "summary": "List recent scans in one project",
        "description": "Lists recent scans for a project in the token's organization. Requires read scope.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Project scan list.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "required": [
                            "projectSlug",
                            "scans"
                          ],
                          "properties": {
                            "projectSlug": {
                              "type": "string"
                            },
                            "scans": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/ProjectScanListItem"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/scans": {
      "get": {
        "tags": [
          "Scans"
        ],
        "operationId": "listScans",
        "summary": "List recent scans",
        "description": "Lists recent scans across the token's organization, newest first. Requires read scope.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recent scans.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "required": [
                            "scans"
                          ],
                          "properties": {
                            "scans": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/ScanListItem"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Scans"
        ],
        "operationId": "runScan",
        "summary": "Run a synchronous martech scan",
        "description": "Renders one URL in a headless browser and returns the full report. Requires write scope. The request blocks while the page renders; set a client timeout of at least 120 seconds. Send Idempotency-Key on retries to avoid duplicate scans.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 160
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "examples": [
                      "shopify.com"
                    ]
                  },
                  "projectSlug": {
                    "type": "string",
                    "description": "Optional project slug. Defaults to the workspace's first project."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Full scan report.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Scan"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/scans/{id}": {
      "get": {
        "tags": [
          "Scans"
        ],
        "operationId": "getScan",
        "summary": "Get one scan report",
        "description": "Returns one scan and its normalized detection list. Requires read scope.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Full scan report.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Scan"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/scans/{id}/diff": {
      "get": {
        "tags": [
          "Scans"
        ],
        "operationId": "getScanDiff",
        "summary": "Diff a scan against the prior scan of the same domain",
        "description": "Returns added, removed, and unchanged tools versus the previous done scan for the same project and domain. Requires read scope.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Scan diff.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/ScanDiff"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/scans/{id}/memo": {
      "post": {
        "tags": [
          "Scans"
        ],
        "operationId": "generateScanMemo",
        "summary": "Generate or return the cached AI memo",
        "description": "Generates an optional analyst memo over the detected vendor names only. The deterministic scan result is unchanged. Requires write scope.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "AI memo.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "required": [
                            "memo",
                            "cached"
                          ],
                          "properties": {
                            "memo": {
                              "type": "string"
                            },
                            "cached": {
                              "type": "boolean"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/scans/{id}/export": {
      "get": {
        "tags": [
          "Scans"
        ],
        "operationId": "exportScan",
        "summary": "Download a scan report",
        "description": "Downloads a scan report as PDF or Excel. Requires read scope.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "pdf",
                "xlsx"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Binary export.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/scan-batches": {
      "get": {
        "tags": [
          "Batches"
        ],
        "operationId": "listScanBatches",
        "summary": "List recent scan batches",
        "description": "Lists recent CSV/list batches across the token's organization. Requires read scope.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recent scan batches.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "required": [
                            "batches"
                          ],
                          "properties": {
                            "batches": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/ScanBatchSummary"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Batches"
        ],
        "operationId": "createScanBatch",
        "summary": "Queue a CSV/list batch",
        "description": "Queues up to 50 valid URLs for asynchronous scanning. Requires write scope. Send Idempotency-Key on retries to replay the same queued batch.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 160
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectSlug": {
                    "type": "string"
                  },
                  "csv": {
                    "type": "string",
                    "examples": [
                      "url\\nhttps://shopify.com\\nhttps://stripe.com"
                    ]
                  },
                  "urls": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "examples": [
                      [
                        "https://shopify.com",
                        "https://stripe.com"
                      ]
                    ]
                  },
                  "webhookUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "Optional public https endpoint for signed scan_batch.completed delivery."
                  }
                },
                "oneOf": [
                  {
                    "required": [
                      "csv"
                    ]
                  },
                  {
                    "required": [
                      "urls"
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Batch queued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "required": [
                            "batch",
                            "rejected",
                            "limit"
                          ],
                          "properties": {
                            "batch": {
                              "$ref": "#/components/schemas/ScanBatch"
                            },
                            "rejected": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "additionalProperties": true
                              }
                            },
                            "limit": {
                              "type": "integer",
                              "examples": [
                                50
                              ]
                            },
                            "webhookSigningSecret": {
                              "type": "string",
                              "description": "Returned only on creation when webhookUrl was provided."
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/scan-batches/{id}": {
      "get": {
        "tags": [
          "Batches"
        ],
        "operationId": "getScanBatch",
        "summary": "Get batch status and child scans",
        "description": "Returns a batch and its child scan summaries. Requires read scope.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Scan batch.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/ScanBatch"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/scan-batches/{id}/compare/export": {
      "get": {
        "tags": [
          "Batches",
          "Compare"
        ],
        "operationId": "exportScanBatchComparison",
        "summary": "Download a comparison matrix for one batch",
        "description": "Exports the exact scans in a batch as a PDF or Excel comparison matrix. Requires read scope.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "pdf",
                "xlsx"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Binary export.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/compare": {
      "get": {
        "tags": [
          "Compare"
        ],
        "operationId": "compareStacks",
        "summary": "Compare latest scans across domains",
        "description": "Returns a tools-by-domains matrix for the latest done scan of each requested domain. Requires read scope.",
        "parameters": [
          {
            "name": "domains",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated domains, up to 12. Example: shopify.com,stripe.com"
          },
          {
            "name": "projectSlug",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Stack comparison.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/StackComparison"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/compare/export": {
      "get": {
        "tags": [
          "Compare"
        ],
        "operationId": "exportComparison",
        "summary": "Download a latest-scan comparison matrix",
        "description": "Downloads the latest-scan comparison matrix as PDF or Excel. Requires read scope.",
        "parameters": [
          {
            "name": "domains",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "projectSlug",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "pdf",
                "xlsx"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Binary export.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/watches": {
      "get": {
        "tags": [
          "Watches"
        ],
        "operationId": "listWatches",
        "summary": "List watched domains",
        "description": "Lists DomainWatch change-alert subscriptions. Requires read scope.",
        "parameters": [
          {
            "name": "projectSlug",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Watched domains.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "required": [
                            "watches"
                          ],
                          "properties": {
                            "watches": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/DomainWatch"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Watches"
        ],
        "operationId": "createWatch",
        "summary": "Create or update a watched domain",
        "description": "Creates or updates a scheduled re-scan. Infervo POSTs a signed JSON webhook when tools are added or removed. Requires write scope.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain",
                  "webhookUrl"
                ],
                "properties": {
                  "projectSlug": {
                    "type": "string"
                  },
                  "domain": {
                    "type": "string",
                    "examples": [
                      "shopify.com"
                    ]
                  },
                  "webhookUrl": {
                    "type": "string",
                    "format": "uri",
                    "examples": [
                      "https://hooks.example.com/infervo"
                    ]
                  },
                  "intervalHours": {
                    "type": "integer",
                    "enum": [
                      6,
                      24,
                      168
                    ],
                    "default": 24
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Watch updated.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/WatchMutation"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "201": {
            "description": "Watch created.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/WatchMutation"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Watches"
        ],
        "operationId": "deleteWatch",
        "summary": "Delete a watched domain",
        "description": "Deletes a DomainWatch by id. The id may be passed as a query parameter or JSON body. Requires write scope.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Watch deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "required": [
                            "deleted",
                            "id"
                          ],
                          "properties": {
                            "deleted": {
                              "type": "boolean",
                              "const": true
                            },
                            "id": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked, or invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "description": "Error response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "inv_pat",
        "description": "Infervo personal access token minted at /app/settings/tokens. Scopes are read, write, and admin."
      },
      "oauth2": {
        "type": "oauth2",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://staging.infervo.app/oauth/authorize",
            "tokenUrl": "https://staging.infervo.app/api/oauth/token",
            "scopes": {
              "read": "Read scans, batches, comparisons, and watches.",
              "write": "Create scans, batches, memos, and watches."
            }
          }
        },
        "description": "OAuth 2.1 / PKCE is supported for remote MCP connectors."
      }
    },
    "schemas": {
      "OkEnvelope": {
        "type": "object",
        "required": [
          "ok",
          "data"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {}
        }
      },
      "ErrorEnvelope": {
        "type": "object",
        "required": [
          "ok",
          "error"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "examples": [
                  "missing_authorization",
                  "insufficient_scope",
                  "validation",
                  "not_found"
                ]
              },
              "message": {
                "type": "string"
              },
              "field": {
                "type": "string"
              },
              "hint": {
                "type": "string"
              },
              "docs": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "Whoami": {
        "type": "object",
        "required": [
          "user",
          "org",
          "scopes"
        ],
        "properties": {
          "user": {
            "$ref": "#/components/schemas/User"
          },
          "org": {
            "$ref": "#/components/schemas/Organization"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "read",
                "write",
                "admin"
              ]
            }
          }
        }
      },
      "User": {
        "type": "object",
        "required": [
          "id",
          "email",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Organization": {
        "type": "object",
        "required": [
          "id",
          "name",
          "slug"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          }
        }
      },
      "Project": {
        "type": "object",
        "required": [
          "id",
          "name",
          "slug",
          "description",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProjectMutation": {
        "type": "object",
        "required": [
          "project",
          "created"
        ],
        "properties": {
          "project": {
            "$ref": "#/components/schemas/Project"
          },
          "created": {
            "type": "boolean"
          }
        }
      },
      "ScanQuality": {
        "type": "object",
        "required": [
          "key",
          "label",
          "shortLabel",
          "description",
          "tone",
          "isLimited",
          "isFullRuntime",
          "runtimeStatus",
          "reason",
          "fallback",
          "finalStrategy",
          "attempts",
          "retryUsed",
          "staticFallbackError"
        ],
        "properties": {
          "key": {
            "type": "string",
            "enum": [
              "runtime",
              "runtime_limited",
              "static_html",
              "dns_only",
              "blocked_challenge",
              "queued",
              "running",
              "failed"
            ]
          },
          "label": {
            "type": "string"
          },
          "shortLabel": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "tone": {
            "type": "string",
            "enum": [
              "ok",
              "warn",
              "bad",
              "muted"
            ]
          },
          "isLimited": {
            "type": "boolean"
          },
          "isFullRuntime": {
            "type": "boolean"
          },
          "runtimeStatus": {
            "type": "string"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "fallback": {
            "type": [
              "string",
              "null"
            ]
          },
          "finalStrategy": {
            "type": [
              "string",
              "null"
            ]
          },
          "attempts": {
            "type": "integer",
            "minimum": 0
          },
          "retryUsed": {
            "type": "boolean"
          },
          "staticFallbackError": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Detection": {
        "type": "object",
        "required": [
          "category",
          "vendor",
          "source",
          "confidence",
          "evidence"
        ],
        "properties": {
          "category": {
            "type": "string",
            "examples": [
              "Analytics",
              "Email/CRM",
              "E-commerce"
            ]
          },
          "vendor": {
            "type": "string",
            "examples": [
              "Google Analytics 4",
              "Klaviyo",
              "Shopify"
            ]
          },
          "source": {
            "type": "string",
            "enum": [
              "signature",
              "dns"
            ]
          },
          "confidence": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "high",
              "medium",
              null
            ]
          },
          "evidence": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "InfraVendor": {
        "type": "object",
        "description": "Back-office / internal tool INFERRED from DNS (SPF include chains) or a Certificate Transparency subdomain's CNAME target. Not observed running on the page; excluded from totalTools.",
        "required": [
          "vendor",
          "category",
          "confidence",
          "signal"
        ],
        "properties": {
          "vendor": {
            "type": "string",
            "examples": [
              "Okta",
              "Zendesk",
              "Greenhouse",
              "Statuspage"
            ]
          },
          "category": {
            "type": "string",
            "examples": [
              "Identity/SSO",
              "Customer Support",
              "ATS/Hiring"
            ]
          },
          "confidence": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "high",
              "medium",
              "low",
              null
            ]
          },
          "signal": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "SPF include chain",
              "CNAME sso.acme.com -> acme.okta.com"
            ]
          }
        }
      },
      "DisclosedVendor": {
        "type": "object",
        "description": "Vendor SELF-REPORTED by the domain on its own privacy / subprocessor page. Not observed running; excluded from totalTools.",
        "required": [
          "vendor",
          "category",
          "source"
        ],
        "properties": {
          "vendor": {
            "type": "string",
            "examples": [
              "Salesforce",
              "Snowflake",
              "Twilio"
            ]
          },
          "category": {
            "type": "string",
            "examples": [
              "CRM",
              "Data Warehouse",
              "Communications"
            ]
          },
          "source": {
            "type": [
              "string",
              "null"
            ],
            "description": "URL of the disclosure page the vendor was named on."
          }
        }
      },
      "Scan": {
        "type": "object",
        "required": [
          "id",
          "url",
          "domain",
          "status",
          "quality",
          "batchId",
          "title",
          "totalTools",
          "error",
          "createdAt",
          "startedAt",
          "completedAt",
          "summary",
          "detections",
          "infrastructure",
          "disclosures",
          "disclosureSources"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "domain": {
            "type": "string",
            "examples": [
              "shopify.com"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "error"
            ]
          },
          "quality": {
            "$ref": "#/components/schemas/ScanQuality"
          },
          "batchId": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "totalTools": {
            "type": "integer",
            "minimum": 0,
            "description": "Count of OBSERVED tools (detections) only; excludes the inferred infrastructure + disclosures layers."
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "startedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "summary": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Raw scanner payload, including runtime, consent, signals, martech, email_infra, infrastructure, and disclosures summaries."
          },
          "detections": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Detection"
            }
          },
          "infrastructure": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InfraVendor"
            }
          },
          "disclosures": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DisclosedVendor"
            }
          },
          "disclosureSources": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "URLs of the privacy/subprocessor pages disclosures were read from."
          }
        }
      },
      "ScanListItem": {
        "type": "object",
        "required": [
          "id",
          "batchId",
          "domain",
          "url",
          "status",
          "quality",
          "totalTools",
          "projectSlug",
          "createdAt",
          "startedAt",
          "completedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "batchId": {
            "type": [
              "string",
              "null"
            ]
          },
          "domain": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "error"
            ]
          },
          "quality": {
            "$ref": "#/components/schemas/ScanQuality"
          },
          "totalTools": {
            "type": "integer",
            "minimum": 0
          },
          "projectSlug": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "startedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ProjectScanListItem": {
        "type": "object",
        "required": [
          "id",
          "batchId",
          "domain",
          "url",
          "status",
          "totalTools",
          "createdAt",
          "startedAt",
          "completedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "batchId": {
            "type": [
              "string",
              "null"
            ]
          },
          "domain": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "error"
            ]
          },
          "totalTools": {
            "type": "integer",
            "minimum": 0
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "startedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ScanBatchSummary": {
        "type": "object",
        "required": [
          "id",
          "projectSlug",
          "source",
          "status",
          "total",
          "completed",
          "failed",
          "createdAt",
          "completedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "projectSlug": {
            "type": "string"
          },
          "source": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "error"
            ]
          },
          "total": {
            "type": "integer",
            "minimum": 0
          },
          "completed": {
            "type": "integer",
            "minimum": 0
          },
          "failed": {
            "type": "integer",
            "minimum": 0
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ScanBatch": {
        "type": "object",
        "required": [
          "id",
          "projectId",
          "source",
          "status",
          "total",
          "completed",
          "failed",
          "error",
          "createdAt",
          "completedAt",
          "scans"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "projectId": {
            "type": "string"
          },
          "source": {
            "type": "string",
            "examples": [
              "api",
              "web"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "error"
            ]
          },
          "total": {
            "type": "integer",
            "minimum": 0
          },
          "completed": {
            "type": "integer",
            "minimum": 0
          },
          "failed": {
            "type": "integer",
            "minimum": 0
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "webhookUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "webhookStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "webhookDeliveredAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "scans": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BatchScanItem"
            }
          }
        }
      },
      "BatchScanItem": {
        "type": "object",
        "required": [
          "id",
          "url",
          "domain",
          "status",
          "quality",
          "totalTools",
          "error",
          "createdAt",
          "startedAt",
          "completedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "domain": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "error"
            ]
          },
          "quality": {
            "$ref": "#/components/schemas/ScanQuality"
          },
          "totalTools": {
            "type": "integer",
            "minimum": 0
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "startedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ToolRef": {
        "type": "object",
        "required": [
          "vendor",
          "category",
          "source"
        ],
        "properties": {
          "vendor": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "source": {
            "type": "string",
            "enum": [
              "signature",
              "dns"
            ]
          }
        }
      },
      "ScanDiff": {
        "type": "object",
        "required": [
          "scanId",
          "domain",
          "scanStatus",
          "previousScanId",
          "previousScannedAt",
          "isFirstScan",
          "added",
          "removed",
          "unchanged"
        ],
        "properties": {
          "scanId": {
            "type": "string"
          },
          "domain": {
            "type": "string"
          },
          "scanStatus": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "error"
            ]
          },
          "previousScanId": {
            "type": [
              "string",
              "null"
            ]
          },
          "previousScannedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "isFirstScan": {
            "type": "boolean"
          },
          "added": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ToolRef"
            }
          },
          "removed": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ToolRef"
            }
          },
          "unchanged": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ToolRef"
            }
          }
        }
      },
      "CompareColumn": {
        "type": "object",
        "required": [
          "domain",
          "scanStatus",
          "scanId",
          "scannedAt",
          "totalTools",
          "quality",
          "runtimeStatus",
          "runtimeReason",
          "runtimeFallback"
        ],
        "properties": {
          "domain": {
            "type": "string"
          },
          "scanStatus": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "error"
            ]
          },
          "scanId": {
            "type": "string"
          },
          "scannedAt": {
            "type": "string",
            "format": "date-time"
          },
          "totalTools": {
            "type": "integer",
            "minimum": 0
          },
          "quality": {
            "$ref": "#/components/schemas/ScanQuality"
          },
          "runtimeStatus": {
            "type": "string"
          },
          "runtimeReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "runtimeFallback": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "CompareRow": {
        "type": "object",
        "required": [
          "vendor",
          "category",
          "present"
        ],
        "properties": {
          "vendor": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "present": {
            "type": "array",
            "items": {
              "type": "boolean"
            }
          }
        }
      },
      "StackComparison": {
        "type": "object",
        "required": [
          "columns",
          "missing",
          "rows"
        ],
        "properties": {
          "columns": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompareColumn"
            }
          },
          "missing": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompareRow"
            }
          }
        }
      },
      "DomainWatch": {
        "type": "object",
        "required": [
          "id",
          "domain",
          "webhookUrl",
          "intervalHours",
          "active",
          "lastRunAt",
          "lastNotifiedAt",
          "lastStatus",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "projectSlug": {
            "type": "string"
          },
          "projectName": {
            "type": "string"
          },
          "domain": {
            "type": "string"
          },
          "webhookUrl": {
            "type": "string",
            "format": "uri"
          },
          "signingSecret": {
            "type": "string",
            "description": "HMAC secret for X-Infervo-Signature verification. Returned from create/update and write-scoped list responses only."
          },
          "intervalHours": {
            "type": "integer",
            "enum": [
              6,
              24,
              168
            ]
          },
          "active": {
            "type": "boolean"
          },
          "lastRunAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastNotifiedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WatchMutation": {
        "type": "object",
        "required": [
          "watch",
          "created",
          "baselineQueued",
          "baselineSkippedReason"
        ],
        "properties": {
          "watch": {
            "$ref": "#/components/schemas/DomainWatch"
          },
          "created": {
            "type": "boolean"
          },
          "baselineQueued": {
            "type": "boolean"
          },
          "baselineSkippedReason": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "WatchWebhook": {
        "type": "object",
        "required": [
          "event",
          "domain",
          "project",
          "scanId",
          "scanUrl",
          "added",
          "removed",
          "previousScanAt",
          "scannedAt"
        ],
        "properties": {
          "event": {
            "type": "string",
            "const": "stack_changed"
          },
          "domain": {
            "type": "string"
          },
          "project": {
            "type": "string"
          },
          "scanId": {
            "type": "string"
          },
          "scanUrl": {
            "type": "string",
            "format": "uri"
          },
          "added": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ToolRef"
            }
          },
          "removed": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ToolRef"
            }
          },
          "previousScanAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "scannedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  },
  "externalDocs": {
    "description": "Agent onboarding and CLI/MCP docs.",
    "url": "https://staging.infervo.app/agents"
  }
}