{
  "openapi": "3.1.0",
  "info": {
    "title": "Plumbline",
    "version": "1.0.0",
    "description": "Email and phone validation for AI agents and apps. Each check costs 1 credit. Without an API key, each client gets 50 free checks per day (UTC). Email checks cover format, domain existence, MX / null MX, disposable providers, role addresses, free providers and typo suggestions; they do not probe the mailbox. Phone checks cover validity, country, line type and formatting; they do not check whether the line is active.",
    "contact": { "email": "sawhorsesites@gmail.com", "url": "https://plumbline-verify.pages.dev" }
  },
  "servers": [{ "url": "https://plumbline-verify.pages.dev" }],
  "security": [{}, { "bearer": [] }, { "apiKeyHeader": [] }, { "apiKeyQuery": [] }],
  "components": {
    "securitySchemes": {
      "bearer": { "type": "http", "scheme": "bearer", "description": "Authorization: Bearer pl_..." },
      "apiKeyHeader": { "type": "apiKey", "in": "header", "name": "X-API-Key" },
      "apiKeyQuery": { "type": "apiKey", "in": "query", "name": "key" }
    },
    "schemas": {
      "EmailResult": {
        "type": "object",
        "properties": {
          "input": { "type": "string" },
          "email": { "type": ["string", "null"], "description": "Normalized address (domain lowercased)." },
          "verdict": { "type": "string", "enum": ["valid", "risky", "invalid"] },
          "score": { "type": "integer", "minimum": 0, "maximum": 100 },
          "valid_format": { "type": "boolean" },
          "domain": { "type": ["string", "null"] },
          "domain_exists": { "type": ["boolean", "null"] },
          "accepts_mail": { "type": ["boolean", "null"] },
          "mx": { "type": "array", "items": { "type": "string" } },
          "disposable": { "type": "boolean" },
          "role_account": { "type": "boolean" },
          "free_provider": { "type": "boolean" },
          "suggestion": { "type": ["string", "null"], "description": "Corrected address when the domain looks like a typo of a popular provider." },
          "mailbox_checked": { "type": "boolean", "const": false },
          "reasons": { "type": "array", "items": { "type": "string" } }
        }
      },
      "PhoneResult": {
        "type": "object",
        "properties": {
          "input": { "type": "string" },
          "verdict": { "type": "string", "enum": ["valid", "risky", "invalid"] },
          "valid": { "type": "boolean" },
          "possible": { "type": "boolean" },
          "e164": { "type": ["string", "null"] },
          "international": { "type": ["string", "null"] },
          "national": { "type": ["string", "null"] },
          "country": { "type": ["string", "null"], "description": "ISO 3166-1 alpha-2" },
          "calling_code": { "type": ["string", "null"] },
          "type": { "type": ["string", "null"], "enum": ["mobile", "landline", "landline_or_mobile", "toll_free", "premium_rate", "shared_cost", "voip", "personal_number", "pager", "uan", "voicemail", null] },
          "line_checked": { "type": "boolean", "const": false },
          "reasons": { "type": "array", "items": { "type": "string" } }
        }
      },
      "Error": {
        "type": "object",
        "properties": { "error": { "type": "string" }, "message": { "type": "string" }, "buy_url": { "type": "string" } }
      }
    },
    "responses": {
      "PaymentRequired": { "description": "Out of credits, or the free daily allowance is used up.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Unauthorized": { "description": "Unknown API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    }
  },
  "paths": {
    "/v1/email": {
      "get": {
        "operationId": "validateEmail",
        "summary": "Validate an email address",
        "parameters": [{ "name": "email", "in": "query", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Result", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailResult" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" }
        }
      }
    },
    "/v1/phone": {
      "get": {
        "operationId": "validatePhone",
        "summary": "Validate a phone number",
        "parameters": [
          { "name": "phone", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "country", "in": "query", "required": false, "schema": { "type": "string", "pattern": "^[A-Za-z]{2}$" }, "description": "Default country when the number has no +country code." }
        ],
        "responses": {
          "200": { "description": "Result", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PhoneResult" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" }
        }
      }
    },
    "/v1/batch": {
      "post": {
        "operationId": "validateBatch",
        "summary": "Validate up to 100 emails and phone numbers (1 credit each)",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "emails": { "type": "array", "items": { "type": "string" } }, "phones": { "type": "array", "items": { "type": "string" } }, "country": { "type": "string" } } } } } },
        "responses": {
          "200": { "description": "Results", "content": { "application/json": { "schema": { "type": "object", "properties": { "count": { "type": "integer" }, "emails": { "type": "array", "items": { "$ref": "#/components/schemas/EmailResult" } }, "phones": { "type": "array", "items": { "$ref": "#/components/schemas/PhoneResult" } } } } } } },
          "402": { "$ref": "#/components/responses/PaymentRequired" }
        }
      }
    },
    "/v1/account": {
      "get": {
        "operationId": "getAccount",
        "summary": "Credits remaining for the API key",
        "responses": { "200": { "description": "Balance" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    }
  }
}
