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

> 購読者を作成し、特典メールのために Kit のフォームに追加し、設置場所のタグを付けるリレーで、自作のニュースレターフォームを Kit につなぎます。

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

自分のフォームを Ikiform に送信し、Ikiform の Webhook を、Kit v4 API を最大 3 回呼び出すリレーに向けます。購読者の作成、Kit のフォームへの追加、登録場所のタグ付けです。購読者を `inactive` として作成し、特典メールがオンのフォームに追加すれば、Kit が独自の確認メールを送るはずです。Kit がこれを一括エンドポイントについてしか説明していないので、自分のアドレスで一度テストしてください。Kit の API キーはリレーが保持し、ページからは見えません。

## Kit で異なる点

Kit は 1 件の登録を別々のオブジェクトに分けます。購読者は人です。フォームはその人がどこから来たかを表し、確認メールを持っています。Kit はこれを特典メール（incentive email）とも呼び、ダブルオプトインの手順になっています。タグはセグメント分けに使うラベルです。v4 API にはそれぞれ 1 つのエンドポイントがあり、フォームとタグのエンドポイントはどちらも先に購読者が存在している必要があります。そのため、Ikiform の 1 件の登録は、短い一連の呼び出しを順番に行うことになります。

図: 1 件の登録でリレーが行う Kit API の呼び出し

1. リレーが Ikiform の署名付き Webhook を受け取り、検証します。
2. Kit に購読者を inactive として作成します。アドレスがすでに存在する場合、Kit は名を更新し、状態はそのままにします。
3. 購読者を Kit のフォームに追加します。そのフォームの特典メールがオンなら、Kit が確認メールを送ります。
4. 登録の source にタグがあれば、購読者にタグを付けます。この手順は任意です。

各呼び出しは前の呼び出しに依存するので、リレーは最初の失敗で止まり、Ikiform にリトライさせます。

| 呼び出し | Kit の応答 | 意味 |
| --- | --- | --- |
| 購読者の作成 | `201` | 新しい購読者が、送った状態で作成された |
| 購読者の作成 | `200` | すでに存在する。名は更新され、状態は変わらない |
| フォームへの追加 | `201` または `200` | 追加された、またはすでにフォームにいる。Kit の一括フォームのドキュメントによると、すでにフォームにいる人には特典メールは再送されない |
| すべての呼び出し | `401` | API キーが間違っているか、ない |
| フォームへの追加またはタグ付け | `404` | フォームまたはタグの id がこのアカウントに存在しない |
| すべての呼び出し | `429` | API キーのレート制限を超えた |

## Kit を設定する

1. Kit で、これらの登録用のフォームを「Website signup」などの名前で作成します。埋め込みはしません。購読者がどこから来たかの記録であり、特典メールの置き場所です。
2. ダブルオプトインにしたい場合は、フォームの設定を開いて特典メールをオンにします。メールの文面を書き、確認リンクの遷移先を設定してください。
3. フォームの数値の id を、Kit のアプリか API のフォーム一覧エンドポイントで確認します。必要なら設置場所ごとにタグを作成し、その id を控えておきます。
4. Kit の開発者設定で v4 の API キーを作成し、リレーの環境変数用に保管しておきます。

> Kit の API ドキュメントによると、API へのアクセスはアカウントのプランが対象かどうかによって決まり、料金ページではアプリと連携は Creator プラン以上に記載されています。これを前提に作り込む前に、自分のプランで v4 のキーを作成できるか確認してください。

## リレー

Next.js のルートハンドラーが Webhook を受け取り、小さなモジュールが Kit とやり取りします。同じ 2 つのファイルは、公開の HTTPS URL が得られる Node やエッジのホストならどこでも動きます。

`lib/kit.ts`:

```ts
const KIT = "https://api.kit.com/v4";

const TAGS: Record<string, number> = {
  footer: 1111111,
  post: 2222222,
};

function call(path: string, body: Record<string, unknown>) {
  return fetch(`${KIT}${path}`, {
    method: "POST",
    headers: {
      "X-Kit-Api-Key": process.env.KIT_API_KEY!,
      "Content-Type": "application/json",
      Accept: "application/json",
    },
    body: JSON.stringify(body),
  });
}

export async function addToKit(signup: { email: string; firstName?: string; source: string; referrer?: string }) {
  const created = await call("/subscribers", {
    email_address: signup.email,
    first_name: signup.firstName ?? null,
    state: "inactive",
  });
  if (!created.ok) return created;

  const joined = await call(`/forms/${process.env.KIT_FORM_ID}/subscribers`, {
    email_address: signup.email,
    referrer: signup.referrer ?? null,
  });
  if (!joined.ok) return joined;

  const tag = TAGS[signup.source];
  if (!tag) return joined;
  return call(`/tags/${tag}/subscribers`, { email_address: signup.email });
}
```

`app/api/kit/route.ts`:

```ts
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";
import { addToKit } from "@/lib/kit";

export async function POST(request: Request) {
  const payload = await request.text();
  let event;
  try {
    event = await verifyWebhook({ payload, headers: request.headers, secret: process.env.IKIFORM_WEBHOOK_SECRET! });
  } catch (error) {
    if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
    throw error;
  }
  if (event.type !== "submission.created") return new Response(null, { status: 204 });

  const { email, first_name, consent, source } = event.data.fields;
  if (consent !== true || typeof email !== "string") return new Response("Skipped", { status: 200 });

  const answer = await addToKit({
    email,
    firstName: typeof first_name === "string" ? first_name : undefined,
    source: typeof source === "string" ? source : "unknown",
    referrer: typeof event.data.meta.referrer === "string" ? event.data.meta.referrer : undefined,
  });

  if (answer.status === 429) return new Response("Kit rate limit", { status: 503, headers: { "retry-after": "60" } });
  if (!answer.ok) return new Response(`Kit answered ${answer.status}: ${await answer.text()}`, { status: 502 });
  return new Response(null, { status: 204 });
}
```

`.env.local`:

```bash
IKIFORM_WEBHOOK_SECRET=whsec_...
KIT_API_KEY=your-v4-api-key
KIT_FORM_ID=1234567
```

- Kit の v4 API は、個人の API キーを `X-Kit-Api-Key` で受け取ります。OAuth は他の人がインストールするアプリ向けで、自分のリスト用のリレーには不要です。
- `state: "inactive"` を送ることで、新しい購読者はクリックするまで未確認のままになります。省略すると Kit は `active` として作成し、同じエンドポイントであとから状態を変えることはできません。
- ページのリファラーはフォームの `referrer` として Kit に送られるので、読者がどの記事から来たかが Kit に表示されます。
- Kit のエラーのボディは `502` に入るので、Ikiform の配信ログに Kit 自身のメッセージが表示されます。
- 各 `source` の値を `TAGS` のタグ id に対応付けます。タグのない source では 3 つ目の呼び出しを飛ばします。

Ikiform のダッシュボードの **Webhooks** にルートの URL を追加し、ニュースレターのフォームを選び、`whsec_` のシークレットを `IKIFORM_WEBHOOK_SECRET` にコピーします。URL と各応答のルールはすべて [Webhook のドキュメント](https://ikiform.com/ja/docs/webhooks.md)にあります。

## レート制限とリトライ

Kit は API キーごとに、直近 60 秒で 120 リクエストまで許可します。1 件の登録で 2〜3 リクエスト使うので、Kit が `429` を返すまでに 1 分あたりおよそ 40〜60 件の登録が収まります。フッターのフォームには十分ですが、投稿がバズった日には上限に近づきます。

Kit が `429` を返すと、リレーは `Retry-After` 付きの `503` を返し、Ikiform は待ってからリトライします。リトライでは一連の処理を最初から実行します。それでも安全です。作成は既存の購読者に `200` を返し、すでにフォームにいる人の追加も `200` を返します。Kit の一括フォームのドキュメントでは、すでにフォームにいる人に特典メールは再送されないとされていますが、単一のエンドポイントについては記載がないので、自分のアドレスでリトライをテストしてください。完了済みのリトライを飛ばしたいなら、各 `webhook-id` を記録する方法を[自前のサーバー](https://ikiform.com/blog/form-submissions-to-your-own-server.md)で紹介しています。

## ハマりどころ

- **カスタムフィールドは先に作っておく。** 作成の呼び出しに `fields` を追加するなら、`last_name` のような各フィールドのキーを使い、送る前に Kit でフィールドを作成してください。
- **アクティブな購読者はアクティブのまま。** 作成は既存の購読者の状態を変えないので、確認済みの読者がもう一度登録しても、再び確認を求められることはありません。
- **配信停止した読者。** 配信停止したあとに再び登録した読者はすでに存在するので、作成は `200` を返し、状態は変わりません。そうした読者をどう扱うかは Kit で確認してください。
- **1 つのフォームを複数の場所で。** ウェブサイトには Kit のフォームを 1 つ使い、設置場所はタグで区別します。Kit のフォームが複数あると、そろえておくべき特典メールも複数になります。
- **Ikiform からのメールはない。** Ikiform はフォームに入力した人にはメールを送りません。通知メールが届くのはあなたとチームだけなので、自動返信、確認メール、ダブルオプトインはありません。それらは [Webhook](https://ikiform.com/ja/docs/webhooks.md) をきっかけにメールツールから送ってください。

## 動作を確認する

1. Ikiform でエンドポイントの **Send test** を選びます。配信には `204` が表示され、Kit では何も変わりません。
2. 自分のアドレスでページから購読します。配信には `204` が表示されます。
3. Kit では、購読者がフォーム上に未確認として、設置場所のタグ付きで表示されます。
4. 特典メールが届きます。リンクをクリックすると、購読者が確認済みになります。
5. 同じアドレスでもう一度購読します。配信には `204` が表示され、2 通目の特典メールは届きません。
6. 一時的に間違った `KIT_FORM_ID` を設定します。配信には Kit の `404` のメッセージ付きで `502` が表示されます。

## よくある質問

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

はい。フォームを Ikiform に送信し、リレーに Kit の v4 API で各登録を追加させます。購読者を作成し、Kit のフォームに追加し、必要ならタグを付けます。Kit はその購読者をそのフォームから来たものとして扱います。

### 自作のフォームからの登録にも、Kit は確認メールを送りますか？

リレーが購読者を inactive として作成し、特典メールがオンの Kit のフォームに追加すれば、送られるはずです。Kit の一括フォームのドキュメントでは、ダブルオプトインのフォームに購読者を追加すると特典メールが送られるとされています。このリレーが使う単一購読者のエンドポイントについては記載がないので、自分のアドレスでテストしてください。

### Kit の OAuth フローは必要ですか？

いいえ。OAuth はほかの Kit ユーザーがインストールするアプリ向けです。自分のリストなら、X-Kit-Api-Key ヘッダーに入れた v4 の API キーで十分で、キーはリレーの環境に置いたままにできます。

### リレーは 1 分あたり何件の登録を処理できますか？

Kit は API キーごとに直近 60 秒で 120 リクエストまで許可し、1 件の登録に 2〜3 リクエストかかるので、1 分あたりおよそ 40〜60 件です。それを超えるとリレーは 503 を返し、Ikiform があとでリトライします。

### コードなしで Kit につなげますか？

はい。Ikiform の Webhook を Zapier か Make に向け、その Kit のアクションで購読者をフォームに追加します。タグとフォームについては、アクションのオプションを確認してください。

## その他のガイド

- [ニュースレター登録フォーム](https://ikiform.com/ja/guides/newsletter-signup.md): 自分のマークアップでニュースレター登録フォームを作り、同意を記録し、ボットの登録を防ぎ、購読者を Kit、MailerLite、beehiiv など任意のツールに渡します。
- [ダブルオプトイン](https://ikiform.com/ja/guides/newsletter-signup/double-opt-in.md): Ikiform は確認メールを送らないので、ダブルオプトインはメールツールで行います。中継が各登録を未確認として追加し、ツールがリンクを送ります。
- [MailerLite](https://ikiform.com/ja/guides/newsletter-signup/mailerlite.md): 自作の登録フォームを MailerLite につなぎます。リレーが新しい読者をダブルオプトインのために未確認として追加し、購読者に二度確認を求めることはありません。
- [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 に追加します。
