{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/billionverify-com/json-schema/billionverify-com-single-verify-api-response-schema.json",
  "title": "SingleVerifyApiResponse",
  "x-generated": "2026-11-02",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/billionverify-com-openapi.yaml#/components/schemas/SingleVerifyApiResponse",
  "type": "object",
  "properties": {
    "success": {
      "type": "boolean"
    },
    "code": {
      "type": "string"
    },
    "message": {
      "type": "string"
    },
    "data": {
      "$ref": "#/$defs/EmailVerificationResult"
    },
    "error": {
      "allOf": [
        {
          "$ref": "#/$defs/ErrorInfo"
        }
      ],
      "nullable": true,
      "description": "Mirror of `response.StandardResponse.Error`. Populated only on\nnon-200 paths (omitempty)—success responses won't include it.\n"
    }
  },
  "$defs": {
    "DomainReputation": {
      "type": "object",
      "description": "MX IP reputation (blacklist) info attached to a verification result.\nDisplay-only: never affects `status`, `score`, `reason`, or billing.\n\n`drop_*` fields come from the Spamhaus DROP dataset (netblocks controlled by\ncriminal operations / bulletproof hosters). DROP is used under the Spamhaus\nProject's free licence, which requires attribution — `drop_attribution` carries\nthe copyright and terms text and must be surfaced wherever `drop_listed` is shown.\n",
      "properties": {
        "mx_ip": {
          "type": [
            "string",
            "null"
          ],
          "description": "IP address of the MX server (omitempty in Go)."
        },
        "is_listed": {
          "type": "boolean",
          "description": "Whether the IP is on any blacklist"
        },
        "blacklists": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "List of blacklists where the IP appears (sorted)"
        },
        "advisory": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Zones that answered with a graded, non-listing code (currently `dnsbl.spfbl.net`:\nany code other than `127.0.0.2`, e.g. grey-list `.3` / low-reputation `.4`).\nInformational only: never sets `is_listed`.\nOmitted when empty.\n"
        },
        "checked": {
          "type": "boolean",
          "description": "Whether reputation check was performed"
        },
        "drop_listed": {
          "type": "boolean",
          "description": "MX IP falls inside a Spamhaus DROP netblock. Omitted when false."
        },
        "drop_cidr": {
          "type": "string",
          "description": "The matching DROP netblock. Present only when `drop_listed` is true."
        },
        "drop_sblid": {
          "type": "string",
          "description": "Spamhaus SBL reference for the listing; look it up on spamhaus.org for the reason."
        },
        "drop_attribution": {
          "type": "string",
          "description": "Required Spamhaus Project attribution for the DROP data (copyright + terms URL)."
        }
      }
    },
    "EmailVerificationResult": {
      "type": "object",
      "properties": {
        "email": {
          "type": "string",
          "format": "email",
          "description": "The verified email address"
        },
        "status": {
          "type": "string",
          "enum": [
            "valid",
            "invalid",
            "unknown",
            "risky",
            "disposable",
            "catchall",
            "role"
          ],
          "description": "Verification status. `unknown` is the only status meaning \"we could not\nreach a conclusion\" (retry may help; not billed). `risky` means the address\nis most likely deliverable but its domain carries a confirmed abuse\nhistory — see `risk_reasons`; it is a deterministic conclusion, is billed,\nand retrying will not change it.\n"
        },
        "score": {
          "type": "number",
          "format": "float",
          "minimum": 0,
          "maximum": 1,
          "description": "Quality score (0-1)"
        },
        "is_deliverable": {
          "type": "boolean",
          "description": "Whether the verification channel confirmed that the target address is deliverable. The evidence may come from SMTP or a supported provider API, and can coexist with is_catchall and is_role."
        },
        "is_disposable": {
          "type": "boolean",
          "description": "Whether it's a disposable/temporary email"
        },
        "is_catchall": {
          "type": "boolean",
          "description": "Whether the domain accepts all emails"
        },
        "is_role": {
          "type": "boolean",
          "description": "Whether it's a role-based email (info@, support@, etc.)"
        },
        "is_free": {
          "type": "boolean",
          "description": "Whether it's from a free email provider"
        },
        "domain": {
          "type": "string",
          "description": "Email domain"
        },
        "domain_age": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Domain age in years (omitempty in Go = pointer; absent when unknown)."
        },
        "mx_records": {
          "type": [
            "array",
            "null"
          ],
          "items": {
            "type": "string"
          },
          "description": "MX records for the domain (omitempty array)."
        },
        "mx_ip": {
          "type": [
            "string",
            "null"
          ],
          "description": "Primary MX IP address when known (pointer in Go; absent if MX unresolved)."
        },
        "mx_asn": {
          "type": "integer",
          "description": "Primary MX autonomous system number when known"
        },
        "mx_as_org": {
          "type": [
            "string",
            "null"
          ],
          "description": "Primary MX autonomous system organization when known (pointer in Go)."
        },
        "domain_reputation": {
          "allOf": [
            {
              "$ref": "#/$defs/DomainReputation"
            }
          ],
          "nullable": true,
          "description": "MX IP reputation (blacklist) info. Populated only when the request had\n`include_domain_reputation: true` AND a cached result was returned (otherwise\nit's recomputed on the live SMTP path and inlined). Pointer in Go = absent when\nnot requested.\n"
        },
        "check_smtp": {
          "type": "boolean",
          "description": "Whether the result includes SMTP verification data"
        },
        "smtp_result": {
          "type": "string",
          "enum": [
            "not_checked",
            "deliverable",
            "undeliverable",
            "inconclusive"
          ],
          "description": "SMTP channel outcome, independent of the main status. For example, disposable, role, and catchall can coexist with deliverable."
        },
        "catch_all_evidence": {
          "type": "string",
          "enum": [
            "direct_no",
            "cached_no",
            "direct_yes",
            "cached_yes",
            "blocked",
            "tempfail",
            "unknown"
          ],
          "description": "How the domain-level catch-all probe behaved for this result. direct_no / cached_no mean a random recipient was rejected (the domain is not catch-all); direct_yes / cached_yes mean it was accepted (the domain is catch-all); blocked / tempfail / unknown mean the probe was inconclusive. Absent when no SMTP probe ran (provider API path, syntax / DNS short-circuit, legacy history). A valid result scores 1.00 only with direct_no or cached_no."
        },
        "reason": {
          "type": "string",
          "description": "Detailed reason for the verification result. Rule-based reasons include smtp_deliverable, mailbox_not_found, no_mx_records, invalid_syntax, disposable_domain, role_account, catch_all_domain, catch_all_inferred, m365_internal_relay, gateway_accept_all, forwarding_alias, smtp_unverifiable, smtp_greylisted, mimecast_greylist, proofpoint_ad_lookup, smtp_timeout, dns_timeout, smtp_rate_limited, smtp_blocked, smtp_eof_blocked, smtp_connection_failed, smtp_egress_unavailable, smtp_egress_unselectable, yahoo_api_rate_limited, m365_ip_rep_block, google_rate_limit, google_dmarc_misalignment, mailbox_full, policy_temp_fail, smtp_550_unclassified, and null_mx."
        },
        "suggestion": {
          "type": "string",
          "description": "Deprecated. Use `domain_suggestion`."
        },
        "domain_suggestion": {
          "type": "string",
          "description": "Suggested correction if typo detected"
        },
        "alias_of": {
          "type": "string",
          "format": "email",
          "description": "The canonical email address, present only when the submitted address differs\nfrom it. Detects Gmail dot-insensitive / plus-addressing aliases\n(`alice+news@gmail.com` -> `alice@gmail.com`) and Microsoft consumer\nplus-addressing (`outlook.com`, `hotmail.com`, `live.com`, `msn.com`). Display\nhint only: does not affect status, score, or billing. See\ndocs/verification/alias/alias-detection.md.\n"
        },
        "risk_reasons": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "spamhaus_dbl_spam",
              "spamhaus_dbl_phish",
              "spamhaus_dbl_malware",
              "spamhaus_dbl_botnet_cc"
            ]
          },
          "description": "External risk signals that downgraded an otherwise deliverable address to\n`risky`. Historically sourced from the Spamhaus Domain Blocklist (DBL) lookup\non the recipient domain; that lookup was retired on 2026-09-11, so new results\nno longer carry these values. Absent or empty when no external risk signal matched.\n\nA `risky` result means the mailbox most likely *does* accept mail, but the\nrecipient domain has a confirmed abuse history — sending to it can damage\nyour sending reputation. Treat it like `disposable`: remove it from the list.\nDomains listed by Spamhaus as *abused legitimate sites* (compromised but\notherwise normal websites) are deliberately excluded and never produce a\n`risky` result.\n"
        },
        "smtp_response": {
          "type": [
            "string",
            "null"
          ],
          "description": "Redacted raw SMTP response detail. Only returned for accounts or API keys in the verbose SMTP response allowlist (pointer in Go = absent for most clients)."
        },
        "response_time": {
          "type": "integer",
          "description": "Response time in milliseconds"
        },
        "credits_used": {
          "type": "integer",
          "description": "Number of credits consumed"
        },
        "verification_mode": {
          "type": "string",
          "description": "Q4 (2026-05-08): Which verification path produced this result. Helps clients\ninterpret retryability and explain billing.\n",
          "enum": [
            "smtp",
            "yahoo_api",
            "cache_hit_global",
            "cache_hit_user",
            "cache_hit_db_history",
            "cache_hit_transient",
            "skip_anti_verification",
            "skip_smtp_unknown",
            "syntax_only",
            "unknown_path"
          ]
        },
        "retryable": {
          "type": "boolean",
          "description": "Q4 (2026-05-08): Whether re-running with `force_refresh=true` could yield a\ndifferent result. true for transient unknowns (rate_limited / timeouts /\nyahoo_api unavailable). false for deterministic outcomes and for\nanti-verification / smtp_blocked / dmarc misalignment.\n\nOnly `unknown` results can ever be retryable. `risky` is a deterministic\nconclusion and is always false (changed 2026-08-04).\n"
        },
        "smtputf8": {
          "type": "boolean",
          "description": "P1-2 (2026-05-13): True when the email's local-part contains any non-ASCII\ncharacter (RFC 6531 SMTPUTF8 required). Clients planning to send mail to\nthis address must use an outbound MTA that supports the SMTPUTF8 extension;\nMTAs lacking SMTPUTF8 will reject non-ASCII local-parts.\nBorrowed from JoshData/python-email-validator `ValidatedEmail.smtputf8`.\n"
        },
        "retry_after_seconds": {
          "type": [
            "integer",
            "null"
          ],
          "description": "SMTP retry hint parsed from a transient remote SMTP response body\n(for example greylisting \"try again in 60 seconds\"). Omitted when\nthe remote server did not provide a usable delay.\n"
        }
      }
    },
    "ErrorInfo": {
      "type": "object",
      "properties": {
        "code": {
          "type": "string",
          "description": "Detailed error code"
        },
        "message": {
          "type": "string",
          "description": "Detailed error message"
        },
        "description": {
          "type": [
            "string",
            "null"
          ],
          "description": "Additional human-readable context (omitempty in Go)"
        },
        "details": {
          "description": "Additional structured context (any type; usually map or string; omitempty in Go).",
          "nullable": true
        }
      }
    }
  }
}