Forms SDK

Email Validation

Deliverability verdicts on visitor email addresses: automatic annotation, on-demand checks, and blocking policies

Two ways to validate email addresses against Surface's provider waterfall and the environment's spam blacklist. Both share one per-address verdict cache, so an address is billed once.

What happens on an invalid address is a policy decision

The form owner decides:

PolicyBehaviorImplementation
Annotate only (default)Visitor continues; the response carries the verdict for workflows and scoringemailValidation: true on init
Soft warnInline note, visitor continuesvalidateEmail in your handler, message on !isValid
BlockVisitor stays on the step until deliverablevalidateEmail gate before next()
DisqualifyEnd on the "not a fit" endingvalidateEmail, then disqualify()

Never gate navigation on validation unless the owner explicitly chose block or disqualify.

Automatic mode

SurfaceForms.init({ ..., emailValidation: true });

Validates email answers as captured (debounced) and writes the same verdict keys native forms write (isEmailValid, emailValidation, emailValidationFailed) to the response's metadata. Never blocks navigation or submission. Off by default because verdicts bill the owner's validator quota.

On-demand checks

form.validateEmail(email) resolves the server-side verdict { isValid, status, reason, failed? }:

// Block: keep the visitor on the step with a message.
continueButton.addEventListener("click", async () => {
  const { isValid, reason, failed } = await form.validateEmail(emailInput.value);
  if (isValid || failed) return form.next(); // fail open: never lock out on a vendor outage
  errorEl.textContent = `That address looks undeliverable (${reason}).`;
});

For the disqualify policy, call form.disqualify() instead of showing the message. Pages with a gate should carry data-surface-nav="js" so validate_form_html knows navigation is code-driven. Pairing a gate with emailValidation: true is fine; the shared cache bills once.

Verdicts fail open

When no validator can run, the result is { isValid: true, failed: true, status: "unknown" }: an outage must never lock a visitor out. Check failed to soft-warn instead of hard-block, and expect an address accepted today (fail open) to return a definitive invalid tomorrow as the waterfall resolves.

In preview mode, validation resolves synthetic valid verdicts with no network and no billing. form.capabilities.emailValidation reports whether automatic mode is active.

On this page