REST API
Traducido automáticamente del inglés.
Leer en inglésURL base https://api.proxymail.qa/v1, con Authorization: Bearer qa_…. Todos los endpoints están en la referencia de REST API, generada a partir de la descripción OpenAPI.
¿Usas JavaScript o TypeScript? El paquete proxymail-qa en npm envuelve estas llamadas, con tipos.
Inicio rápido
Sección titulada «Inicio rápido»Obtén una clave de API
Sección titulada «Obtén una clave de API»Usa una clave de API del proyecto que estás probando.
Crea una dirección
Sección titulada «Crea una dirección»Crea una dirección con una persona y una contraseña, y regístrate con ella en tu app.
curl -X POST https://api.proxymail.qa/v1/addresses \-H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" -H "Content-Type: application/json" \-d '{"label": "Sign-up test", "persona": true, "password": true}'Espera el código
Sección titulada «Espera el código»Luego espera el código. Llega solo.
curl "https://api.proxymail.qa/v1/addresses/k7f2x9ab12cd@mailclub.dev/code" \-H "Authorization: Bearer $PROXYMAIL_QA_API_KEY"{"type": "code","subtype": "otp","value": "986512","service": "Spotify","received_at": "2026-10-12T10:00:03Z","expires_at": "2026-10-12T10:10:03Z"}
Endpoints
Sección titulada «Endpoints»| Método y ruta | Qué hace |
|---|---|
GET /v1/project |
El proyecto de la clave, sus dominios de correo y sus límites |
PATCH /v1/project |
Cambia name, website, environments, email_prefix o mail_domain del proyecto, como en Configuración del proyecto. Requiere al responsable del proyecto, o a un propietario o administrador de la organización; cada cambio queda en el registro de actividad |
POST /v1/project/dev-hosts |
Marca un host como localhost:3000 como entorno de desarrollo (host), para que las comprobaciones del correo acepten los enlaces a él |
POST /v1/addresses |
Crea una dirección (label, name, persona, password, lifetime_days, domain, age o date_of_birth, country, y dónde se usa: site y environment) |
POST /v1/form-values |
Qué escribir o elegir en cada campo de un formulario de registro, a partir de una dirección nueva creada para él o de una creada antes (más) |
GET /v1/addresses |
Lista las direcciones, de la más reciente a la más antigua, o busca las creadas antes (tag, country, min_age, max_age, has_password, search, site, environment) |
GET / PATCH / DELETE /v1/addresses/{address} |
Lee, actualiza (first_name, last_name, username, date_of_birth, country, label, notes, tags) o elimina una |
GET /v1/addresses/{address}/code |
Espera un código (timeout, service, since; unused=true omite los códigos ya usados; type=coupon para un cupón o código promocional, con su descuento y fecha de validez) |
GET /v1/addresses/{address}/link |
Espera un enlace (type: verify, magic_signin, invite, reset o any; unsubscribe o list_unsubscribe para un enlace para darse de baja; app=true para un enlace que abre una app móvil) |
GET /v1/addresses/{address}/count |
Cuenta los correos que llegaron (since, service, type: código o enlace, subtype, category), cada uno resumido sin su contenido |
GET /v1/addresses/{address}/none |
Comprueba que no llegue ningún correo: espera hasta 30 segundos (timeout), 200 si no llegó nada, 409 en cuanto llega algo; category=marketing vigila solo ofertas y boletines, y until mantiene una ventana abierta hasta una hora entre llamadas |
GET, PUT /v1/addresses/{address}/consent |
Labs. Lo que la dirección aceptó al registrarse (marketing, una casilla marcada de antemano, doble opt-in), para las reglas de marketing por país |
POST /v1/addresses/{address}/coupon-result |
Labs. Lo que hizo el pago con un cupón (accepted, rejected o error, con el mensaje de la app), y el veredicto coupon_result |
POST /v1/addresses/{address}/sequence |
Labs. Una serie de bienvenida o campaña de goteo: si cada correo llegó en orden y a tiempo después de since |
GET /v1/addresses/{address}/offers |
Labs. Los cupones de cada remitente y a qué versión de la dirección (simple, mayúsculas, +tag) llegó cada uno: reused cuando una persona recibió una oferta dos veces |
GET, POST, DELETE /v1/addresses/{address}/unsubscribed |
Labs. Vigilancia de bajas: POST registra que la dirección se dio de baja de un remitente, DELETE que se suscribió de nuevo, y GET lista los correos de marketing que llegaron después |
GET /v1/addresses/{address}/checks |
Comprueba un correo: SPF, DKIM y DMARC, enlaces locales y http simples, adónde lleva su enlace (expect_host), una caducidad indicada, cuándo termina un cupón, una parte de texto plano, el tamaño, correos solo de imágenes, frases dadas (expect_text), su idioma (expect_language), cuánto tardó en llegar (expect_within) y sus adjuntos (expect_attachment). Solo veredictos |
GET /v1/addresses/{address}/messages |
Correos con sus códigos y enlaces; include=text añade el texto, include=html el HTML, include=text,html ambos |
GET /v1/addresses/{address}/variants |
Versiones inusuales de la dirección para probar formularios de registro: las que llegan a su bandeja de entrada, las que no pueden y las no válidas (más) |
GET /v1/variants/names |
Nombres difíciles para el campo de nombre de un formulario (field, max_length): los que hay que aceptar, los que hay que rechazar, HTML o SQL que hay que guardar como texto, y aquellos en los que las apps difieren (más) |
GET /v1/variants/passwords |
Contraseñas difíciles dentro de las reglas del campo (min_length, max_length, symbols): el mínimo y el máximo, cada clase de símbolo, espacios, no ASCII, y aquellas en las que las apps difieren (más) |
GET /v1/addresses/{address}/delivery |
Comprueba una dirección: qué le llegó, qué rechazamos, qué rechazaron los servidores de correo de Cloudflare y por qué, y una pista |
GET /v1/addresses/{address}/password |
Una contraseña guardada, para las claves con permiso para leerlas |
POST /v1/addresses/sign-in |
La cuenta de prueba guardada con la que iniciar sesión y su contraseña, en una sola llamada (site, environment, tags), para las claves con permiso para leer contraseñas (más) |
PUT /v1/addresses/{address}/password |
Guarda la contraseña que usa ahora la cuenta de prueba (password, u omítela para generar una, que se devuelve una sola vez); cualquier clave puede guardar una |
POST /v1/addresses/checkout |
Toma una cuenta del grupo: una que coincida con los filtros (tags, country, min_age, max_age, has_password, search, site, environment) y que nadie más tenga, durante minutes (más) |
POST /v1/addresses/{address}/extend, POST /v1/addresses/{address}/return |
Consérvala más tiempo o devuélvela (token; al devolverla, add_tags y remove_tags registran su estado) |
POST /v1/runs |
Inicia una ejecución (name, source, meta): las llamadas que envían su id en el encabezado Proxy-Mail-QA-Run añaden lo que vieron (más) |
GET /v1/runs, GET /v1/runs/{id} |
Lista las ejecuciones o lee una con su evidencia; format=markdown da el informe |
POST /v1/runs/{id}/notes, POST /v1/runs/{id}/tests, POST /v1/runs/{id}/finish |
Añade una línea propia (text, failed), un resultado de prueba o los totales, o finalízala (failed) |
Los endpoints marcados con Labs forman parte de Correos de marketing y ofertas, una función de Labs que el creador de la clave activa en la app en Account settings → Labs. Hasta entonces, responden 403 forbidden con un mensaje que lo indica.
Cupones y ofertas
Sección titulada «Cupones y ofertas»Después de registrarte en una oferta, espera su cupón con type=coupon (any para un cupón o un código de inicio de sesión). Las ofertas suelen tardar minutos, así que espera más o vuelve a preguntar; con since, after_seconds indica cuánto tiempo después llegó el correo:
curl "https://api.proxymail.qa/v1/addresses/$ADDRESS/code?type=coupon&since=$SIGNED_UP&timeout=30" -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY"# { "value": "WELCOME20", "subtype": "coupon", "discount": "20% off", "valid_until": "2026-10-31", "after_seconds": 212, … }discount es nuestra lectura de la oferta (20% off, $10 off, Free shipping, Buy one, get one), y valid_until la fecha en que el correo dice que termina (“valid until 31 October”, “ends Oct 31”, “valid for 7 days”). Cada uno es null cuando el correo no lo dice. Las esperas sin tipo dejan fuera los cupones, así que una prueba de inicio de sesión nunca toma el código de una oferta. Para comprobar que llegó a tiempo, GET /checks?since=$SIGNED_UP&expect_within=5m falla arrival_time cuando llegó más tarde.
Una oferta por persona. El correo enviado a name+promo1@… y a NAME@… llega a la misma bandeja de entrada, así que sirven como “segundos clientes” para una oferta única o de bienvenida. Regístrate con la dirección simple y con versiones de GET /variants, espera cada cupón y luego usa GET /v1/addresses/{address}/offers: para cada remitente, sus cupones y la versión a la que llegó cada uno (plain, capitals o la +tag), reused cuando dos o más versiones recibieron uno, y same_code cuando era un único código compartido. passed es false cuando se reutilizó alguna oferta, y cada cupón después del primero falla offer_reuse en /checks. El truco del punto de Gmail no se puede probar aquí: en estos dominios, un punto crea otra dirección.
En el pago. Aplica el cupón con tus propias herramientas, solo en staging con pagos de prueba, y luego registra lo que hizo la app:
curl -X POST "https://api.proxymail.qa/v1/addresses/$ADDRESS/coupon-result" \ -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" -H "Content-Type: application/json" \ -d '{"result": "rejected", "code": "WELCOME20", "message": "Code already used", "one_time": true}'result es accepted, rejected o error; code es por defecto el cupón más reciente de la dirección. Se guarda cada intento, y el check de la respuesta es el veredicto coupon_result: falla si se aceptó un cupón después de su fecha de fin, si se aceptó para una oferta ya dada a otra versión de la dirección, o si se aceptó dos veces con one_time, y avisa ante un error o un rechazo sin motivo. Con una ejecución, un veredicto fallido la hace fallar.
Valores para un formulario de registro
Sección titulada «Valores para un formulario de registro»Cuando un formulario pide más que un correo, envía lo que dice la página sobre cada campo y recibe qué escribir o elegir en cada uno. Los valores salen de una dirección nueva creada para el formulario: recibe una fecha de nacimiento cuando el formulario la pide (adulta salvo que pases age), un país que ofrezca el desplegable del formulario (o country) y una contraseña que cumpla min_length, max_length y pattern del campo de contraseña, guardada en la dirección y mostrada solo en esta respuesta. Pasa address para usar una creada antes (no se devuelve su contraseña guardada).
curl -s -X POST https://api.proxymail.qa/v1/form-values -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" \ -H "Content-Type: application/json" -d '{ "fields": [ { "id": "email", "type": "email", "label": "Email" }, { "id": "user", "name": "username", "label": "Username", "max_length": 15 }, { "id": "pw", "type": "password", "label": "Password", "min_length": 12 }, { "id": "dob", "type": "text", "label": "Date of birth", "placeholder": "DD/MM/YYYY" }, { "id": "country", "type": "select", "label": "Country", "options": [{ "value": "de", "label": "Germany" }, { "value": "uk", "label": "United Kingdom" }] }, { "id": "terms", "type": "checkbox", "label": "I agree to the terms" } ] }'# { "address": "k7f2x9ab12cd@mailclub.dev", "created": true, "password": "…", "persona": {…},# "values": [ { "id": "email", "kind": "email", "value": "k7f2x9ab12cd@mailclub.dev" },# { "id": "dob", "kind": "date_of_birth", "value": "04/05/1990" },# { "id": "country", "kind": "country", "value": "uk" },# { "id": "terms", "kind": "other", "value": null, "note": "Not a sign-up detail Proxy Mail QA fills: fill it yourself, or leave it." }, … ] }Cada campo puede tener un id (el tuyo, devuelto con su valor), type (o select), name, label, placeholder, autocomplete, group (lo que dice el grupo que lo rodea, como “Fecha de nacimiento” sobre los desplegables de día, mes y año), pattern, min_length, max_length y options (value y label). Los campos de día, mes y año reciben cada uno su parte, un selector de fecha recibe YYYY-MM-DD, y una fecha escrita recibe el formato que indica su etiqueta o placeholder (orden de EE. UU. con note cuando no dice nada). Proxy Mail QA nunca ve ni rellena la página: lo haces tú, y luego esperas el código o enlace como siempre.
Variantes de una dirección
Sección titulada «Variantes de una dirección»Donde API acepta una dirección, también sirve una variante: mayúsculas, o una etiqueta con más como k7f2x9ab12cd+o'brien@mailclub.dev (codificada para URL en la ruta). Actúa sobre la dirección simple, y una espera o lista de mensajes para una variante +tag solo encuentra el correo enviado a esa variante. Consulta Direcciones de correo inusuales.
Nombres y contraseñas
Sección titulada «Nombres y contraseñas»Los demás campos del formulario reciben el mismo trato. GET /v1/variants/names?field=first_name&max_length=50 lista nombres en cuatro grupos: accept (el nombre de una persona real: O'Brien, Smith-Jones, José, Straße, 李, Мария, محمد, สมชาย, una letra, exactamente el máximo), reject (uno por encima del máximo, vacío, solo espacios), store_as_text (<b>Jane</b>, una etiqueta script, una inyección SQL, {{7*7}}: la app debe guardarlos y mostrarlos exactamente como se escribieron, nunca ejecutarlos) y decide (dígitos, símbolos, un emoji, espacios alrededor, tildes compuestas y descompuestas, y 64, 128 y 255 caracteres cuando no se da un máximo: las apps difieren, así que anota lo que hizo la app). GET /v1/variants/passwords?min_length=8&max_length=64&symbols=true hace lo mismo con una contraseña: accept (exactamente el mínimo y el máximo, cada clase de símbolo incluidas comillas, una barra invertida y un espacio, letras no ASCII), reject y decide (solo dígitos, solo minúsculas, un espacio al inicio o al final, un emoji, y un par que comparte sus primeros 72 bytes cuando el máximo lo permite, donde bcrypt se detiene). Las contraseñas son nuevas en cada llamada. Cada entrada tiene tests (qué comprueba) y value (qué escribir). Consulta Nombres y contraseñas inusuales.
Fechas de nacimiento
Sección titulada «Fechas de nacimiento»Añade "age": "under13" (o adult, over21, under18, under15, any, turns18tomorrow, turned18today) para darle a la persona una fecha de nacimiento en ese rango, o envía la tuya como "date_of_birth": "2012-05-01". Se devuelve como persona.date_of_birth, AAAA-MM-DD, y solo cuando la dirección tiene una.
Volver a encontrar una cuenta de prueba
Sección titulada «Volver a encontrar una cuenta de prueba»GET /v1/addresses acepta filtros, así que una prueba o un agente puede volver a una cuenta que creó antes en lugar de crear otra. Todos los filtros dados deben coincidir:
tag: repítelo, o separa las etiquetas con comas, para exigirlas todascountry: el país de la persona, comoGBmin_ageymax_age: la edad actual en años completos, según la fecha de nacimiento de la persona (max_age=17encuentra a menores de 18); las direcciones sin fecha de nacimiento no coincidenhas_password:trueofalse, si tiene una contraseña guardadasearch: parte de la dirección, la etiqueta, las notas, el nombre o el nombre de usuariositeyenvironment: dónde se usa, como host o URL de página (site=staging.example.com) y un nombre (environment=staging). Defínelos al crear una dirección; la extensión Chrome también los registra, y la app los muestra en cada cuenta
curl "https://api.proxymail.qa/v1/addresses?tag=age-gate&country=GB&max_age=17&has_password=true" \ -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY"Volver a iniciar sesión como cuenta de prueba
Sección titulada «Volver a iniciar sesión como cuenta de prueba»POST /v1/addresses/sign-in elige la cuenta de prueba guardada con la que iniciar sesión y la devuelve con su contraseña, como hace la lista de inicio de sesión de la extensión Chrome: una con contraseña guardada y todas las etiquetas dadas, que ninguna prueba haya tomado, primero la usada en este site y luego la que recibió correo más recientemente. Con un site, una cuenta que aún no se ha usado en ningún sitio se marca como usada allí. Requiere una clave con permiso para leer contraseñas guardadas, cada revelación queda registrada, y responde 404 cuando ninguna cuenta encaja.
curl -s -X POST https://api.proxymail.qa/v1/addresses/sign-in -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" \ -H "Content-Type: application/json" -d '{"site": "staging.example.com", "tags": ["verified"]}'# { "address": "…", "password": "…", "persona": {…}, "site": "staging.example.com", "used_on_site": true, … }Comprobar el propio correo
Sección titulada «Comprobar el propio correo»Para probar una plantilla de correo (el texto, el botón, el pie), pide su contenido al endpoint de mensajes: GET /v1/addresses/{address}/messages?include=html añade el HTML de cada correo y include=text su texto plano. El HTML es la copia limpiada cuando llegó el correo: se eliminan scripts, frames y multimedia, se desenvuelven los controles de formulario y se descartan los enlaces que no sean web, mailto: o tel: (los enlaces de app como myapp:// se mantienen en los códigos y enlaces), hasta 200 KB. El contenido lo escribe el remitente, así que trátalo como no confiable. Los agentes conectados mediante MCP nunca reciben el contenido del correo, solo el código o el enlace.
Añade "country": "GB" (cualquier código ISO 3166-1 alfa-2, en mayúsculas o minúsculas) para formularios de registro que preguntan dónde vive alguien, o para probar contenido según la ubicación. Se devuelve como persona.country, en mayúsculas, y solo cuando la dirección tiene uno. Cámbialo o bórralo con PATCH y "country": null. Los agentes conectados mediante MCP también pasan country a create_address y update_address.
Cómo funciona la espera
Sección titulada «Cómo funciona la espera»Una espera devuelve el código o enlace coincidente más reciente recibido en los últimos 15 minutos, o espera hasta 30 segundos (timeout, de 0 a 30) a que llegue uno, y luego responde 404. Pasa since para ignorar todo lo anterior, por ejemplo la hora en que tu prueba envió el formulario. Filtra por remitente con service=spotify.
Un código o enlace cuenta como usado en cuanto una espera lo devuelve, o alguien lo abre, lo copia o lo rellena en la app o Proxy Mail QA for Chrome. La app lo muestra como usado, y unused=true en una espera lo omite: para una prueba que pide un segundo código sin cambiar since.
Comprobar que un correo no llegó, o que llegó una vez
Sección titulada «Comprobar que un correo no llegó, o que llegó una vez»Algunas pruebas pasan cuando no ocurre nada: un usuario dado de baja no recibe el boletín, una segunda solicitud de restablecimiento en menos de un minuto se rechaza, un registro envía un correo de verificación, no dos.
GET /v1/addresses/{address}/none?since=…&timeout=20se mantiene abierta como una espera. Si no llega ningún correo que coincida, responde 200 con"arrived": falseyuntil, el momento hasta el que vigiló. En cuanto llega uno, responde 409 con"error": "email_arrived", elcount, y la hora, el servicio y el tipo del primer correo, así quecurl --failhace fallar el paso. Pasasincecon la hora de la acción, para que un correo que llegó antes de la llamada también cuente; sin él, vigila desde la solicitud. Para más tiempo, añadeuntil(hasta 60 minutos después desince) y vuelve a llamar con los mismossinceyuntilmientras la respuesta diga"done": false;seconds_leftindica cuánto falta.GET /v1/addresses/{address}/count?since=…responde al instante concounty los primeros 50 correos, del más antiguo al más reciente. Para “exactamente uno”, cuenta después de que la espera del primer correo haya devuelto un resultado y un duplicado haya tenido tiempo de llegar.
Ambos filtran como una espera: service, y type=code (códigos que no son cupones) o type=link (enlaces de verificación, inicio de sesión mágico, invitación y restablecimiento), acotados con subtype, como type=link&subtype=reset, y por category. Para “un usuario dado de baja no recibe el boletín”, usa category=marketing, así un recibo o un restablecimiento de contraseña que aún debe llegar no la hace fallar. Una variante +tag cuenta solo su propio correo. Ninguno devuelve lo que dicen los correos: cada uno se resume con received_at, service y el type y subtype de su código o enlace principal. El cliente de Node.js tiene countEmails y expectNoEmail, y el helper de Python count_emails y expect_no_email.
Tipos de correo, enlaces para darse de baja y adjuntos
Sección titulada «Tipos de correo, enlaces para darse de baja y adjuntos»Cada correo que llega recibe un tipo, con los motivos:
category |
Cuándo |
|---|---|
auth |
Su código o enlace principal inicia sesión, verifica, restablece una contraseña o invita, o su asunto dice que lo hace |
transactional |
Su asunto parece un recibo, pedido, pago, reserva o aviso de seguridad, o lleva adjunto un PDF de factura o una invitación de calendario; y todo lo que no sea inicio de sesión ni marketing |
marketing |
Tiene cabeceras List-Unsubscribe, List-Unsubscribe-Post o List-Id, Precedence: bulk, un cupón, un asunto promocional o un enlace para darse de baja |
Los recibos van antes que el marketing a propósito: un recibo con un código de “10 % de descuento la próxima vez” sigue siendo un recibo. /count, /none y /checks aceptan category, y /count da el category de cada correo, gap_seconds (desde el anterior) y los tipos, extensiones y tamaños de sus adjuntos. Los correos que llegaron antes de que existieran los tipos no tienen ninguno.
Los enlaces para darse de baja de un correo se guardan como valores: GET /link?type=unsubscribe da su propio enlace para darse de baja o de preferencias de correo, y type=list_unsubscribe el de la cabecera List-Unsubscribe, el que usa el botón Unsubscribe de Gmail. Para hacer lo que hace ese botón, envía un POST con List-Unsubscribe=One-Click a él con tus propias herramientas. Proxy Mail QA nunca abre ninguno.
Los adjuntos se guardan como nombre, tipo y tamaño de cada archivo, nunca el archivo. GET /checks?expect_attachment=.pdf (repítelo para más) hace fallar expected_attachment cuando falta uno; un patrón es un tipo (application/pdf, image/*), una extensión (.ics) o un nombre de archivo con * (invoice-*.pdf). /messages los lista con sus nombres.
Vigilancias de bajas
Sección titulada «Vigilancias de bajas»Para probar que darse de baja funciona, da de baja la dirección con tus propias herramientas: abre el enlace para darse de baja del correo (GET /link?type=unsubscribe), o envía un POST con List-Unsubscribe=One-Click al enlace de la cabecera (type=list_unsubscribe). Luego regístralo:
curl -X POST "https://api.proxymail.qa/v1/addresses/k7f2x9ab12cd@mailclub.dev/unsubscribed" \ -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" -H "Content-Type: application/json" \ -d '{"email_id": "…", "at": "2026-10-19T10:02:00Z"}'email_id es el correo del que te diste de baja; service (un nombre o un dominio como acme.com) también sirve, y sin ninguno decide el correo de marketing más reciente. at le pone una fecha anterior; marcarlo dos veces no causa problemas. grace (como 2h o 1d, hasta 30 días) deja pasar el marketing que ya estaba en camino: un correo dentro de ese plazo no cuenta. A partir de entonces, cualquier correo de marketing de ese remitente hace fallar after_unsubscribe en /checks, por mucho tiempo que pase, y aparece en el panel Reports. Los recibos, restablecimientos de contraseña y correos de inicio de sesión nunca cuentan. GET /unsubscribed lista cada vigilancia con los correos que llegaron después (after_unsubscribe: ids, horas y after_seconds, nunca su contenido) y passed; con una ejecución, una comprobación fallida la hace fallar. DELETE /unsubscribed?service=acme.com registra que la dirección se suscribió de nuevo.
Para comprobarlo dentro de la prueba, lanza otra campaña y mantén GET /none?category=marketing&since=…&until=… abierta unos minutos.
Una serie de correos
Sección titulada «Una serie de correos»Para una serie de bienvenida o una campaña por goteo, POST /v1/addresses/{address}/sequence comprueba que cada correo llegó en orden y a tiempo:
curl -X POST "https://api.proxymail.qa/v1/addresses/$ADDRESS/sequence" \ -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" -H "Content-Type: application/json" \ -d '{"since": "2026-10-01T10:00:00Z", "steps": [{"subject": "Welcome"}, {"subject": "tips", "after": "1d"}, {"category": "marketing", "after": "7d", "within": "12h"}]}'Cada paso puede indicar una frase de su subject, su category, su type (código o enlace), after (cuándo debe llegar, desde since, hasta 90 días) y within (cuánto desvío sigue pasando; por defecto una décima parte de after, al menos 10 minutos). Cada uno toma el primer correo que encaje después del paso anterior, y responde pass, early, late, missing o waiting (aún no toca), con received_at y off_seconds. done es false mientras algún paso esté esperando, así que vuelve a comprobar más tarde. Los asuntos se comparan en el servidor y nunca se devuelven.
Consentimiento y normas de marketing por país
Sección titulada «Consentimiento y normas de marketing por país»Registra lo que la cuenta aceptó al registrarse, y /checks evalúa cada correo de marketing según las normas del lugar donde vive el persona (su country, definido al crear la dirección):
curl -X PUT "https://api.proxymail.qa/v1/addresses/$ADDRESS/consent" \ -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" -H "Content-Type: application/json" \ -d '{"marketing": "declined", "prechecked": false}'marketing es opted_in o declined, prechecked indica que la casilla ya estaba marcada cuando se cargó el formulario, y double_opt_in es confirmed o pending (se confirma solo cuando se usa el enlace de verificación). Indica solo lo que cambió; GET /consent lo devuelve. Se cubren la UE y el EEE, Reino Unido, EE. UU., Canadá, Australia, Nueva Zelanda, Suiza, Japón, Corea del Sur, Singapur y Hong Kong, y cada veredicto cita su fuente oficial y la fecha en que la revisamos. Consulta las normas de correo de marketing por país para cada norma. Es una ayuda para pruebas, no asesoría legal.
Comprobar el envío y la construcción de un correo
Sección titulada «Comprobar el envío y la construcción de un correo»GET /v1/addresses/{address}/checks ejecuta comprobaciones sobre un correo: el más reciente que coincide con los mismos filtros que un recuento (since, service, type, subtype), o el indicado por email_id. No espera, así que espera primero al correo. Cada comprobación pasa, falla, avisa o se omite, con un motivo de una línea, y passed es true cuando ninguna falló:
| Comprobación | Falla o avisa cuando |
|---|---|
spf, dkim, dmarc |
El servidor de envío no está autorizado a enviar por el dominio, el correo no está firmado o no cumple la política DMARC del dominio From: las bandejas de entrada pueden marcarlo como spam o rechazarlo. Son los veredictos del propio servidor receptor |
local_links |
Un enlace lleva a localhost o a una dirección privada, directamente o tras una redirección como el redirect_to de Supabase. Los enlaces a un host marcado como entorno de desarrollo (en Project settings, o POST /v1/project/dev-hosts) pasan |
plain_http |
Un enlace en un sitio público no es https |
expected_host |
Con expect_host=staging.example.com: el enlace de inicio de sesión, verificación, restablecimiento o invitación termina en otro lugar, como producción. Los enlaces que abren una app (myapp://…) no tienen sitio y no se comparan. Se omite sin él |
expiry |
El correo no dice cuándo deja de funcionar su código o enlace de inicio de sesión (un aviso; se omite para otros enlaces, como los de un boletín) |
coupon_expiry |
Labs. El correo tiene un cupón pero no dice cuándo vence, como “válido hasta el 31 de octubre” o “válido durante 7 días” (un aviso). Cuenta cada cupón que contenga, no solo el código principal. Se omite cuando no hay cupón |
plain_text |
No hay versión de texto plano (un aviso) |
size |
El HTML supera los 102 KB, donde Gmail lo corta (un aviso) |
images_only |
Es casi todo imágenes, así que con las imágenes desactivadas casi no dice nada |
layout |
Puede que no quepa en un teléfono: partes con un ancho fijo mayor de 640 px sin estilos móviles (@media), imágenes sin texto alternativo (en blanco con las imágenes desactivadas) o texto de menos de 11 px. Un aviso, solo a partir del HTML, sin renderizarlo |
expected_text |
Con expect_text (repítelo hasta 10 frases): al asunto o al cuerpo le falta una, como el nombre del usuario o el del producto. Las mayúsculas, las comillas tipográficas y los espacios no importan, y el motivo nombra solo las frases que faltan. Se omite sin él |
expected_attachment |
Con expect_attachment=.pdf (repítelo para más): uno de ellos no está adjunto. Se omite sin él, o para un correo que llegó antes de que se registraran los adjuntos |
arrival_time |
Con since y expect_within=5m (o 90s, 2h, un número de segundos): llegó más tarde de ese tiempo después de since, como un cupón de bienvenida que tardó 14 minutos. Se omite sin él |
list_unsubscribe |
Labs. Solo correo de marketing: no hay cabecera List-Unsubscribe (falla), o solo tiene una dirección de correo o una http simple (avisa). Las bandejas de entrada muestran con ella su propio botón para darse de baja, y Gmail y Yahoo la exigen a los remitentes masivos |
one_click_unsubscribe |
Labs. Solo correo de marketing: no hay cabecera List-Unsubscribe-Post: List-Unsubscribe=One-Click con una dirección https, o una firma DKIM verificada no cubre ambas cabeceras List-Unsubscribe (RFC 8058 lo exige, y si no, las bandejas de entrada no ofrecen la baja en un clic), así que el botón de una bandeja de entrada no puede dar de baja en un solo paso (un aviso) |
unsubscribe_link |
Labs. Solo correo de marketing: el propio correo no tiene enlace para darse de baja ni de preferencias de correo |
after_unsubscribe |
Labs. Solo correo de marketing: llegó después de que la dirección se diera de baja de su remitente (consulta Vigilancias de bajas). Se omite cuando la dirección no está marcada como dada de baja de él |
offer_reuse |
Labs. Un cupón de un remitente que ya envió uno a otra versión de la dirección (name+promo1@…, mayúsculas): la app trató a una persona como varias. Se omite para correos sin cupón |
coupon_result |
Labs. Qué hizo el proceso de pago con su cupón, una vez registrado un resultado: aceptado después de su fecha de fin, para una oferta reutilizada o dos veces cuando era de un solo uso (falla); un error, o un rechazo en el primer uso dentro de sus fechas (avisa). Se omite sin resultado |
marketing_consent |
Labs. Correo de marketing a una cuenta que lo rechazó, donde se necesita consentimiento previo (UE y EEE, Reino Unido, Canadá, Australia, Nueva Zelanda, Suiza, Japón, Corea del Sur). Se omite en EE. UU., o cuando no hay consentimiento registrado |
prechecked_consent |
Labs. El consentimiento vino de una casilla premarcada (UE y EEE, Reino Unido) |
double_opt_in |
Labs. Marketing antes de que la dirección confirmara su suscripción: un aviso en Alemania, un fallo donde el proyecto exige doble opt-in |
unsubscribe_deadline |
Labs. Marketing después de una baja: un aviso dentro del plazo permitido (EE. UU., Canadá, Singapur y Hong Kong 10 días hábiles, Australia y Nueva Zelanda 5, UE, Reino Unido, Suiza, Japón y Corea del Sur sin demora), un fallo después |
postal_address |
Labs. No se encontró dirección postal en un correo de marketing (EE. UU., Canadá; un aviso) |
sender_identity |
Labs. La línea From no indica ningún remitente (un aviso) |
minor_marketing |
Labs. Marketing a un persona menor de 13 años en Reino Unido o de 16 en la UE y el EEE (un aviso) |
misleading_subject |
Labs. EE. UU., Canadá, Hong Kong: un asunto que empieza como una respuesta o un reenvío (Re:, Fwd:), o que dice que la oferta termina hoy cuando su cupón dura más días (un aviso; las leyes de Washington y California se aplican a personas de EE. UU.) |
ad_label |
Labs. Singapur: marketing a una cuenta que lo rechazó cuyo asunto no empieza con <ADV> (un aviso) |
harmful_attachment |
Labs. Un adjunto es de un tipo de archivo que Gmail bloquea (.exe, .js, .iso y otros), o un programa con nombre de documento (.pdf.exe): falla. Los archivos de Office que pueden llevar macros, las páginas HTML o SVG y los archivos comprimidos avisan. El motivo nombra extensiones, nunca archivos. Se omite sin adjuntos |
spam_risk |
Labs. Una puntuación de spam sobre 10 (score), que suma las señales que valoran los filtros de spam (markers, cada una con sus puntos): falla desde 5, donde las bandejas de entrada probablemente rechazarían el correo o lo marcarían como spam, y avisa desde 1,5. Los bloqueos graves suman 5 o 6: DMARC fallando con una política de rechazo o cuarentena, ni SPF ni DKIM pasando, un adjunto bloqueado. Luego de 2 a 3: sin política DMARC, sin DKIM, casi todo imágenes, un enlace a una dirección IP sin dominio, un acortador de enlaces, marketing sin cabecera List-Unsubscribe. Luego hasta 1,5: SPF fallando, adjuntos de riesgo, baja en un clic sin firmar, un asunto de marketing que empieza con Re:, un asunto en mayúsculas o con !!!, frases típicas de spam, texto de enlace que muestra otro sitio, sin texto plano. Es una estimación: las bandejas de entrada también valoran la reputación del remitente, que solo ellas pueden ver |
preheader |
Labs. Solo correo de marketing: no hay texto de vista previa oculto al principio del HTML, así que las bandejas de entrada muestran sus primeras palabras, a menudo “Ver en el navegador” (un aviso) |
language |
Con expect_language=de (o pt-BR): está en otro idioma. Avisa cuando hay muy poco texto para saberlo. Inglés, alemán, francés, español, italiano, neerlandés, portugués, sueco, polaco, turco, finés, ruso, ucraniano, japonés, chino, coreano, árabe, hebreo, griego, tailandés e hindi. Se omite sin él |
Las comprobaciones marcadas con Labs quedan fuera hasta que el creador de la clave activa su función Labs: Marketing and offer emails para las comprobaciones de cupones y marketing, y Spam score and attachment checks para harmful_attachment y spam_risk.
curl "https://api.proxymail.qa/v1/addresses/k7f2x9ab12cd@mailclub.dev/checks?type=link&expect_host=staging.example.com" \ -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY"{ "address": "k7f2x9ab12cd@mailclub.dev", "email": { "id": "…", "received_at": "2026-10-17T10:00:01Z", "service": "Supabase", "service_domain": "supabase.io", "type": "link", "subtype": "verify" }, "passed": false, "failed": ["local_links"], "warnings": ["expiry"], "checks": [ { "check": "spf", "status": "pass", "detail": "The sending server is allowed to send for supabase.io." }, { "check": "local_links", "status": "fail", "detail": "A link goes to localhost:3000, which only opens on the computer running that server." } ]}Los correos de inicio de sesión y los recibos omiten las cuatro comprobaciones de marketing, igual que los correos anteriores a la llegada de los tipos. Una comprobación sobre el correo más reciente ahora puede caer en un boletín y fallar list_unsubscribe: pasa type, subtype o category para comprobar el correo que quieres.
Para correos localizados, comprueba la copia de cada idioma: ?type=code&expect_language=fr&expect_text=Bonjour%20Maya.
La respuesta nunca contiene lo que dice el correo ni sus enlaces: los motivos nombran como mucho un dominio, el host de un enlace o tus propias frases, así que un agente puede actuar sobre ella sin leer texto de correo no confiable. El cliente de Node.js tiene checkEmail, y el helper de Python check_email.
Un grupo de cuentas para pruebas en paralelo
Sección titulada «Un grupo de cuentas para pruebas en paralelo»Mantén las cuentas de prueba en estados conocidos y deja que cada prueba tome una prestada: etiquétalas con su estado (verified, premium, trial) y luego reserva una. Obtienes una cuenta que cumple todos los filtros y que nadie más tiene reservada, primero la usada hace más tiempo, retenida durante minutes (10 si no pides otra cosa, 60 como máximo). Las pruebas en paralelo nunca obtienen la misma.
curl -s -X POST https://api.proxymail.qa/v1/addresses/checkout -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" \ -H "Content-Type: application/json" -d '{"tags":["verified","premium"],"country":"DE","minutes":5}'# { "address": "…", "persona": {…}, "tags": ["premium", "verified"], "checkout": { "token": "…", "until": "…" } }Inicia sesión con ella (con su contraseña guardada, si la clave puede leerla), ejecuta la prueba y luego devuélvela con el token. Al devolverla puedes registrar el estado en que la dejó la prueba, para que el grupo siga siendo exacto:
curl -s -X POST "https://api.proxymail.qa/v1/addresses/$ADDRESS/return" -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" \ -H "Content-Type: application/json" -d "{\"token\":\"$TOKEN\",\"add_tags\":[\"cancelled\"],\"remove_tags\":[\"premium\"]}"El cliente de Node.js y el helper de Python también pueden crear, comprobar y restablecer cuentas con hooks desde tus pruebas, para que cada prueba obtenga una cuenta en el estado que necesita.
Una reserva que no se devuelve se libera sola cuando se le acaba el tiempo, así que una prueba que falla nunca retiene una cuenta mucho tiempo; extend le da más tiempo. Cuando todas las coincidencias están reservadas, la respuesta es 409 e indica cuándo se libera la primera; cuando ninguna coincide, 404. La app muestra quién tiene una cuenta y hasta cuándo, y cualquiera del proyecto puede liberarla.
Ejecuciones: un informe para la pull request
Sección titulada «Ejecuciones: un informe para la pull request»Una ejecución agrupa un job de CI o la sesión de un agente, y guarda la evidencia de lo que vieron sus llamadas. Inicia una y luego envía su id en el encabezado Proxy-Mail-QA-Run de cada llamada:
RUN=$(curl -s -X POST https://api.proxymail.qa/v1/runs -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" \ -H "Content-Type: application/json" -d '{"name":"Sign-up tests","source":"ci"}' | jq -r .id)curl -s "https://api.proxymail.qa/v1/addresses/$ADDRESS/code?since=$SUBMITTED" \ -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" -H "Proxy-Mail-QA-Run: $RUN"curl -s -X POST "https://api.proxymail.qa/v1/runs/$RUN/finish" -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY"curl -s "https://api.proxymail.qa/v1/runs/$RUN?format=markdown" -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY"GET /v1/runs/{id} también compara la ejecución con las últimas 10 ejecuciones terminadas con el mismo nombre (comparison en JSON, una lista cerca del inicio del informe Markdown): correos mucho más lentos, un número distinto de correos, fallos de prueba nuevos e intermitentes, comprobaciones que empiezan a fallar y un nuevo remitente o sitio de enlace. Cuando comprobó correos, deliverability indica cuántos probablemente serían rechazados o enviados a spam (likely_spam), podrían ir a spam (may_spam) o llevan un adjunto que las bandejas de entrada bloquean, contando cada correo una vez según su última comprobación; el informe Markdown dice lo mismo en la línea bajo su título. Es una estimación a partir de la comprobación spam_risk de cada correo, así que necesita que las comprobaciones de puntuación de spam y de adjuntos estén activadas en Labs.
Cada llamada añade lo que vio: una dirección creada (incluidos los valores del formulario) o reservada del grupo, un correo que llegó (el código, o el sitio y la ruta del enlace con todo lo que parezca un token oculto, y cuántos segundos después de since), un recuento, comprobaciones de correo, de baja de suscripción, de ofertas y de cupones, y un correo que nunca llegó o que llegó cuando no debía. Al terminar, la ejecución se aprueba salvo que una de esas cosas haya fallado, una nota la haya marcado como fallida o envíes {"failed": true}. El informe es Markdown, para un comentario de pull request o un resumen de job, y la ejecución está en la app para todos los del proyecto, durante 90 días.
Para indicar a qué prueba pertenece una llamada, envía su nombre, codificado con porcentajes, en Proxy-Mail-QA-Test (por ejemplo sign-up%20%E2%80%BA%20sends%20one%20code): el informe lo muestra junto a lo que vio la llamada. El reporter de un ejecutor de pruebas envía resultados a POST /v1/runs/{id}/tests: cada prueba fallida o inestable como {"name", "status": "failed", "duration_ms", "error"} (una prueba fallida hace fallar la ejecución), y luego {"totals": {"passed", "failed", "skipped", "flaky"}} para el inicio del informe.
La evidencia nunca hace fallar una llamada: si una llamada no se puede añadir (la ejecución terminó, o no está en el proyecto de esta clave), funciona igual y el encabezado Proxy-Mail-QA-Run-Warning explica por qué. Un cliente que vuelve a preguntar cuando una espera regresa vacía envía Proxy-Mail-QA-Run-Retry: true en todos los intentos menos el último, así que solo cuenta el último “no llegó nada”. En GitHub Actions, la acción Proxy Mail QA hace todo esto.
Cuando no llega nada
Sección titulada «Cuando no llega nada»El 404 también explica por qué, hasta donde podemos saberlo: hint indica todo lo que rechazamos para la dirección (demasiado grande, o por encima de un límite) o, cuando nunca le ha llegado nada, la causa más probable; refused lista lo que rechazamos desde since; blocked lista el correo que los servidores de correo de Cloudflare rechazaron antes de que nos llegara (rechazado por no pasar SPF, DKIM o DMARC, descartado o no entregado), con el dominio del remitente y sus resultados de SPF, DKIM y DMARC; y help enlaza a ¿No llega el correo de prueba?. Las herramientas MCP responden a found: false con los mismos message, refused y help. Para comprobar una dirección en cualquier momento sin esperar, llama a GET /v1/addresses/{address}/delivery: devuelve los correos recibidos hasta ahora, cada rechazo aún registrado y un hint. Tu app tiene que poder enviar correo a cualquier dirección: un proveedor de correo en modo sandbox o de prueba solo envía a direcciones que hayas verificado.
Errores
Sección titulada «Errores»Los errores se devuelven como {"error": "not_found", "message": "…"} con el estado correspondiente: 400, 401, 402 (una organización sin plan), 403 (sin permiso, o una función de Labs desactivada), 404, 409 (llegó un correo durante una comprobación none) o 429. Los límites de solicitudes están en Límites y seguridad. Un 500, o solicitudes que agotan el tiempo para todos, puede ser un incidente de nuestra parte: consulta nuestra página de estado.
¿Tienes una pregunta sobre esta página?
Pregunta en cualquier idioma. La documentación puede responderte de inmediato, o puedes enviar tu pregunta al equipo de Proxy Mail QA, que responde en inglés.