{
  "openapi": "3.0.3",
  "info": {
    "title": "Mailganer SPF Score API",
    "version": "1.1.0",
    "description": "Публичный рейтинг ESP в SPF-записи домена (A–D).\n\nАлгоритм совпадает с platform `POST /api/v2/domain-spf-score/`: Google DoH, дерево `include`/`redirect`, веса `SPF_SCORE_RULES`.\n\nПубличный REST: `POST /tools/spf-score/v1` (nginx → WP REST `mg/v1/spf-score`). Сырой числовой `score` наружу не отдаётся.\n\nUI: `/tools/spf-score` (SmartCaptcha + квоты). На apex `/api/` занят личным кабинетом."
  },
  "servers": [
    {
      "url": "https://mailganer.com",
      "description": "Production"
    },
    {
      "url": "https://newland.mailganer.com",
      "description": "Staging (Basic Auth на HTML; REST без Basic Auth)"
    }
  ],
  "tags": [
    {
      "name": "spf-score",
      "description": "Рейтинг SPF"
    },
    {
      "name": "system",
      "description": "Статус и квоты"
    }
  ],
  "paths": {
    "/tools/spf-score/v1/status": {
      "get": {
        "tags": [
          "system"
        ],
        "summary": "Квоты и captcha",
        "operationId": "spfScoreStatus",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusResponse"
                }
              }
            }
          }
        }
      }
    },
    "/tools/spf-score/v1": {
      "post": {
        "tags": [
          "spf-score"
        ],
        "summary": "Оценить рейтинг SPF",
        "description": "Синхронная проверка. При включённой captcha нужен токен SmartCaptcha (`X-Captcha-Token` или поле `captchaToken`).\n\n`rating`: `A` | `B` | `C` | `D`.\n`providers` — распознанные правила из карты ESP (host + публичное имя).\n`hasMailganer` — в цепочке есть Mailganer / mlgnr.\n\nНе путать с `/tools/spf` (синтаксис / lookup).",
        "operationId": "spfScoreCheck",
        "parameters": [
          {
            "name": "X-Captcha-Token",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Токен SmartCaptcha (если captcha обязательна)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckRequest"
              },
              "example": {
                "domain": "example.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Проверка выполнена",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckResponse"
                },
                "examples": {
                  "rating_b": {
                    "summary": "Российский ESP (B)",
                    "value": {
                      "status": "ok",
                      "domain": "unisender.com",
                      "rating": "B",
                      "spf": "v=spf1 include:_spf.google.com include:spf.unisender.com …",
                      "providers": [
                        {
                          "host": "unisender.com",
                          "name": "Unisender"
                        },
                        {
                          "host": "google.com",
                          "name": "Google"
                        }
                      ],
                      "hasMailganer": false,
                      "warning": null,
                      "dailyRemaining": 19,
                      "dailyLimit": 20
                    }
                  },
                  "rating_d_mailganer": {
                    "summary": "Уже Mailganer (D)",
                    "value": {
                      "status": "ok",
                      "domain": "mailganer.com",
                      "rating": "D",
                      "spf": "v=spf1 include:spf.mailganer.com …",
                      "providers": [
                        {
                          "host": "mailganer.com",
                          "name": "Mailganer"
                        }
                      ],
                      "hasMailganer": true,
                      "warning": null,
                      "dailyRemaining": 18,
                      "dailyLimit": 20
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Некорректный домен",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "Укажите домен вида example.com"
                }
              }
            }
          },
          "403": {
            "description": "Captcha не пройдена",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Дневной лимит IP или глобальный лимит",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Сервис не настроен или антибот недоступен",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "CheckRequest": {
        "type": "object",
        "required": [
          "domain"
        ],
        "properties": {
          "domain": {
            "type": "string",
            "description": "Домен (без схемы и пути)",
            "example": "example.com",
            "maxLength": 253
          },
          "captchaToken": {
            "type": "string",
            "description": "Токен SmartCaptcha (альтернатива заголовку X-Captcha-Token)"
          },
          "website": {
            "type": "string",
            "description": "Honeypot — должно быть пустым"
          }
        }
      },
      "Provider": {
        "type": "object",
        "properties": {
          "host": {
            "type": "string",
            "description": "Ключ правила без ведущей точки (например unisender.com)"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Публичное имя бренда, если известно"
          }
        }
      },
      "CheckResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "ok"
          },
          "domain": {
            "type": "string"
          },
          "rating": {
            "type": "string",
            "enum": [
              "A",
              "B",
              "C",
              "D"
            ],
            "description": "Класс ESP в SPF-цепочке"
          },
          "spf": {
            "type": "string",
            "nullable": true,
            "description": "Apex SPF TXT (несколько записей через ` | `)"
          },
          "providers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Provider"
            }
          },
          "hasMailganer": {
            "type": "boolean"
          },
          "warning": {
            "type": "string",
            "nullable": true,
            "description": "Например ошибки в include DNS-имён"
          },
          "dailyRemaining": {
            "type": "integer"
          },
          "dailyLimit": {
            "type": "integer"
          }
        }
      },
      "StatusResponse": {
        "type": "object",
        "properties": {
          "configured": {
            "type": "boolean"
          },
          "captchaRequired": {
            "type": "boolean"
          },
          "captchaMode": {
            "type": "string",
            "enum": [
              "invisible",
              "visible",
              "off"
            ]
          },
          "siteKey": {
            "type": "string"
          },
          "dailyLimit": {
            "type": "integer"
          },
          "dailyRemaining": {
            "type": "integer"
          },
          "globalDailyRemaining": {
            "type": "integer"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      }
    }
  }
}
