{
  "openapi": "3.1.0",
  "info": {
    "title": "trustyourinbox API",
    "version": "1.0.0",
    "description": "The public REST API for trustyourinbox DMARC monitoring. Authenticate with a workspace-scoped API key as a bearer token (mint one in the app under Settings -> Integrations -> API keys; Pro plan and up). Every response is JSON; errors use a stable { error, message } envelope. Requests are rate-limited per key (honor Retry-After on a 429). The /v1 path is a versioned, stable contract. This document is generated from the internal contract and contains only the public /v1 surface."
  },
  "servers": [
    {
      "url": "https://api.trustyourinbox.com",
      "description": "Public REST API"
    }
  ],
  "security": [
    {
      "apiKeyBearer": []
    }
  ],
  "paths": {
    "/v1/domains": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "List the domains in the workspace (public REST API)",
        "description": "Public REST API. Served at https://api.trustyourinbox.com/v1/domains (the api host rewrites /v1/* to /api/v1/*). Bearer API key; read scope; scoped to the key's workspace. Rate-limited (120/60s per key).\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "The workspace's domains",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1DomainsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Add a domain to the workspace (public REST API, write)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; WRITE scope (the key carries `write` AND the creator's live role allows edits). Reuses the same add-domain core the dashboard uses - validation, the plan domain cap, the AUP gate, provider detection, insert, and audit. Honors an optional Idempotency-Key header (a retry replays the created domain). Audited \"via API key X\".\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional client key; a retry with the same value replays the original response.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "The domain to add, e.g. acme.com"
                  },
                  "purpose": {
                    "type": "string",
                    "enum": [
                      "sending",
                      "monitor_only"
                    ],
                    "description": "Defaults to sending."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Domain added (pending verification); body carries the TXT to publish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1AddDomainResponse"
                }
              }
            }
          },
          "400": {
            "description": "invalid_request (missing/invalid name)"
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "403": {
            "description": "forbidden (read-only key or role lacks edit_data)"
          },
          "409": {
            "description": "conflict (duplicate) or plan_limit"
          },
          "422": {
            "description": "aup_block (domain fails the acceptable use policy)"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/workspace/health": {
      "get": {
        "tags": [
          "Workspace"
        ],
        "summary": "Workspace-wide health summary (public REST API)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; read scope. Spoofer-adjusted alignment, trend, and totals - the same loader the dashboard hero uses. Rate-limited (120/60s per key).\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Workspace health summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1WorkspaceHealth"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/issues": {
      "get": {
        "tags": [
          "Issues"
        ],
        "summary": "Open issues across the workspace (public REST API)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; read scope. Open (non-dismissed) issues across every domain, worst-first. Rate-limited (120/60s per key).\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Open issues",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1IssuesResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/domains/{domain}/compliance": {
      "parameters": [
        {
          "name": "domain",
          "in": "path",
          "required": true,
          "description": "domain name (workspace-scoped)",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Authentication & deliverability"
        ],
        "summary": "The four mandate compliance tiles for one domain (public REST API)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; read scope; the domain must belong to the key's workspace.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Compliance tiles",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1ComplianceResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "Domain not found in the caller's workspace"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/domains/{domain}/senders": {
      "parameters": [
        {
          "name": "domain",
          "in": "path",
          "required": true,
          "description": "domain name (workspace-scoped)",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Authentication & deliverability"
        ],
        "summary": "Sender breakdown with spoofer-adjusted alignment (public REST API)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; read scope; workspace-scoped domain. The alignment headline is spoofer-adjusted (the Q12 invariant) - it matches the dashboard, not raw aligned/total.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "range",
            "in": "query",
            "description": "Window in days (clamped to 90, default 30)",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sender breakdown",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1SendersResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "Domain not found in the caller's workspace"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/domains/{domain}/protocol": {
      "parameters": [
        {
          "name": "domain",
          "in": "path",
          "required": true,
          "description": "domain name (workspace-scoped)",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Authentication & deliverability"
        ],
        "summary": "Published authentication records for a domain (public REST API)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; read scope; workspace-scoped domain. The DMARC / SPF / DKIM / MTA-STS records and what's wrong with them - mirrors the MCP get_protocol_status payload.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authentication records",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1ProtocolResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "Domain not found in the caller's workspace"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/domains/{domain}/subdomains": {
      "parameters": [
        {
          "name": "domain",
          "in": "path",
          "required": true,
          "description": "domain name (workspace-scoped)",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Authentication & deliverability"
        ],
        "summary": "Subdomains sending as a domain, bucketed (public REST API)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; read scope; workspace-scoped domain. One row per sending subdomain with its spoofer-adjusted alignment and a Known / Review / Not in DNS bucket - mirrors the MCP get_subdomain_breakdown payload. Probes live DNS for the \"Not in DNS\" split.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "range",
            "in": "query",
            "description": "Window in days (clamped to 90, default 30)",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Subdomain breakdown",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1SubdomainsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "Domain not found in the caller's workspace"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/domains/{domain}/spoof-activity": {
      "parameters": [
        {
          "name": "domain",
          "in": "path",
          "required": true,
          "description": "domain name (workspace-scoped)",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Authentication & deliverability"
        ],
        "summary": "Blocked-spoof volume and top sources over a window (public REST API)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; read scope; workspace-scoped domain. Uses the canonical blocked-spoof filter.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "range",
            "in": "query",
            "description": "Window in days (clamped to 90, default 30)",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Spoof activity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1SpoofActivityResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "Domain not found in the caller's workspace"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/domains/{domain}/reports": {
      "parameters": [
        {
          "name": "domain",
          "in": "path",
          "required": true,
          "description": "domain name (workspace-scoped)",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Recent aggregate (RUA) reports for one domain (public REST API)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; read scope; workspace-scoped domain. Newest first.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Max reports (clamped to 200, default 50)",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recent reports",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1DomainReportsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "Domain not found in the caller's workspace"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/reports/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "aggregate-report id (from .../reports; workspace-scoped)",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Drill into one aggregate (RUA) report (public REST API)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; read scope. The report's provider, window, outcome composition, and diagnosed failing senders - mirrors the MCP get_report payload. A report id from another tenant returns 404 with no existence leak.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Report detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1ReportDetailResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "Report not found in the caller's workspace"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/domains/{domain}/issues": {
      "parameters": [
        {
          "name": "domain",
          "in": "path",
          "required": true,
          "description": "domain name (workspace-scoped)",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Issues"
        ],
        "summary": "Open issues for one domain (public REST API)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; read scope; workspace-scoped domain. Dismissal-aware (the same source the action inbox and digest read).\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Open issues for the domain",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1DomainIssuesResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "Domain not found in the caller's workspace"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/domains/{domain}/issues/{type}/dismiss": {
      "parameters": [
        {
          "name": "domain",
          "in": "path",
          "required": true,
          "description": "domain name (workspace-scoped)",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "type",
          "in": "path",
          "required": true,
          "description": "the issue (anomaly) type to hide, e.g. tls_rpt_missing",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Issues"
        ],
        "summary": "Dismiss / snooze an open issue (public REST API, write)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; WRITE scope. Hides the issue: body { \"permanent\": false } snoozes 7 days (default), true dismisses indefinitely. Reverse with .../restore. Reuses dismissAnomalyCore (the same core the dashboard + MCP use); audited \"via API key X\".\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "permanent": {
                    "type": "boolean",
                    "description": "true = dismiss indefinitely; false (default) = snooze 7 days."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Issue dismissed / snoozed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1DismissIssueResponse"
                }
              }
            }
          },
          "400": {
            "description": "invalid_request (unknown issue type)"
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "403": {
            "description": "forbidden (read-only key or role lacks edit_data)"
          },
          "404": {
            "description": "Domain not found in the caller's workspace"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/domains/{domain}/issues/{type}/restore": {
      "parameters": [
        {
          "name": "domain",
          "in": "path",
          "required": true,
          "description": "domain name (workspace-scoped)",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "type",
          "in": "path",
          "required": true,
          "description": "the issue (anomaly) type to restore",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Issues"
        ],
        "summary": "Restore a dismissed / snoozed issue (public REST API, write)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; WRITE scope. Un-hides the issue so it returns to the open inbox. The inverse of .../dismiss. Reuses restoreAnomalyCore; audited \"via API key X\".\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Issue restored",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1RestoreIssueResponse"
                }
              }
            }
          },
          "400": {
            "description": "invalid_request (unknown issue type)"
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "403": {
            "description": "forbidden (read-only key or role lacks edit_data)"
          },
          "404": {
            "description": "Domain not found in the caller's workspace"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/domains/{domain}/dns-fixes": {
      "parameters": [
        {
          "name": "domain",
          "in": "path",
          "required": true,
          "description": "domain name (workspace-scoped)",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "DNS fixes"
        ],
        "summary": "Stage a one-click DNS fix for an issue (public REST API, write)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; WRITE scope. Body { \"type\": \"<issue_type>\" } for a one-click-fixable issue. The fix is STAGED (not applied immediately) through the same 5-minute-delay / email-with-one-click-cancel / 24-hour-undo safety pipeline the dashboard + MCP use - no second DNS-write path. Reuses stageFixForAnomalyById; audited \"via API key X\". Honors Idempotency-Key (no double-stage).\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional client key; a retry with the same value replays the original response.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type"
                ],
                "properties": {
                  "type": {
                    "type": "string",
                    "description": "A one-click-fixable issue type, e.g. dmarc_np_missing."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Nothing to stage (the fix is already in place)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "201": {
            "description": "Fix staged; body carries the change id, record, and apply time",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1StageFixResponse"
                }
              }
            }
          },
          "400": {
            "description": "invalid_request (not a one-click-fixable type / invalid input)"
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "403": {
            "description": "forbidden (read-only key or role lacks edit_data)"
          },
          "404": {
            "description": "Domain not found in the caller's workspace"
          },
          "409": {
            "description": "conflict (no provider connected, already pending, etc.)"
          },
          "429": {
            "description": "Rate limited (per key, or the 5-changes/24h staging cap)"
          }
        }
      }
    },
    "/v1/domains/{domain}/recheck": {
      "parameters": [
        {
          "name": "domain",
          "in": "path",
          "required": true,
          "description": "domain name (workspace-scoped)",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Re-detect the domain's DNS provider now (public REST API, write)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; WRITE scope. Re-runs DNS-provider detection immediately instead of waiting for the daily sweep (after moving DNS / connecting a provider). No body. Reuses refreshDnsProviderForDomain; audited \"via API key X\" only when the provider flips.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Re-checked (body reports the detected provider + whether it changed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1RecheckResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "403": {
            "description": "forbidden (read-only key or role lacks edit_data)"
          },
          "404": {
            "description": "Domain not found in the caller's workspace"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/senders": {
      "get": {
        "tags": [
          "Senders"
        ],
        "summary": "List your custom senders (public REST API)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; read scope. Every custom (source \"user\") sender in the workspace - including ones with no observed traffic yet - with ranges, optional icon URL, and trailing-30-day attributed volume. The same loader the dashboard's management card reads.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "The workspace's custom senders",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1CustomSendersResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "403": {
            "description": "Plan does not include API access"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "post": {
        "tags": [
          "Senders"
        ],
        "summary": "Identify a sending IP / CIDR as a named vendor (public REST API, write)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; WRITE scope. Body { \"ipOrCidr\", \"name\", \"category\" } - labels a source so it counts as a known sender instead of an unknown source. Reuses identifySenderCore; audited \"via API key X\". Honors Idempotency-Key.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional client key; a retry with the same value replays the original response.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "ipOrCidr",
                  "name",
                  "category"
                ],
                "properties": {
                  "ipOrCidr": {
                    "type": "string",
                    "description": "An IP or CIDR, e.g. 198.2.132.1 or 198.2.128.0/18."
                  },
                  "name": {
                    "type": "string",
                    "description": "The vendor / sender name, e.g. Mailchimp."
                  },
                  "category": {
                    "type": "string",
                    "enum": [
                      "email_provider",
                      "esp_marketing",
                      "esp_transactional",
                      "security",
                      "infrastructure",
                      "crm",
                      "other"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Sender identified",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1IdentifySenderResponse"
                }
              }
            }
          },
          "400": {
            "description": "invalid_request (missing name / invalid category / invalid CIDR)"
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "403": {
            "description": "forbidden (read-only key or role lacks edit_data)"
          },
          "409": {
            "description": "conflict (a sender with that name already exists)"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/senders/{id}": {
      "patch": {
        "tags": [
          "Senders"
        ],
        "summary": "Update a custom sender (public REST API, write)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; WRITE scope. Partial update of a custom sender you created - rename, recategorize, add an IP range (how you register a second IP of the same server), or remove one. Removing a range re-attributes its traffic (past reports included) back to the catalog vendor underneath, or back to unknown. Reuses modifySenderCore (the SAME composition the dashboard + MCP update_sender use); audited \"via API key X\". Honors Idempotency-Key.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional client key; a retry with the same value replays the original response.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "At least one property required.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "category": {
                    "type": "string",
                    "enum": [
                      "email_provider",
                      "esp_marketing",
                      "esp_transactional",
                      "security",
                      "infrastructure",
                      "crm",
                      "other"
                    ]
                  },
                  "addRange": {
                    "type": "string",
                    "description": "An IP or CIDR to add (widest /24 IPv4, /48 IPv6; max 16 ranges per sender)."
                  },
                  "removeRange": {
                    "type": "string",
                    "description": "An existing range to remove (refused for the last range - delete the sender instead)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sender updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1UpdateSenderResponse"
                }
              }
            }
          },
          "400": {
            "description": "invalid_request (nothing to do / invalid category / invalid or too-wide CIDR / range not found)"
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "403": {
            "description": "forbidden (read-only key or role lacks edit_data)"
          },
          "404": {
            "description": "Not this workspace's custom sender (catalog vendors are never editable)"
          },
          "409": {
            "description": "conflict (duplicate name or range, overlap with your own sender, range cap, last range)"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "delete": {
        "tags": [
          "Senders"
        ],
        "summary": "Delete a custom sender (public REST API, write)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; WRITE scope. Deletes a custom sender you created; its traffic - past reports included - re-attributes to the catalog vendor underneath, or back to unknown, and the workspace known-IP set reconverges. Reuses deleteSenderCore; audited \"via API key X\". Honors Idempotency-Key.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional client key; a retry with the same value replays the original response.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sender deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1DeleteSenderResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "403": {
            "description": "forbidden (read-only key or role lacks edit_data)"
          },
          "404": {
            "description": "Not this workspace's custom sender"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List your outbound webhook endpoints (public REST API)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; read scope. Every outbound webhook endpoint in the workspace, with its URL, event filter, status, and failure streak. Never returns the signing secret, only its masked prefix. Requires the outboundWebhooks plan capability (Pro and up).\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "The workspace's webhook endpoints",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1WebhooksResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "403": {
            "description": "Plan does not include outbound webhooks"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Register an outbound webhook endpoint (public REST API, write)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; WRITE scope. Registers an https endpoint we POST a signed payload to on each critical event. Body { \"url\", \"description\"?, \"eventTypes\"? } - eventTypes omitted means all types. Returns the signing secret ONCE. Reuses createWebhookEndpointCore; audited \"via API key X\". Honors Idempotency-Key. Requires the outboundWebhooks capability (Pro and up).\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional client key; a retry with the same value replays the original response.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "The https endpoint URL to POST events to."
                  },
                  "description": {
                    "type": "string",
                    "description": "Optional label, e.g. Prod SIEM."
                  },
                  "eventTypes": {
                    "type": "array",
                    "description": "Which events to receive; omit for all.",
                    "items": {
                      "type": "string",
                      "enum": [
                        "anomaly.crit",
                        "dns_fix.applied",
                        "dns_fix.reverted",
                        "dns_fix.failed"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Endpoint created (signing secret returned once)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1CreateWebhookResponse"
                }
              }
            }
          },
          "400": {
            "description": "invalid_request (invalid URL / not https / private address / unknown event type)"
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "403": {
            "description": "forbidden (read-only key or role lacks edit_data), or plan_required"
          },
          "409": {
            "description": "conflict (endpoint limit reached)"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/webhooks/{id}": {
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete an outbound webhook endpoint (public REST API, write)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; WRITE scope. Removes an endpoint; we stop delivering to it immediately and its delivery log is cascaded. Reuses deleteWebhookEndpointCore; audited \"via API key X\". Honors Idempotency-Key.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional client key; a retry with the same value replays the original response.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Endpoint deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1DeleteWebhookResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "403": {
            "description": "forbidden (read-only key or role lacks edit_data)"
          },
          "404": {
            "description": "Not this workspace's webhook endpoint"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/webhooks/{id}/deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Recent delivery attempts for an endpoint (public REST API)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; read scope. The most recent delivery attempts for one endpoint (newest first) - the event type, status, HTTP response code, failure reason, and attempt number - for debugging your receiver.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recent delivery attempts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1WebhookDeliveriesResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "Not this workspace's webhook endpoint"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/changes": {
      "get": {
        "tags": [
          "DNS fixes"
        ],
        "summary": "Staged / in-undo-window DNS changes for the workspace (public REST API)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; read scope. The read half of the staged-fix loop - DNS fixes currently staged (in the 5-minute pre-apply window) or recently applied but still inside the 24-hour undo window. Cancel one via DELETE /v1/changes/{id}.\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Active staged / in-undo-window changes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1ChangesResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/changes/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "the staged change id (from GET /v1/changes)",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "DNS fixes"
        ],
        "summary": "Cancel a staged DNS change (public REST API, write)",
        "description": "Public REST API (api.trustyourinbox.com/v1). Bearer API key; WRITE scope. Cancels a fix that is still staged (inside the 5-minute pre-apply window); an applied change has the 24-hour undo in the app. Reuses cancelStagedChange, workspace-scoped so a foreign change id can't be canceled. Audited \"via API key X\".\n",
        "security": [
          {
            "apiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Change canceled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1CancelChangeResponse"
                }
              }
            }
          },
          "400": {
            "description": "invalid_request (malformed id)"
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "403": {
            "description": "forbidden (read-only key or role lacks edit_data)"
          },
          "404": {
            "description": "No staged change with that id in the caller's workspace"
          },
          "409": {
            "description": "conflict (the change is no longer staged)"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKeyBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "tyi-api-key",
        "description": "trustyourinbox customer API key for the public REST API (SHA-256 hashed at rest, tyi_api_ prefix). Carries a read and optionally write scope. Resolved by resolveTenancyFromApiKey() with a live workspace-role re-read (instant revocation). Header-only, never a cookie. Used on api.trustyourinbox.com/v1.\n"
      }
    },
    "schemas": {
      "V1AddDomainResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "domain": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "purpose": {
            "type": "string",
            "enum": [
              "sending",
              "monitor_only"
            ]
          },
          "verification": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "recordType": {
                "type": "string"
              },
              "recordName": {
                "type": "string"
              },
              "recordValue": {
                "type": "string"
              }
            },
            "required": [
              "recordType",
              "recordName",
              "recordValue"
            ]
          },
          "dnsProvider": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "domain",
          "status",
          "purpose",
          "verification",
          "dnsProvider"
        ]
      },
      "V1CancelChangeResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "canceled": {
            "const": true
          },
          "change": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/V1Change"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "canceled",
          "change"
        ]
      },
      "V1Change": {
        "type": "object",
        "additionalProperties": false,
        "description": "A staged or recently-applied one-click DNS change. The email cancel secret is never exposed.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "domain": {
            "type": [
              "string",
              "null"
            ]
          },
          "fixType": {
            "type": "string"
          },
          "recordName": {
            "type": "string"
          },
          "recordType": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "staged, applied, canceled, etc."
          },
          "stagedAt": {
            "type": "string",
            "format": "date-time"
          },
          "applyAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "undoUntil": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "domain",
          "fixType",
          "recordName",
          "recordType",
          "status",
          "stagedAt",
          "applyAt",
          "undoUntil"
        ]
      },
      "V1ChangesResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "changes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/V1Change"
            }
          }
        },
        "required": [
          "changes"
        ]
      },
      "V1ComplianceResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "domain": {
            "type": "string"
          },
          "compliance": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "google": {
                "$ref": "#/components/schemas/V1ComplianceTile"
              },
              "microsoft": {
                "$ref": "#/components/schemas/V1ComplianceTile"
              },
              "bimi": {
                "$ref": "#/components/schemas/V1ComplianceTile"
              },
              "tls": {
                "$ref": "#/components/schemas/V1ComplianceTile"
              }
            },
            "required": [
              "google",
              "microsoft",
              "bimi",
              "tls"
            ]
          }
        },
        "required": [
          "domain",
          "compliance"
        ]
      },
      "V1ComplianceTile": {
        "type": "object",
        "additionalProperties": false,
        "description": "One mandate tile (Google/Yahoo, Microsoft, BIMI, or TLS) for a domain.",
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "google",
              "microsoft",
              "bimi",
              "tls"
            ]
          },
          "title": {
            "type": "string"
          },
          "tier": {
            "type": "string",
            "enum": [
              "pass",
              "at_risk",
              "fail",
              "coming_soon",
              "no_data"
            ]
          },
          "compliantCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "totalScored": {
            "type": [
              "integer",
              "null"
            ]
          },
          "noun": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "title",
          "tier",
          "compliantCount",
          "totalScored",
          "noun"
        ]
      },
      "V1CreateWebhookResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "secret": {
            "type": "string",
            "description": "The signing secret - returned ONCE, not retrievable later."
          },
          "secretPrefix": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "secret",
          "secretPrefix"
        ]
      },
      "V1CustomSendersResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "count": {
            "type": "integer"
          },
          "senders": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "senderId": {
                  "type": "string",
                  "format": "uuid"
                },
                "name": {
                  "type": "string"
                },
                "category": {
                  "type": "string",
                  "enum": [
                    "email_provider",
                    "esp_marketing",
                    "esp_transactional",
                    "security",
                    "infrastructure",
                    "crm",
                    "other"
                  ]
                },
                "ranges": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "iconUrl": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "volume30d": {
                  "type": "number"
                },
                "lastSeen": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "createdAt": {
                  "type": "string"
                }
              },
              "required": [
                "senderId",
                "name",
                "category",
                "ranges",
                "iconUrl",
                "volume30d",
                "lastSeen",
                "createdAt"
              ]
            }
          }
        },
        "required": [
          "count",
          "senders"
        ]
      },
      "V1DeleteSenderResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "senderId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "deleted": {
            "type": "boolean"
          }
        },
        "required": [
          "senderId",
          "name",
          "deleted"
        ]
      },
      "V1DeleteWebhookResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "deleted": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "deleted"
        ]
      },
      "V1DismissIssueResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "domain": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "dismiss",
              "snooze"
            ]
          },
          "dismissed": {
            "const": true
          }
        },
        "required": [
          "domain",
          "type",
          "mode",
          "dismissed"
        ]
      },
      "V1DomainIssuesResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "domain": {
            "type": "string"
          },
          "issues": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/V1Issue"
            }
          }
        },
        "required": [
          "domain",
          "issues"
        ]
      },
      "V1DomainReportsResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "domain": {
            "type": "string"
          },
          "reports": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/V1ReportRow"
            }
          }
        },
        "required": [
          "domain",
          "reports"
        ]
      },
      "V1DomainsResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "domains": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "name": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "pending",
                    "verified",
                    "paused",
                    "deleted"
                  ]
                },
                "purpose": {
                  "type": "string",
                  "enum": [
                    "sending",
                    "monitor_only"
                  ]
                },
                "dnsProvider": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "required": [
                "name",
                "status",
                "purpose",
                "dnsProvider",
                "createdAt"
              ]
            }
          }
        },
        "required": [
          "domains"
        ]
      },
      "V1Error": {
        "type": "object",
        "additionalProperties": true,
        "description": "Stable error envelope for every /v1 endpoint.",
        "properties": {
          "error": {
            "type": "string",
            "description": "Stable machine code (unauthorized, forbidden, plan_required, not_found, invalid_request, conflict, plan_limit, aup_block, idempotency_in_progress, idempotency_key_reused, rate_limited, internal)."
          },
          "message": {
            "type": "string"
          },
          "retryAfterSec": {
            "type": "integer"
          }
        },
        "required": [
          "error",
          "message"
        ]
      },
      "V1FailureGroup": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "sender": {
            "type": "string"
          },
          "vendorName": {
            "type": [
              "string",
              "null"
            ]
          },
          "ipCount": {
            "type": "integer"
          },
          "failedVolume": {
            "type": "integer"
          },
          "class": {
            "type": "string",
            "enum": [
              "forwarding",
              "esp_misconfig",
              "spoof_likely",
              "unknown"
            ]
          },
          "diagnosis": {
            "type": "string"
          }
        },
        "required": [
          "sender",
          "vendorName",
          "ipCount",
          "failedVolume",
          "class",
          "diagnosis"
        ]
      },
      "V1IdentifySenderResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "senderId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "enum": [
              "email_provider",
              "esp_marketing",
              "esp_transactional",
              "security",
              "infrastructure",
              "crm",
              "other"
            ]
          },
          "ipOrCidr": {
            "type": "string"
          }
        },
        "required": [
          "senderId",
          "name",
          "category",
          "ipOrCidr"
        ]
      },
      "V1Issue": {
        "type": "object",
        "additionalProperties": false,
        "description": "An open issue (anomaly / action item). Shared by /v1/issues and /v1/domains/{domain}/issues.",
        "properties": {
          "key": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "description": "The anomaly type id (the issue's stable machine key)."
          },
          "severity": {
            "type": "string",
            "enum": [
              "opportunity",
              "warn",
              "crit"
            ]
          },
          "headline": {
            "type": "string"
          },
          "why": {
            "type": "string"
          },
          "domain": {
            "type": "string"
          },
          "firstSeenAt": {
            "type": "string",
            "format": "date-time"
          },
          "impact": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "key",
          "type",
          "severity",
          "headline",
          "why",
          "domain",
          "firstSeenAt",
          "impact"
        ]
      },
      "V1IssuesResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "issues": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/V1Issue"
            }
          }
        },
        "required": [
          "issues"
        ]
      },
      "V1ProtocolResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "domain": {
            "type": "string"
          },
          "dmarc": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "properties": {
              "policy": {
                "type": "string"
              },
              "pct": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "testMode": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "y",
                  "n",
                  null
                ],
                "description": "DMARCbis t= - \"y\" means published but receivers don't enforce."
              },
              "np": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "DMARCbis non-existent-subdomain policy."
              },
              "reportsToUs": {
                "type": [
                  "boolean",
                  "null"
                ]
              },
              "multiRecord": {
                "type": [
                  "boolean",
                  "null"
                ]
              }
            },
            "required": [
              "policy",
              "pct",
              "testMode",
              "np",
              "reportsToUs",
              "multiRecord"
            ]
          },
          "spf": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "properties": {
              "lookupCount": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "DNS lookups consumed vs the RFC 7208 limit of 10."
              },
              "allQualifier": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The qualifier on the final all mechanism (~, -, +, ?)."
              },
              "multiRecord": {
                "type": [
                  "boolean",
                  "null"
                ]
              }
            },
            "required": [
              "lookupCount",
              "allQualifier",
              "multiRecord"
            ]
          },
          "dkimSelectors": {
            "type": "integer",
            "description": "Count of published selectors found."
          },
          "dkimMinKeyBits": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Smallest published key size in bits."
          },
          "mtaSts": {
            "type": [
              "string",
              "null"
            ],
            "description": "MTA-STS mode (testing / enforce), or null when not published."
          }
        },
        "required": [
          "domain",
          "dmarc",
          "spf",
          "dkimSelectors",
          "dkimMinKeyBits",
          "mtaSts"
        ]
      },
      "V1RecheckResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "domain": {
            "type": "string"
          },
          "detected": {
            "type": "boolean"
          },
          "provider": {
            "type": "string"
          },
          "providerName": {
            "type": "string",
            "description": "Present only when detected is true."
          },
          "changed": {
            "type": "boolean"
          }
        },
        "required": [
          "domain",
          "detected",
          "provider",
          "changed"
        ]
      },
      "V1ReportDetailResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "reportId": {
            "type": "string",
            "format": "uuid"
          },
          "externalReportId": {
            "type": "string"
          },
          "reportingOrg": {
            "type": "string"
          },
          "domain": {
            "type": "string"
          },
          "rangeStart": {
            "type": "string",
            "format": "date-time"
          },
          "rangeEnd": {
            "type": "string",
            "format": "date-time"
          },
          "outcome": {
            "$ref": "#/components/schemas/V1ReportOutcome"
          },
          "failureGroups": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/V1FailureGroup"
            }
          }
        },
        "required": [
          "reportId",
          "externalReportId",
          "reportingOrg",
          "domain",
          "rangeStart",
          "rangeEnd",
          "outcome",
          "failureGroups"
        ]
      },
      "V1ReportOutcome": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "aligned": {
            "type": "integer"
          },
          "dkimOnly": {
            "type": "integer"
          },
          "spfOnly": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "alignmentPct": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Raw aligned/total - the report's factual composition."
          },
          "spoofVolume": {
            "type": "integer"
          },
          "adjustedPct": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Verdict-grade alignment of the legitimate mail; null for an all-spoof report."
          },
          "recordCount": {
            "type": "integer"
          }
        },
        "required": [
          "aligned",
          "dkimOnly",
          "spfOnly",
          "failed",
          "total",
          "alignmentPct",
          "spoofVolume",
          "adjustedPct",
          "recordCount"
        ]
      },
      "V1ReportRow": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "reportingOrg": {
            "type": "string"
          },
          "rangeStart": {
            "type": "string",
            "format": "date-time"
          },
          "rangeEnd": {
            "type": "string",
            "format": "date-time"
          },
          "volume": {
            "type": "integer"
          },
          "passing": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "reportingOrg",
          "rangeStart",
          "rangeEnd",
          "volume",
          "passing"
        ]
      },
      "V1RestoreIssueResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "domain": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "restored": {
            "const": true
          }
        },
        "required": [
          "domain",
          "type",
          "restored"
        ]
      },
      "V1SenderRow": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "ip": {
            "type": "string"
          },
          "network": {
            "type": [
              "string",
              "null"
            ],
            "description": "The ASN / org name for the IP."
          },
          "volume": {
            "type": "integer"
          },
          "alignedPct": {
            "type": "integer"
          },
          "known": {
            "type": "boolean"
          }
        },
        "required": [
          "ip",
          "network",
          "volume",
          "alignedPct",
          "known"
        ]
      },
      "V1SendersResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "domain": {
            "type": "string"
          },
          "rangeDays": {
            "type": "integer"
          },
          "totalVolume": {
            "type": "integer"
          },
          "alignmentPct": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Spoofer-adjusted (never raw aligned/total)."
          },
          "blockedSpoof": {
            "type": "integer"
          },
          "knownVolume": {
            "type": "integer",
            "description": "Present only when totalVolume > 0."
          },
          "unknownVolume": {
            "type": "integer",
            "description": "Present only when totalVolume > 0."
          },
          "distinctSenders": {
            "type": "integer",
            "description": "Present only when totalVolume > 0."
          },
          "senders": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/V1SenderRow"
            }
          }
        },
        "required": [
          "domain",
          "rangeDays",
          "totalVolume",
          "alignmentPct",
          "blockedSpoof",
          "senders"
        ]
      },
      "V1SpoofActivityResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "domain": {
            "type": "string"
          },
          "rangeDays": {
            "type": "integer"
          },
          "blockedSpoof": {
            "type": "integer"
          },
          "blockedSpoofPrior": {
            "type": "integer"
          },
          "topSources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/V1SpoofSource"
            }
          },
          "daily": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "date": {
                  "type": "string",
                  "format": "date-time"
                },
                "volume": {
                  "type": "integer"
                }
              },
              "required": [
                "date",
                "volume"
              ]
            }
          }
        },
        "required": [
          "domain",
          "rangeDays",
          "blockedSpoof",
          "blockedSpoofPrior",
          "topSources",
          "daily"
        ]
      },
      "V1SpoofSource": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "ip": {
            "type": "string"
          },
          "attempts": {
            "type": "integer"
          },
          "network": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "disposition": {
            "type": "string"
          }
        },
        "required": [
          "ip",
          "attempts",
          "network",
          "country",
          "disposition"
        ]
      },
      "V1StageFixResponse": {
        "description": "Either a staged change (201) or an already-applied no-op (200).",
        "oneOf": [
          {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "domain": {
                "type": "string"
              },
              "type": {
                "type": "string"
              },
              "staged": {
                "const": true
              },
              "changeId": {
                "type": "string",
                "format": "uuid"
              },
              "recordName": {
                "type": "string"
              },
              "newValue": {
                "type": "string"
              },
              "applyAt": {
                "type": "string",
                "format": "date-time"
              },
              "warning": {
                "type": "string"
              }
            },
            "required": [
              "domain",
              "type",
              "staged",
              "changeId",
              "recordName",
              "newValue",
              "applyAt"
            ]
          },
          {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "domain": {
                "type": "string"
              },
              "type": {
                "type": "string"
              },
              "staged": {
                "const": false
              },
              "alreadyApplied": {
                "const": true
              }
            },
            "required": [
              "domain",
              "type",
              "staged",
              "alreadyApplied"
            ]
          }
        ]
      },
      "V1SubdomainRow": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "subdomain": {
            "type": "string"
          },
          "label": {
            "type": "string",
            "description": "The relative label (e.g. \"marketing\" for marketing.acme.com)."
          },
          "status": {
            "type": "string",
            "enum": [
              "Known",
              "Review",
              "Not in DNS"
            ]
          },
          "bucket": {
            "type": "string",
            "enum": [
              "known",
              "worth_a_look",
              "nonexistent"
            ]
          },
          "volume": {
            "type": "integer"
          },
          "alignmentPct": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Spoofer-adjusted; null when no legit mail is measurable."
          },
          "spoofVolume": {
            "type": "integer"
          },
          "blockedSpoof": {
            "type": "integer"
          },
          "distinctSenders": {
            "type": "integer"
          },
          "lastSeen": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "inDns": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "true = the subdomain resolves; false = NXDOMAIN; null = inconclusive."
          },
          "verdict": {
            "type": "string"
          },
          "whatItMeans": {
            "type": "string"
          }
        },
        "required": [
          "subdomain",
          "label",
          "status",
          "bucket",
          "volume",
          "alignmentPct",
          "spoofVolume",
          "blockedSpoof",
          "distinctSenders",
          "lastSeen",
          "inDns",
          "verdict",
          "whatItMeans"
        ]
      },
      "V1SubdomainsResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "domain": {
            "type": "string"
          },
          "rangeDays": {
            "type": "integer"
          },
          "subdomainCount": {
            "type": "integer"
          },
          "knownCount": {
            "type": "integer",
            "description": "Present only when subdomainCount > 0."
          },
          "worthALookCount": {
            "type": "integer",
            "description": "Present only when subdomainCount > 0."
          },
          "nonexistentCount": {
            "type": "integer",
            "description": "Present only when subdomainCount > 0."
          },
          "unrecognizedCount": {
            "type": "integer",
            "description": "Present only when subdomainCount > 0."
          },
          "totalVolume": {
            "type": "integer",
            "description": "Present only when subdomainCount > 0."
          },
          "nxdomainPolicy": {
            "type": [
              "string",
              "null"
            ],
            "description": "What a non-existent subdomain resolves to (np ?? sp ?? p). Present only when subdomainCount > 0."
          },
          "extendedSubdomainCount": {
            "type": "integer",
            "description": "Distinct subdomains over a wider 90-day lookback (subdomain abuse is bursty)."
          },
          "subdomains": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/V1SubdomainRow"
            }
          }
        },
        "required": [
          "domain",
          "rangeDays",
          "subdomainCount",
          "extendedSubdomainCount",
          "subdomains"
        ]
      },
      "V1UpdateSenderResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "senderId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "corpusOverlaps": {
            "type": "array",
            "description": "Input ranges that sit inside a global catalog vendor's published ranges (warn-and-allow).",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "range": {
                  "type": "string"
                },
                "corpusSender": {
                  "type": "string"
                }
              },
              "required": [
                "range",
                "corpusSender"
              ]
            }
          }
        },
        "required": [
          "senderId",
          "name",
          "corpusOverlaps"
        ]
      },
      "V1WebhookDeliveriesResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "endpointId": {
            "type": "string",
            "format": "uuid"
          },
          "count": {
            "type": "integer"
          },
          "deliveries": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "eventType": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "pending",
                    "delivered",
                    "failed"
                  ]
                },
                "responseCode": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "error": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "attempt": {
                  "type": "integer"
                }
              },
              "required": [
                "id",
                "eventType",
                "status",
                "responseCode",
                "error",
                "attempt"
              ]
            }
          }
        },
        "required": [
          "endpointId",
          "count",
          "deliveries"
        ]
      },
      "V1WebhooksResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "count": {
            "type": "integer"
          },
          "webhooks": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "url": {
                  "type": "string"
                },
                "description": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "secretPrefix": {
                  "type": "string"
                },
                "eventTypes": {
                  "type": [
                    "array",
                    "null"
                  ],
                  "items": {
                    "type": "string"
                  }
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "active",
                    "disabled"
                  ]
                },
                "consecutiveFailures": {
                  "type": "integer"
                }
              },
              "required": [
                "id",
                "url",
                "description",
                "secretPrefix",
                "eventTypes",
                "status",
                "consecutiveFailures"
              ]
            }
          }
        },
        "required": [
          "count",
          "webhooks"
        ]
      },
      "V1WorkspaceHealth": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "alignmentPct": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Spoofer-adjusted workspace alignment %."
          },
          "trendDeltaPp": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Change in points vs the prior window."
          },
          "totalMessages": {
            "type": "integer"
          },
          "totalReports": {
            "type": "integer"
          },
          "totalDomains": {
            "type": "integer"
          }
        },
        "required": [
          "alignmentPct",
          "trendDeltaPp",
          "totalMessages",
          "totalReports",
          "totalDomains"
        ]
      }
    }
  }
}
