{
  "openapi": "3.1.0",
  "info": {
    "title": "ShieldMX API",
    "version": "1.1.0",
    "summary": "Stop free-trial abuse at signup.",
    "description": "Checks an email address at signup and returns ALLOW, REVIEW or REJECT with a reason and a risk score. Authenticate with a bearer key from the ShieldMX dashboard. Keys starting with smx_test_ run in test mode and do not count toward quota.",
    "contact": { "email": "heysharad@proton.me" },
    "license": { "name": "Proprietary" }
  },
  "servers": [
    { "url": "https://shieldmx-core-api.slotboard-v.workers.dev", "description": "Production (after release)" }
  ],
  "security": [{ "bearerKey": [] }],
  "tags": [
    { "name": "Verification", "description": "Check one or many addresses." }
  ],
  "paths": {
    "/v1/verify": {
      "post": {
        "tags": ["Verification"],
        "operationId": "verifyEmail",
        "summary": "Check one email address",
        "description": "Returns one verdict. Only well-formed live-mode requests count toward the monthly quota.",
        "parameters": [
          { "name": "mx", "in": "query", "required": false, "description": "Set to false to skip the mail-server lookup.", "schema": { "type": "string", "enum": ["true", "false"], "default": "true" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/VerifyRequest" },
              "examples": { "default": { "value": { "email": "someone@relay.firefox.com" } } }
            }
          }
        },
        "responses": {
          "200": { "description": "A verdict.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Verdict" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "503": { "$ref": "#/components/responses/Unavailable" }
        }
      }
    },
    "/v1/verify/batch": {
      "post": {
        "tags": ["Verification"],
        "operationId": "verifyEmailBatch",
        "summary": "Check up to 100 email addresses in one call",
        "description": "Returns one result per submitted address, in the same order. A batch is refused with 429 QUOTA_EXCEEDED if the addresses it needs exceed what remains this month.",
        "parameters": [
          { "name": "mx", "in": "query", "required": false, "schema": { "type": "string", "enum": ["true", "false"], "default": "true" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/BatchRequest" },
              "examples": { "default": { "value": { "emails": ["a@relay.firefox.com", "b@mailinator.com", "c@example.com"] } } }
            }
          }
        },
        "responses": {
          "200": { "description": "Batch results and a summary.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BatchResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "503": { "$ref": "#/components/responses/Unavailable" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerKey": { "type": "http", "scheme": "bearer", "description": "A ShieldMX API key. Live keys start with smx_live_, test keys with smx_test_." }
    },
    "schemas": {
      "VerifyRequest": {
        "type": "object",
        "required": ["email"],
        "additionalProperties": false,
        "properties": {
          "email": { "type": "string", "format": "email", "minLength": 5, "maxLength": 254 }
        }
      },
      "Verdict": {
        "type": "object",
        "required": ["valid_syntax", "email", "domain", "disposable", "alias", "matched_domain", "mx_valid", "recommendation", "reason", "risk_score", "suggestion", "mode", "evaluated_at"],
        "properties": {
          "valid_syntax": { "type": "boolean", "const": true },
          "email": { "type": "string", "description": "The address, trimmed and lowercased." },
          "domain": { "type": "string" },
          "disposable": { "type": "boolean", "description": "True when the domain or a parent domain is on the disposable list." },
          "alias": { "type": "boolean", "description": "True for privacy alias services such as Firefox Relay." },
          "matched_domain": { "type": ["string", "null"], "description": "The list entry that matched, if any." },
          "mx_valid": { "type": ["boolean", "null"], "description": "Whether the domain can receive mail. Null when unknown, skipped, or decided by a rule or the list." },
          "recommendation": { "type": "string", "enum": ["ALLOW", "REVIEW", "REJECT"] },
          "reason": { "type": "string", "enum": ["clean", "disposable_domain", "no_mail_server", "privacy_alias", "customer_denied", "customer_allowed"] },
          "risk_score": { "type": "integer", "minimum": 0, "maximum": 100 },
          "suggestion": { "type": ["string", "null"], "description": "A likely intended provider, for example gmail.com for gmial.com." },
          "mode": { "type": "string", "enum": ["live", "test"] },
          "evaluated_at": { "type": "integer", "description": "Unix time in seconds." }
        }
      },
      "BatchRequest": {
        "type": "object",
        "required": ["emails"],
        "additionalProperties": false,
        "properties": {
          "emails": { "type": "array", "minItems": 1, "maxItems": 100, "items": { "type": "string" } }
        }
      },
      "BatchResponse": {
        "type": "object",
        "required": ["results", "summary"],
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "oneOf": [
                { "$ref": "#/components/schemas/Verdict" },
                {
                  "type": "object",
                  "required": ["valid_syntax", "email", "error"],
                  "properties": {
                    "valid_syntax": { "type": "boolean", "const": false },
                    "email": { "type": "string" },
                    "error": { "$ref": "#/components/schemas/Error/properties/error" }
                  }
                }
              ]
            }
          },
          "summary": {
            "type": "object",
            "properties": {
              "submitted": { "type": "integer" },
              "checked": { "type": "integer" },
              "rejected": { "type": "integer" },
              "reviewed": { "type": "integer" },
              "allowed": { "type": "integer" },
              "invalid": { "type": "integer" }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": { "type": "string" },
              "message": { "type": "string" }
            }
          }
        }
      }
    },
    "responses": {
      "BadRequest": { "description": "Malformed JSON, invalid syntax, or an invalid batch. See error.code.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Unauthorized": { "description": "The key is missing, malformed, invalid or revoked.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "RateLimited": { "description": "RATE_LIMITED (60 a minute per key, Retry-After: 60) or QUOTA_EXCEEDED for the month.", "headers": { "Retry-After": { "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Unavailable": { "description": "The disposable list is not available yet. Retry shortly. Never treat this as an allow.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    }
  }
}
