# 自作のニュースレターフォームの登録を MailerLite に送る

> 自作の登録フォームを MailerLite につなぎます。リレーが新しい読者をダブルオプトインのために未確認として追加し、購読者に二度確認を求めることはありません。

公開日 2026-10-25 · https://ikiform.com/ja/guides/newsletter-signup/mailerlite

自作のフォームを Ikiform に送信し、Webhook を MailerLite の API を呼ぶリレーに向けます。リレーはまずアドレスを検索します。新しい読者は `unconfirmed` として作成され、API の設定がオンなら MailerLite がダブルオプトインのメールを送ります。既存の購読者には新しいグループが追加されるだけです。この検索があるからこそ、確認済みの購読者が再び確認を求められずに済みます。

## MailerLite で異なる点

MailerLite の購読者作成のエンドポイントは upsert です。`/api/subscribers` への 1 回の `POST` で新しい購読者を作成するか既存の購読者を更新し、`201` か `200` を返します。破壊的ではないので、省略したグループやフィールドは元のままです。落とし穴は `status` です。アカウント設定の **Double opt-in for API and integrations** がオンなら、MailerLite は API で追加された新しい購読者に確認メールを送ります。既存のアクティブな購読者や配信停止した購読者に送られるのは、呼び出しで `status: "unconfirmed"` を送ったときだけです。

MailerLite のヘルプによると、既存のアクティブな購読者や配信停止した購読者には、呼び出しに `"status": "unconfirmed"` が含まれない限り確認メールは送られません。つまり、常に `unconfirmed` を送るリレーでは、熱心な読者が再登録するたびに新たに確認を求めることになります。解決策は、送る内容を決める前にアドレスを検索することです。

図: リレーが MailerLite に送る内容を決める方法

1. リレーが Ikiform の Webhook を検証します。
2. MailerLite にメールアドレスで購読者を問い合わせます。
3. 見つからない場合：ステータス unconfirmed と設置場所のグループで作成し、MailerLite が確認メールを送ります。
4. 見つかり、アクティブか未確認の場合：ステータスなしでグループを追加するので、確認の状態は何も変わりません。
5. 見つかり、配信停止済みの場合：何もしないので、登録が配信停止をひそかに取り消すことはありません。

1 件の登録で API の呼び出しは 2 回。2 回目は 1 回目の結果によって決まります。

## MailerLite を設定する

1. MailerLite で購読の設定を開き、**Double opt-in for API and integrations** をオンにします。確認メールはそこで編集します。MailerLite は API で追加されたすべての購読者に同じメールを送ります。
2. 「Website footer」や「Blog posts」のように設置場所ごとにグループを作り、各グループの id を控えておきます。
3. **Integrations** で API トークンを作成し、リレーの環境変数用に保管しておきます。

> MailerLite の料金の FAQ によると、最も安いプランでは API へのアクセスが制限されています。頼る前に、自分のプランでトークンが購読者を作成できるかテストしてください。

## リレー

これは Express のサーバーです。署名は Ikiform が送ったバイト列そのものが対象で、先に JSON を解析すると変わってしまうので、生のボディをテキストとして読みます。

`server.ts`:

```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 はトークンを `Authorization: Bearer` で受け取ります。ベース URL は `https://connect.mailerlite.com/api` です。
- `GET /api/subscribers/{email}` はメールアドレスか id を受け付け、購読者が存在しなければ `404` を返します。
- `groups` には既存のグループの id を指定します。間違った id は `422` で拒否され、リレーはそれを MailerLite のメッセージ付きの `502` として返します。
- `fields.name` は MailerLite のデフォルトの名のフィールドです。カスタムフィールドは、送る前に MailerLite に存在している必要があります。
- `208` は配信停止済みの読者を表す内部用の目印です。Ikiform には `200` と、配信ログのメモとして見えます。

ポート 443 の公開 HTTPS の URL が得られる場所ならどこにでもデプロイし、その URL を Ikiform の **Webhooks** に追加して、`whsec_` のシークレットを `IKIFORM_WEBHOOK_SECRET` に入れます。リトライとペイロードについては [Webhook のドキュメント](https://ikiform.com/ja/docs/webhooks.md)に、データベースを使った同じ Express の構成は[自前のサーバー](https://ikiform.com/blog/form-submissions-to-your-own-server.md)にあります。

## 同意の記録はどこに残るか

MailerLite の作成の呼び出しは `subscribed_at`、`ip_address`、`opted_in_at`、`optin_ip` を受け付けます。IP のフィールドは省いてください。Ikiform の Webhook には訪問者の IP アドレスもそのハッシュも含まれないので、リレーには送るべき正しい値がなく、でっち上げた値はないよりも悪いからです。

登録の記録は Ikiform に残ります。送信データには `consent: true` が、タイムスタンプ、オリジン、リファラー、国と一緒に保存されます。確認の記録は MailerLite にあり、読者がリンクをクリックした日時が記録されます。この 2 つで、いつチェックボックスにチェックが入り、いつ本人のアドレスであることが確かめられたかを示せます。必要なときは **Submissions** から Ikiform 側を CSV でエクスポートしてください。

## レート制限

MailerLite の API 全体の上限は 1 分あたり 120 リクエストです。リレーは 1 件の登録につき 2 回呼び出すので、1 分あたり約 60 件の登録が収まります。それを超えると MailerLite は `429` を返し、リレーは `Retry-After` 付きの `503` を返し、Ikiform は同じ配信をあとでリトライします。

| MailerLite の応答 | リレーの応答 | Ikiform |
| --- | --- | --- |
| `201` または `200` | `204` | 配信成功 |
| `429` | `Retry-After` 付きの `503` | 待ってからリトライ |
| `401` | `502` | リトライ。トークンを直してから、失敗した配信で **Retry now** を使う |
| `422` | エラー付きの `502` | リトライ。たいていはグループの id かフィールド名の間違い |

## ハマりどころ

- **`status: "active"` は送らない。** その人を購読済みとして扱いますが、それでも確認メールが送られるかどうかは MailerLite のドキュメントに書かれていません。送るステータスでテストしてください。
- **再購読は選択の問題。** MailerLite には、配信停止した人向けの `resubscribe` フラグがあります。フォームでそう明記し、読者が再び購読することをわかっている場合にだけ送ってください。
- **設定のオン・オフが重要。** MailerLite が API での登録に確認メールを送るのは、**Double opt-in for API and integrations** がオンのときだけです。
- **確認メールは 1 つ。** API のダブルオプトインのメールは、API でのすべての登録で共通です。設置場所ごとに文面を変えたいなら、MailerLite のフォームを分けて使ってください。
- **Ikiform での重複。** Ikiform は受け付けたすべての送信を保持し、MailerLite はメールアドレスごとに 1 人の購読者を保持します。購読者数は Ikiform のダッシュボードではなく MailerLite で数えてください。

## 動作を確認する

1. Ikiform でエンドポイントからテストを送ります。配信には `204` が表示されます。
2. 自分の新しいアドレスで購読します。MailerLite ではフッターのグループに未確認の購読者として表示され、確認メールが届きます。
3. 確認してから、ブログ記事からもう一度購読します。購読者はアクティブのままで 2 つ目のグループに加わり、新しい確認メールは届きません。
4. MailerLite でそのアドレスを配信停止にし、もう一度購読します。配信ログに配信停止のメモが表示され、MailerLite では配信停止のままです。

## よくある質問

### MailerLite の埋め込みの代わりに、自作の HTML フォームを使えますか？

はい。フォームを Ikiform に送信し、リレーに MailerLite の API で各登録を正しいグループ付きで追加させます。フォームは自分のマークアップのまま、ニュースレターは引き続き MailerLite が送ります。

### API での登録に MailerLite が確認メールを送らないのはなぜですか？

MailerLite の購読の設定で Double opt-in for API and integrations がオフになっているか、リレーが購読者を active として作成しています。設定をオンのままにし、ステータス unconfirmed を送り、自分のアドレスでテストしてください。

### 2 回登録した購読者には確認メールが 2 通届きますか？

このリレーでは届きません。先にアドレスを検索し、unconfirmed を送るのは新しい購読者の場合だけです。既存の購読者は新しいグループに追加されるだけで、ほかには何も変わりません。

### フッターとブログからの登録を MailerLite の別々のグループに入れるには？

フォームの非表示の source 入力欄に設置場所ごとに違う値を入れ、リレーで各値を MailerLite のグループ id に対応付けます。2 つ目の設置場所から登録した既存の購読者は、そのグループにも加わり、最初のグループもそのまま残ります。

### Ikiform は購読者に何か送りますか？

いいえ。Ikiform はフォームを送信した人に一切メールを送りません。確認メール、毎号のニュースレター、配信停止のリンクは MailerLite が送ります。

## その他のガイド

- [ニュースレター登録フォーム](https://ikiform.com/ja/guides/newsletter-signup.md): 自分のマークアップでニュースレター登録フォームを作り、同意を記録し、ボットの登録を防ぎ、購読者を Kit、MailerLite、beehiiv など任意のツールに渡します。
- [ダブルオプトイン](https://ikiform.com/ja/guides/newsletter-signup/double-opt-in.md): Ikiform は確認メールを送らないので、ダブルオプトインはメールツールで行います。中継が各登録を未確認として追加し、ツールがリンクを送ります。
- [Kit (ConvertKit)](https://ikiform.com/ja/guides/newsletter-signup/kit.md): 購読者を作成し、特典メールのために Kit のフォームに追加し、設置場所のタグを付けるリレーで、自作のニュースレターフォームを Kit につなぎます。
- [beehiiv](https://ikiform.com/ja/guides/newsletter-signup/beehiiv.md): 自作の登録フォームを beehiiv につなぎます。リレーがダブルオプトインをオンにし、設置場所を UTM ソースに、ページをリファラーにして各購読を作成します。
- [静的サイト](https://ikiform.com/ja/guides/newsletter-signup/static-site.md): Hugo、Jekyll、Eleventy、Astro の静的サイトにニュースレター登録フォームを追加し、メールツールの API キーをページに入れず、確認はツールで行います。
- [Next.js のニュースレターフォーム](https://ikiform.com/ja/guides/newsletter-signup/nextjs.md): Next.js のフッターのニュースレターフォームです。同意のチェックと Turnstile を行うサーバーアクションが Ikiform に送信し、ルートハンドラーが読者を Kit に追加します。
