openapi: 3.1.0
info:
  title: emailzeno API
  version: "1"
  description: |
    Real-time SMTP email verification API. Every probe uses EHLO → MAIL FROM → RCPT TO → QUIT —
    no DATA command is ever sent. Your sender reputation stays clean.

    **Base URL:** `https://app.emailzeno.com/api`

    **Authentication:** Bearer token. Pass your API key in the `Authorization` header.

    **Credits:** Single verify costs 1 credit per call (deducted before probing; refunded if engine is
    unavailable). Bulk costs 1 credit per email, deducted upfront.

    **Test mode:** Use an API key with test-mode enabled → synthetic result returned, no credits used,
    no engine call, no DB writes.

    # If you change the response shape in PublicVerifyController.php, update this file too.
  contact:
    email: hello@emailzeno.com
  license:
    name: Proprietary
    url: https://emailzeno.com/terms

servers:
  - url: https://app.emailzeno.com/api
    description: Production

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: >
        Obtain your API key from the **API & Usage** page inside the app.
        Keys are prefixed `ec_live_` (production) or `ec_test_` (test mode).

  schemas:
    VerifySingleRequest:
      type: object
      required: [email]
      properties:
        email:
          type: string
          format: email
          example: test@example.com

    VerifySingleResponse:
      type: object
      properties:
        email:
          type: string
          example: test@example.com
        status:
          type: string
          enum: [deliverable, undeliverable, risky, unknown]
          example: deliverable
        flags:
          type: array
          items:
            type: string
          example: []
        smtp_code:
          type: [integer, 'null']
          example: 250
        smtp_message:
          type: [string, 'null']
          example: "2.1.5 OK"
        mx_host:
          type: [string, 'null']
          example: aspmx.l.google.com
        duration_ms:
          type: [integer, 'null']
          example: 312
        quality_score:
          type: integer
          description: 0–100 confidence score
          example: 92
        quality_band:
          type: string
          enum: [excellent, good, risky, poor]
          example: excellent
        mx_provider:
          type: [string, 'null']
          example: google
        is_free_mailbox:
          type: [boolean, 'null']
          example: false
        signals:
          type: object
          properties:
            has_mx:
              type: boolean
            is_disposable:
              type: boolean
            is_role:
              type: boolean
            smtp_ok:
              type: boolean
          example:
            has_mx: true
            is_disposable: false
            is_role: false
            smtp_ok: true

    VerifyBulkRequest:
      type: object
      required: [emails]
      properties:
        emails:
          type: array
          items:
            type: string
            format: email
          minItems: 1
          maxItems: 1000
          example: ["a@example.com", "b@example.com"]

    VerifyBulkResponse:
      type: object
      properties:
        job_id:
          type: integer
          example: 42
        email_count:
          type: integer
          example: 2
        status:
          type: string
          enum: [queued]
          example: queued
        message:
          type: string
          example: "Job queued. Poll /api/jobs/{job_id} for status."
        credits_used:
          type: integer
          example: 2

    ErrorInsufficientCredits:
      type: object
      properties:
        error:
          type: string
          example: "Insufficient credits. Purchase a credit pack or upgrade your plan."
        code:
          type: string
          enum: [insufficient_credits]
        required:
          type: integer
          description: Credits required (bulk only)
          example: 50
        balance:
          type: integer
          description: Current balance (bulk only)
          example: 10

    ErrorEngineUnavailable:
      type: object
      properties:
        error:
          type: string
          example: "Verification service temporarily unavailable."

security:
  - bearerAuth: []

paths:
  /v1/verify:
    post:
      operationId: verifySingle
      summary: Verify a single email address
      description: |
        Synchronously probe one email address. Returns full SMTP result + quality score.

        Costs **1 credit**. Credit is deducted before the probe runs. If the verification engine
        is unavailable (503), the credit is automatically refunded.

        Rate limited: 60 req/min on free tier, higher on paid plans.
      tags: [Verification]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VerifySingleRequest'
            example:
              email: test@example.com
      responses:
        "200":
          description: Verification complete
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VerifySingleResponse'
              example:
                email: test@example.com
                status: deliverable
                flags: []
                smtp_code: 250
                smtp_message: "2.1.5 OK"
                mx_host: aspmx.l.google.com
                duration_ms: 312
                quality_score: 92
                quality_band: excellent
                mx_provider: google
                is_free_mailbox: false
                signals:
                  has_mx: true
                  is_disposable: false
                  is_role: false
                  smtp_ok: true
        "402":
          description: Insufficient credits
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorInsufficientCredits'
        "429":
          description: Rate limit exceeded
        "503":
          description: Verification engine temporarily unavailable (credit refunded)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEngineUnavailable'

  /v1/verify/bulk:
    post:
      operationId: verifyBulk
      summary: Verify up to 1,000 emails in one batch
      description: |
        Enqueue an async verification job for up to **1,000 email addresses**. Returns a `job_id`
        immediately (HTTP 202). Poll `GET /api/jobs/{job_id}` for progress and results.

        Credits equal to `email_count` are deducted upfront before queuing. If your balance is
        insufficient the entire request is rejected (402) — no partial runs.
      tags: [Verification]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VerifyBulkRequest'
            example:
              emails:
                - a@example.com
                - b@example.com
      responses:
        "202":
          description: Job queued successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VerifyBulkResponse'
              example:
                job_id: 42
                email_count: 2
                status: queued
                message: "Job queued. Poll /api/jobs/{job_id} for status."
                credits_used: 2
        "402":
          description: Insufficient credits
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorInsufficientCredits'
              example:
                error: "Insufficient credits. Need 50, balance is 10."
                code: insufficient_credits
                required: 50
                balance: 10
        "422":
          description: Validation error (e.g. more than 1,000 emails)
        "429":
          description: Rate limit exceeded

tags:
  - name: Verification
    description: Email verification endpoints
