Command Palette

Search for a command to run...

Back to Projects
Multi-Channel Outreach Automation Engine
In-progressNext.jsTypeScriptExpress.js+10 more

Multi-Channel Outreach Automation Engine

Campaign runtime delivering personalized message sequences across thousands of pooled LinkedIn and WhatsApp accounts, with rate-limit-aware pacing, failover, and AI reply handling.

Timeline

Oct 2024 - Present

Role

Senior Full Stack Engineer

Team

Second Brain Labs

Status
In-progress

Technology Stack

Next.js
TypeScript
Express.js
PostgreSQL
Drizzle ORM
Redis
BullMQ
AWS ECR
AWS ECS
AWS S3
Docker
Turborepo
GitHub Actions

Key Challenges

  • Delivering at volume while respecting per-account platform limits
  • Making a long-running job queue safe to retry
  • Consolidating separately deployed services without slowing releases

Key Learnings

  • Idempotent job identity is a correctness feature, not an optimization
  • Rate limits are a product constraint, not a performance knob
  • Remote caching changes how often a team is willing to deploy

Overview

This is the delivery half of the platform: the system that takes a campaign and a list of prospects and actually gets messages sent, across LinkedIn and WhatsApp, from a pool of connected accounts, without any of those accounts getting restricted.

Used by thousands of business workspaces across a pool of thousands of connected accounts, with millions of messages delivered to date.

Sending at volume, within platform limits

Every messaging platform enforces its own per-account limits, and those limits are the binding constraint on the whole system. So throughput is deliberately not what the scheduler optimizes for.

Volume is not the hard part. Staying inside each platform's limits across thousands of separate accounts is.

Throughput is therefore bounded deliberately rather than maximised: the scheduler treats each account's capacity as a hard constraint and spreads work across the pool instead of concentrating it. Campaigns can also lead with lighter engagement steps before the first message, so outreach arrives as a warm touch rather than a cold one.

Fault tolerance

Delivery runs on a background queue, which means every job has to be safe to retry.

Jobs carry idempotent identity so a retried job cannot double-send to the same prospect, because sending twice is a far worse outcome than sending late. Failures back off and retry rather than dropping work, and when an account disconnects, the campaign fails over to a healthy one instead of stalling. There is a real trap in deterministic job ids that I learned the hard way: a failed job holds its id for as long as the queue retains it, and any later job reusing that id vanishes silently. Queue visibility is not optional.

AI reply handling

Replies come back into the same system. Inbound messages are classified, context-aware responses are drafted, anything uncertain escalates to a human review queue rather than auto-sending, and the resulting conversation syncs into a CRM pipeline so follow-up has somewhere to live.

Platform work

Alongside the product surface, I consolidated separately deployed microservices into a Turborepo with remote caching. Parallel GitHub Actions builds took a full-stack deploy from several minutes per service to roughly 2.5 minutes for all of them, and release-branch deploys gave production and development genuinely isolated environments to test against.

Tech Stack

Next.js, TypeScript, Express.js, PostgreSQL with Drizzle ORM, Redis, BullMQ, AWS ECR/ECS/S3 and Secrets Manager, Docker, Turborepo, and GitHub Actions.