Formulario de suscripción a newsletter6 min de lectura
Envía a MailerLite las suscripciones de tu propio formulario de newsletter
Conecta tu propio formulario de suscripción con MailerLite: un relay añade a los lectores nuevos como no confirmados para el doble opt-in y nunca pide a un suscriptor que confirme dos veces.
Envía tu propio formulario a Ikiform y apunta el webhook a un relay que llame a la API de MailerLite. El relay busca primero la dirección: un lector nuevo se crea como unconfirmed, lo que hace que MailerLite envíe su email de doble opt-in cuando el ajuste de la API está activado, y un suscriptor existente solo recibe el nuevo grupo. Esa búsqueda es lo que evita que a un suscriptor confirmado se le pida confirmar otra vez.
Qué cambia con MailerLite
El endpoint de crear suscriptor de MailerLite es un upsert: un POST a /api/subscribers crea un suscriptor nuevo o actualiza uno existente, y responde 201 o 200. No es destructivo, así que los grupos y campos que no incluyas se quedan como estaban. El problema es status. Con el ajuste de cuenta Double opt-in for API and integrations activado, MailerLite envía una confirmación a los nuevos suscriptores de la API. Un suscriptor existente activo o dado de baja solo la recibe cuando la llamada envía status: "unconfirmed".
La ayuda de MailerLite dice que los suscriptores existentes activos o dados de baja no reciben el email de confirmación a menos que la llamada incluya "status": "unconfirmed". Así que un relay que siempre envía unconfirmed convierte cada suscripción repetida de un lector fiel en una nueva petición de confirmación. La solución es buscar la dirección antes de decidir qué enviar:
- El relay verifica el webhook de Ikiform.
- Pide a MailerLite el suscriptor por email.
- No existe: lo crea con estado unconfirmed y el grupo de la ubicación, y MailerLite envía el email de confirmación.
- Existe y está activo o sin confirmar: añade el grupo sin estado, así que nada de su confirmación cambia.
- Existe y se dio de baja: no hace nada, así que una suscripción nunca deshace en silencio una baja.
Configura MailerLite
- En MailerLite, abre los ajustes de suscripción y activa Double opt-in for API and integrations. Edita ahí el email de confirmación; MailerLite envía el mismo a todos los suscriptores añadidos mediante la API.
- Crea un grupo por ubicación, como "Website footer" y "Blog posts", y apunta el id de cada grupo.
- En Integrations, crea un token de API y guárdalo para el entorno del relay.
El relay
Esto es un servidor Express. Lee el cuerpo sin procesar como texto, porque la firma cubre los bytes exactos que envió Ikiform, y analizar el JSON primero los cambiaría.
import express from "express";
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";
const API = "https://connect.mailerlite.com/api";
const GROUPS: Record<string, string> = {
footer: process.env.MAILERLITE_GROUP_FOOTER!,
post: process.env.MAILERLITE_GROUP_POST!,
};
function mailerlite(path: string, init: RequestInit = {}) {
return fetch(`${API}${path}`, {
...init,
headers: {
Authorization: `Bearer ${process.env.MAILERLITE_API_KEY}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
}
async function subscribe(email: string, name: string | undefined, source: string) {
const found = await mailerlite(`/subscribers/${encodeURIComponent(email)}`);
if (found.status !== 200 && found.status !== 404) return found;
const groups = [GROUPS[source] ?? process.env.MAILERLITE_GROUP_FOOTER!];
const fields = name ? { name } : {};
if (found.status === 404) {
return mailerlite("/subscribers", {
method: "POST",
body: JSON.stringify({ email, fields, groups, status: "unconfirmed" }),
});
}
const existing = await found.json();
if (existing.data.status === "unsubscribed") return new Response(null, { status: 208 });
return mailerlite("/subscribers", { method: "POST", body: JSON.stringify({ email, fields, groups }) });
}
const app = express();
app.post("/hooks/mailerlite", express.text({ type: "application/json", limit: "512kb" }), async (req, res) => {
let event;
try {
event = await verifyWebhook({ payload: req.body, headers: req.headers, secret: process.env.IKIFORM_WEBHOOK_SECRET! });
} catch (error) {
if (error instanceof WebhookVerificationError) return res.status(400).send(error.reason);
return res.status(500).end();
}
if (event.type !== "submission.created") return res.status(204).end();
const { email, first_name, consent, source } = event.data.fields;
if (consent !== true || typeof email !== "string") return res.status(200).send("Skipped");
const answer = await subscribe(
email,
typeof first_name === "string" ? first_name : undefined,
typeof source === "string" ? source : "footer",
);
if (answer.status === 208) return res.status(200).send("Unsubscribed earlier, left alone");
if (answer.status === 429) {
return res.status(503).set("retry-after", answer.headers.get("retry-after") ?? "60").send("MailerLite rate limit");
}
if (!answer.ok) return res.status(502).send(`MailerLite answered ${answer.status}: ${await answer.text()}`);
res.status(204).end();
});
app.listen(Number(process.env.PORT ?? 3000));- MailerLite recibe el token como
Authorization: Bearer. La URL base eshttps://connect.mailerlite.com/api. GET /api/subscribers/{email}acepta un email o un id y responde404cuando el suscriptor no existe.groupsrecibe ids de grupos que ya existen. Un id incorrecto se rechaza con422, que el relay transmite como502con el mensaje de MailerLite.fields.namees el campo de nombre por defecto de MailerLite. Los campos personalizados deben existir en MailerLite antes de enviarlos.- El
208es una marca interna para un lector dado de baja. Ikiform ve200y una nota en el registro de entregas.
Despliégalo en cualquier sitio que te dé una URL HTTPS pública en el puerto 443, añade esa URL en Webhooks en Ikiform y pon el secreto whsec_ en IKIFORM_WEBHOOK_SECRET. La documentación de webhooks cubre los reintentos y el payload, y tu propio servidor tiene la misma configuración de Express con una base de datos.
Dónde vive el registro del consentimiento
La llamada de creación de MailerLite acepta subscribed_at, ip_address, opted_in_at y optin_ip. Deja fuera los campos de IP. El webhook de Ikiform nunca incluye la dirección IP del visitante ni su hash, así que el relay no tiene nada verdadero que enviar, y un valor inventado es peor que ninguno.
El registro de la suscripción se queda en Ikiform: el envío conserva consent: true con su marca de tiempo, origen, referrer y país. El registro de la confirmación vive en MailerLite, que anota cuándo hizo clic el lector en el enlace. Entre los dos puedes mostrar cuándo alguien marcó la casilla y cuándo demostró que la dirección era suya. Exporta la parte de Ikiform como CSV desde Submissions cuando la necesites.
Límites de frecuencia
El límite global de la API de MailerLite es de 120 peticiones por minuto. El relay hace dos por suscripción, así que caben unas 60 suscripciones por minuto. Por encima de eso, MailerLite responde 429, el relay responde 503 con Retry-After, e Ikiform vuelve a intentarlo más tarde con la misma entrega.
| MailerLite responde | El relay responde | Ikiform |
|---|---|---|
201 o 200 | 204 | Entrega correcta |
429 | 503 con Retry-After | Espera y reintenta |
401 | 502 | Reintenta. Arregla el token y luego usa Retry now en las entregas fallidas |
422 | 502 con los errores | Reintenta. Normalmente es un id de grupo o un nombre de campo incorrecto |
Errores comunes
- No envíes
status: "active". Marca a la persona como suscrita, y la documentación de MailerLite no dice si aun así sale una confirmación. Prueba con el estado que envíes. - Volver a suscribir es una decisión. MailerLite tiene un flag
resubscribepara quienes se dieron de baja. Envíalo solo cuando tu formulario lo diga, y cuando el lector sepa que vuelve a darse de alta. - El interruptor importa. MailerLite solo envía un email de confirmación a las suscripciones de la API cuando Double opt-in for API and integrations está activado.
- Un solo email de confirmación. El email de doble opt-in de la API es el mismo para todas las suscripciones de la API. Si necesitas un texto distinto por ubicación, usa formularios de MailerLite separados.
- Duplicados en Ikiform. Ikiform guarda cada envío aceptado, mientras que MailerLite guarda un suscriptor por email. Cuenta los suscriptores en MailerLite, no en el panel de Ikiform.
Comprueba que funciona
- Envía una prueba desde el endpoint en Ikiform. La entrega muestra
204. - Suscríbete con una dirección nueva tuya. MailerLite muestra el suscriptor como no confirmado en el grupo del footer, y llega el email de confirmación.
- Confírmalo y vuelve a suscribirte desde un post del blog. El suscriptor sigue activo, se une al segundo grupo y no recibe un nuevo email de confirmación.
- Da de baja esa dirección en MailerLite y suscríbete una vez más. El registro de entregas muestra la nota de baja, y MailerLite sigue mostrándolo como dado de baja.
Preguntas
Sí. Envía el formulario a Ikiform y deja que un relay añada cada suscripción mediante la API de MailerLite con el grupo correcto. El formulario se queda en tu markup y MailerLite sigue enviando la newsletter.
O bien Double opt-in for API and integrations está desactivado en los ajustes de suscripción de MailerLite, o bien el relay creó el suscriptor como activo. Mantén el ajuste activado, envía el estado unconfirmed y prueba con una dirección tuya.
No con este relay. Primero busca la dirección y solo envía unconfirmed para un suscriptor nuevo. Un suscriptor existente se añade al nuevo grupo y no cambia nada más.
Da a cada ubicación un valor distinto en el input oculto source del formulario y relaciona cada valor con un id de grupo de MailerLite en el relay. Un suscriptor existente que se suscribe desde una segunda ubicación también se une a ese grupo, y conserva el primero.
No. Ikiform nunca envía email a la persona que envía un formulario. MailerLite envía la confirmación, cada newsletter y el enlace para darse de baja.
¿Te atascaste en un paso o quieres una guía para tu caso? Escribe a hello@ikiform.com.