Claude Code से ईमेल ऑटोमेशन: लीड कैप्चर से monetization तक
Claude Code से lead magnet, consent, unsubscribe, retry, analytics और revenue ईमेल ऑटोमेशन बनाएं।
ईमेल ऑटोमेशन सिर्फ इतना नहीं है कि फॉर्म submit होते ही एक मेल भेज दिया जाए। अच्छा revenue flow lead magnet भेजता है, onboarding sequence शुरू करता है, consultation request के बाद follow-up भेजता है, consent record करता है, unsubscribe संभालता है, bounce वाले addresses को रोकता है, temporary failure पर retry करता है, और यह भी बताता है कि कौन सा CTA product, training या consulting conversation तक पहुंचा।
Claude Code इस काम में उपयोगी है क्योंकि ईमेल फीचर कई हिस्सों को साथ छूता है: schema, template, provider adapter, background queue, webhook, analytics event और documentation। Masa ने इस site के free PDF funnel को सुधारते समय पहले सिर्फ Resend send function बनवाया था। function चलता था, लेकिन consent, unsubscribe URL, bounce handling और CTA analytics बाद में जोड़ने पड़े। बेहतर तरीका है कि Claude Code से पहले design table और file list बनवाई जाए, फिर उसे केवल उन्हीं files तक सीमित करके implement कराया जाए।
इस लेख में हम Node.js/TypeScript से provider-agnostic setup बनाएंगे, जिसे Resend-style या SendGrid-style API के साथ चलाया जा सके। इसमें lead magnet delivery, onboarding, consultation follow-up, SPF/DKIM/DMARC basics, safe outreach boundaries, rate limits, queue/retry, templates, analytics और products/training/consulting CTA शामिल हैं। संबंधित लेखों में content funnel audit, analytics implementation और cookie/consent management भी देखें।
पहले system design करें
Lead magnet वह free resource है, जैसे PDF, checklist या template, जो email address के बदले दिया जाता है। Onboarding वह sequence है जो signup, purchase या training join करने के बाद user को शुरू करने में मदद करता है। Consultation follow-up वह business email है जिसमें meeting notes, next steps, proposal या booking link होता है।
इन सबको एक ही newsletter tag में न डालें। Consent, tone, metrics और risk अलग होते हैं।
| लक्ष्य | प्राप्तकर्ता | ईमेल उदाहरण | revenue path | risk |
|---|---|---|---|---|
| Lead capture | PDF मांगने वाला reader | Download link और related guide | Free PDF से products | Consent और unsubscribe URL save करें |
| Onboarding | Customer या training participant | Start guide, checklist, common blockers | Templates, course, support | Receipt को aggressive promotion न बनाएं |
| Consultation follow-up | Qualified lead | Notes, proposal, next booking | training and consulting | असली conversation के अनुसार personalize करें |
| Re-engagement | Consent वाला inactive reader | Practical failure story या बड़ा update | Product या consultation | Frequency, bounce और opt-out देखें |
Terms साफ रखना जरूरी है। SPF DNS record है जो बताता है कि कौन से servers आपके domain से email भेज सकते हैं। DKIM signature जोड़ता है ताकि receiver verify कर सके कि email authorized है और रास्ते में बदला नहीं गया। DMARC policy बताती है कि SPF या DKIM alignment fail होने पर receiver क्या करे। Bounce का मतलब delivery failure है। Rate limit का मतलब provider request slow या reject कर रहा है क्योंकि आप बहुत तेज भेज रहे हैं, quota लगा है या reputation risk है।
Authentication और deliverability के लिए official docs देखें। Gmail के लिए Google email sender guidelines, Resend के लिए Resend domain management, और SendGrid के लिए Twilio SendGrid domain authentication पढ़ें। DMARC को 2026 में RFC 9989 ने update किया और पुराने RFC 7489 को replace किया। U.S. commercial email के लिए FTC का CAN-SPAM guide भी देखें। यह implementation guide है, legal advice नहीं।
flowchart LR
Visitor["Article reader"]
Form["Lead form"]
Consent["Consent log"]
Queue["Email queue"]
Provider["Resend / SendGrid"]
Inbox["Inbox"]
Webhook["Delivery events"]
Analytics["Analytics"]
Offer["Product / training / consulting"]
Visitor --> Form --> Consent --> Queue --> Provider --> Inbox
Provider --> Webhook --> Analytics --> Offer
Inbox --> Offer
Claude Code prompt
Vague prompt से सिर्फ send function बन सकता है। Prompt में goal, boundaries और verification लिखें।
इस repository में email automation implement करें।
Goals: lead magnet delivery, 3-email onboarding sequence, consultation follow-up.
Constraints:
- Node.js 20+ और TypeScript use करें।
- Resend-style और SendGrid-style API के बीच switch करने वाला provider adapter बनाएं।
- API keys केवल server-side env में रखें, browser में expose न करें।
- lead, email job, unsubscribe और provider event schemas बनाएं।
- 429 और 5xx को exponential backoff से retry करें।
- unsubscribed, complaint या suppression addresses को न भेजें।
- repeated hard bounces को suppression list में डालें।
- text body, HTML body, unsubscribe URL और clear sender details रखें।
- README में official provider और authentication docs links रखें।
- runnable scripts और focused tests जोड़ें।
पहले design table और file list दें। Approval के बाद ही edit करें।
Copy-paste starter
यह starter demo के लिए local JSON file को queue की तरह use करता है। Production में Postgres, Redis, SQS, Cloud Tasks या durable queue use करें जिसमें locking और audit हो।
{
"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("hi"),
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);
}
}
पहले सिर्फ अपने test email पर चलाएं। Domain authentication और unsubscribe route verify करने से पहले real readers को email न भेजें।
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
Bounce, unsubscribe और analytics
Provider API success सिर्फ request accept होने की सूचना है। यह open, click या future consent साबित नहीं करता। Webhook events को अपने internal model में normalize करें और hard bounce, complaint, unsubscribe को suppression list में डालें।
// 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(),
});
}
सिर्फ open rate पर भरोसा न करें। Privacy protection और image blocking इसे unreliable बना सकते हैं। Download completion, CTA click, consultation form start, reply, unsubscribe rate, bounce rate और purchase देखें। Event names जैसे lead_magnet_requested, email_cta_click, consultation_request_started रखें।
Practical use cases
पहला use case technical article के नीचे free PDF है। Reader checklist मांगे तो तुरंत download भेजें। अगली email common setup failure समझाए, तीसरी product template दिखाए, चौथी training या consulting invite करे। हर email में एक main action और unsubscribe link रखें।
दूसरा use case purchase के बाद onboarding है। Gumroad guide या workshop खरीदने वाले व्यक्ति को start करने, blockers हटाने और advanced use सीखने में मदद चाहिए। Receipt को promotion blast न बनाएं। Buyer को successful बनाना सबसे अच्छा upsell है।
तीसरा use case consultation follow-up है। इसमें meeting notes, decisions, next steps, relevant links, deadline और booking/proposal CTA होना चाहिए। अगर email असली conversation से जुड़ा नहीं है तो requested होने पर भी spam जैसा लगेगा।
चौथा use case inactive leads का low-frequency re-engagement है। Consent है लेकिन response नहीं है तो सिर्फ बड़ी update, useful failure story या नया resource भेजें। Click और reply वापस न आएं तो frequency कम करें या रोकें।
Common failure modes
पहली गलती API key को browser code में रखना है। Email sending server-side रहना चाहिए।
दूसरी गलती unauthenticated domain से भेजना है। अपने domain पर SPF, DKIM और DMARC configure करें।
तीसरी गलती unsubscribe और bounce ignore करना है। Unsubscribe, complaint और hard bounce को normal campaigns से exclude करें।
चौथी गलती rate limit के बाद तुरंत retry करना है। 429 और temporary 5xx को backoff से handle करें। Exact limits account, plan, reputation और receiver पर depend करती हैं।
पांचवीं गलती transactional और promotional email mix करना है। Password reset, receipt और account alert साफ और functional रहने चाहिए। Commercial CTA consent और context वाले email में रखें।
Monetization CTA
System तब पूरा है जब reader next step आसानी से चुन सके। ClaudeCodeLab में beginners free PDF से शुरू कर सकते हैं, builders products और templates देख सकते हैं, और teams real repository में rollout के लिए training और consulting ले सकती हैं।
इस workflow को आजमाने पर सबसे बड़ा सुधार provider-specific code से नहीं, बल्कि consent, unsubscribe, bounce और CTA analytics को शुरू से design करने से आया। पहले एक lead magnet email बनाएं, send, unsubscribe, bounce और click verify करें, फिर onboarding और consultation follow-up बढ़ाएं।
मुफ़्त PDF: Claude Code cheatsheet
Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.
हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.
लेखक के बारे में
Masa
Claude Code workflow और team adoption पर काम करने वाला engineer.
संबंधित लेख
Claude Code Obsidian to CLAUDE.md workflow: context बार-बार न समझाएं
Obsidian notes को CLAUDE.md operating notes में बदलकर Claude Code sessions को resume करना आसान बनाएं.
Claude Code Revenue CTA Routing: article से PDF, Gumroad और consultation तक
Reader intent के आधार पर free PDF, Gumroad products और consultation तक CTA route करने वाला workflow.
Claude Code टीम हैंडऑफ नियम: review proof, permissions, rollback और revenue path
Claude Code टीम काम के लिए evidence, permission rules, rollback, free PDF, Gumroad और consultation path वाला handoff.