Widget AT Valid — Demo e Integraciones

Validación de email en tiempo real para cualquier formulario. Pruébalo abajo y copia el snippet de tu plataforma.

↓ Ir a los ejemplos de integración

Se requiere verificación de seguridad antes de usar la demo

Validación detallada

Resultado completo con puntuación, calificación e indicadores

Email
Puntuación
Calificación
Desechable
Proveedor gratuito

Validación automática (modo widget)

widget.js con reglas de aceptación, validación HTML5 y preventSubmit

// Widget events will appear here...

API programática

ATValid.validate(email, callback) — valida sin vincularse al DOM

// Programmatic result will appear here...

Ejemplos de integración

Una etiqueta de script, un ATValid.init(). Reemplaza pk_live_… por la clave pública de app.atvalid.com/apis (restríngela a tus dominios).

HTML puro — inicio rápido

Cualquier sitio: pega antes de </body>. Cada input[type="email"] se valida al salir del campo.

<!-- Add before </body> -->
<script src="https://www.atvalid.com/widget/v2/widget.js"></script>
<script>
  ATValid.init({
    publicKey: 'pk_live_your_public_key_here',
    preventSubmit: true            // block the submit while the email is invalid
  });
</script>
El widget usa un MutationObserver, así que los formularios inyectados después (AJAX, SPAs, constructores) se detectan solos. No se envía nada a la dirección — la verificación ocurre en el servidor.

Todas las opciones, reglas de aceptación y mensajes

Se muestran los valores por defecto. messages.sub (v2.1) da el motivo específico en lugar de un "inválido" genérico.

ATValid.init({
  publicKey: 'pk_live_your_public_key_here',
  selector: 'input[type="email"]',   // which inputs to bind
  validateOn: 'blur',                // 'blur' | 'input'
  verifyAfterDelay: 1000,            // ms after typing stops (validateOn: 'input')
  timeout: 10,                       // seconds
  showFeedback: true,                // message under the input
  preventSubmit: false,              // block submit when blocked/invalid
  formValidation: false,             // HTML5 setCustomValidity()
  blockOnRateLimit: false,
  statusAppendTo: null,              // CSS selector or element for the feedback
  ignoredForms: [],                  // form names/ids to skip
  ignoredInputs: [],                 // input names/ids to skip

  allow: {
    states: ['deliverable', 'risky', 'unknown'],  // 'undeliverable' is never accepted by default
    free: true,                      // Gmail, Outlook, Yahoo…
    role: true,                      // info@, sales@, admin@…
    disposable: false                // temp-mail domains
  },

  messages: {
    checking: 'Verifying email...',
    valid: 'Valid email address',
    invalid: 'This email appears to be invalid',
    disposable: 'Disposable email addresses are not allowed',
    role: 'Role-based email addresses are not allowed',
    free: 'Free email addresses are not allowed',
    suggestion: 'Did you mean {suggestion}?',
    networkError: 'Could not verify email. Please check your connection.',
    rateLimited: 'Too many requests. Please wait a moment.',
    noCredits: 'Validation service temporarily unavailable',
    error: 'Could not verify email at this time',
    // v2.1 — specific reasons (partial overrides are fine)
    sub: {
      not_found: 'This mailbox does not exist',
      mailbox_full: 'This mailbox is full and cannot receive email',
      null_mx: 'This domain does not accept email',
      no_mx: 'This domain has no mail server',
      blacklist: 'This email domain is blacklisted',
      parked: 'This domain is parked and does not receive email',
      no_reply: 'No-reply addresses cannot be used',
      greylist: 'The mail server asked us to try again later',
      toxic: 'This email address cannot be accepted',
      catch_all: 'We could not confirm this mailbox exists',
      smtp_unknown: 'The mail server did not respond',
      suspicious: 'This email address looks suspicious'
    }
  },

  onValidate: function (email, result) { console.log(email, result); },
  onError: function (email, error) { console.error(email, error.code, error.message); }
});

WordPress — Contact Form 7, WPForms, Gravity Forms, Elementor, Formidable, Ninja Forms

Agrega en footer.php antes de </body>, o con el plugin "Insert Headers and Footers" / "WPCode" (pie de todo el sitio).

<script src="https://www.atvalid.com/widget/v2/widget.js"></script>
<script>
  ATValid.init({
    publicKey: 'pk_live_your_public_key_here',
    selector: [
      '.wpcf7-email',                          // Contact Form 7
      '.wpforms-field-email input',            // WPForms
      '.gfield input[type="email"]',           // Gravity Forms
      '.elementor-field-type-email input',     // Elementor Forms
      '.frm_form_field input[type="email"]',   // Formidable
      '.nf-form-cont input[type="email"]',     // Ninja Forms
      'input[type="email"]'                    // anything else
    ].join(', '),
    validateOn: 'blur',
    preventSubmit: true,
    allow: { disposable: false, role: false }
  });
</script>
Contact Form 7 se vuelve a renderizar tras un envío AJAX — el widget se vuelve a enlazar solo. Si un plugin cachea/minifica JS, excluye widget.js de la combinación.

RD Station — landing pages y pop-ups

Landing page → Configuración → "Código personalizado (HTML/JS)" → antes de </body>. Los formularios de RD cargan dinámicamente; el widget los espera.

<script src="https://www.atvalid.com/widget/v2/widget.js"></script>
<script>
  ATValid.init({
    publicKey: 'pk_live_your_public_key_here',
    selector: 'input[type="email"], input[name="email"]',
    preventSubmit: true,
    formValidation: true,     // RD's own validation shows our message
    allow: { disposable: false }
  });
</script>
Para formularios de RD Station incrustados en tu propio sitio (embed por script), agrega el mismo snippet en la página que aloja el embed.

HubSpot — formularios, landing pages, pop-ups

Configuración → Sitio web → Páginas → Avanzado → "HTML del pie del sitio" (o por página). Los formularios de HubSpot se renderizan dinámicamente — se detectan automáticamente.

<script src="https://www.atvalid.com/widget/v2/widget.js"></script>
<script>
  ATValid.init({
    publicKey: 'pk_live_your_public_key_here',
    selector: 'input[type="email"], .hs-input[name="email"]',
    preventSubmit: true,
    allow: { disposable: false }
  });

  // Optional: re-scan when HubSpot signals a form is ready
  window.addEventListener('message', function (e) {
    if (e.data && e.data.type === 'hsFormCallback' && e.data.eventName === 'onFormReady') {
      ATValid.refresh();
    }
  });
</script>

Mailchimp — formulario de suscripción incrustado

Pega después del código de embed de Mailchimp en tu sitio (las landing pages alojadas por Mailchimp no aceptan JS personalizado).

<!-- your Mailchimp embed form is above -->
<script src="https://www.atvalid.com/widget/v2/widget.js"></script>
<script>
  ATValid.init({
    publicKey: 'pk_live_your_public_key_here',
    selector: '#mc-embedded-subscribe-form input[type="email"], #mce-EMAIL',
    validateOn: 'input',        // validate while typing (debounced 1s)
    verifyAfterDelay: 1000,
    preventSubmit: true,
    allow: { disposable: false, role: false }
  });
</script>

Google Tag Manager — etiqueta Custom HTML

Tipo de etiqueta "HTML personalizado", activador "Todas las páginas" (o solo páginas con formularios). Funciona en cualquier CMS sin tocar el tema.

<script>
  (function () {
    var s = document.createElement('script');
    s.src = 'https://www.atvalid.com/widget/v2/widget.js';
    s.async = true;
    s.onload = function () {
      ATValid.init({
        publicKey: 'pk_live_your_public_key_here',
        preventSubmit: true,
        allow: { disposable: false },
        onValidate: function (email, result) {
          // push to the dataLayer for GA4 / conversion rules
          window.dataLayer = window.dataLayer || [];
          window.dataLayer.push({
            event: 'atvalid_email_verified',
            atv_accepted: result.accepted,
            atv_state: result.state,
            atv_sub_status: result.subStatus,
            atv_score: result.score
          });
        }
      });
    };
    document.head.appendChild(s);
  })();
</script>
El snippet no necesita "Admitir document.write". El callback onValidate envía un evento al dataLayer para GA4 / reglas de conversión.

Webflow, Wix, Shopify, Squarespace — código personalizado del sitio

Webflow: Configuración del proyecto → Código personalizado → Pie. Wix: Configuración → Código personalizado → Fin del body. Shopify: theme.liquid antes de </body>. Squarespace: Configuración → Avanzado → Inyección de código → Pie.

<script src="https://www.atvalid.com/widget/v2/widget.js"></script>
<script>
  ATValid.init({
    publicKey: 'pk_live_your_public_key_here',
    // covers Webflow (.w-input), Wix, Shopify (customer/contact forms) and Squarespace
    selector: 'input[type="email"], input.w-input[type="email"], input[name="customer[email]"], input[name="contact[email]"]',
    preventSubmit: true,
    allow: { disposable: false }
  });
</script>
El checkout de Shopify no permite JS personalizado: usa el widget en los formularios de la tienda (newsletter, contacto, cuenta) y la API REST en el servidor para el checkout.

React / Next.js

Carga una vez, vincula a tu input, destruye al desmontar. En Next.js marca el componente con 'use client'.

import { useEffect } from 'react';

export function ContactForm() {
  useEffect(() => {
    const script = document.createElement('script');
    script.src = 'https://www.atvalid.com/widget/v2/widget.js';
    script.async = true;
    script.onload = () => {
      window.ATValid.init({
        publicKey: 'pk_live_your_public_key_here',
        selector: '#email-input',
        preventSubmit: true,
        formValidation: true,
        allow: { disposable: false },
        onValidate: (email, result) => console.log(email, result.accepted, result.subStatus)
      });
    };
    document.body.appendChild(script);
    return () => {
      window.ATValid && window.ATValid.destroy();
      document.body.removeChild(script);
    };
  }, []);

  return (
    <form>
      <input id="email-input" type="email" placeholder="Email" />
      <button type="submit">Submit</button>
    </form>
  );
}
Tras una navegación client-side que renderice nuevos formularios, llama a ATValid.refresh() (o confía en el MutationObserver, activo por defecto).

Vue 3 / Nuxt

Misma idea con onMounted / onBeforeUnmount. En Nuxt mantenlo dentro de <ClientOnly> o de un componente client.

<script setup>
import { onMounted, onBeforeUnmount } from 'vue';

let script;
onMounted(() => {
  script = document.createElement('script');
  script.src = 'https://www.atvalid.com/widget/v2/widget.js';
  script.async = true;
  script.onload = () => window.ATValid.init({
    publicKey: 'pk_live_your_public_key_here',
    selector: '#email',
    preventSubmit: true,
    allow: { disposable: false }
  });
  document.body.appendChild(script);
});
onBeforeUnmount(() => {
  window.ATValid && window.ATValid.destroy();
  script && script.remove();
});
</script>

<template>
  <form><input id="email" type="email" /><button>Submit</button></form>
</template>

Callbacks, eventos DOM y el objeto de resultado (v2.1)

Usa onValidate o escucha atv:verified / atv:error en document.

document.addEventListener('atv:verified', function (e) {
  var r = e.detail.result;
  // r = {
  //   valid: true|false,            // API-level verdict
  //   accepted: true|false,         // passed your allow rules → decides the submit
  //   state: 'deliverable'|'risky'|'unknown'|'undeliverable',
  //   subStatus: 'ok'|'not_found'|'mailbox_full'|'disposable'|'null_mx'|'no_mx'|'typo'|
  //              'blacklist'|'parked'|'no_reply'|'greylist'|'catch_all'|'suspicious'|
  //              'role_based'|'toxic'|'smtp_unknown'|'risky'|'invalid',   (v2.1)
  //   toxicity: 0..5,               // reputation risk (≥3 is never accepted)  (v2.1)
  //   catchAllConfidence: 0..10|null, // evidence the mailbox exists on a catch-all domain (v2.1)
  //   score: 0..100, grade: 'A'..'F',
  //   disposable, free, role: booleans,
  //   suggestion: '[email protected]' | null,   // typo fix (clickable in the feedback)
  //   email: '[email protected]',
  //   raw: { ...full API response }
  // }
  if (!e.detail.accepted) console.log('blocked:', r.subStatus);
});

document.addEventListener('atv:error', function (e) {
  // e.detail.error: NETWORK_ERROR | TIMEOUT | RATE_LIMITED | NO_CREDITS |
  //                 DOMAIN_BLOCKED | AUTH_ERROR | API_ERROR | PARSE_ERROR | NO_KEY
  console.warn(e.detail.email, e.detail.error);
});
Atributo en el inputValoresSignificado
data-atv-valid"true" | "false"Aceptado por tus reglas (lo que revisa preventSubmit)
data-atv-statedeliverable | risky | unknown | undeliverableEstado derivado
data-atv-bound"1"El widget está vinculado a este input

API programática — sin vínculo con el DOM

Valida bajo demanda (p. ej. antes de un envío XHR), vuelve a escanear, limpia la caché, desmonta.

// Validate a single address (publicKey optional if init() already ran)
ATValid.validate('[email protected]', 'pk_live_your_key', function (result) {
  console.log(result.accepted, result.state, result.subStatus, result.score, result.toxicity);
});

ATValid.refresh();     // re-scan the DOM for new email inputs (SPA navigation)
ATValid.clearCache();  // forget the 5-minute in-page cache
ATValid.destroy();     // remove listeners, observer, styles and feedback
Límites para claves públicas: 60 solicitudes/min por clave y 30/min por IP del visitante. Los resultados se cachean en la página 5 minutos, así que volver a escribir la misma dirección no cuesta nada.

Estilos

El widget inyecta un CSS mínimo; sobrescribe cualquier clase en tu hoja de estilos.

.atv-input-valid    { border-color: #22c55e !important; box-shadow: 0 0 0 3px rgba(34,197,94,.1) !important; }
.atv-input-invalid  { border-color: #ef4444 !important; box-shadow: 0 0 0 3px rgba(239,68,68,.1) !important; }
.atv-input-checking { border-color: #9ca3af !important; }

.atv-feedback   { font-size: .8125rem; margin-top: .25rem; font-family: inherit; }
.atv-valid      { color: #22c55e; }
.atv-invalid    { color: #ef4444; }
.atv-checking   { color: #9ca3af; }
.atv-suggestion { color: #d97706; cursor: pointer; text-decoration: underline; }
.atv-suggestion:hover { color: #b45309; }