Formulário de inscrição em newsletter

Formulário de inscrição em newsletter6 min de leitura

Envie as inscrições do seu próprio formulário de newsletter para o MailerLite

Conecte seu próprio formulário de inscrição ao MailerLite: um relay adiciona novos leitores como não confirmados para o double opt-in e nunca pede a um assinante que confirme duas vezes.

Preet SutharFundador, Ikiform

Envie seu próprio formulário ao Ikiform e aponte o webhook para um relay que chama a API do MailerLite. O relay procura o endereço primeiro: um leitor novo é criado como unconfirmed, o que faz o MailerLite enviar o e-mail de double opt-in quando a opção da API está ligada, e um assinante existente só ganha o novo grupo. É essa consulta que impede que um assinante confirmado seja convidado a confirmar de novo.

O que muda com o MailerLite

O endpoint de criar assinante do MailerLite é um upsert: um POST em /api/subscribers cria um novo assinante ou atualiza um existente, respondendo 201 ou 200. Ele não é destrutivo, então grupos e campos que você omite continuam como estavam. O porém é o status. Com a opção de conta Double opt-in for API and integrations ligada, o MailerLite envia uma confirmação por e-mail aos novos assinantes via API. Um assinante existente, ativo ou descadastrado, só recebe uma quando a chamada envia status: "unconfirmed".

A ajuda do MailerLite diz que assinantes existentes, ativos ou descadastrados, não recebem o e-mail de confirmação a menos que a chamada inclua "status": "unconfirmed". Então um relay que sempre envia unconfirmed transforma cada nova inscrição de um leitor fiel num novo pedido de confirmação. A solução é procurar o endereço antes de decidir o que enviar:

Como o relay decide o que enviar ao MailerLiteWebhook doIkiformassinatura verificadaGET subscriberpelo e-mail404: novocriar como nãoconfirmadoativosó adicionar o grupodescadastradonão mexernão encontradoencontradoComo o relay decide o que enviar ao MailerLiteWebhook doIkiformassinatura verificadaGET subscriberpelo e-mail404: novocriar como nãoconfirmadoativosó adicionaro grupodescadastradonão mexernão encontradoencontrado
  1. O relay verifica o webhook do Ikiform.
  2. Ele pede ao MailerLite o assinante pelo e-mail.
  3. Não encontrado: ele o cria com status unconfirmed e o grupo da posição, e o MailerLite envia o e-mail de confirmação.
  4. Encontrado e ativo ou não confirmado: ele adiciona o grupo sem status, então nada muda na confirmação.
  5. Encontrado e descadastrado: ele não faz nada, para que uma inscrição nunca desfaça um descadastro sem ninguém perceber.
Duas chamadas de API por inscrição, e a segunda depende do que a primeira encontrou.

Configure o MailerLite

  1. No MailerLite, abra as configurações de inscrição e ligue Double opt-in for API and integrations. Edite ali o e-mail de confirmação; o MailerLite envia o mesmo para todo assinante adicionado pela API.
  2. Crie um grupo por posição, como "Website footer" e "Blog posts", e anote o id de cada grupo.
  3. Em Integrations, crie um token de API e guarde-o para o ambiente do relay.
O FAQ de preços do MailerLite diz que o acesso à API é limitado no plano mais baixo. Teste se o token consegue criar assinantes no seu plano antes de confiar nele.

O relay

Este é um servidor Express. Ele lê o corpo bruto como texto, porque a assinatura cobre exatamente os bytes que o Ikiform enviou, e interpretar o JSON antes os alteraria.

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));
  • O MailerLite recebe o token como Authorization: Bearer. A URL base é https://connect.mailerlite.com/api.
  • GET /api/subscribers/{email} aceita um e-mail ou um id e responde 404 quando o assinante não existe.
  • groups recebe ids de grupos que já existem. Um id errado é recusado com 422, que o relay repassa como 502 com a mensagem do MailerLite.
  • fields.name é o campo padrão de primeiro nome do MailerLite. Campos personalizados precisam existir no MailerLite antes de você enviá-los.
  • O 208 é um marcador interno para um leitor descadastrado. O Ikiform vê 200 e uma nota no log de entregas.

Faça o deploy em qualquer lugar que ofereça uma URL HTTPS pública na porta 443, adicione essa URL em Webhooks no Ikiform e coloque o segredo whsec_ em IKIFORM_WEBHOOK_SECRET. A documentação de webhooks cobre as novas tentativas e o payload, e seu próprio servidor tem a mesma configuração Express com um banco de dados.

A chamada de create do MailerLite aceita subscribed_at, ip_address, opted_in_at e optin_ip. Deixe os campos de IP de fora. O webhook do Ikiform nunca inclui o endereço IP do visitante nem o hash dele, então o relay não tem nada verdadeiro para enviar, e um valor inventado é pior que nenhum.

O registro da inscrição fica no Ikiform: o envio guarda consent: true com data e hora, origem, referrer e país. O registro da confirmação fica no MailerLite, que anota quando o leitor clicou no link. Com os dois, você consegue mostrar quando alguém marcou a caixa e quando provou que o endereço era seu. Exporte o lado do Ikiform como CSV em Submissions quando precisar.

Limites de taxa

O limite global da API do MailerLite é de 120 requisições por minuto. O relay faz duas por inscrição, então cabem cerca de 60 inscrições por minuto. Acima disso, o MailerLite responde 429, o relay responde 503 com Retry-After e o Ikiform tenta de novo mais tarde com a mesma entrega.

O MailerLite respondeO relay respondeIkiform
201 ou 200204Entrega bem-sucedida
429503 com Retry-AfterEspera e tenta de novo
401502Tenta de novo. Corrija o token e use Retry now nas entregas com falha
422502 com os errosTenta de novo. Geralmente um id de grupo ou nome de campo errado

Pegadinhas

  • Não envie status: "active". Ele marca a pessoa como inscrita, e a documentação do MailerLite não diz se uma confirmação ainda é enviada. Teste com o status que você envia.
  • Reinscrever é uma escolha. O MailerLite tem uma flag resubscribe para quem se descadastrou. Só a envie quando o seu formulário disser isso, e quando o leitor souber que está voltando a se inscrever.
  • A chave importa. O MailerLite só envia e-mail de confirmação para inscrições via API quando Double opt-in for API and integrations está ligado.
  • Um único e-mail de confirmação. O e-mail de double opt-in da API é compartilhado por todas as inscrições via API. Se você precisa de textos diferentes por posição, use formulários separados do MailerLite.
  • Duplicados no Ikiform. O Ikiform guarda todo envio aceito, enquanto o MailerLite guarda um assinante por e-mail. Conte os assinantes no MailerLite, não no painel do Ikiform.

Confira se funciona

  1. Envie um teste pelo endpoint no Ikiform. A entrega mostra 204.
  2. Inscreva-se com um endereço novo seu. O MailerLite mostra o assinante como não confirmado no grupo do rodapé, e o e-mail de confirmação chega.
  3. Confirme e depois inscreva-se de novo a partir de um post do blog. O assinante continua ativo, entra no segundo grupo e não recebe um novo e-mail de confirmação.
  4. Descadastre esse endereço no MailerLite e inscreva-se mais uma vez. O log de entregas mostra a nota de descadastrado, e o MailerLite continua mostrando o endereço como descadastrado.

Perguntas

Travou em algum passo ou quer um guia para o seu caso? Envie um e-mail para hello@ikiform.com.