Smartlead inbox manager skill

Programmatic inbox management for Smartlead.

by growthenginenowoslawski·MIT license·★ 736 Stars on the repo·GitHub ↗

Use now

Files of Smartlead inbox manager

growthenginenowoslawski/main1 file shown
SKILL.md
Show the full text264 lines

Smartlead Inbox Manager

Zapmail hands you hundreds of new inboxes. Smartlead needs them configured: warmup enabled with the right ramp, signatures set so emails don't look bare, tags applied so you know which are active vs insurance, and health monitored so dead inboxes get recycled.

This skill does all of that via the Smartlead API.

Core operations

Operation What it does Script
Enable warmup Turns on warmup with correct ramp (start 1/day, +5/day up to 40/day) set-warmup.ts --mode=enable
Disable warmup Turns off warmup (use for insurance inboxes) set-warmup.ts --mode=disable
Set signatures Bulk-applies a signature template to inboxes set-signatures.ts
Tag as active Adds the "active" tag tag-inboxes.ts --tag=active
Tag as insurance Adds the "insurance" tag tag-inboxes.ts --tag=insurance
Health dashboard Lists all inboxes with warmup status, reputation, daily sent list-health.ts

Active vs insurance (the tagging convention)

Not every inbox should send all the time. A common pattern:

  • Active inboxes — currently sending in live campaigns. Warmup OFF (or minimal) to prioritize real sends.
  • Insurance inboxes — warmed but idle, held in reserve. Warmup ON to maintain reputation. Swap in when an active inbox burns out.

Tags are how you track this at scale. Filter by tag in Smartlead UI or via API.

Inputs

Every script reads:

  • SMARTLEAD_API_KEY (env var)
  • Inbox selector: --ids=1,2,3 OR --domain=example.com OR --tag=insurance OR --all
  • Operation-specific flags (see each script)

Warmup ramp settings

Default warmup config for a NEW inbox (week 1-4 of warmup period):

{
  "warmup_enabled": "true",
  "total_warmup_per_day": 40,
  "daily_rampup": 5,
  "reply_rate_percentage": "20"
}
  • total_warmup_per_day: max emails/day the inbox sends in warmup network (peak)
  • daily_rampup: increment per day (day 1 = 1, day 2 = 6, day 3 = 11, etc. up to the cap)
  • reply_rate_percentage: how often warmup peers reply (20% is realistic)

For INSURANCE inboxes (maintaining reputation long-term):

{
  "warmup_enabled": "true",
  "total_warmup_per_day": 15,
  "daily_rampup": 0,
  "reply_rate_percentage": "20"
}

Lower volume, no ramp — just keeps the inbox warm.

For ACTIVE inboxes (currently in live campaigns):

{
  "warmup_enabled": "false"
}

Warmup off so the daily send budget goes to real prospects.

Signature template

The default template, applied in bulk via set-signatures.ts, is:

{from_name}
{title}
{company}
{address}

Rendered (for example):

Jane Smith
Founder
Acme
123 Main St, Suite 400, Austin, TX 78701

Where each value comes from:

  • {from_name} — the inbox's own from_name field if set (different personas per inbox), else SENDER_FIRST_NAME + SENDER_LAST_NAME from .env
  • {title} — SENDER_TITLE env var
  • {company} — SENDER_COMPANY_NAME env var
  • {address} — SENDER_PHYSICAL_ADDRESS env var (recommended — a real mailing address in the footer keeps you on the right side of CAN-SPAM and similar rules)

Required .env entries for the default template:

SENDER_FIRST_NAME=Jane
SENDER_LAST_NAME=Smith
SENDER_TITLE=Founder
SENDER_COMPANY_NAME=Acme
SENDER_PHYSICAL_ADDRESS=123 Main St, Suite 400, Austin, TX 78701

Custom template override:

# Pass inline (use \n for newlines)
npx tsx scripts/set-signatures.ts --all --template="Cheers,\n{from_name}\n{title}\n{company}\n{address}"

# Or from a file
npx tsx scripts/set-signatures.ts --all --template-file=./my-signature.txt

Available placeholders: {from_name}, {from_email}, {domain}, {title}, {company}, {address}.

Email body order in campaigns:

In Smartlead sequences, always end the body with %signature% — Smartlead injects the inbox's signature there. Order:

<body content>

<PS unsubscribe line>

%signature%

This puts %signature% (with name + title + company + address) on the bottom, so every inbox sends with a real mailing address in the footer.

Health dashboard output

list-health.ts prints a CSV + summary:

email                              warmup   reputation   sent_today   health_status
[email protected]                    on       good         18           healthy
[email protected]                    on       fair         5            warming
[email protected]                     off      n/a          28           active
[email protected]                   on       bad          0            blocked

Summary:

Total inboxes: 80
  Warmup on: 50
  Active (warmup off): 28
  Blocked/failed: 2

Reputation:
  Good: 65
  Fair: 10
  Bad: 3
  Unknown: 2

Action items:
  - 2 inboxes blocked — run /email-deliverability-audit
  - 3 inboxes with "bad" reputation — consider pausing

Common workflows

Day 1 after Zapmail provisioning
# 1. Enable warmup on all newly created inboxes
npx tsx scripts/set-warmup.ts --mode=enable --tag=new --warmup-per-day=40 --ramp=5

# 2. Set signatures
npx tsx scripts/set-signatures.ts --tag=new --template="Best,\n{from_name}"

# 3. Tag them as insurance (they aren't active yet — warmup for 2 weeks first)
npx tsx scripts/tag-inboxes.ts --tag=new --add-tag=insurance
npx tsx scripts/tag-inboxes.ts --tag=new --remove-tag=new
Activating insurance inboxes into a live campaign
# 1. Identify warm insurance inboxes with good reputation
npx tsx scripts/list-health.ts --tag=insurance --filter=reputation:good --out=activate-candidates.csv

# 2. Flip them to active
npx tsx scripts/tag-inboxes.ts --ids-from-csv=activate-candidates.csv --add-tag=active --remove-tag=insurance

# 3. Disable warmup (they're sending for real now)
npx tsx scripts/set-warmup.ts --mode=disable --tag=active
Weekly health check
npx tsx scripts/list-health.ts --all --out=health-$(date +%Y-%m-%d).csv

Review the action items. Replace blocked inboxes.

When to retire an inbox

This skill is the mechanics of retiring an inbox. The decision belongs to /inbox-lifecycle-manager, which applies the full ladder — age guard, send floors, bounce fork, and a check that you have a warmed reserve to swap in before you take capacity away.

The cancel line is the 1% rule: reply rate <1% over 200+ sends (or zero replies on 150+), on a domain at least 30 days old. The floors do the protecting, not a softer threshold.

Three rules that matter here, because it is easy to retire the wrong thing from this skill:

  1. ⛔ Retire whole domains, never single inboxes. Mailbox providers judge reputation at the domain level. Keeping two "good" inboxes on a domain you declared burned means the good ones inherit the bad one's reputation, and you are still sending from a domain you condemned.
  2. ⛔ Never retire a domain under 30 days old, at any reply rate. It hasn't finished ramping.
  3. ⛔ Never retire on under 200 sends. Below that floor a 0% reply rate cannot distinguish a burned inbox from an unlucky week.

Once /inbox-lifecycle-manager has produced an approved plan, execute it here:

# actions.csv comes from /inbox-lifecycle-manager — reviewed and explicitly approved
npx tsx scripts/tag-inboxes.ts --domain=burned-domain.co --add-tag=retired --remove-tag=active
npx tsx scripts/set-warmup.ts --mode=disable --domain=burned-domain.co

Snapshot the current tags before you start — nothing here records prior state, and without a snapshot there is no clean undo. Read the tags back afterwards; a 200 is not proof.

Common gotchas

  • Warmup settings are per-inbox. There's no global setting. Scripts loop and hit each inbox individually.
  • Rate-limiting. Smartlead allows ~33 req/sec. The scripts use 5 concurrent by default. Don't crank this higher without testing.
  • Warmup blocked. If is_warmup_blocked: true, the inbox is flagged (usually for spam-like behavior in warmup). Manual investigation needed.
  • Tags have no delete endpoint in some Smartlead versions — tags can only be ADDED. To "remove" a tag, replace the full tag list with a new one that omits it. The script handles this automatically.
  • Signature formatting. HTML signatures can break rendering across Gmail/Outlook/Apple Mail. Keep it plain text or use minimal <br> tags. Test in Gmail/Outlook after setting.
  • Warmup ramp accumulates. If you set daily_rampup: 5, the inbox sends 1 on day 1, 6 on day 2, 11 on day 3... up to total_warmup_per_day. To reset (e.g. after a break), disable then re-enable.

What to do next

If you just provisioned new inboxes: enable warmup (set-warmup.ts --mode=enable), apply signatures (set-signatures.ts), tag as insurance, then wait 2 weeks for warmup. After 2 weeks, promote insurance → active.

If inboxes are already warm: proceed to list-building (/prospeo-full-export, /disco-like, etc.) using active-tagged inboxes.

Or wait: if the health dashboard shows bad-reputation inboxes, run /inbox-lifecycle-manager to decide which ones actually warrant retirement (and what replaces their capacity) before launching a new campaign.

  • /zapmail-domain-setup-public — creates the inboxes this skill configures
  • /email-deliverability-audit — when health dashboard shows problems
  • /inbox-lifecycle-manager — decides which inboxes to retire, promote, or buy; this skill executes it
  • /smartlead-api — underlying API reference

Scripts

  • scripts/set-warmup.ts — enable/disable warmup, configure ramp
  • scripts/set-signatures.ts — bulk signature setting
  • scripts/tag-inboxes.ts — add/remove tags
  • scripts/list-health.ts — health dashboard CSV + summary
  • scripts/_lib.ts — shared: API client, inbox selector parser, concurrency
1---
2name: smartlead-inbox-manager
3description: Programmatic inbox management for Smartlead. Enable/disable warmup with correct ramp settings, set signatures in bulk, tag inboxes (active vs insurance), and pull inbox health dashboards. Use after creating a new batch of inboxes via /zapmail-domain-setup-public, or when managing an existing Smartlead account at scale. Triggers on "turn on warmup", "set signatures", "tag inboxes", "inbox health", "set up new inboxes".
4---
5 
6# Smartlead Inbox Manager
7 
8Zapmail hands you hundreds of new inboxes. Smartlead needs them configured: warmup enabled with the right ramp, signatures set so emails don't look bare, tags applied so you know which are active vs insurance, and health monitored so dead inboxes get recycled.
9 
10This skill does all of that via the Smartlead API.
11 
12## Core operations
13 
14| Operation | What it does | Script |
15|---|---|---|
16| Enable warmup | Turns on warmup with correct ramp (start 1/day, +5/day up to 40/day) | `set-warmup.ts --mode=enable` |
17| Disable warmup | Turns off warmup (use for insurance inboxes) | `set-warmup.ts --mode=disable` |
18| Set signatures | Bulk-applies a signature template to inboxes | `set-signatures.ts` |
19| Tag as active | Adds the "active" tag | `tag-inboxes.ts --tag=active` |
20| Tag as insurance | Adds the "insurance" tag | `tag-inboxes.ts --tag=insurance` |
21| Health dashboard | Lists all inboxes with warmup status, reputation, daily sent | `list-health.ts` |
22 
23## Active vs insurance (the tagging convention)
24 
25Not every inbox should send all the time. A common pattern:
26 
27- **Active inboxes** — currently sending in live campaigns. Warmup OFF (or minimal) to prioritize real sends.
28- **Insurance inboxes** — warmed but idle, held in reserve. Warmup ON to maintain reputation. Swap in when an active inbox burns out.
29 
30Tags are how you track this at scale. Filter by tag in Smartlead UI or via API.
31 
32## Inputs
33 
34Every script reads:
35- `SMARTLEAD_API_KEY` (env var)
36- Inbox selector: `--ids=1,2,3` OR `--domain=example.com` OR `--tag=insurance` OR `--all`
37- Operation-specific flags (see each script)
38 
39## Warmup ramp settings
40 
41Default warmup config for a NEW inbox (week 1-4 of warmup period):
42 
43```json
44{
45 "warmup_enabled": "true",
46 "total_warmup_per_day": 40,
47 "daily_rampup": 5,
48 "reply_rate_percentage": "20"
49}
50```
51 
52- `total_warmup_per_day`: max emails/day the inbox sends in warmup network (peak)
53- `daily_rampup`: increment per day (day 1 = 1, day 2 = 6, day 3 = 11, etc. up to the cap)
54- `reply_rate_percentage`: how often warmup peers reply (20% is realistic)
55 
56For INSURANCE inboxes (maintaining reputation long-term):
57```json
58{
59 "warmup_enabled": "true",
60 "total_warmup_per_day": 15,
61 "daily_rampup": 0,
62 "reply_rate_percentage": "20"
63}
64```
65 
66Lower volume, no ramp — just keeps the inbox warm.
67 
68For ACTIVE inboxes (currently in live campaigns):
69```json
70{
71 "warmup_enabled": "false"
72}
73```
74 
75Warmup off so the daily send budget goes to real prospects.
76 
77## Signature template
78 
79The default template, applied in bulk via `set-signatures.ts`, is:
80 
81```
82{from_name}
83{title}
84{company}
85{address}
86```
87 
88Rendered (for example):
89```
90Jane Smith
91Founder
92Acme
93123 Main St, Suite 400, Austin, TX 78701
94```
95 
96**Where each value comes from:**
97- `{from_name}` — the inbox's own `from_name` field if set (different personas per inbox), else `SENDER_FIRST_NAME + SENDER_LAST_NAME` from `.env`
98- `{title}` — `SENDER_TITLE` env var
99- `{company}` — `SENDER_COMPANY_NAME` env var
100- `{address}` — `SENDER_PHYSICAL_ADDRESS` env var (recommended — a real mailing address in the footer keeps you on the right side of CAN-SPAM and similar rules)
101 
102**Required `.env` entries for the default template:**
103```
104SENDER_FIRST_NAME=Jane
105SENDER_LAST_NAME=Smith
106SENDER_TITLE=Founder
107SENDER_COMPANY_NAME=Acme
108SENDER_PHYSICAL_ADDRESS=123 Main St, Suite 400, Austin, TX 78701
109```
110 
111**Custom template override:**
112```bash
113# Pass inline (use \n for newlines)
114npx tsx scripts/set-signatures.ts --all --template="Cheers,\n{from_name}\n{title}\n{company}\n{address}"
115 
116# Or from a file
117npx tsx scripts/set-signatures.ts --all --template-file=./my-signature.txt
118```
119 
120Available placeholders: `{from_name}`, `{from_email}`, `{domain}`, `{title}`, `{company}`, `{address}`.
121 
122**Email body order in campaigns:**
123 
124In Smartlead sequences, always end the body with `%signature%` — Smartlead injects the inbox's signature there. Order:
125```
126<body content>
127 
128<PS unsubscribe line>
129 
130%signature%
131```
132 
133This puts `%signature%` (with name + title + company + address) on the bottom, so every inbox sends with a real mailing address in the footer.
134 
135## Health dashboard output
136 
137`list-health.ts` prints a CSV + summary:
138 
139```
140email warmup reputation sent_today health_status
141[email protected] on good 18 healthy
142[email protected] on fair 5 warming
143[email protected] off n/a 28 active
144[email protected] on bad 0 blocked
145```
146 
147Summary:
148```
149Total inboxes: 80
150 Warmup on: 50
151 Active (warmup off): 28
152 Blocked/failed: 2
153 
154Reputation:
155 Good: 65
156 Fair: 10
157 Bad: 3
158 Unknown: 2
159 
160Action items:
161 - 2 inboxes blocked — run /email-deliverability-audit
162 - 3 inboxes with "bad" reputation — consider pausing
163```
164 
165## Common workflows
166 
167### Day 1 after Zapmail provisioning
168 
169```bash
170# 1. Enable warmup on all newly created inboxes
171npx tsx scripts/set-warmup.ts --mode=enable --tag=new --warmup-per-day=40 --ramp=5
172 
173# 2. Set signatures
174npx tsx scripts/set-signatures.ts --tag=new --template="Best,\n{from_name}"
175 
176# 3. Tag them as insurance (they aren't active yet — warmup for 2 weeks first)
177npx tsx scripts/tag-inboxes.ts --tag=new --add-tag=insurance
178npx tsx scripts/tag-inboxes.ts --tag=new --remove-tag=new
179```
180 
181### Activating insurance inboxes into a live campaign
182 
183```bash
184# 1. Identify warm insurance inboxes with good reputation
185npx tsx scripts/list-health.ts --tag=insurance --filter=reputation:good --out=activate-candidates.csv
186 
187# 2. Flip them to active
188npx tsx scripts/tag-inboxes.ts --ids-from-csv=activate-candidates.csv --add-tag=active --remove-tag=insurance
189 
190# 3. Disable warmup (they're sending for real now)
191npx tsx scripts/set-warmup.ts --mode=disable --tag=active
192```
193 
194### Weekly health check
195 
196```bash
197npx tsx scripts/list-health.ts --all --out=health-$(date +%Y-%m-%d).csv
198```
199 
200Review the action items. Replace blocked inboxes.
201 
202## When to retire an inbox
203 
204This skill is the **mechanics** of retiring an inbox. The **decision** belongs to
205`/inbox-lifecycle-manager`, which applies the full ladder — age guard, send floors, bounce fork,
206and a check that you have a warmed reserve to swap in before you take capacity away.
207 
208The cancel line is the 1% rule: **reply rate <1% over 200+ sends** (or zero replies on 150+), on a
209domain at least 30 days old. The floors do the protecting, not a softer threshold.
210 
211Three rules that matter here, because it is easy to retire the wrong thing from this skill:
212 
2131. ⛔ **Retire whole domains, never single inboxes.** Mailbox providers judge reputation at the
214 domain level. Keeping two "good" inboxes on a domain you declared burned means the good ones
215 inherit the bad one's reputation, and you are still sending from a domain you condemned.
2162. ⛔ **Never retire a domain under 30 days old**, at any reply rate. It hasn't finished ramping.
2173. ⛔ **Never retire on under 200 sends.** Below that floor a 0% reply rate cannot distinguish a
218 burned inbox from an unlucky week.
219 
220Once `/inbox-lifecycle-manager` has produced an approved plan, execute it here:
221 
222```bash
223# actions.csv comes from /inbox-lifecycle-manager — reviewed and explicitly approved
224npx tsx scripts/tag-inboxes.ts --domain=burned-domain.co --add-tag=retired --remove-tag=active
225npx tsx scripts/set-warmup.ts --mode=disable --domain=burned-domain.co
226```
227 
228Snapshot the current tags before you start — nothing here records prior state, and without a
229snapshot there is no clean undo. Read the tags back afterwards; a `200` is not proof.
230 
231## Common gotchas
232 
233- **Warmup settings are per-inbox.** There's no global setting. Scripts loop and hit each inbox individually.
234- **Rate-limiting.** Smartlead allows ~33 req/sec. The scripts use 5 concurrent by default. Don't crank this higher without testing.
235- **Warmup blocked.** If `is_warmup_blocked: true`, the inbox is flagged (usually for spam-like behavior in warmup). Manual investigation needed.
236- **Tags have no delete endpoint** in some Smartlead versions — tags can only be ADDED. To "remove" a tag, replace the full tag list with a new one that omits it. The script handles this automatically.
237- **Signature formatting.** HTML signatures can break rendering across Gmail/Outlook/Apple Mail. Keep it plain text or use minimal `<br>` tags. Test in Gmail/Outlook after setting.
238- **Warmup ramp accumulates.** If you set `daily_rampup: 5`, the inbox sends 1 on day 1, 6 on day 2, 11 on day 3... up to `total_warmup_per_day`. To reset (e.g. after a break), disable then re-enable.
239 
240## What to do next
241 
242**If you just provisioned new inboxes:** enable warmup (`set-warmup.ts --mode=enable`), apply signatures (`set-signatures.ts`), tag as `insurance`, then **wait 2 weeks for warmup**. After 2 weeks, promote `insurance` → `active`.
243 
244**If inboxes are already warm:** proceed to list-building (`/prospeo-full-export`, `/disco-like`, etc.) using `active`-tagged inboxes.
245 
246**Or wait:** if the health dashboard shows bad-reputation inboxes, run `/inbox-lifecycle-manager`
247to decide which ones actually warrant retirement (and what replaces their capacity) before
248launching a new campaign.
249 
250## Related skills
251 
252- `/zapmail-domain-setup-public` — creates the inboxes this skill configures
253- `/email-deliverability-audit` — when health dashboard shows problems
254- `/inbox-lifecycle-manager` — decides *which* inboxes to retire, promote, or buy; this skill executes it
255- `/smartlead-api` — underlying API reference
256 
257## Scripts
258 
259- `scripts/set-warmup.ts` — enable/disable warmup, configure ramp
260- `scripts/set-signatures.ts` — bulk signature setting
261- `scripts/tag-inboxes.ts` — add/remove tags
262- `scripts/list-health.ts` — health dashboard CSV + summary
263- `scripts/_lib.ts` — shared: API client, inbox selector parser, concurrency
264 

Discussion