You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
SendDock today supports three send patterns: transactional (single /send), batch with per-recipient variables, and broadcast (one email to a segment). What it does not support is the most important pattern in modern email marketing: automated sequences triggered by subscriber events.
Examples that are currently impossible:
Welcome series: 4 emails over 14 days after signup
Onboarding course: 1 lesson per day for 7 days
Re-engagement: if no opens in 60 days, send "we miss you" → wait 7 days → if still no open, unsubscribe
Abandoned-form / abandoned-purchase: trigger via API when an external event fires
Post-event follow-up: 3 emails after webinar attendance
This is the single feature most cited when reviewers compare email tools. Mailchimp Customer Journeys, ConvertKit Visual Automations, EmailOctopus Workflows, Mautic Campaigns — all platforms competing in marketing automation have this. Without it, SendDock loses every "marketing-platform" evaluation by default.
This is the largest single piece of work in the roadmap. It deserves dedicated scoping, not a "later" bucket.
Proposal
A visual workflow editor plus a server-side runner that walks subscribers through a directed graph of steps.
Data model
CREATETABLEautomations (
id UUID PRIMARY KEY,
project_id UUID NOT NULLREFERENCES projects(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft | active | paused
trigger JSONB NOT NULL, -- { type: 'subscriber.created' | 'tag.added' | 'custom_field.changed' | 'api', config: {...} }
graph JSONB NOT NULL, -- nodes + edges
created_at TIMESTAMPTZNOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZNOT NULL DEFAULT NOW()
);
CREATETABLEautomation_runs (
id UUID PRIMARY KEY,
automation_id UUID NOT NULLREFERENCES automations(id) ON DELETE CASCADE,
subscriber_id UUID NOT NULLREFERENCES subscribers(id) ON DELETE CASCADE,
current_node_id VARCHAR(64) NOT NULL,
state VARCHAR(20) NOT NULL, -- running | waiting | completed | exited | failed
next_run_at TIMESTAMPTZ, -- when state=waiting, queue picks it up at this time
history JSONB NOT NULL DEFAULT '[]',
started_at TIMESTAMPTZNOT NULL DEFAULT NOW(),
completed_at TIMESTAMPTZ,
UNIQUE(automation_id, subscriber_id) -- a subscriber is in an automation at most once
);
CREATEINDEXidx_automation_runs_nextON automation_runs(state, next_run_at) WHERE state ='waiting';
Triggers (entry points)
subscriber.created — when a sub is added (via form, API, import, manual)
tag.added — when a specific tag is applied
tag.removed
custom_field.changed — when a field matches a condition (plan_tier = pro)
api — external trigger via POST /api/v1/projects/{id}/automations/{aid}/trigger { email: ... } (this is what enables abandoned-cart, webinar-attended, etc.)
Send a specific template, using subscriber's variables + custom fields
Wait
Pause for N minutes / hours / days / weeks
Wait until time of day
Pause until next occurrence of HH:MM (subscriber's timezone if custom.timezone exists, else project default)
Branch (if/else)
Split on: opened last email, clicked last email, has tag, custom field matches, was active in last N days
Add tag
Apply a tag to the subscriber
Remove tag
Remove a tag
Set custom field
Update a custom field value
Exit
Remove subscriber from this automation
Goal
Marks a goal node; subscribers reaching it count toward conversion (for analytics)
Runner
Background worker polls automation_runs WHERE state = 'waiting' AND next_run_at <= NOW(), advances each one node-by-node until it hits a wait/branch/exit
Uses Redis for distributed locks on a run (multi-worker safe)
A subscriber can be in multiple distinct automations simultaneously, but at most once in the same automation
Subscriber's status = unsubscribed or status = bounced immediately exits all active runs
UI
New section Project → Automations with list of automations and their stats (active subs, completed, conversion rate to goal)
Visual editor using React Flow / Vue Flow style (drag-and-drop nodes, connect edges, configure each node in a side panel)
Activate / Pause toggle per automation
Run history per subscriber (see which node they're on, when they entered, what happened)
Why this is a multi-version effort
This is not a v0.7 feature. Realistic scoping:
Phase 1 (v0.9?): Linear sequences only (trigger → send → wait → send → wait → send → exit), no branches, single trigger type (subscriber.created). Ships welcome series and onboarding courses.
Phase 2 (v1.0?): Branches, all trigger types, all node types, full editor.
Pro — automation is the headline Pro feature for marketing-platform positioning. Self-host Pro license unlocks it; included by default in Cloud Starter and above.
Problem
SendDock today supports three send patterns: transactional (single
/send), batch with per-recipient variables, and broadcast (one email to a segment). What it does not support is the most important pattern in modern email marketing: automated sequences triggered by subscriber events.Examples that are currently impossible:
This is the single feature most cited when reviewers compare email tools. Mailchimp Customer Journeys, ConvertKit Visual Automations, EmailOctopus Workflows, Mautic Campaigns — all platforms competing in marketing automation have this. Without it, SendDock loses every "marketing-platform" evaluation by default.
This is the largest single piece of work in the roadmap. It deserves dedicated scoping, not a "later" bucket.
Proposal
A visual workflow editor plus a server-side runner that walks subscribers through a directed graph of steps.
Data model
Triggers (entry points)
subscriber.created— when a sub is added (via form, API, import, manual)tag.added— when a specific tag is appliedtag.removedcustom_field.changed— when a field matches a condition (plan_tier = pro)api— external trigger viaPOST /api/v1/projects/{id}/automations/{aid}/trigger { email: ... }(this is what enables abandoned-cart, webinar-attended, etc.)segment.entered(later phase, requires segments from Subscriber tags and segmentation #40)Node types
custom.timezoneexists, else project default)Runner
automation_runs WHERE state = 'waiting' AND next_run_at <= NOW(), advances each one node-by-node until it hits a wait/branch/exitstatus = unsubscribedorstatus = bouncedimmediately exits all active runsUI
Why this is a multi-version effort
This is not a v0.7 feature. Realistic scoping:
subscriber.created). Ships welcome series and onboarding courses.The phasing matters because Phase 1 alone covers ~60% of demand and is shippable in 4-6 weeks; Phase 2 takes longer.
Acceptance criteria (Phase 1)
automationsandautomation_runstables migratedsubscriber.createdtrigger functionalAcceptance criteria (Phase 2)
Tier
Pro — automation is the headline Pro feature for marketing-platform positioning. Self-host Pro license unlocks it; included by default in Cloud Starter and above.
Blocked by
tag.addedtrigger and "has tag" branch condition depend on tags shipping firstcustom_field.changedtrigger and field-based branches depend on custom fields shipping first