AIエージェントでニュースレターを自動化する方法
Claudeエージェントがコンテンツキューを読み取り、その週の最も強い切り口を選び、私の声でニュースレターを下書きし、エンゲージメント層別にリストをセグメント化し、Kit API経由で送信をスケジュールします——私がエディタを開かずに。レンダリングされたプレビューを確認して承認ボタンを押すだけです。難しいクリエイティブな作業は私のもの。機械的な実行はエージェントのものです。
毎週水曜。28,400人以上の読者。無駄なし。
✓ メールをご確認ください — 確認リンクをクリックして登録を完了してください。
✓ 登録が完了しました!
✓ すでに登録済みです。
目次
2026年6月更新。
TL;DR: Claudeエージェントがコンテンツキューを読み取り、その週の最も強い切り口を選び、私の声でニュースレターを下書きし、エンゲージメント層別にリストをセグメント化し、Kit API経由で送信をスケジュールします——私がエディタを開かずに。レンダリングされたプレビューを確認して承認ボタンを押すだけです。難しいクリエイティブな作業は私のもの。機械的な実行はエージェントのものです。
[オペレーターの視点] 一貫して送信されるニュースレターは、「より良い」が霊感が湧いたときだけ発行されるものに勝ります。制約はアイデアではなく、実行のオーバーヘッドでした。アイデアはあった。毎週それをフォーマット、スケジュール、セグメント化する帯域幅がなかっただけです。エージェントがそのギャップを埋めました。
ほとんどのニュースレターワークフローにおける本当のボトルネック
ほとんどのニュースレター自動化のアドバイスは間違ったことに焦点を当てています:ウェルカムシーケンス、オートメーション、タギングロジック。それらは問題ありませんが、毎週のコンテンツ作成問題を解決しません。
本当の妨げはこれです:言いたいことはわかっている。しかし座ってフォーマットし、件名のバリエーションを書き、適切なセグメントを選び、適切なタイミングでスケジュールすることが週に2〜3時間のコンテキストスイッチングを要します。52週で掛けると、ニュースレターを送信するだけに丸々1週間を費やしたことになります。
エージェントは「今週の切り口がわかった」の後のすべてのステップを処理します。
使用しているスタック
- Kit(旧ConvertKit)——メールプラットフォーム。優れたAPI、確実な購読者タグ付け、クリーンな分析。エージェントフレンドリーなAPIが決め手でした。
- Claude (Anthropic SDK)——生成レイヤー
- Cloudflare Workers——スケジュールトリガー(毎週火曜日午前8時CTに実行)
- Airtable——コンテンツキューと承認受信トレイ
Kitを使っていなくても、ブロードキャストを作成・スケジュールするREST APIを持つプラットフォームなら同じパターンが機能します。
ステップ1:コンテンツキュー
エージェントには「何について書くか」の信頼できる情報源が必要です。私のは以下の列を持つAirtableテーブルです:
Topic——切り口や質問Status——Queue / Approved / SentTier——すべての購読者向けか、エンゲージメントの高い購読者のみかNotes——制約事項(このトーンは避ける、このリンクを含める、など)
毎週、キューに2〜3個のトピックを追加するのに10分を費やします。それが私のクリエイティブな入力です。残りはエージェントの仕事です。
ステップ2:下書きエージェント
// workers/newsletter-agent/index.ts
import Anthropic from "@anthropic-ai/sdk";
import Airtable from "airtable";
const client = new Anthropic();
const VOICE_SYSTEM = `You are writing a weekly newsletter for Alejandro Rioja's subscribers.
His audience: founders and operators interested in AI agents, SEO, and growing a one-person business.
Voice: direct, first-person, practitioner. No hype, no "exciting times," no excessive bullet lists.
Structure every newsletter as:
1. One-sentence hook (the problem or observation)
2. The core insight (3–5 paragraphs, no headers, conversational)
3. One concrete action the reader can take this week
4. A short sign-off (2 sentences max)
Subject line: specific, outcome-oriented, under 50 chars. No clickbait.
Return JSON: { "subject": "...", "preheader": "...", "body": "..." }`;
async function getNextTopic(): Promise<{ id: string; topic: string; notes: string; tier: string }> {
const base = new Airtable({ apiKey: process.env.AIRTABLE_API_KEY }).base(process.env.AIRTABLE_BASE_ID!);
const records = await base("Newsletter Queue")
.select({ filterByFormula: "{Status} = 'Queue'", sort: [{ field: "Created", direction: "asc" }], maxRecords: 1 })
.firstPage();
if (!records.length) throw new Error("Queue is empty. Add topics.");
const r = records[0];
return { id: r.id, topic: r.get("Topic") as string, notes: (r.get("Notes") as string) ?? "", tier: (r.get("Tier") as string) ?? "all" };
}
async function draftNewsletter(topic: string, notes: string): Promise<{ subject: string; preheader: string; body: string }> {
const msg = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 2048,
system: VOICE_SYSTEM,
messages: [{ role: "user", content: `Write this week's newsletter on: "${topic}". Additional notes: ${notes || "none"}` }],
});
const text = (msg.content[0] as any).text.replace(/```json\n?/, "").replace(/```/, "").trim();
return JSON.parse(text);
}
async function scheduleWithKit(draft: { subject: string; preheader: string; body: string }, tier: string): Promise<string> {
const segmentId = tier === "engaged" ? process.env.KIT_ENGAGED_SEGMENT_ID : null;
const sendAt = new Date();
sendAt.setDate(sendAt.getDate() + ((4 - sendAt.getDay() + 7) % 7)); // next Thursday
sendAt.setHours(9, 0, 0, 0); // 9am CT
const payload: any = {
broadcast: {
subject: draft.subject,
content: draft.body,
description: draft.preheader,
send_at: sendAt.toISOString(),
email_layout_template: "minimal",
},
};
if (segmentId) payload.broadcast.segment_id = segmentId;
const res = await fetch("https://api.kit.com/v4/broadcasts", {
method: "POST",
headers: { "Content-Type": "application/json", "X-Kit-Api-Key": process.env.KIT_API_KEY! },
body: JSON.stringify(payload),
});
const data = await res.json();
return data.broadcast?.id ?? "";
}
export default {
async scheduled(_event: ScheduledEvent, env: Env) {
// Inject env vars
Object.assign(process.env, env);
const { id, topic, notes, tier } = await getNextTopic();
const draft = await draftNewsletter(topic, notes);
const broadcastId = await scheduleWithKit(draft, tier);
// Mark as Approved in Airtable (not Sent — human reviews the Kit preview before confirm)
const base = new Airtable({ apiKey: env.AIRTABLE_API_KEY }).base(env.AIRTABLE_BASE_ID);
await base("Newsletter Queue").update(id, { Status: "Approved", KitBroadcastId: broadcastId });
console.log(`Scheduled broadcast ${broadcastId} for topic: ${topic}`);
},
};ステップ3:承認ステップ
エージェントはKitの下書き状態でブロードキャストを作成し、Airtableレコードを「Approved」としてマークします。Kitがプレビューリンク付きの通知を送ってきます。クリックして読み、問題なければ送信を確認します。変更が必要な場合はKitで直接編集します。
これがエージェントが送信メールで完全に自律的になるのを防ぐゲートです。下書きは約90%の確率で信頼しています。レビューで見つける10%——わずかにずれたトーン、確認したい統計、追加したいリンク——は3分間のレビューの価値があります。
エージェントが処理する、二度とやりたくないこと
- 件名のバリエーションを書き、最善のものを選ぶ
- プレヘッダーテキストをフォーマットする
- 適切な送信時間を計算する(私のオーディエンスは木曜の朝に開封する;エージェントはこれを知っている)
- トピックの層に基づいて正確にセグメント化する
- すべてをAirtableに記録して履歴を保持する
私がまだ所有していること
アイデア。キューのトピックは私のもの。切り口は私のもの。エージェントは明確なブリーフの優れた実行者です。戦略レイヤーではありません。キューに悪いトピックを入れれば、悪いトピックについてうまく書かれたニュースレターが得られます。
また:最初のレビューゲート。すべての送信は配信前に私の目を通します。これは変わりません。
オペレーターの結論
ニュースレターの機械的作業——フォーマット、スケジューリング、セグメンテーション——に週1時間以上費やしているなら、自動化すべきです。Kit APIはクリーンで、WorkerのCronトリガーは岩のように堅固で、Claudeの下書き品質は私が第一稿の約90%を変更なしで承認できるほど高いです。Airtableにキューを構築し、Workerを接続して、送信を実行する代わりにアイデアを作ることに戻りましょう。
毎週水曜。28,400人以上の読者。無駄なし。
✓ メールをご確認ください — 確認リンクをクリックして登録を完了してください。
✓ 登録が完了しました!
✓ すでに登録済みです。
AIプレイブックをメールでお届け
毎週水曜。28,400人以上の読者。無駄なし。
メールをご確認ください。
確認メールをお送りしました — リンクをクリックして登録を完了してください。1分以内に届かない場合は迷惑メールをご確認ください。
登録が完了しました。
ようこそ — 次号がまもなくお手元に届きます。
すでに登録済みです — 毎週水曜日にお届けします。