1---
2name: warm-outbound-engine
3description: This skill should be used when the user wants to run the LinkedIn Warm Outbound skill, or asks to "scrape LinkedIn post engagers", "build outbound from people engaging with my market", "find warm leads on LinkedIn", "turn LinkedIn engagement into a sequence", "write outreach from what people commented", "offer each lead a free personalized resource", "auto-reply to LinkedIn or cold email replies", or "put my LinkedIn outbound on autopilot", or wants recurring engagement-sourced prospecting staged into HeyReach, Instantly, AgentMail or their own Gmail. Runs on the superagnt MCP: LinkedIn post + engager data, first-party enrichment, workspace database, and one sending lane of the user's choice, with a research-backed cold outreach library and a recurring version that runs daily or every other day. Stages campaigns stopped; starting a sequence is always the human's.
4version: 0.3.1
5---
6
7# LinkedIn Warm Outbound
8
9Outbound sourced from engagement, not from a list. Every lead in the table
10did something in public first: reacted to, or commented on, a post about the
11problem you solve. Builds on the base `lead-generation` skill for the
12enrichment mechanics — read that for per-tool detail; this one adds the
13engagement source, the scoring gate, the first message, and the four sending
14lanes.
15
16The intended arc: run it once end to end in this client, look at what it
17staged, then put it on autopilot: the same sweep daily or every other day,
18on this client's own scheduler or a deployed superagnt agent (step 7).
19
20## The outreach library
21
22This skill ships a reference library in `references/`, researched September
232026 from full YouTube transcripts (Alex Hormozi, Nick Abraham, Alex
24Berman), Nick Abraham's LinkedIn outreach posts on X, and agnt_'s own
25production system. Read the matching file before the step that needs it,
26and prefer it over generic outreach folklore.
27
28- `references/icp-scoring-rubric.md`: the two-layer scorer (a narrow gate,
29 then one holistic score against the ICP description) and the weekly
30 calibration loop. Read before step 4.
31- `references/cold-outreach-playbook.md`: targeting, connection requests,
32 first messages, follow-ups, replies, sending limits and benchmarks, per
33 lane. Read before step 5.
34- `references/free-resource-first-touch.md`: build each qualified lead a
35 personalized asset from their own data and offer it instead of a pitch.
36 Read when step 1 set a `first_touch_asset`.
37- `references/webhook-auto-reply.md`: vendor webhooks into a deployed agent
38 that classifies replies, logs them, and drafts or sends answers under
39 approval.
40- `references/autopilot-agent-team.md`: the deployed version of step 7, a
41 recurring agent team on superagnt: the team shape, data jobs for the
42 per-lead work, cadence, cost, and how real deployments broke.
43
44For LinkedIn via HeyReach, also load the platform's own doctrine:
45`agnt_guidance_load({ "name": "linkedin-outreach" })`.
46
47No `references/` folder next to this file (a harness that saved only
48SKILL.md)? Fetch the one you need from
49`https://raw.githubusercontent.com/superagnt/plugins/main/plugins/warm-outbound-engine/skills/warm-outbound-engine/references/<file>`.
50If that fails, say so once and proceed: the steps below stand alone.
51
52## Step 0 — tools
53
54Codex: superagnt's tools can take a few seconds to appear in a new chat.
55If none are listed, wait about 10 seconds and check again before concluding
56superagnt isn't connected or falling back to web search.
57
58`agnt_tools_list_enabled`; if missing on a flat server:
59`agnt_tools_enable({ "families": ["data:linkedin", "database"] })` (the
60first-party `data_agnt_*` people/company tools are always on). A grouped
61server already serves every data source and the database: nothing to enable.
62That is the whole first run — do not enable a sending lane yet.
63`requires_upgrade` → `confirm_url` to the user → wait → re-run. (The
64`warm-outbound-engine` skill endpoint pre-enables these two.)
65
66### Tool names on grouped servers
67
68This skill writes flat (per-operation) names. New servers list grouped tools
69instead: one tool per resource with an `action` argument. Flat names still
70resolve there but are not listed, so most clients will not let you call
71them. The calls this skill makes:
72
73| Flat | Grouped tool, action |
74|---|---|
75| `data_linkedin_search_posts` / `_search_post_by_hashtag` | `data_linkedin_search`: `posts` / `hashtag_posts` |
76| `data_linkedin_get_company_s_post` | `data_linkedin_companies`: `posts` |
77| `data_linkedin_get_profile_s_posts` | `data_linkedin_activity`: `posts` |
78| `data_linkedin_get_post_reactions` | `data_linkedin_posts`: `reactions` |
79| `data_linkedin_get_profile_post_comment` | `data_linkedin_posts`: `comments` |
80| `data_linkedin_get_profile_data_by_url` | `data_linkedin_profiles`: `profile_by_url` |
81| `data_linkedin_get_company_by_domain` | `data_linkedin_companies`: `by_domain` |
82| `data_agnt_companies_enrich` | `data_agnt_companies`: `enrich` |
83| `data_agnt_people_email_finder` / `_email_verifier` | `data_agnt_contacts`: `find_email` / `verify_email` |
84| `agnt_db_select` | `agnt_db_read`: `select` |
85| `agnt_db_insert` / `agnt_db_upsert` | `agnt_db_write`: `insert` / `upsert` |
86| `agnt_db_execute_sql` | `agnt_db_sql`: `execute_sql` |
87| `agnt_tools_enable` | `agnt_tools_write`: `enable` |
88| `agnt_guidance_load` | `agnt_guidance_read`: `load` |
89| `connection_heyreach_campaign_get_by_id` / `_get_sequence` | `connection_heyreach_campaigns_read`: `get` / `sequence` |
90| `connection_heyreach_list_add_leads_v2` | `connection_heyreach_list_members_write`: `add_leads` |
91| `connection_heyreach_campaign_add_leads_v2` | `connection_heyreach_campaign_leads_write`: `add` |
92| `connection_heyreach_campaign_start` | `connection_heyreach_campaigns_write`: `start` |
93| `connection_instantly_listcampaign` | `connection_instantly_campaigns_read`: `list` |
94| `connection_instantly_bulkaddleads` | `connection_instantly_leads_write`: `add` |
95| `connection_instantly_activatecampaign` | `connection_instantly_campaigns_write`: `activate` |
96| `connection_agentmail_create_inbox` | `connection_agentmail_inboxes_write`: `create` |
97| `connection_agentmail_create_draft` | `connection_agentmail_drafts_write`: `create` |
98| `connection_agentmail_send_draft` / `_reply_to_message` | `connection_agentmail_messages_send`: `send_draft` / `reply` |
99| `connection_agentmail_list_messages` | `connection_agentmail_messages_read`: `list` |
100| `connection_gmail_save_draft` | `connection_gmail_mail_write`: `save_draft` |
101| `connection_gmail_send_email` | `connection_gmail_mail_send`: `send` |
102
103`agnt_tools_list_enabled` keeps its name on both surfaces. `connection_*` tools
104are grouped the same way as the rest; the lane calls are in the table.
105
106**Grouped data calls return a markdown table that drops fields this skill
107keys on.** Pass `response_format: "json"` on every LinkedIn call in steps 3
108and 4: you get every field of the view instead of the default columns. Then
109posts carry `urn` (the comments call takes it) and `url`; reactions carry
110`urn` (the member id for `linkedin_member_urn`) and `url` (the reactor's
111profile); comments carry `author_url` (the commenter's profile) and `url`,
112which is the link to the comment itself, never a lead's profile. For any
113field the view does not carry, such as a post author's profile URL, pass
114`response_format: "raw"`: the full payload, exactly what the flat tool
115returns.
116
117## Step 1 — the ICP, and the searches it implies (first run only)
118
119Ask once, in one message, and draft what you can from their website so they
120only have to correct it:
121
122- who buys, in their words: the roles that own the problem, the kind of
123 company, the situation in which they feel it, and who looks close but
124 never buys. This becomes the ICP description the score reads
125 (`references/icp-scoring-rubric.md`);
126- where they can sell, and who never to contact: competitors, customers,
127 partners, their own team;
128- a watchlist: the competitors whose posts their buyers react to, and 5 to
129 10 creators whose audience is the buyer. Optional, and the steadiest
130 source there is;
131- which channel they send from: HeyReach, Instantly, AgentMail or Gmail
132 (recorded now, enabled at step 6);
133- optionally, what they could give a prospect for free that proves they are
134 good at their job (a report, a teardown, a sample list, a working setup of
135 their product). See `references/free-resource-first-touch.md`; if they
136 have nothing, skip it and the plain opener still works.
137
138Create the tables. If they exist from an earlier version of this skill, add
139the missing columns instead of recreating them:
140
141```
142icp_profile(id, sells text, problem text, icp_description text,
143 trigger_signals text, target_geography text, personas text,
144 do_not_contact text, qualify_threshold int default 50,
145 lane text, first_touch_asset text, updated_at timestamptz)
146watchlist(linkedin_url unique, kind text, name text, added_at timestamptz)
147leads(id, person, linkedin_url unique, linkedin_member_urn unique, title,
148 company, company_domain, email, email_verified boolean,
149 source_kind text, pond text, source_post_url, engagement text,
150 engagement_text text, persona text, fit_score int, fit_reason text,
151 opener_variant text, first_message text, status text,
152 staged_at timestamptz, replied_at timestamptz, reply_intent text,
153 updated_at timestamptz)
154search_history(term, kind, pond, searched_at default now())
155scraped_posts(post_url unique, post_urn, pond, term_used, author, snippet,
156 scraped_at)
157```
158
159Then write the answers to `icp_profile` (`agnt_db_insert`) and the watchlist
160(`kind` is `competitor` or `creator`). The do-not-contact list goes in before
161the first scrape: engager lists are full of competitors, and messaging one
162is the fastest way to get your outreach posted about.
163
164Upsert key on `leads` is `linkedin_url` — it is the only identifier that
165survives a job change. LinkedIn also spells the same person as an encoded
166member id (`ACoA…`) in some payloads, so keep `linkedin_member_urn` too and
167dedupe on both. On a grouped server the default markdown view leaves out
168the member id and a commenter's profile URL: request
169`response_format: "json"` (see Step 0).
170
171## Step 2 — choose this run's sources, then write its searches
172
173Where you fish matters more than how hard. There are three ponds, and
174only one of them is the obvious one:
175
1761. Buyer behavior (write these first). Posts written by people who have
177 the problem right now: hiring for the function ("hiring a [role that owns
178 the problem]"), scaling or straining ("our [process] is breaking"),
179 evaluating ("anyone using a [category] tool for [job]?"), or the pain
180 stated plainly at the end of a bad week. On these posts **the author is a
181 lead too**, and the strongest one there is: writing the post is the
182 intent signal.
1832. The watchlist. The recent posts of the competitors and creators in
184 `watchlist`. Everyone reacting to a competitor's product content is
185 already shopping; a creator's audience is the buyer by construction.
1863. Topic. Keywords and hashtags for the space. The widest pond and the
187 noisiest: people who talk about a category for a living are often the
188 ones selling into it, which is why it is one pond of three.
189
190Over a week, aim for roughly 40% buyer-behavior searches, 30% watchlist and
19130% topic. A run gives its searches to one or two ponds rather than all
192three shallowly; the first run covers all three.
193
194Do not keep a fixed keyword menu. Generate 3–6 fresh searches per run
195from the ICP description, steered by two queries. Run both with
196`agnt_db_execute_sql`; `agnt_db_select` can't express a join or a
197`now() - interval` filter:
198
199```sql
200-- what has actually converted, per pond and term
201SELECT sp.pond, sp.term_used, count(l.id) AS leads,
202 count(*) FILTER (WHERE l.fit_score > 50) AS qualified,
203 count(*) FILTER (WHERE l.replied_at IS NOT NULL) AS replied
204FROM scraped_posts sp JOIN leads l ON l.source_post_url = sp.post_url
205GROUP BY 1, 2 ORDER BY replied DESC, qualified DESC;
206
207-- what not to repeat yet
208SELECT term FROM search_history WHERE searched_at > now() - interval '14 days';
209```
210
211Write more phrases like the families with replies; drop the phrasing
212patterns that produced leads and no replies. Never reuse a term from the
21314-day window; everything older is fair game again. Give the next run's
214searches to the pond that is producing.
215
216**The single highest-leverage rule here: write the language your BUYER
217posts in, not the language your industry sells in.** Category and execution
218terms ("shelf reset", "category review", "Q3 launch at Target") surface
219posts whose engagers are buyers. Vendor/martech terms ("retail media
220network", "martech stack") surface posts engaged with by other vendors, and
221a run of those poisons the table with people who will never buy. Test every
222generated term: *would a buyer plausibly write or react to a post using this
223phrase?*
224
225Insert each term into `search_history` with its `kind` (`keyword`,
226`hashtag`, `watchlist`) and `pond` as soon as its search call returns,
227zero results included, so a run that dies halfway keeps the history of what
228it actually searched. A term whose call failed (credits, provider error) is
229not recorded and stays eligible next run.
230
231## Step 3 — the sweep
232
2331. Find the candidate posts: `data_linkedin_search_posts` for keyword
234 searches (buyer behavior and topic), `data_linkedin_search_post_by_hashtag`
235 for hashtags (topic only), and for a watchlist entry
236 `data_linkedin_get_company_s_post` (a company page) or
237 `data_linkedin_get_profile_s_posts` (a person), taking its 1–2 most
238 engaged posts from the last couple of months. 3–5 posts per run across
239 all ponds — that cap is deliberate, it is the run's whole data cost.
2402. Prefer posts with real engagement; a post with 4 reactions is not worth
241 a scrape when a 90-reaction one is in the same result set.
2423. Skip posts already in `scraped_posts`. Record the rest, with their
243 `pond`, as you take them.
2444. `data_linkedin_get_post_reactions` for reactors,
245 `data_linkedin_get_profile_post_comment` for commenters (grouped:
246 `data_linkedin_posts` actions `reactions` and `comments`, with
247 `response_format: "json"`). Take a reactor's profile from `url` and
248 member id from `urn`; take a commenter's profile from `author_url`, never
249 from the comment's `url`. Record
250 `engagement` ('reacted' | 'commented') per lead — commenters convert
251 better and are worth a different opener. Keep what they did in
252 `engagement_text`: the comment itself for commenters, the reaction type
253 for reactors. It is the one personalization hook every lead comes with.
2545. On a buyer-behavior post written by a person, add the author as a
255 lead too (`source_kind = 'author'`, with the post itself as
256 `engagement_text`); everyone else is `source_kind = 'engager'`. Never add
257 a competitor as the author of its own post, and drop the employees of a
258 watched company when you pull its engagers.
2596. Drop anyone whose `linkedin_url` or `linkedin_member_urn` is already in
260 `leads`. Dedup at the source, never at send time. A grouped call's
261 default markdown table has no member id, and on comments its `url` is the
262 comment link, so a dedupe against it silently lets duplicates through:
263 fetch the json view first.
264
265**Geography and other filters belong on the enriched profile, not on the
266search payload.** LinkedIn's post-search results omit location for plenty of
267genuinely in-market posters; filtering there silently starves the funnel.
268Take the lead, filter it in step 4.
269
270## Step 4 — score, then enrich
271
272Two layers, in this order, from `references/icp-scoring-rubric.md` (read it
273before the first scoring pass). **The score gates the spend: a lead that
274fails it never costs an email lookup.**
275
2761. Check the profile URL first (free): a missing or malformed one never
277 becomes a paid call.
2782. `data_linkedin_get_profile_data_by_url` → real title, company, location,
279 and the canonical profile URL. Dedupe again on both keys.
2803. The gate: reject only an explicitly out-of-region location or an
281 empty or fake profile (`fit_score = 3`, with the reason), and drop anyone
282 on the do-not-contact list. Never judge the role here: a gate that
283 guesses at fit from a headline starves the funnel.
2844. `data_agnt_companies_enrich` (or `data_linkedin_get_company_by_domain`)
285 → domain, size, industry. Research the company further only when the
286 score can't be settled without it.
2875. The score: one holistic judgment against
288 `icp_profile.icp_description`: `fit_score` 0–100, a two- or
289 three-sentence `fit_reason` that cites the evidence and names the
290 uncertainty, and a `persona` only when confident. Above the bar
291 (`qualify_threshold`, default 50) qualifies, and an unresolved maybe
292 qualifies; a score at or under it needs a concrete wrong-way reason.
293 Below the bar → `status = 'disqualified'` and stop: no email lookup, no
294 message, ever.
2956. Only for leads that cleared the bar, and only when the lane sends email:
296 `data_agnt_people_email_finder` from name + domain, then always
297 `data_agnt_people_email_verifier`. LinkedIn URL → email is not a one-step
298 lookup. Unverified stays `email_verified = false`; those rows are still
299 workable on LinkedIn. When `icp_profile.lane` is HeyReach, skip both calls.
3007. `agnt_db_upsert` with `status` moving new → scored → enriched → staged,
301 so a failed run resumes instead of restarting. `enriched` means ready to
302 message. Never overwrite a known value with a blank.
303
304Above ~500 leads a run, this belongs in a data job rather than a longer
305session — one cheap structured call per lead
306(`references/autopilot-agent-team.md`).
307
308## Step 5 — write the first touch
309
310Read `references/cold-outreach-playbook.md` before writing a single
311message; it has the rules per lane. The ones that matter most:
312
313- The message has one job: a reply. It reads one-to-one. No company
314 intro, no feature list, no calendar link in the first touch.
315- Open with the topic they engage with, never the creator. "Saw you've
316 been in a lot of conversations about <topic>" works; "saw you liked
317 <creator>'s post" reads as surveillance and gets screenshotted back to the
318 creator. For commenters, the substance of what they said
319 (`engagement_text`) is fair game, in your own words; never quote it back
320 or link the post. For authors (`source_kind = 'author'`), their own post
321 is the natural opener ("saw you're hiring your first SDRs"): it's their
322 public statement, not someone else's audience. A generic compliment is
323 worse than nothing.
324- Give before you ask, then ask small. A yes/no question or permission
325 to send something useful, not a meeting.
326- Never invent a detail or a number. Exact figures from their own data,
327 or none at all.
328
329Write each lead's message into `leads.first_message` and tag
330`opener_variant` (`plain`, or `asset` when step 1 set a
331`first_touch_asset`). With an asset, build it for the top-scored leads first
332and offer it in the message: `references/free-resource-first-touch.md`.
333
334Show the user the first five messages (and the first five assets) before
335anything is staged. After they approve the style, show a sample each run.
336
337## Step 6 — stage into the lane, once
338
339Enable only the lane recorded in `icp_profile.lane`. Four lanes:
340
341### HeyReach — LinkedIn sequences
342
343`agnt_tools_enable({ "families": ["connection:heyreach"] })`, then load the
344platform's HeyReach doctrine once:
345`agnt_guidance_load({ "name": "linkedin-outreach" })`. The campaign engine
346owns delivery (timing, acceptance, reply-stop); this skill owns judgment
347(who, what to say, what to do with replies).
348
3491. The user builds the campaign in HeyReach: the sequence, a sender
350 account, created with "Create empty list". Read it before touching it:
351 `connection_heyreach_campaign_get_by_id` for the status,
352 `connection_heyreach_campaign_get_sequence` for the variables each step
353 uses.
3542. Personalization rides on the lead at enrollment and can't be changed
355 afterwards. Pass the whole first message as one custom variable
356 (`customUserFields: [{ "name": "fullMessage", "value": ... }]`) and make
357 the sequence's message step just that variable; also fill every other
358 variable the sequence references, plus `firstName`, `lastName`,
359 `companyName` and `position`. A missing variable falls back to generic
360 copy silently. Write the message to the row, read it back, then enroll.
3613. Stage without starting: `connection_heyreach_list_add_leads_v2` into
362 the campaign's lead list works while the campaign is paused, and nothing
363 sends until the user resumes it. `connection_heyreach_campaign_add_leads_v2`
364 only works on an active campaign and starts delivery for those leads, so
365 use it only when the user has said the campaign is live and wants leads
366 flowing into it.
3674. Leads already in HeyReach are skipped silently: a successful call does
368 not mean every lead was added. Stamp `staged_at` only on rows you can see
369 in the list afterwards.
370
371Two settings that decide whether this works: **send a blank connection
372request** (a request carrying a pitch converts materially worse than an
373empty one), and turn on exclude-in-other-campaigns so one person never
374appears in two sequences.
375
376### Instantly — email sequences
377
378`agnt_tools_enable({ "families": ["connection:instantly"] })`
379
380`connection_instantly_listcampaign` to pick the persona-matched campaign,
381then read its first step to see which variable it expects.
382`connection_instantly_bulkaddleads` with `campaign_id`, the verified-email
383rows only (an unverified address in an Instantly campaign is a bounce
384charged against the sending domain), and `skip_if_in_workspace: true`. The
385first message rides in the lead's `personalization` field or a
386`custom_variables` key that the step references.
387
388### AgentMail — an inbox the agent owns
389
390`agnt_tools_enable({ "families": ["connection:agentmail"] })`
391
392`connection_agentmail_create_inbox` once (its own domain — never the user's
393primary), then `connection_agentmail_create_draft` per lead (`to`,
394`subject`, `text`; never `send_at`) and `connection_agentmail_send_draft`
395when the user releases them. Replies come back through
396`connection_agentmail_list_messages` /
397`connection_agentmail_reply_to_message`, which is what makes this lane the
398one where the agent can actually hold the thread.
399
400### Gmail — the user's own mailbox
401
402Nothing to enable: a connected mailbox is already on. Once the user has
403connected Gmail, `connection_gmail_*` is served on the workspace MCP like
404any connected vendor; `agnt_tools_enable` cannot connect it. Not connected
405yet → ask the user to connect Gmail on their superagnt connections page,
406then re-run `agnt_tools_list_enabled`.
407
408`connection_gmail_save_draft` per lead (`to`, `subject`, `body` as HTML)
409puts each first message in the user's Drafts, where they review and send it
410themselves. That is the default. `connection_gmail_send_email` sends for real
411from the user's own address, gated only by their client's tool approval, so
412call it only on an explicit instruction for that run. For the standing
413version (step 7), wire the same ops onto the deployed agent as flat `tools`
414entries: `{ kind: "integration", provider_id: "gmail", operation_id:
415"save_draft" }` and `"send_email"`, where every send surfaces for approval.
416
417In every lane: stage, do not start. Add the leads, leave the campaign
418stopped, and report what is waiting. Pressing start is the user's act, and
419it is the last point at which a bad list can still be caught.
420
421## Step 7 — put it on autopilot
422
423One batch is not the product. A list built once and never refilled goes
424quiet within weeks: the sequence outruns the leads and the channel looks
425dead. Once a run has worked end to end and the user liked what it staged,
426offer the standing version: the same sweep (steps 2 to 6), run daily or
427every other day. Smaller, more frequent runs keep the leads fresh (people
428who engaged in the last day or two are the warm ones), keep each run cheap,
429and keep the sending account under its limits.
430
431There are two ways to run it. Offer whichever fits the user:
432
433- This client's own scheduler. If the client can schedule a recurring
434 task whose runs have the superagnt tools, set the sweep up there. Each
435 run starts with no chat history, which is fine: the ICP, the leads and
436 the search history are in the workspace database, and the run reads them
437 like step 2 does. Write the task so a fresh session can do it alone: run
438 this skill's sweep for the stored ICP, the lane, the per-run cap, stage
439 and never start, then report. Runs use the user's plan for this client;
440 data calls still spend superagnt data credits. A scheduled run starts with
441 nobody signed in, so check two things before promising it: the client can
442 run unattended on this machine (its own sign-in, such as a long-lived token
443 for Claude Code, or an API key or ChatGPT login for Codex), and something
444 will fire it (the client's own scheduler, cron or launchd). Test one
445 unattended run. Every run reads strangers' posts and comments, nobody is
446 there to approve a tool call, and superagnt doesn't gate tools for a
447 client. So give the scheduled run an allowlist in its client permissions,
448 not a deny list: the `agnt_*` and `data_*` tools in the table under
449 Step 0, the lane's read tool, and its one staging call:
450 `connection_heyreach_list_members_write` (HeyReach),
451 `connection_instantly_leads_write` (Instantly) or
452 `connection_gmail_mail_write` (Gmail drafts). Nothing that sends or starts
453 goes on it: not `connection_gmail_mail_send`,
454 `connection_agentmail_messages_send`, `connection_heyreach_inbox_send`,
455 `connection_heyreach_campaigns_write`,
456 `connection_heyreach_campaign_leads_write` (its add can resume a paused
457 campaign), `connection_instantly_emails_send` or
458 `connection_instantly_campaigns_write`, nor their flat names on a
459 per-operation server. The AgentMail lane stays out of this route: an
460 AgentMail draft created with `send_at` sends itself, and no permission
461 stops that, so run it attended or on the deployed agent. In the test run,
462 check the scheduled run's tool list: none of those tools may appear. If the
463 client can't restrict a scheduled run's tools, use the deployed agent. Have each run report into a table
464 or canvas the user reads, not only a log, and have the next attended
465 session check the last run's `searched_at` so a dead schedule gets
466 noticed. If you are not sure the client can schedule, or that its
467 scheduled runs see the superagnt tools, say so and use the other way.
468- A deployed superagnt agent on a schedule. For when the client can't
469 schedule, runs must happen with the user's machine off, replies should be
470 answered as they arrive, or the volume calls for splitting the work.
471 `references/autopilot-agent-team.md` covers the team shape (who does the
472 judgment, what moves into a data job to cut AI cost, what webhooks
473 trigger) and the ways real deployments of this exact motion have broken.
474 Agent runs spend AI credits.
475
476The reply side is `references/webhook-auto-reply.md` either way: without it
477`leads.replied_at` stays empty, step 2's steering query has nothing to learn
478from, and the engine generates volume without ever getting warmer.
479
480Confirm the cadence and the expected spend with the user before scheduling
481or deploying anything, and hand over the `confirm_url` on any
482`requires_upgrade` (a plan limit, spent data credits, or a trial that
483ended).
484
485## Step 8 — report
486
487From the table, never from memory (`agnt_db_select`): posts scraped, new
488leads, how many cleared the score bar, how many staged and into what, and
489the reply rate per search-term family, per pond and per `opener_variant`.
490Every report carries the score distribution line ("scored N: X qualified, Y
491under the bar, Z gated"), which tells thin sourcing apart from a bar set too
492high; when nothing qualified, list the five closest calls with their reasons
493instead of an empty table. Once replies flow back: replies by intent and the
494median time to answer one. Once a week, run the calibration loop in
495`references/icp-scoring-rubric.md` with the user.
496Name the terms that earned replies and the terms that did not — that
497paragraph is what the next run reads.
498
499## Hard rules
500
501- Stage, never start. No campaign is started, activated or resumed by
502 this skill. `connection_heyreach_campaign_start` and
503 `connection_instantly_activatecampaign` are the user's to call.
504- **Never send from the user's own mailbox without an explicit instruction
505 for that run.** Drafts are the default in the Gmail and AgentMail lanes.
506- Replies to humans are approved. A deployed reply agent sends only what
507 the user approved, unless the user explicitly relaxed a named reply class
508 (`references/webhook-auto-reply.md`).
509- One lane. Enabling all four loads hundreds of tools into the context
510 for no gain, and staging the same person into two channels is how a warm
511 lead becomes a complaint.
512- Never invent. No made-up personalization, numbers or claims about a
513 lead's business, in a message or an asset.
514- The do-not-contact list is absolute. Nobody on it is looked up,
515 staged or messaged, ever. A lead under the score bar never costs an email
516 lookup, an asset or a message.
517- Never scrape a post's engagers twice, and never contact a `linkedin_url`
518 already in `leads`.
519
520<!-- skill_id: warm-outbound-engine · source: https://github.com/superagnt/plugins -->