Use Cases (Aktualisiert: 2.6.2026)

Claude Code E-Mail-Automation: Leads erfassen und monetarisieren

Baue E-Mail-Automation mit Claude Code: Lead Magnet, Consent, Retry, Analytics und Umsatzpfad.

Claude Code E-Mail-Automation: Leads erfassen und monetarisieren

E-Mail-Automation ist mehr als eine Nachricht nach einem Formular-Submit. Ein umsatzfähiger Ablauf liefert den Lead Magnet aus, startet ein Onboarding, beantwortet Beratungsanfragen, speichert Einwilligung, verarbeitet Abmeldungen, stoppt harte Bounces, wiederholt temporäre Fehler und misst, welcher CTA zu Produktkauf, Training oder Beratung führt.

Claude Code ist dafür nützlich, weil E-Mail-Funktionen mehrere Ebenen verbinden: Schemas, Templates, Provider-Adapter, Queue-Jobs, Webhooks, Analytics und Dokumentation. Beim Überarbeiten des kostenlosen PDF-Funnels dieser Website war der erste Fehler, nur eine Resend-Sendefunktion zu erzeugen. Sie funktionierte, aber Consent, Unsubscribe, Bounce-Handling und CTA-Analytics wurden nachträglich angebaut. Stabiler ist ein enger Plan mit Dateiliste, bevor Claude Code Änderungen ausführt.

Dieser Artikel baut eine provider-neutrale Node.js/TypeScript-Basis für Resend-ähnliche und SendGrid-ähnliche APIs. Er umfasst Lead-Magnet-Versand, Onboarding, Beratungs-Follow-up, SPF/DKIM/DMARC-Grundlagen, sichere Outreach-Grenzen, Rate Limits, Queue/Retry, Templates, Analytics und CTAs zu Produkten, Training und Consulting. Für den Umsatzpfad passen Content-Funnel-Audits, Analytics-Implementierung und Cookie/Consent-Management.

Erst entwerfen, dann senden

Ein Lead Magnet ist ein kostenloses PDF, eine Checkliste oder ein Template im Tausch gegen eine E-Mail-Adresse. Onboarding ist eine Sequenz, die nach Anmeldung oder Kauf beim Start hilft. Ein Beratungs-Follow-up fasst Gespräch, nächste Schritte, Angebot oder Terminlink zusammen.

Diese Typen sollten nicht in einem allgemeinen Newsletter landen. Consent, Ton, Metriken und Risiko unterscheiden sich.

ZielEmpfängerBeispielUmsatzpfadRisiko
Lead CaptureLeser mit PDF-AnfrageDownload-Link und passende AnleitungGratis-PDF zu ProduktenConsent und Unsubscribe speichern
OnboardingKäufer oder TrainingsteilnehmerStartguide, Checkliste, BlockerTemplates, Kurs, SupportQuittungen nicht aggressiv bewerben
BeratungQualifizierter LeadNotizen, Vorschlag, nächster TerminTraining und ConsultingGesprächskontext verwenden
ReaktivierungEinwilligung vorhanden, aber inaktivPraxisfehler oder großes UpdateProdukt oder BeratungFrequenz, Bounces, Opt-outs beobachten

Begriffe sollten klar sein. SPF ist ein DNS-Eintrag, der erlaubt, welche Server für eine Domain senden dürfen. DKIM signiert Nachrichten, damit Empfänger Autorisierung und Integrität prüfen können. DMARC beschreibt, was passieren soll, wenn SPF oder DKIM nicht ausgerichtet sind. Bounce bedeutet fehlgeschlagene Zustellung. Rate Limit bedeutet, dass der Provider Anfragen verlangsamt oder ablehnt, weil Tempo, Kontingent oder Reputation kritisch sind.

Für Authentifizierung und Zustellbarkeit zählen offizielle Quellen: Google email sender guidelines, Resend domain management und Twilio SendGrid domain authentication. DMARC wurde 2026 mit RFC 9989 aktualisiert und ersetzt RFC 7489. Für kommerzielle E-Mails in die USA ist die CAN-SPAM-Anleitung der FTC relevant. Das ist technische Orientierung, keine Rechtsberatung.

flowchart LR
  Visitor["Leser"]
  Form["Lead-Formular"]
  Consent["Consent-Log"]
  Queue["E-Mail-Queue"]
  Provider["Resend / SendGrid"]
  Inbox["Posteingang"]
  Webhook["Delivery Events"]
  Analytics["Analytics"]
  Offer["Produkt / Training / Consulting"]

  Visitor --> Form --> Consent --> Queue --> Provider --> Inbox
  Provider --> Webhook --> Analytics --> Offer
  Inbox --> Offer

Prompt für Claude Code

Ein unklarer Prompt erzeugt nur eine Sendefunktion. Ein guter Prompt benennt Ziel, Grenzen und Prüfung.

Implementiere E-Mail-Automation in diesem Repository.
Ziele: Lead-Magnet-Versand, 3-teilige Onboarding-Sequenz, Beratungs-Follow-up.

Constraints:
- Node.js 20+ und TypeScript.
- Provider-Adapter für Resend-ähnliche und SendGrid-ähnliche APIs.
- API Keys nur serverseitig in Env Vars.
- Schemas für lead, email job, unsubscribe und provider event.
- 429 und 5xx mit exponential backoff wiederholen.
- Nicht an unsubscribed, complaint oder suppression senden.
- Wiederholte hard bounces in suppression list aufnehmen.
- Text, HTML, Unsubscribe URL und klare Absenderdaten einschließen.
- Offizielle Provider- und Authentifizierungslinks dokumentieren.
- Ausführbare Scripts und fokussierte Tests hinzufügen.

Zuerst Design-Tabelle und Dateiliste ausgeben. Vor Änderungen auf Freigabe warten.

Kopierbarer Starter

Das Beispiel nutzt lokal eine JSON-Datei als Queue. Für Produktion sollte sie durch Postgres, Redis, SQS, Cloud Tasks oder eine andere dauerhafte Queue mit Locking und Audit ersetzt werden.

{
  "type": "module",
  "scripts": {
    "lead:send": "tsx scripts/send-lead-magnet.ts",
    "email:worker": "tsx scripts/email-worker.ts"
  },
  "dependencies": {
    "zod": "latest"
  },
  "devDependencies": {
    "@types/node": "latest",
    "tsx": "latest",
    "typescript": "latest"
  }
}
// src/email/schema.ts
import { z } from "zod";

export const leadSchema = z.object({
  email: z.string().email(),
  name: z.string().trim().min(1).max(80),
  locale: z.enum(["ja", "en", "zh", "ko", "es", "fr", "de", "pt", "hi", "id"]).default("de"),
  source: z.enum(["article", "product", "workshop", "consultation"]),
  consentAt: z.string().datetime(),
  tags: z.array(z.string()).default([]),
});

export const sendMessageSchema = z.object({
  to: z.string().email(),
  from: z.string().email(),
  fromName: z.string().min(1),
  replyTo: z.string().email().optional(),
  subject: z.string().min(1).max(120),
  text: z.string().min(1),
  html: z.string().min(1),
  unsubscribeUrl: z.string().url(),
  category: z.enum(["lead_magnet", "onboarding", "consultation_followup"]),
  metadata: z.record(z.string()).default({}),
});

export const emailJobSchema = z.object({
  message: sendMessageSchema,
  maxAttempts: z.number().int().min(1).max(8).default(4),
});

export type SendMessage = z.infer<typeof sendMessageSchema>;
export type EmailJobInput = z.infer<typeof emailJobSchema>;
// src/email/provider.ts
import { randomUUID } from "node:crypto";
import type { SendMessage } from "./schema";

type SendResult = { providerMessageId: string; acceptedAt: string };
export interface EmailProvider { send(message: SendMessage): Promise<SendResult>; }

function requiredEnv(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`Missing env: ${name}`);
  return value;
}

async function parseProviderError(response: Response): Promise<Error> {
  const body = await response.text().catch(() => "");
  const retryable = response.status === 429 || response.status >= 500;
  const error = new Error(`Email provider error ${response.status}: ${body || response.statusText}`);
  (error as Error & { retryable?: boolean }).retryable = retryable;
  return error;
}

export class ResendProvider implements EmailProvider {
  async send(message: SendMessage): Promise<SendResult> {
    const response = await fetch("https://api.resend.com/emails", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${requiredEnv("RESEND_API_KEY")}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        from: `${message.fromName} <${message.from}>`,
        to: [message.to],
        reply_to: message.replyTo,
        subject: message.subject,
        text: message.text,
        html: message.html,
        headers: {
          "List-Unsubscribe": `<${message.unsubscribeUrl}>`,
          "List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
        },
      }),
    });
    if (!response.ok) throw await parseProviderError(response);
    const data = (await response.json().catch(() => ({}))) as { id?: string };
    return { providerMessageId: data.id ?? randomUUID(), acceptedAt: new Date().toISOString() };
  }
}

export class SendGridProvider implements EmailProvider {
  async send(message: SendMessage): Promise<SendResult> {
    const response = await fetch("https://api.sendgrid.com/v3/mail/send", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${requiredEnv("SENDGRID_API_KEY")}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        personalizations: [{ to: [{ email: message.to }], custom_args: message.metadata }],
        from: { email: message.from, name: message.fromName },
        reply_to: message.replyTo ? { email: message.replyTo } : undefined,
        subject: message.subject,
        content: [
          { type: "text/plain", value: message.text },
          { type: "text/html", value: message.html },
        ],
        headers: {
          "List-Unsubscribe": `<${message.unsubscribeUrl}>`,
          "List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
        },
      }),
    });
    if (!response.ok) throw await parseProviderError(response);
    return { providerMessageId: response.headers.get("x-message-id") ?? randomUUID(), acceptedAt: new Date().toISOString() };
  }
}

export function createEmailProvider(): EmailProvider {
  return process.env.EMAIL_PROVIDER === "sendgrid" ? new SendGridProvider() : new ResendProvider();
}
// src/email/queue.ts
import { readFile, writeFile } from "node:fs/promises";
import { existsSync } from "node:fs";
import { randomUUID } from "node:crypto";
import { emailJobSchema, type EmailJobInput } from "./schema";

type StoredJob = EmailJobInput & {
  id: string;
  status: "scheduled" | "processing" | "sent" | "failed";
  attempts: number;
  nextAttemptAt: string;
  lastError?: string;
};

const queueFile = process.env.EMAIL_QUEUE_FILE ?? ".email-queue.json";

async function loadQueue(): Promise<StoredJob[]> {
  if (!existsSync(queueFile)) return [];
  return JSON.parse(await readFile(queueFile, "utf8")) as StoredJob[];
}

async function saveQueue(jobs: StoredJob[]) {
  await writeFile(queueFile, JSON.stringify(jobs, null, 2) + "\n");
}

export async function enqueueEmail(input: EmailJobInput) {
  const parsed = emailJobSchema.parse(input);
  const jobs = await loadQueue();
  const job: StoredJob = { ...parsed, id: randomUUID(), status: "scheduled", attempts: 0, nextAttemptAt: new Date().toISOString() };
  jobs.push(job);
  await saveQueue(jobs);
  return job.id;
}

export async function claimDueJobs(limit = 5): Promise<StoredJob[]> {
  const now = Date.now();
  const jobs = await loadQueue();
  const due = jobs.filter((job) => job.status === "scheduled" && Date.parse(job.nextAttemptAt) <= now).slice(0, limit);
  for (const job of due) job.status = "processing";
  await saveQueue(jobs);
  return due;
}

export async function completeJob(id: string) {
  const jobs = await loadQueue();
  const job = jobs.find((item) => item.id === id);
  if (job) job.status = "sent";
  await saveQueue(jobs);
}

export async function failJob(id: string, error: unknown) {
  const jobs = await loadQueue();
  const job = jobs.find((item) => item.id === id);
  if (!job) return;
  job.attempts += 1;
  job.lastError = error instanceof Error ? error.message : String(error);
  if (job.attempts >= job.maxAttempts) {
    job.status = "failed";
  } else {
    const delayMs = Math.min(15 * 60_000, 2 ** job.attempts * 1000);
    job.status = "scheduled";
    job.nextAttemptAt = new Date(Date.now() + delayMs).toISOString();
  }
  await saveQueue(jobs);
}
// scripts/email-worker.ts
import { claimDueJobs, completeJob, failJob } from "../src/email/queue";
import { createEmailProvider } from "../src/email/provider";

const provider = createEmailProvider();
const jobs = await claimDueJobs(Number(process.env.EMAIL_WORKER_BATCH ?? 3));

for (const job of jobs) {
  try {
    const result = await provider.send(job.message);
    await completeJob(job.id);
    console.log(`sent ${job.id} as ${result.providerMessageId}`);
  } catch (error) {
    await failJob(job.id, error);
    console.error(`failed ${job.id}`, error);
  }
}

Erst mit einer eigenen Testadresse ausführen. Nicht an Leser senden, bevor Domain-Authentifizierung und Unsubscribe-Route geprüft sind.

npm install
EMAIL_TO=you@example.com APP_URL=https://example.com npm run lead:send
EMAIL_PROVIDER=resend RESEND_API_KEY=re_xxx npm run email:worker

Bounces, Abmeldungen und Analytics

Eine erfolgreiche Provider-Antwort bedeutet nur, dass die Anfrage akzeptiert wurde. Sie beweist keinen Posteingang, keinen Klick und kein weiteres Interesse. Webhooks sollten in ein eigenes Event-Modell normalisiert werden.

// src/email/events.ts
import { z } from "zod";

const providerEventSchema = z.object({
  provider: z.enum(["resend", "sendgrid", "unknown"]),
  type: z.enum(["delivered", "bounce", "complaint", "unsubscribe", "open", "click", "deferred"]),
  email: z.string().email().optional(),
  providerMessageId: z.string().optional(),
  reason: z.string().optional(),
  occurredAt: z.string().datetime(),
});

export function normalizeProviderEvent(payload: unknown) {
  const raw = payload as Record<string, unknown>;
  const type = String(raw.type ?? raw.event ?? "delivered");
  const mappedType =
    type.includes("bounce") ? "bounce" :
    type.includes("complaint") || type.includes("spam") ? "complaint" :
    type.includes("unsubscribe") ? "unsubscribe" :
    type.includes("click") ? "click" :
    type.includes("open") ? "open" :
    type.includes("defer") ? "deferred" :
    "delivered";

  return providerEventSchema.parse({
    provider: raw.sg_event_id ? "sendgrid" : raw.created_at ? "resend" : "unknown",
    type: mappedType,
    email: String(raw.email ?? raw.recipient ?? "") || undefined,
    providerMessageId: String(raw.email_id ?? raw.sg_message_id ?? ""),
    reason: typeof raw.reason === "string" ? raw.reason : undefined,
    occurredAt: new Date(String(raw.created_at ?? Date.now())).toISOString(),
  });
}

Öffnungsraten allein sind schwach, weil Bildblocker und Privacy-Funktionen verzerren. Messen Sie Downloads, CTA-Klicks, gestartete Beratungsformulare, Antworten, Abmeldungen, Bounces und Käufe. Namen wie lead_magnet_requested, email_cta_click und consultation_request_started bleiben auswertbar.

Praxisfälle

Erster Fall: Gratis-PDF unter einem technischen Artikel. Senden Sie sofort den Download, danach den häufigsten Setup-Fehler, dann ein Produkt-Template und schließlich einen Trainings- oder Beratungs-CTA.

Zweiter Fall: Onboarding nach Kauf. Wer einen Guide oder Workshop gekauft hat, braucht Start, Fehlerhilfe und fortgeschrittene Anwendung. Erfolgreiche Nutzung ist oft der beste Upsell.

Dritter Fall: Beratungs-Follow-up. Gute E-Mails enthalten Notizen, Entscheidungen, Vorbereitung, relevante Links, Frist und Buchungs- oder Angebots-CTA. Ohne Gesprächskontext wirkt auch eine angeforderte Nachricht wie Spam.

Vierter Fall: Reaktivierung. Bei inaktiven, aber eingewilligten Lesern nur große Updates, praktische Fehlergeschichten oder neue Ressourcen senden. Wenn Klicks und Antworten ausbleiben, Frequenz senken oder stoppen.

Typische Fehler

Der erste Fehler ist ein API Key im Browser. E-Mail-Versand gehört auf den Server.

Der zweite Fehler ist Versand ohne authentifizierte Domain. SPF, DKIM und DMARC gehören zur Mindestbasis.

Der dritte Fehler ist das Ignorieren von Abmeldungen und Bounces. Unsubscribe, complaint und hard bounce müssen normale Kampagnen ausschließen.

Der vierte Fehler ist sofortiges Wiederholen nach Rate Limit. 429 und temporäre 5xx brauchen Backoff. Exakte Grenzen hängen von Konto, Plan, Reputation und Empfänger ab.

Der fünfte Fehler ist das Mischen von transaktional und werblich. Passwort-Reset, Quittung und Kontoalarm sollen klar bleiben. Kommerzielle CTAs gehören in passende, eingewilligte Nachrichten.

Monetarisierungs-CTA

Fertig ist das System, wenn Leser den nächsten Schritt natürlich wählen können. Bei ClaudeCodeLab starten Einsteiger mit dem Gratis-PDF, Umsetzer prüfen Produkte und Templates, und Teams nutzen Training und Consulting für echte Repositories und Revenue-Flows.

In der Praxis brachte nicht die Provider-Funktion den größten Gewinn, sondern das frühe Design von Consent, Abmeldung, Bounce und CTA-Analytics. Starten Sie mit einer Lead-Magnet-Mail, prüfen Sie Versand, Abmeldung, Bounce und Klick, und erweitern Sie danach zu Onboarding und Beratung.

#Claude Code #E-Mail-Automation #Lead Capture #Resend #SendGrid #Node.js
Kostenlos

Kostenloses PDF: Claude-Code-Cheatsheet

E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.

Wir schützen Ihre Daten und senden keinen Spam.

Masa

Über den Autor

Masa

Engineer für praktische Claude-Code-Workflows und Team-Einführung.