Formulir pendaftaran newsletter

Formulir pendaftaran newsletter5 menit baca

Kirim pendaftaran dari formulir newsletter Anda sendiri ke MailerLite

Hubungkan formulir pendaftaran Anda sendiri ke MailerLite: relay menambahkan pembaca baru sebagai belum terkonfirmasi untuk double opt-in dan tidak pernah meminta subscriber mengonfirmasi dua kali.

Preet SutharPendiri, Ikiform

Kirim formulir Anda sendiri ke Ikiform dan arahkan webhook ke relay yang memanggil API MailerLite. Relay mencari alamatnya lebih dulu: pembaca baru dibuat sebagai unconfirmed, yang membuat MailerLite mengirim email double opt-in-nya saat pengaturan API aktif, dan subscriber yang sudah ada hanya mendapat grup baru. Pencarian itulah yang mencegah subscriber terkonfirmasi diminta mengonfirmasi lagi.

Apa yang berbeda dengan MailerLite

Endpoint create subscriber MailerLite adalah upsert: satu POST ke /api/subscribers membuat subscriber baru atau memperbarui yang sudah ada, dengan jawaban 201 atau 200. Endpoint ini tidak destruktif, jadi grup dan field yang tidak Anda sertakan tetap seperti semula. Masalahnya ada di status. Dengan pengaturan akun Double opt-in for API and integrations aktif, MailerLite mengirim konfirmasi ke subscriber API baru. Subscriber yang sudah ada dan berstatus active atau unsubscribed hanya mendapatkannya jika panggilan mengirim status: "unconfirmed".

Bantuan MailerLite menyatakan subscriber yang sudah ada dan berstatus active atau unsubscribed tidak mendapat email konfirmasi kecuali panggilannya menyertakan "status": "unconfirmed". Jadi relay yang selalu mengirim unconfirmed mengubah setiap pendaftaran ulang dari pembaca setia menjadi permintaan konfirmasi baru. Solusinya adalah mencari alamatnya sebelum memutuskan apa yang dikirim:

Bagaimana relay memutuskan apa yang dikirim ke MailerLiteWebhook Ikiformtanda tanganterverifikasiGET subscriberberdasarkan email404: barubuat sebagaiunconfirmedactivehanya tambah grupunsubscribedbiarkantidak ditemukanditemukanBagaimana relay memutuskan apa yang dikirim ke MailerLiteWebhook Ikiformtanda tanganterverifikasiGET subscriberberdasarkan email404: barubuat sebagaiunconfirmedactivehanya tambahgrupunsubscribedbiarkantidak ditemukanditemukan
  1. Relay memverifikasi webhook Ikiform.
  2. Relay meminta subscriber berdasarkan email ke MailerLite.
  3. Tidak ditemukan: relay membuatnya dengan status unconfirmed dan grup penempatannya, dan MailerLite mengirim email konfirmasi.
  4. Ditemukan dan berstatus active atau unconfirmed: relay menambahkan grup tanpa status, jadi tidak ada yang berubah dari konfirmasinya.
  5. Ditemukan dan berstatus unsubscribed: relay tidak melakukan apa pun, jadi pendaftaran tidak pernah diam-diam membatalkan unsubscribe.
Dua panggilan API per pendaftaran, dan yang kedua bergantung pada temuan yang pertama.

Siapkan MailerLite

  1. Di MailerLite, buka pengaturan subscribe dan aktifkan Double opt-in for API and integrations. Edit email konfirmasi di sana; MailerLite mengirim email yang sama ke setiap subscriber yang ditambahkan lewat API.
  2. Buat grup per penempatan, seperti "Footer website" dan "Artikel blog", dan catat ID setiap grup.
  3. Di Integrations, buat API token dan simpan untuk environment relay.
FAQ harga MailerLite menyatakan akses API dibatasi di paket terendahnya. Uji bahwa token bisa membuat subscriber di paket Anda sebelum mengandalkannya.

Relay

Ini adalah server Express. Server membaca raw body sebagai teks, karena tanda tangan mencakup byte persis yang dikirim Ikiform, dan mem-parsing JSON lebih dulu akan mengubahnya.

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 menerima token sebagai Authorization: Bearer. Base URL-nya https://connect.mailerlite.com/api.
  • GET /api/subscribers/{email} menerima email atau ID dan menjawab 404 saat subscriber tidak ada.
  • groups menerima ID grup yang sudah ada. ID yang salah ditolak dengan 422, yang diteruskan relay sebagai 502 dengan pesan MailerLite.
  • fields.name adalah field nama depan default MailerLite. Custom field harus ada di MailerLite sebelum Anda mengirimnya.
  • 208 adalah penanda internal untuk pembaca yang sudah unsubscribe. Ikiform melihat 200 dan catatan di log pengiriman.

Deploy di mana saja yang memberi Anda URL HTTPS publik di port 443, tambahkan URL itu di Webhooks di Ikiform, dan taruh secret whsec_ di IKIFORM_WEBHOOK_SECRET. Dokumentasi webhook membahas percobaan ulang dan payload, dan server Anda sendiri punya penyiapan Express yang sama dengan database.

Panggilan create MailerLite menerima subscribed_at, ip_address, opted_in_at, dan optin_ip. Jangan sertakan field IP. Webhook Ikiform tidak pernah menyertakan alamat IP pengunjung maupun hash-nya, jadi relay tidak punya nilai benar untuk dikirim, dan nilai karangan lebih buruk daripada tidak ada.

Catatan pendaftaran tetap di Ikiform: kiriman menyimpan consent: true bersama timestamp, origin, referrer, dan negaranya. Catatan konfirmasi ada di MailerLite, yang mencatat kapan pembaca mengklik tautan. Dari keduanya, Anda bisa menunjukkan kapan seseorang mencentang kotak dan kapan mereka membuktikan alamat itu milik mereka. Ekspor sisi Ikiform sebagai CSV dari Submissions saat Anda membutuhkannya.

Rate limit

Batas API global MailerLite adalah 120 request per menit. Relay melakukan dua per pendaftaran, jadi sekitar 60 pendaftaran per menit muat. Lebih dari itu, MailerLite menjawab 429, relay menjawab 503 dengan Retry-After, dan Ikiform mencoba lagi nanti dengan pengiriman yang sama.

Jawaban MailerLiteJawaban relayIkiform
201 atau 200204Pengiriman berhasil
429503 dengan Retry-AfterMenunggu, lalu mencoba ulang
401502Mencoba ulang. Perbaiki token, lalu gunakan Retry now pada pengiriman yang gagal
422502 dengan error-nyaMencoba ulang. Biasanya ID grup atau nama field yang salah

Hal yang sering menjebak

  • Jangan kirim status: "active". Status itu menandai orang tersebut sebagai sudah berlangganan, dan dokumentasi MailerLite tidak menyebutkan apakah konfirmasi tetap dikirim. Uji dengan status yang Anda kirim.
  • Berlangganan ulang adalah pilihan. MailerLite punya flag resubscribe untuk orang yang sudah unsubscribe. Kirim hanya jika formulir Anda menyatakannya, dan saat pembaca tahu mereka mendaftar kembali.
  • Toggle-nya penting. MailerLite hanya mengirim email konfirmasi untuk pendaftaran API saat Double opt-in for API and integrations aktif.
  • Satu email konfirmasi. Email double opt-in API dipakai bersama oleh setiap pendaftaran API. Jika Anda butuh kata-kata berbeda per penempatan, gunakan formulir MailerLite terpisah.
  • Duplikat di Ikiform. Ikiform menyimpan setiap kiriman yang diterima, sedangkan MailerLite menyimpan satu subscriber per email. Hitung subscriber di MailerLite, bukan di dashboard Ikiform.

Pastikan berfungsi

  1. Kirim uji dari endpoint di Ikiform. Pengiriman menampilkan 204.
  2. Berlangganan dengan alamat baru milik Anda. MailerLite menampilkan subscriber sebagai unconfirmed di grup footer, dan email konfirmasi tiba.
  3. Konfirmasi, lalu berlangganan lagi dari artikel blog. Subscriber tetap active, bergabung ke grup kedua, dan tidak mendapat email konfirmasi baru.
  4. Unsubscribe alamat itu di MailerLite lalu berlangganan sekali lagi. Log pengiriman menampilkan catatan unsubscribed, dan MailerLite tetap menampilkannya sebagai unsubscribed.

Pertanyaan

Mentok di salah satu langkah, atau ingin panduan untuk situasi Anda? Email hello@ikiform.com.