Formulaire d'inscription à la newsletter

Formulaire d'inscription à la newsletter6 min de lecture

Envoyer les inscriptions de votre propre formulaire de newsletter vers MailerLite

Reliez votre propre formulaire d'inscription à MailerLite : un relais ajoute les nouveaux lecteurs comme non confirmés pour le double opt-in, sans jamais demander deux fois à un abonné de confirmer.

Preet SutharFondateur, Ikiform

Envoyez votre propre formulaire à Ikiform et faites pointer le webhook vers un relais qui appelle l'API de MailerLite. Le relais cherche d'abord l'adresse : un nouveau lecteur est créé en unconfirmed, ce qui fait envoyer à MailerLite son e-mail de double opt-in quand le réglage API est activé, et un abonné existant reçoit seulement le nouveau groupe. C'est cette recherche qui évite de redemander à un abonné confirmé de confirmer.

Ce qui change avec MailerLite

L'endpoint de création d'abonné de MailerLite est un upsert : un seul POST vers /api/subscribers crée un nouvel abonné ou met à jour un abonné existant, avec une réponse 201 ou 200. Il n'est pas destructif : les groupes et les champs que vous omettez restent tels quels. Le piège, c'est status. Avec le réglage de compte Double opt-in for API and integrations activé, MailerLite envoie une confirmation aux nouveaux abonnés ajoutés par l'API. Un abonné existant, actif ou désinscrit, n'en reçoit une que si l'appel envoie status: "unconfirmed".

L'aide de MailerLite indique que les abonnés existants, actifs ou désinscrits, ne reçoivent pas l'e-mail de confirmation sauf si l'appel inclut "status": "unconfirmed". Un relais qui envoie toujours unconfirmed transforme donc chaque réinscription d'un lecteur fidèle en nouvelle demande de confirmation. La solution consiste à chercher l'adresse avant de décider quoi envoyer :

Comment le relais décide quoi envoyer à MailerLiteWebhook Ikiformsignature vérifiéeGET subscriberpar e-mail404 : nouveaucréation enunconfirmedactifajout du groupeseulementdésinscritne rien fairenon trouvétrouvéComment le relais décide quoi envoyer à MailerLiteWebhook Ikiformsignature vérifiéeGET subscriberpar e-mail404 : nouveaucréation enunconfirmedactifajout dugroupeseulementdésinscritne rienfairenon trouvétrouvé
  1. Le relais vérifie le webhook d'Ikiform.
  2. Il demande l'abonné à MailerLite par e-mail.
  3. Introuvable : il le crée avec le statut unconfirmed et le groupe de l'emplacement, et MailerLite envoie l'e-mail de confirmation.
  4. Trouvé, actif ou non confirmé : il ajoute le groupe sans statut, rien ne change donc pour sa confirmation.
  5. Trouvé et désinscrit : il ne fait rien, pour qu'une inscription n'annule jamais une désinscription en silence.
Deux appels d'API par inscription, et le second dépend de ce qu'a trouvé le premier.

Configurer MailerLite

  1. Dans MailerLite, ouvrez les réglages d'inscription et activez Double opt-in for API and integrations. Modifiez-y l'e-mail de confirmation ; MailerLite envoie le même à chaque abonné ajouté via l'API.
  2. Créez un groupe par emplacement, comme « Pied de page du site » et « Articles du blog », et notez l'id de chaque groupe.
  3. Sous Integrations, créez un token d'API et gardez-le pour l'environnement du relais.
La FAQ tarifaire de MailerLite indique que l'accès à l'API est limité sur son offre la plus basse. Vérifiez que le token peut créer des abonnés sur votre offre avant de vous y fier.

Le relais

Voici un serveur Express. Il lit le corps brut comme du texte, car la signature couvre les octets exacts envoyés par Ikiform, et analyser le JSON d'abord les modifierait.

server.ts
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 reçoit le token via Authorization: Bearer. L'URL de base est https://connect.mailerlite.com/api.
  • GET /api/subscribers/{email} accepte un e-mail ou un id et répond 404 quand l'abonné n'existe pas.
  • groups prend des id de groupes existants. Un id erroné est refusé avec 422, que le relais transmet en 502 avec le message de MailerLite.
  • fields.name est le champ prénom par défaut de MailerLite. Les champs personnalisés doivent exister dans MailerLite avant que vous ne les envoyiez.
  • Le 208 est un marqueur interne pour un lecteur désinscrit. Ikiform voit 200 et une note dans le journal de livraison.

Déployez-le n'importe où qui fournit une URL HTTPS publique sur le port 443, ajoutez cette URL sous Webhooks dans Ikiform, et mettez le secret whsec_ dans IKIFORM_WEBHOOK_SECRET. La documentation des webhooks couvre les nouvelles tentatives et le payload, et votre propre serveur propose la même configuration Express avec une base de données.

L'appel de création de MailerLite accepte subscribed_at, ip_address, opted_in_at et optin_ip. Laissez les champs d'IP de côté. Le webhook d'Ikiform n'inclut jamais l'adresse IP du visiteur ni son hash : le relais n'a donc rien de vrai à envoyer, et une valeur inventée est pire que rien.

La preuve de l'inscription reste dans Ikiform : la soumission garde consent: true avec son horodatage, son origine, son referrer et son pays. La preuve de la confirmation est dans MailerLite, qui note quand le lecteur a cliqué sur le lien. À eux deux, ils montrent quand quelqu'un a coché la case et quand il a prouvé que l'adresse était la sienne. Exportez la partie Ikiform en CSV depuis Submissions quand vous en avez besoin.

Limites de débit

La limite globale de l'API de MailerLite est de 120 requêtes par minute. Le relais en fait deux par inscription : environ 60 inscriptions par minute passent. Au-delà, MailerLite répond 429, le relais répond 503 avec Retry-After, et Ikiform réessaie plus tard avec la même livraison.

MailerLite répondLe relais répondIkiform
201 ou 200204Livraison réussie
429503 avec Retry-AfterAttend, puis réessaie
401502Réessaie. Corrigez le token, puis utilisez Retry now sur les livraisons échouées
422502 avec les erreursRéessaie. Généralement un id de groupe ou un nom de champ erroné

Pièges

  • N'envoyez pas status: "active". Cela marque la personne comme abonnée, et la documentation de MailerLite ne dit pas si une confirmation part quand même. Testez avec le statut que vous envoyez.
  • Réabonner est un choix. MailerLite a une option resubscribe pour les personnes désinscrites. Ne l'envoyez que si votre formulaire le dit, et que le lecteur sait qu'il se réabonne.
  • Le réglage compte. MailerLite n'envoie un e-mail de confirmation aux inscriptions par API que si Double opt-in for API and integrations est activé.
  • Un seul e-mail de confirmation. L'e-mail de double opt-in de l'API est commun à toutes les inscriptions par API. S'il vous faut un texte différent par emplacement, utilisez plutôt des formulaires MailerLite distincts.
  • Les doublons dans Ikiform. Ikiform garde chaque envoi accepté, tandis que MailerLite garde un abonné par e-mail. Comptez les abonnés dans MailerLite, pas dans le tableau de bord d'Ikiform.

Vérifiez que tout fonctionne

  1. Envoyez un test depuis l'endpoint dans Ikiform. La livraison affiche 204.
  2. Abonnez-vous avec une nouvelle adresse à vous. MailerLite affiche l'abonné comme non confirmé dans le groupe du pied de page, et l'e-mail de confirmation arrive.
  3. Confirmez, puis abonnez-vous à nouveau depuis un article du blog. L'abonné reste actif, rejoint le second groupe et ne reçoit pas de nouvel e-mail de confirmation.
  4. Désinscrivez cette adresse dans MailerLite et abonnez-vous encore une fois. Le journal de livraison affiche la note de désinscription, et MailerLite l'affiche toujours comme désinscrit.

Questions

Une étape vous bloque, ou vous voulez un guide pour votre cas ? Écrivez à hello@ikiform.com.