2.2 KiB
2.2 KiB
Scheduling & Timing
Campaign duration
| Setting | Value |
|---|---|
| Recommended | 30–45 days |
| Minimum | 14 days (not recommended for new domains) |
| Maximum | 45 days (MVP cap) |
endsAt = startedAt + durationDays
Daily send distribution
Send windows
Avoid off-hours sends that signal automation.
| Parameter | Default |
|---|---|
| Timezone | Campaign or mailbox TZ |
| Window start | 08:00 |
| Window end | 18:00 |
| Weekends | Optional: reduce volume 50% or pause |
Jitter between sends
| Parameter | Default | Rationale |
|---|---|---|
| Min gap | 45 minutes | Avoid burst patterns |
| Max gap | 180 minutes | Stay within daily quota |
| Distribution | Uniform random | Human-like variance |
nextSendAt = lastSendAt + random(45min, 180min)
if nextSendAt outside window: roll to next window start
48-hour volume cap
Industry guidance: never increase average daily volume more than 20% in 48 hours.
function cap48hIncrease(history: number[], proposed: number): number {
const avg48h = average(last48h(history));
return Math.min(proposed, Math.ceil(avg48h * 1.20));
}
Applies to all recipes except explicit expert override in Custom (with warning).
Cron vs queue architecture
Current (MVP)
node-cronfires dispatcher every 15 min- All eligible sends in one batch
Target
| Component | Role |
|---|---|
| Scheduler cron | Every 5 min: enqueue due sends |
| Send queue | BullMQ / pg-boss / SQLite queue |
| Worker | One send per job with jitter |
| Rescuer cron | Every 10 min: IMAP scan per mailbox |
Benefits: precise timing, backoff, idempotency keys, pause per campaign.
Campaign day rollover
At midnight (campaign TZ):
- Increment
currentDay - Load
DailySchedulefor new day - Reset
actualSends,actualRepliescounters - If
currentDay > durationDays→ complete or maintenance mode
Maintenance mode (post-campaign)
After primary campaign completes:
- Continue Flat recipe at 20% of maxEmailsPerDay
- Keeps engagement signals during live cold outreach
- Operator can disable
Timezone handling
- Store all timestamps UTC in DB
- Display in campaign timezone in UI
- Send windows evaluated in campaign timezone