Alejandro Rioja.
AI Agents Growth

Hoe je je Newsletter Automatiseert met een AI-Agent

Alejandro Rioja
Alejandro Rioja
5 min lezen
TL;DR

Een Claude-agent leest mijn contentqueue, kiest de sterkste invalshoek van de week, stelt een newsletter op in mijn stem, segmenteert de lijst op betrokkenheidsniveau en plant de verzending via de Kit API — allemaal zonder dat ik een editor open. Ik bekijk een gerenderde preview en klik op goedkeuren. Het moeilijke creatieve werk is van mij; de mechanische uitvoering is van de agent.

Gratis nieuwsbrief

Elke woensdag. 28.400+ operators. Geen opvulling.

Inhoudsopgave

Bijgewerkt juni 2026.

TL;DR: Een Claude-agent leest mijn contentqueue, kiest de sterkste invalshoek van de week, stelt een newsletter op in mijn stem, segmenteert de lijst op betrokkenheidsniveau en plant de verzending via de Kit API — allemaal zonder dat ik een editor open. Ik bekijk een gerenderde preview en klik op goedkeuren. Het moeilijke creatieve werk is van mij; de mechanische uitvoering is van de agent.

[Lezersnotitie van de operator] Een newsletter die consistent verstuurt wint het van een die “beter” is maar verschijnt wanneer de inspiratie toeslaat. De beperking was de uitvoeringsoverhead, niet de ideeën. Ik had ideeën; ik had niet de bandbreedte om ze elke week te formatteren, plannen en segmenteren. De agent heeft dat gat gedicht.

De echte bottleneck in de meeste newsletter-workflows

De meeste newsletter-automatiseringsadviezen richten zich op het verkeerde: welkomstsequenties, automatiseringen, tagging-logica. Dat is prima, maar het lost het wekelijkse creatieprobleem niet op.

De echte rem is dit: je weet wat je wilt zeggen, maar gaan zitten om het te formatteren, de onderwerpregelvarianten te schrijven, het juiste segment te kiezen en het op het juiste moment te plannen kost 2-3 uur contextwisseling per week. Vermenigvuldig met 52 weken en je hebt een volledige werkweek besteed aan alleen maar versturen van newsletters.

De agent verwerkt elke stap na “ik weet wat de invalshoek van deze week is.”

De stack die ik gebruik

Als je niet op Kit bent, werkt hetzelfde patroon met elk platform dat een REST API heeft voor het aanmaken en plannen van uitzendingen.

Stap 1: De contentqueue

De agent heeft een bron van waarheid nodig voor “waarover schrijven we.” Die van mij is een Airtable-tabel met kolommen:

Elke week besteed ik 10 minuten aan het toevoegen van 2-3 onderwerpen aan de queue. Dat is mijn creatieve inbreng. De rest is het werk van de agent.

Stap 2: De concept-agent

typescript
// 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}`);
  },
};

Stap 3: De goedkeuringsstap

De agent maakt de uitzending aan in de conceptstatus van Kit en markeert het Airtable-record als “Approved.” Kit stuurt me een melding met een previewlink. Ik klik erop, lees het, en als het er goed uitziet, bevestig ik de verzending. Als ik wijzigingen wil, bewerk ik direct in Kit.

Dit is de poort die voorkomt dat de agent volledig autonoom wordt bij uitgaande e-mail. Ik vertrouw de concepten ongeveer 90% van de tijd. De 10% die ik bij de review ontdek — een toon die iets te ver afwijkt, een statistiek die ik wil verifiëren, een link die ik wil toevoegen — is de 3 minuten durende review waard.

Wat de agent afhandelt dat ik nooit meer wil doen

Wat ik nog steeds bezit

Het idee. Het onderwerp in de queue is van mij. De invalshoek is van mij. De agent is een uitstekende uitvoerder van een duidelijke briefing; het is geen strategielaag. Als ik een slecht onderwerp in de queue zet, krijg ik een goed geschreven newsletter over een slecht onderwerp.

Ook: de eerste beoordelingspoort. Elke verzending wordt door mij bekeken voordat hij uitgaat. Dat gaat niet veranderen.

De conclusie van de operator

Als je meer dan een uur per week besteedt aan newsletter-mechanica — formattering, planning, segmentatie — moet je het automatiseren. De Kit API is schoon, de Worker-cron-trigger is ijzersolide, en de kwaliteit van het Claude-concept is hoog genoeg dat ik ~90% van de eerste concepten ongewijzigd goedkeur. Bouw de queue in Airtable, verbind de Worker en ga terug naar het creëren van ideeën in plaats van het uitvoeren van verzendingen.

Lees verder

Ontvang het AI-playbook in je inbox

Elke woensdag. 28.400+ operators. Geen opvulling.

↵ alle resultaten bekijken esc esc om te sluiten