Building Event-Driven Email Outreach Automation with Dual-Mode Nodes
How TenseAI handles long-lived, event-driven workflows with dual-mode nodes that switch seamlessly between DAG steps and background listeners.

Traditional email outreach automation is a batch job: upload a CSV, trigger a campaign, send emails sequentially, done. That model works fine for the sending half of outreach — but it has nothing to say about what happens next. Modern sales and support operations don't stop at "email sent." When a prospect replies, something needs to happen immediately: analyze the sentiment, update the CRM, maybe fire off an AI-generated follow-up before the lead goes cold.
Building that behavior into a workflow engine requires solving a problem batch systems were never designed for: how does a finished workflow run come back to life when an external event happens hours or days later?
TenseAI's answer is Dual-Mode Node Execution.
What is Dual-Mode Execution?
Certain nodes in TenseAI — most notably Gmail Analysis and WhatsApp Analysis — are built to operate in two distinct contexts:
Workflow Initial Execution Run -> Gmail Node: Send Initial Email
-> Gmail Analysis Node (Mode: Register Listener) -> Workflow Run Status: PAUSED
Inbound Prospect Reply -> Webhook / Push Notification
-> Event Trigger Worker (event_trigger_worker.py)
-> Awaken Workflow Run: PAUSED -> RUNNING
-> Gmail Analysis Node (Mode: Process Reply)
-> Execute Downstream Subgraph: Send Reply / Update SheetThe same node definition, the same canvas — just two different lifecycles depending on where the run currently stands.
How It Works Under the Hood
When a workflow containing a Gmail Analysis node finishes its initial outreach step, TenseAI doesn't mark the run as complete. Instead, the engine evaluates downstream triggers: if a reply-listening node is present, the run transitions to a PAUSED state, and a listener key gets registered in Redis via reply_triggers.py.
From there, the workflow effectively goes to sleep — no polling, no wasted compute, just a lightweight key sitting in Redis waiting for a signal.
That signal arrives as a webhook. Inbound replies land at /api/v1/webhooks/reply, and event_trigger_worker.py picks up the event, resolving it back to the correct workflow_run_id using the sender's thread ID or phone number. The worker then:
That last point matters. A naive implementation might just re-run the entire workflow when a reply comes in, which risks re-sending the original outreach email. TenseAI's dual-mode design instead resumes execution exactly where it left off, with the new reply merged into the same context that the original run had already built up.
Why This Matters for Outreach
This pattern turns a static, one-shot campaign into a genuinely reactive conversation. A sales workflow can send an initial email, wait indefinitely, and — the instant a prospect replies — automatically classify the response, log it to a CRM, and either escalate to a human or dispatch a personalized AI follow-up, all without a human having to manually notice the reply and restart anything.
For growth teams, this is the difference between "we send emails" and "we run conversations." For engineers building on TenseAI, it means a single node definition — Gmail Analysis, WhatsApp Analysis, or any future dual-mode node — can serve both a synchronous DAG step and a long-lived, event-driven listener, without maintaining two separate code paths.
Next up: what happens when two of these branches — a scheduled step and an event-driven reply — need to be aggregated back into a single downstream node. That's fan-in, and it's a harder problem than it sounds.
Start Automating with TenseAI
Build production-grade, multi-tool workflows with Kahn's DAG compilation and Universal Output Envelopes.