{
  "swagger": "2.0",
  "info": {
    "description": "Pre-validation of e-mail addresses at registration time.\n\nThe service answers three separate questions and never conflates them:\nis the address well-formed, can the domain receive mail, and did a\ndestination accept this recipient in one transaction.\n\nIt does NOT prove that anybody controls a mailbox. Only a confirmation\nmessage someone acts on does that. An inconclusive answer therefore never\nblocks a registration — the recommended action is to proceed behind a\nconfirmation message.\n\nAn address that fails verification is a SUCCESSFUL request. The 4xx and 5xx\nresponses documented here mean the request was malformed or the service\ncould not answer, never that the address is unusable.",
    "title": "Emvero Verification API",
    "contact": {},
    "version": "1.0"
  },
  "basePath": "/",
  "paths": {
    "/v1/verifications": {
      "post": {
        "security": [
          {
            "CallerToken": []
          }
        ],
        "description": "Runs the deterministic checks — syntax, domain, mail routing — and, where\nthe address survives them, asks for recipient evidence within the caller's\nwait budget. When that budget expires the answer is `pending` with a\n`retry_after_ms` hint; retrieve the finished result by identifier.\n\nA negative verdict about an address is a 200 with a negative `status`, not\nan error status.\n\nEvery metered answer carries `X-Credits-Remaining`, including the refusal when\nthe balance is empty. It is absent when the ledger could not be consulted at\nall — a number this service does not have is not reported as a zero.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "verifications"
        ],
        "summary": "Verify an address",
        "parameters": [
          {
            "type": "string",
            "description": "Repeating a key within the idempotency window returns the original result instead of starting a second verification",
            "name": "Idempotency-Key",
            "in": "header"
          },
          {
            "description": "Address and the signed registration reference",
            "name": "request",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/api.VerificationRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A verdict, or `pending` when deep evidence is still queued",
            "schema": {
              "$ref": "#/definitions/api.VerificationResponse"
            },
            "headers": {
              "X-Credits-Charged": {
                "type": "integer",
                "description": "Credits this request spent. Zero for a pending verification, which is charged where it becomes an answer, and for an answer nobody is charged for"
              },
              "X-Credits-Remaining": {
                "type": "integer",
                "description": "Credits left on the account after this verification"
              }
            }
          },
          "400": {
            "description": "Malformed body, missing email, unsupported mode, or an invalid registration reference",
            "schema": {
              "$ref": "#/definitions/api.ErrorResponse"
            }
          },
          "401": {
            "description": "Missing or unrecognised caller credential",
            "schema": {
              "$ref": "#/definitions/api.ErrorResponse"
            }
          },
          "402": {
            "description": "`insufficient_credits` — the account has nothing left to spend. Never reported as an ambiguous verdict: what somebody has paid for is not something this service is uncertain about",
            "schema": {
              "$ref": "#/definitions/api.ErrorResponse"
            }
          },
          "403": {
            "description": "`delegation_not_permitted` — the request named an account to charge and this credential may not name one",
            "schema": {
              "$ref": "#/definitions/api.ErrorResponse"
            }
          },
          "413": {
            "description": "Request body exceeds the permitted size",
            "schema": {
              "$ref": "#/definitions/api.ErrorResponse"
            }
          },
          "429": {
            "description": "Caller or source budget exceeded, or the request looks like directory harvesting",
            "schema": {
              "$ref": "#/definitions/api.ErrorResponse"
            }
          },
          "500": {
            "description": "The verification engine failed; this says nothing about the address",
            "schema": {
              "$ref": "#/definitions/api.ErrorResponse"
            }
          },
          "503": {
            "description": "A dependency needed to authorise the request is unavailable",
            "schema": {
              "$ref": "#/definitions/api.ErrorResponse"
            }
          }
        }
      }
    },
    "/v1/verifications/{id}": {
      "get": {
        "security": [
          {
            "CallerToken": []
          }
        ],
        "description": "Polling is the only way a caller learns a late result: a verification answered\n`pending` is completed here once its probe returns. There are no callbacks.\n\nAn identifier belonging to another caller is reported as not found, the same\nas one that never existed — distinguishing them would let a caller probe for\nthe existence of other callers' verifications.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "verifications"
        ],
        "summary": "Retrieve a verification",
        "parameters": [
          {
            "type": "string",
            "description": "Verification identifier",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "The stored verification",
            "schema": {
              "$ref": "#/definitions/api.VerificationResponse"
            }
          },
          "400": {
            "description": "No identifier supplied",
            "schema": {
              "$ref": "#/definitions/api.ErrorResponse"
            }
          },
          "401": {
            "description": "Missing or unrecognised caller credential",
            "schema": {
              "$ref": "#/definitions/api.ErrorResponse"
            }
          },
          "403": {
            "description": "`delegation_not_permitted` — the request named an account and this credential may not name one",
            "schema": {
              "$ref": "#/definitions/api.ErrorResponse"
            }
          },
          "404": {
            "description": "No such verification for this caller and account",
            "schema": {
              "$ref": "#/definitions/api.ErrorResponse"
            }
          },
          "500": {
            "description": "The verification could not be retrieved",
            "schema": {
              "$ref": "#/definitions/api.ErrorResponse"
            }
          },
          "503": {
            "description": "Stored results are unavailable",
            "schema": {
              "$ref": "#/definitions/api.ErrorResponse"
            }
          }
        }
      }
    }
  },
  "definitions": {
    "api.ErrorResponse": {
      "type": "object",
      "properties": {
        "error": {
          "type": "string"
        },
        "message": {
          "type": "string"
        }
      }
    },
    "api.NormalizedAddress": {
      "type": "object",
      "properties": {
        "ascii_domain": {
          "type": "string"
        },
        "domain": {
          "type": "string"
        }
      }
    },
    "api.VerificationRequest": {
      "type": "object",
      "properties": {
        "email": {
          "type": "string"
        },
        "variant": {
          "description": "Variant is how deep to check, and is optional.\n\nEmpty means the product's default, which is validated to be its most\ncomplete depth. That default is not a convenience: every integration\nwritten before this field existed omits it, and any other reading would\nquietly start selling them a shallower answer — turning `deliverable` into\n`unknown` on a signup form that never changed.\n\nAn unrecognised value is refused rather than defaulted. A caller asking for\na depth we do not sell has a bug, and answering it with the most expensive\none charges them for the misunderstanding.",
          "type": "string"
        }
      }
    },
    "api.VerificationResponse": {
      "type": "object",
      "properties": {
        "action": {
          "$ref": "#/definitions/domain.Action"
        },
        "checks": {
          "$ref": "#/definitions/domain.CheckStates"
        },
        "confidence": {
          "type": "number"
        },
        "email": {
          "type": "string"
        },
        "freshness": {
          "$ref": "#/definitions/domain.Freshness"
        },
        "generation": {
          "type": "integer"
        },
        "id": {
          "type": "string"
        },
        "normalized": {
          "$ref": "#/definitions/api.NormalizedAddress"
        },
        "reason_codes": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/domain.ReasonCode"
          }
        },
        "retry_after_ms": {
          "type": "integer"
        },
        "status": {
          "$ref": "#/definitions/domain.Status"
        },
        "suggestion": {
          "type": "string"
        }
      }
    },
    "domain.Action": {
      "type": "string",
      "enum": [
        "allow",
        "allow_with_email_confirmation",
        "reject"
      ],
      "x-enum-varnames": [
        "ActionAllow",
        "ActionAllowWithConfirmation",
        "ActionReject"
      ]
    },
    "domain.CheckStates": {
      "type": "object",
      "properties": {
        "accept_all": {
          "type": "string"
        },
        "disposable": {
          "type": "boolean"
        },
        "domain": {
          "type": "string"
        },
        "mail_routing": {
          "type": "string"
        },
        "role_account": {
          "type": "boolean"
        },
        "smtp": {
          "type": "string"
        },
        "smtp_utf8_required": {
          "type": "boolean"
        },
        "syntax": {
          "type": "string"
        }
      }
    },
    "domain.Freshness": {
      "type": "object",
      "properties": {
        "dns_checked_at": {
          "type": "string"
        },
        "smtp_checked_at": {
          "type": "string"
        }
      }
    },
    "domain.ReasonCode": {
      "type": "string",
      "enum": [
        "INVALID_SYNTAX",
        "INVALID_LOCAL_PART",
        "INVALID_DOMAIN",
        "ADDRESS_TOO_LONG",
        "DOMAIN_LITERAL_NOT_ALLOWED",
        "SMTPUTF8_REQUIRED",
        "DNS_NXDOMAIN",
        "DNS_NODATA",
        "DNS_TIMEOUT",
        "DNS_SERVFAIL",
        "DNS_DNSSEC_FAILURE",
        "NULL_MX",
        "IMPLICIT_MX",
        "MX_FOUND",
        "MX_UNRESOLVABLE",
        "MX_TARGET_BLOCKED",
        "DOMAIN_MAIL_ROUTE_MISCONFIGURED",
        "SMTP_QUEUED",
        "SMTP_QUEUE_EXPIRED",
        "SMTP_CONNECT_TIMEOUT",
        "SMTP_CONNECT_REFUSED",
        "SMTP_BANNER_INVALID",
        "SMTP_PROTOCOL_ERROR",
        "SMTP_REPLY_TOO_LARGE",
        "SMTP_STARTTLS_FAILED",
        "SMTP_TLS_CERTIFICATE_FAILED",
        "SMTPUTF8_NOT_SUPPORTED",
        "SMTP_SENDER_REJECTED",
        "SMTP_POLICY_REJECTED",
        "SMTP_EGRESS_REPUTATION_REJECTED",
        "SMTP_PROVIDER_THROTTLED",
        "SMTP_GREYLISTED",
        "SMTP_TEMPORARY_FAILURE",
        "SMTP_RCPT_ACCEPTED",
        "SMTP_MAILBOX_NOT_FOUND",
        "SMTP_MAILBOX_DISABLED",
        "SMTP_MAILBOX_STATUS_ERROR",
        "SMTP_FORWARDING_INDICATED",
        "SMTP_RECIPIENT_REJECTED_AMBIGUOUS",
        "DISPOSABLE_DOMAIN",
        "ROLE_ACCOUNT",
        "ACCEPT_ALL_DOMAIN",
        "ACCEPT_ALL_UNKNOWN",
        "LIKELY_DOMAIN_TYPO",
        "SMTP_PROBE_SKIPPED_PROVIDER_POLICY",
        "SMTP_PROBE_NOT_REQUESTED",
        "PROVIDER_CIRCUIT_OPEN",
        "SMTP_QUEUE_UNAVAILABLE",
        "SMTP_SCHEDULER_UNAVAILABLE",
        "SMTP_EGRESS_UNAVAILABLE",
        "SMTP_PACING_REFUSED"
      ],
      "x-enum-varnames": [
        "ReasonInvalidSyntax",
        "ReasonInvalidLocalPart",
        "ReasonInvalidDomain",
        "ReasonAddressTooLong",
        "ReasonDomainLiteralNotAllowed",
        "ReasonSMTPUTF8Required",
        "ReasonDNSNXDomain",
        "ReasonDNSNoData",
        "ReasonDNSTimeout",
        "ReasonDNSServFail",
        "ReasonDNSSECFailure",
        "ReasonNullMX",
        "ReasonImplicitMX",
        "ReasonMXFound",
        "ReasonMXUnresolvable",
        "ReasonMXTargetBlocked",
        "ReasonRouteMisconfigured",
        "ReasonSMTPQueued",
        "ReasonSMTPQueueExpired",
        "ReasonSMTPConnectTimeout",
        "ReasonSMTPConnectRefused",
        "ReasonSMTPBannerInvalid",
        "ReasonSMTPProtocolError",
        "ReasonSMTPReplyTooLarge",
        "ReasonSMTPSTARTTLSFailed",
        "ReasonSMTPTLSCertFailed",
        "ReasonSMTPUTF8NotSupported",
        "ReasonSMTPSenderRejected",
        "ReasonSMTPPolicyRejected",
        "ReasonSMTPEgressReputationRejected",
        "ReasonSMTPProviderThrottled",
        "ReasonSMTPGreylisted",
        "ReasonSMTPTemporaryFailure",
        "ReasonSMTPRCPTAccepted",
        "ReasonSMTPMailboxNotFound",
        "ReasonSMTPMailboxDisabled",
        "ReasonSMTPMailboxStatusError",
        "ReasonSMTPForwardingIndicated",
        "ReasonSMTPRecipientAmbiguous",
        "ReasonDisposableDomain",
        "ReasonRoleAccount",
        "ReasonAcceptAllDomain",
        "ReasonAcceptAllUnknown",
        "ReasonLikelyDomainTypo",
        "ReasonProbeSkippedPolicy",
        "ReasonProbeNotRequested",
        "ReasonProviderCircuitOpen",
        "ReasonSMTPQueueUnavailable",
        "ReasonSMTPSchedulerUnavailable",
        "ReasonSMTPEgressUnavailable",
        "ReasonSMTPPacingRefused"
      ]
    },
    "domain.Status": {
      "type": "string",
      "enum": [
        "deliverable",
        "undeliverable",
        "risky",
        "unknown",
        "pending"
      ],
      "x-enum-varnames": [
        "StatusDeliverable",
        "StatusUndeliverable",
        "StatusRisky",
        "StatusUnknown",
        "StatusPending"
      ]
    }
  },
  "securityDefinitions": {
    "CallerToken": {
      "description": "Caller credential, sent as \"Bearer emv_...\". Issued by emvero-admin create-caller and shown once; the stored form is a digest, so a database disclosure does not hand over the ability to authenticate.",
      "type": "apiKey",
      "name": "Authorization",
      "in": "header"
    }
  }
}
