Back to KB
Difficulty
Intermediate
Read Time
10 min

Automating 15k Monthly Campaign Events at $12/mo: An Idempotent Event-Driven Marketing Pipeline

By Codcompass TeamΒ·Β·10 min read

Current Situation Analysis

Solo operators and technical founders waste 12-18 hours weekly manually coordinating content across platforms, syncing leads to CRMs, and troubleshooting broken Zapier chains. The standard approach relies on imperative cron jobs or fragile third-party workflow tools. These systems share a fatal flaw: they treat marketing as a sequence of API calls rather than a deterministic state machine. When an API rate-limits, a webhook drops, or a process crashes mid-batch, state diverges. You get duplicate emails, missed posts, and untracked leads.

Tutorials fail because they optimize for setup speed over failure recovery. A typical guide shows you how to chain fetch() calls in a Node script with setInterval. This works until PostgreSQL connection pools exhaust, until Mailgun returns 429 Too Many Requests, or until a timezone shift desynchronizes your scheduled campaigns. You end up debugging race conditions at 2 AM instead of shipping product.

Consider this common anti-pattern:

// BAD: Imperative, stateless, fragile
setInterval(async () => {
  const leads = await db.query("SELECT * FROM leads WHERE status = 'new'");
  for (const lead of leads) {
    await mailgun.send(lead.email, "Welcome");
    await updateStatus(lead.id, "sent");
  }
}, 60000);

This fails catastrophically. If mailgun.send() throws on the 47th lead, the first 46 are already sent but untracked. The next interval re-fetches all 47. You've created a duplication loop. There's no audit trail, no retry logic, and zero observability.

The paradigm shift happens when you treat marketing actions like financial transactions. Exactly-once delivery. Deterministic replay. Sub-100ms orchestration. Bare-metal cloud costs.

WOW Moment

Marketing workflows are not API call sequences; they are immutable event streams that require idempotent reconciliation.

By decoupling event ingestion from execution, you transform a fragile cron chain into a queryable, replayable pipeline. Every campaign trigger, lead capture, or content publish becomes a durable event with a cryptographic idempotency key. Workers consume these events, apply business logic, and emit side-effects. If a worker crashes, the event remains pending. If an API rate-limits, exponential backoff with jitter handles it. You gain full auditability, zero-duplicate guarantees, and the ability to replay historical campaigns for A/B testing.

The aha moment: You stop pushing data to APIs and start materializing views from an event log.

Core Solution

Architecture Overview

  • Ingest Layer: Cloudflare Workers (Node 22 runtime) or Express on VPS receives webhooks, form submissions, and scheduled triggers.
  • Event Store: PostgreSQL 17.2 with Prisma 6.0.1. Events table enforces uniqueness via idempotency_key.
  • Queue/Cache: Redis 7.4.1 for distributed locking and rate-limit token buckets.
  • Workers: Python 3.12.7 processes (via celery or custom async loop) handle external API calls.
  • Observability: OpenTelemetry 1.26.0 + Prometheus 2.53.0 + Grafana 11.2.0.

Step 1: Idempotent Event Ingestion (TypeScript)

This module guarantees exactly-once event recording. It uses a composite unique constraint and ON CONFLICT DO NOTHING to prevent duplicates during webhook retries or network flaps.

// src/events/ingest.ts
import { PrismaClient } from '@prisma/client';
import { createHash } from 'crypto';
import { z } from 'zod';

const prisma = new PrismaClient();

const EventSchema = z.object({
  type: z.enum(['LEAD_CAPTURE', 'CONTENT_PUBLISH', 'CAMPAIGN_TRIGGER']),
  payload: z.record(z.unknown()),
  source: z.string().min(1),
  idempotencyKey: z.string().uuid(),
  scheduledAt: z.coerce.date().optional(),
});

type IngestEvent = z.infer<typeof EventSchema>;

export async function ingestEvent(event: IngestEvent): Promise<{ success: boolean; eventId?: string; error?: string }> {
  try {
    const validated = EventSchema.parse(event);
    
    // Create deterministic fingerprint for audit
    const fingerprint = createHash('sha256')
      .update(JSON.stringify(validated.payload))
      .digest('hex')
      .slice(0, 16);

    const result = await prisma.marketingEvent.create({
      data: {
        idempotencyKey: validated.idempotencyKey,
        type: validated.type,
        payload: validated.payload,
        source: validated.source,
        fingerprint,
        scheduledAt: validated.scheduledAt || new Date(),
        status: 'PENDING',
      },
      // PostgreSQL 17 ON CONFLICT prevents duplicate ingestion
      // Returns null if key exists, avoiding race conditions
    }).catch((err: any) => {
      if (err.code === 'P2002') {
        return null; // Duplicate idempo

πŸŽ‰ Mid-Year Sale β€” Unlock Full Article

Base plan from just $4.99/mo or $49/yr

Sign in to read the full article and unlock all 635+ tutorials.

Sign In / Register β€” Start Free Trial

7-day free trial Β· Cancel anytime Β· 30-day money-back

Sources

  • β€’ ai-deep-generated