All case studies
Just Hired AI · AI agency · 2026

Automated Content Engine — Claude + HeyGen + n8n at scale

Replace 15+ hours of weekly video production with a self-improving multi-tenant pipeline.

4 min read
NestJSBullMQClaude AIHeyGenn8nMulti-tenant
Automated Content Engine — Claude + HeyGen + n8n at scale
15+/wk
Manual hours saved
Multi-tenant
Tenants supported
Self-improving
Pipeline

The problem

A growing agency was producing weekly social videos for multiple brands. The bottleneck wasn't HeyGen rendering — it was the decision-making before rendering:

  • Which topic? Pulled from spreadsheets, manually
  • What script? Drafted in Google Docs, edited 3+ rounds
  • What hook? Best-guess based on yesterday's "feel"
  • Which platforms? Posted manually one by one

Each brand was eating 15+ hours of human time per week. Multi-tenant scaling was impossible without automation.

The approach

A NestJS + BullMQ pipeline that owns the full lifecycle, with feedback loops from real social engagement data feeding back into script generation.

Pipeline (per tenant)

Why NestJS (and not just n8n end-to-end)

n8n was perfect for distribution — it speaks every social platform's API natively, retries cleanly, and the agency's ops team could maintain it. But putting the core pipeline in n8n was the wrong call:

  • Domain logic complexity. Multi-tenant prompt assembly, feedback-loop math, tenant-specific guardrails — these wanted real code.
  • Queue semantics. BullMQ's retry, rate-limit, and priority controls beat n8n's job model for HeyGen renders that can take 5–10 minutes.
  • Test coverage. Jest tests on the prompt builder caught more than a dozen tenant-data leaks before they shipped.

So: NestJS owns the pipeline. n8n owns the last mile.

The feedback loop

The pipeline isn't static. Every video's engagement (views, watch time, saves, comments) updates a per-tenant feature_weights record. The next script generation reads those weights as soft constraints — favoring the hook structures and topics that performed.

Multi-tenant data isolation

Every job carries a tenant_id. Every prompt assembly, every queue read, every metric write asserts it. Two safeguards:

  1. Type-system enforcement. A TenantScoped<T> wrapper makes it impossible to pass a tenant-naked object into a generator.
  2. Logged guardrails. Each generation logs the tenant context; a per-tenant audit log catches any drift.

BullMQ topology

  • Topic queue — low priority, runs nightly per tenant
  • Script queue — medium priority, reads topics
  • Render queue — high priority but rate-limited (HeyGen quota)
  • Distribution queue — handed to n8n via webhook

Outcomes

15+ hrs/wk
Saved per tenant
From manual production to fully automated
Multi-tenant
Safely isolated
Type + runtime + audit-log enforcement
Self-improving
Engagement loop
Yesterday's metrics shape today's scripts

What this taught me

Building this convinced me of one thing: the AI part is the easy part now. The hard part is everything around it — multi-tenant isolation, retry semantics, feedback loops, ops maintainability, and knowing when to use no-code (n8n) vs typed code (NestJS) for each layer.

If you're building something like this and aren't sure where the n8n / NestJS split should land, that's exactly the conversation I love to have.

Building something similar?

Send a quick note — happy to compare notes on the architecture.