{
  "openapi": "3.0.3",
  "info": {
    "title": "EmailValidation API",
    "description": "Honest email validation API with real-time SMTP verification, typo detection, disposable email filtering, and per-email diagnostics. No fake \"99% accuracy\" claims — transparent confidence levels (`verified`, `basic`, `low`).\n\n**Auth**: API key via `Authorization: Bearer <key>` header. Get one in your [dashboard](https://email-validation-api.net/dashboard/api-keys).\n\n**Pricing**: 1 credit = 1 email validation. 25 free credits on signup. Volume discount from $0.0039 down to $0.0025 per credit.",
    "version": "1.0.0",
    "contact": {
      "name": "Support",
      "url": "https://email-validation-api.net/contact"
    },
    "license": {
      "name": "Commercial",
      "url": "https://email-validation-api.net/terms"
    }
  },
  "servers": [
    { "url": "https://email-validation-api.net/api/v1", "description": "Production" }
  ],
  "security": [{ "bearerAuth": [] }],
  "tags": [
    { "name": "Validation",   "description": "Validate one or many emails" },
    { "name": "Batches",      "description": "Async batches for large lists" },
    { "name": "Account",      "description": "Wallet & credits" }
  ],
  "paths": {
    "/validate": {
      "post": {
        "tags": ["Validation"],
        "summary": "Validate a single email",
        "description": "Synchronous validation: full pipeline (syntax, MX/DNS, real SMTP RCPT TO probe with FCrDNS-validated HELO, disposable filter, typo detection). Debits 1 credit on success. Typical response in 100-400ms.",
        "operationId": "validate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ValidateRequest" },
              "example": { "email": "alice@example.com" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Validation result",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ValidationResult" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/InsufficientCredits" },
          "422": { "$ref": "#/components/responses/InvalidInput" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/validate-bulk": {
      "post": {
        "tags": ["Validation"],
        "summary": "Validate up to 200 emails synchronously",
        "description": "Synchronous bulk validation. Use for small lists where you want results immediately. Debits N credits (N = unique valid-format emails). Response time scales linearly with list size.",
        "operationId": "validateBulk",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/BulkRequest" },
              "example": { "emails": ["alice@example.com", "bob@example.com", "invalid-line"] }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bulk results",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BulkResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/InsufficientCredits" },
          "422": { "$ref": "#/components/responses/InvalidInput" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/validate-async": {
      "post": {
        "tags": ["Batches"],
        "summary": "Submit an async batch (up to 10,000 emails)",
        "description": "Returns a `batch_id` immediately. Background workers process the list and optionally POST a webhook on completion. Credits are debited upfront. Poll `/batch/{id}` for status, or set `webhook_url` to be notified.\n\nWebhook payload includes a `X-EmailValidation-Signature: sha256=...` header for HMAC verification when `webhook_secret` is provided.",
        "operationId": "validateAsync",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/AsyncRequest" },
              "example": {
                "emails": ["alice@example.com", "bob@example.com"],
                "webhook_url": "https://your-app.com/hooks/email-batch",
                "webhook_secret": "shhh-this-is-secret"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Batch accepted and queued",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AsyncAccepted" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/InsufficientCredits" },
          "422": { "$ref": "#/components/responses/InvalidInput" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/batch/{id}": {
      "get": {
        "tags": ["Batches"],
        "summary": "Check batch status & results",
        "description": "Returns current status (`queued`, `processing`, `completed`, `failed`), progress (0..1), and full results once `status=completed`.",
        "operationId": "getBatch",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "pattern": "^bat_[a-f0-9]{32}$" },
            "example": "bat_a1b2c3d4e5f6789012345678901234ab"
          }
        ],
        "responses": {
          "200": {
            "description": "Batch status",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BatchStatus" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": {
            "description": "Batch not found",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/credits": {
      "get": {
        "tags": ["Account"],
        "summary": "Get current wallet balance",
        "description": "Free endpoint (no credit debited). Returns balance, total purchased and consumed.",
        "operationId": "getCredits",
        "responses": {
          "200": {
            "description": "Wallet summary",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Credits" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key from your dashboard. Also accepted as `X-API-Key: <key>` header or `?api_key=<key>` query parameter."
      }
    },
    "schemas": {
      "ValidateRequest": {
        "type": "object",
        "required": ["email"],
        "properties": {
          "email": { "type": "string", "format": "email", "example": "alice@example.com" }
        }
      },
      "BulkRequest": {
        "type": "object",
        "required": ["emails"],
        "properties": {
          "emails": {
            "type": "array",
            "items": { "type": "string", "format": "email" },
            "minItems": 1,
            "maxItems": 200,
            "example": ["alice@example.com", "bob@example.com"]
          }
        }
      },
      "AsyncRequest": {
        "type": "object",
        "required": ["emails"],
        "properties": {
          "emails": {
            "type": "array",
            "items": { "type": "string", "format": "email" },
            "minItems": 1,
            "maxItems": 10000
          },
          "webhook_url": {
            "type": "string",
            "format": "uri",
            "description": "Optional. HTTPS URL we POST to once the batch is complete. Private/loopback IPs are rejected."
          },
          "webhook_secret": {
            "type": "string",
            "maxLength": 256,
            "description": "Optional. If provided, we sign the webhook payload with HMAC-SHA256 in `X-EmailValidation-Signature`."
          }
        }
      },
      "ValidationResult": {
        "type": "object",
        "properties": {
          "email":            { "type": "string", "example": "alice@example.com" },
          "valid":            { "type": "boolean", "example": true, "description": "Final boolean verdict. False for syntax errors, disposable, MX missing, SMTP reject, etc." },
          "reason":           { "type": "string", "example": "ok", "description": "Machine-readable reason code: ok, syntax_invalid, disposable, mx_missing, smtp_reject, smtp_anti_probe, smtp_silent, role_account, typo_suggested, ..." },
          "confidence":       { "type": "string", "enum": ["verified", "basic", "low"], "example": "verified" },
          "score":            { "type": "number", "format": "float", "example": 0.92, "description": "0..1 confidence score" },
          "details": {
            "type": "object",
            "properties": {
              "syntax_valid":    { "type": "boolean" },
              "domain":          { "type": "string" },
              "mx_present":      { "type": "boolean" },
              "mx_behavior":     { "type": "string", "enum": ["strict", "catch_all", "anti_probe", "silent", "unknown"] },
              "disposable":      { "type": "boolean" },
              "role_account":    { "type": "boolean" },
              "smtp":            { "type": "boolean", "nullable": true, "description": "true=accepted, false=rejected, null=skipped/inconclusive" },
              "suggested_email": { "type": "string", "nullable": true, "description": "Typo correction (Levenshtein <= 2 to a popular domain)" }
            }
          },
          "cached":             { "type": "boolean", "description": "true if served from MX/SMTP behavior cache" },
          "credits_remaining":  { "type": "integer", "example": 4287 }
        }
      },
      "BulkResponse": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ValidationResult" }
          },
          "summary": {
            "type": "object",
            "properties": {
              "total":             { "type": "integer" },
              "valid":             { "type": "integer" },
              "invalid":           { "type": "integer" },
              "credits_consumed":  { "type": "integer" },
              "credits_remaining": { "type": "integer" }
            }
          }
        }
      },
      "AsyncAccepted": {
        "type": "object",
        "properties": {
          "batch_id":           { "type": "string", "example": "bat_a1b2c3d4e5f6789012345678901234ab" },
          "status":             { "type": "string", "example": "queued" },
          "total":              { "type": "integer", "example": 5000 },
          "credits_remaining":  { "type": "integer", "example": 5000 },
          "webhook_configured": { "type": "boolean" },
          "status_url":         { "type": "string", "format": "uri" }
        }
      },
      "BatchStatus": {
        "type": "object",
        "properties": {
          "batch_id":    { "type": "string" },
          "status":      { "type": "string", "enum": ["queued", "processing", "completed", "failed"] },
          "total":       { "type": "integer" },
          "processed":   { "type": "integer" },
          "progress":    { "type": "number", "format": "float", "example": 0.42 },
          "created_at":  { "type": "string", "format": "date-time" },
          "started_at":  { "type": "string", "format": "date-time", "nullable": true },
          "completed_at":{ "type": "string", "format": "date-time", "nullable": true },
          "has_webhook": { "type": "boolean" },
          "results": {
            "type": "array",
            "description": "Only present when status=completed",
            "items": { "$ref": "#/components/schemas/ValidationResult" }
          },
          "error":       { "type": "string", "nullable": true }
        }
      },
      "Credits": {
        "type": "object",
        "properties": {
          "balance":         { "type": "integer", "example": 4287 },
          "total_purchased": { "type": "integer", "example": 10000 },
          "total_consumed":  { "type": "integer", "example": 5713 },
          "last_topup_at":   { "type": "string", "format": "date-time", "nullable": true },
          "top_up_url":      { "type": "string", "format": "uri" }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error":   { "type": "string", "example": "invalid_input" },
          "message": { "type": "string", "nullable": true }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "InsufficientCredits": {
        "description": "Wallet balance below required credits",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                { "$ref": "#/components/schemas/Error" },
                {
                  "type": "object",
                  "properties": {
                    "balance":    { "type": "integer" },
                    "needed":     { "type": "integer" },
                    "top_up_url": { "type": "string", "format": "uri" }
                  }
                }
              ]
            }
          }
        }
      },
      "InvalidInput": {
        "description": "Validation failed on the request body",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "RateLimited": {
        "description": "Too many requests. Headers `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, `Retry-After` are returned.",
        "headers": {
          "X-RateLimit-Limit":     { "schema": { "type": "integer" } },
          "X-RateLimit-Remaining": { "schema": { "type": "integer" } },
          "X-RateLimit-Reset":     { "schema": { "type": "integer" } },
          "Retry-After":           { "schema": { "type": "integer" } }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  }
}
