{
  "openapi": "3.0.3",
  "info": {
    "title": "Lucenri Email Validation API",
    "version": "1.0.0",
    "description": "Product-true HTTP contract for Lucenri email verification. Limits are deployment-published via GET /api/config/public. Results are decision-support signals — not delivery guarantees. This specification does not document guest trial, warmup, accuracy-smoke, or controlled mail-lab surfaces."
  },
  "servers": [
    {
      "url": "https://lucenri.com",
      "description": "Production"
    }
  ],
  "tags": [
    { "name": "Config" },
    { "name": "Verify" },
    { "name": "Jobs" },
    { "name": "Batches" },
    { "name": "Health" }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Server-side API key with lcr_ prefix. Account email must be verified."
      }
    },
    "schemas": {
      "ErrorEnvelope": {
        "type": "object",
        "required": ["success", "error"],
        "properties": {
          "success": { "type": "boolean", "enum": [false] },
          "error": { "type": "string" },
          "balance": { "type": "number" },
          "required": { "type": "number" },
          "max": { "type": "number" },
          "asyncRequired": { "type": "boolean" },
          "threshold": { "type": "number" },
          "retryAfterSec": { "type": "number" },
          "code": { "type": "string" }
        }
      },
      "CheckStep": {
        "type": "object",
        "properties": {
          "step": { "type": "string" },
          "result": { "type": "string", "enum": ["pass", "fail", "skip"] },
          "code": { "type": "string" }
        }
      },
      "VerificationResult": {
        "type": "object",
        "required": ["email", "valid", "status", "severity", "code", "confidence", "checks"],
        "properties": {
          "email": { "type": "string" },
          "valid": { "type": "boolean" },
          "status": {
            "type": "string",
            "enum": ["valid", "invalid", "disposable", "role", "typo", "catch_all", "risky", "unknown"]
          },
          "severity": {
            "type": "string",
            "enum": ["success", "warning", "error", "info"]
          },
          "code": { "type": "string" },
          "confidence": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Relative decision-support score. Not a delivery guarantee."
          },
          "checks": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CheckStep" }
          },
          "disposable": {
            "type": "boolean",
            "deprecated": true,
            "description": "Deprecated compatibility mirror when status is disposable."
          },
          "input_email": { "type": "string" },
          "alias_normalized": { "type": "boolean" },
          "free_email": { "type": "boolean" },
          "domain_age_days": { "type": "number" }
        }
      },
      "EmailListRequest": {
        "type": "object",
        "required": ["emails"],
        "properties": {
          "emails": {
            "type": "array",
            "items": { "type": "string" },
            "description": "JSON array of email strings. Server trims, lowercases, filters invalid entries, and deduplicates. File/CSV upload is a frontend workflow, not this body format."
          }
        }
      },
      "PublicConfig": {
        "type": "object",
        "properties": {
          "asyncVerifyThreshold": { "type": "integer" },
          "maxBulkEmails": { "type": "integer" },
          "planCredits": { "type": "object", "additionalProperties": { "type": "integer" } },
          "turnstileSiteKey": { "type": "string" },
          "aiRecommendationsEnabled": { "type": "boolean" },
          "inboxLiveEnabled": { "type": "boolean" },
          "oauthProviders": { "type": "object" },
          "checkoutPaymentMethods": { "type": "object" }
        }
      }
    }
  },
  "paths": {
    "/api/config/public": {
      "get": {
        "tags": ["Config"],
        "summary": "Published deployment limits and public product config",
        "security": [],
        "responses": {
          "200": {
            "description": "Public configuration including asyncVerifyThreshold and maxBulkEmails",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PublicConfig" }
              }
            }
          }
        }
      }
    },
    "/api/verify/ping": {
      "get": {
        "tags": ["Health"],
        "summary": "Verify service readiness ping",
        "security": [],
        "responses": {
          "200": {
            "description": "Service reachable",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "service": { "type": "string" },
                    "ready": { "type": "boolean" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/verify/single": {
      "post": {
        "tags": ["Verify"],
        "summary": "Verify one email (synchronous)",
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["email"],
                "properties": {
                  "email": { "type": "string", "example": "name@example.com" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification completed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "result": { "$ref": "#/components/schemas/VerificationResult" },
                    "balance": { "type": "number" }
                  }
                }
              }
            }
          },
          "400": { "description": "Valid email required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "402": { "description": "Insufficient credits", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "403": { "description": "Email not verified (API key) or blocked", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "429": { "description": "Rate limited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "500": { "description": "Verification failed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "503": { "description": "Service unavailable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } }
        }
      }
    },
    "/api/verify/bulk": {
      "post": {
        "tags": ["Verify"],
        "summary": "Verify a synchronous email list (JSON)",
        "description": "Rejects lists at or above the published asyncVerifyThreshold. Prefer POST /api/verify/jobs for large lists. SSE streaming is a separate advanced product transport and is not the primary public integration path.",
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/EmailListRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Synchronous verification completed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "results": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/VerificationResult" }
                    },
                    "balance": { "type": "number" },
                    "mode": { "type": "string", "enum": ["sync"] },
                    "batchId": { "type": "string", "nullable": true },
                    "retentionHours": { "type": "number" },
                    "expiresAt": { "type": "string", "nullable": true }
                  }
                }
              }
            }
          },
          "400": { "description": "Invalid list or async required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "402": { "description": "Insufficient credits", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "429": { "description": "Rate limited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "500": { "description": "Verification failed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "503": { "description": "Service unavailable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } }
        }
      }
    },
    "/api/verify/jobs": {
      "post": {
        "tags": ["Jobs"],
        "summary": "Create asynchronous verification job",
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/EmailListRequest" }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "mode": { "type": "string", "enum": ["async"] },
                    "jobId": { "type": "string" },
                    "status": { "type": "string", "enum": ["queued"] },
                    "total": { "type": "integer" },
                    "balance": { "type": "number" },
                    "message": { "type": "string" }
                  }
                }
              }
            }
          },
          "400": { "description": "Invalid list", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "402": { "description": "Insufficient credits", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "429": { "description": "Rate limited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "500": { "description": "Could not queue job", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "503": { "description": "Service unavailable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } }
        }
      }
    },
    "/api/verify/jobs/{id}": {
      "get": {
        "tags": ["Jobs"],
        "summary": "Poll asynchronous verification job",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Job status payload",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "jobId": { "type": "string" },
                    "status": {
                      "type": "string",
                      "enum": ["queued", "processing", "completed", "failed"]
                    },
                    "total": { "type": "integer" },
                    "processed": { "type": "integer" },
                    "progress": { "type": "integer" },
                    "error": { "type": "string", "nullable": true },
                    "batchId": { "type": "string" },
                    "retentionHours": { "type": "number" },
                    "results": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/VerificationResult" }
                    },
                    "partialResults": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/VerificationResult" }
                    },
                    "summary": { "type": "object" },
                    "chunked": { "type": "boolean" }
                  }
                }
              }
            }
          },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "404": { "description": "Job not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "500": { "description": "Could not fetch job status", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } }
        }
      }
    },
    "/api/verify/batches": {
      "get": {
        "tags": ["Batches"],
        "summary": "List recent verification batches",
        "security": [{ "ApiKeyAuth": [] }],
        "responses": {
          "200": {
            "description": "Recent batches",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "batches": { "type": "array", "items": { "type": "object" } },
                    "retentionHours": { "type": "number" }
                  }
                }
              }
            }
          },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "500": { "description": "Could not list scans", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } }
        }
      }
    },
    "/api/verify/batches/{id}": {
      "get": {
        "tags": ["Batches"],
        "summary": "Get verification batch detail",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Batch detail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "batch": { "type": "object" },
                    "retentionHours": { "type": "number" }
                  }
                }
              }
            }
          },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "404": { "description": "Scan not found or expired", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "500": { "description": "Could not load scan", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } }
        }
      }
    },
    "/api/verify/batches/{id}/results": {
      "get": {
        "tags": ["Batches"],
        "summary": "Paginated batch results",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Results page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "batch": { "type": "object" },
                    "retentionHours": { "type": "number" }
                  }
                }
              }
            }
          },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "404": { "description": "Scan not found or expired", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "500": { "description": "Could not load page", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } }
        }
      }
    },
    "/api/verify/batches/{id}/export.csv": {
      "get": {
        "tags": ["Batches"],
        "summary": "Export batch results as CSV",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "CSV stream",
            "content": {
              "text/csv": {
                "schema": { "type": "string", "format": "binary" }
              }
            }
          },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "404": { "description": "Scan not found or expired", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "500": { "description": "Could not export", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } }
        }
      }
    },
    "/api/verify/batches/{id}/downloaded": {
      "post": {
        "tags": ["Batches"],
        "summary": "Mark batch downloaded (may purge retained result payload)",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Marked downloaded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "purged": { "type": "boolean" }
                  }
                }
              }
            }
          },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "404": { "description": "Scan not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "500": { "description": "Could not mark downloaded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } }
        }
      }
    }
  }
}
