agency-outbound
Skill6 starsChanged 2 months agoMITView on GitHub
This skill should be used when the user wants to run the Agency Outbound skill, or asks to \"pull the people engaging with a competitor's LinkedIn posts\", \"build a list from a LinkedIn hashtag\", \"enrich these engagers and keep the ones who fit\", \"push the leads into my HeyReach, Instantly, Smartlead or lemlist campaign\", \"how are this workspace's campaigns doing\", or \"build a dashboard I can share\", for outbound run in one superagnt workspace per company. Runs on the superagnt MCP: LinkedIn post and engager data, first-party enrichment, the workspace database, the sequencer connected in this workspace, its synced results and the workspace dashboard. One workspace per chat, named before every write. Leads are staged; starting a campaign is always the human's.
In this repository27 other files it gives its agents.
- attio-crmSkill
- company-enrichmentSkill
- dub-linksSkill
- email-finderSkill
- exa-searchSkill
- facebook-dataSkill
- heyreach-outreachSkill
- instagram-dataSkill
- instantly-outreachSkill
- lead-enrichmentSkill
- linkedin-dataSkill
- reddit-dataSkill
- seo-dataSkill
- social-dataSkill
- tiktok-dataSkill
- web-scrapingSkill
- x-twitter-dataSkill
- youtube-dataSkill
- audience-radarSkill
- competitor-ad-researchSkill
- competitor-mindshareSkill
- gtm-prospecting-deskSkill
- newsletter-autopilotSkill
- shortform-script-engineSkill
- super-seoSkill
- video-multiplierSkill
- warm-outbound-engineSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
17 rules. About 0 are common, 11 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈11
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 48
One workspace per chat: If this chat has more than one superagnt connection, ask which workspace it works in before the first call, and use …
- Distinctive: line 70
If none are listed, wait about 10 seconds and check again.
- Distinctive: line 126
Ask once, in one message, and draft what you can from the company's website so the person only corrects it:
- Very specific: line 138
Create the tables with agnt_db_apply_migration.
- Distinctive: line 162
Start from competitor pages and hashtags.
- Very specific: line 172
Keep a run to 3 to 5 posts across all sources.
- Very specific: line 204
Only for fits, and only when the campaign sends email: data_agnt_people_email_finder from name and domain, then always …
- Distinctive: line 224
Read the campaign first with the vendor's campaign read tool: its status, and every variable its steps use.
- Distinctive: line 229
If a step uses a personal first line, write one per lead from what they engaged with: the topic, never the competitor whose post it was.
- Distinctive: line 317
When nothing fit, list the five closest calls with their reasons.
- Distinctive: line 334
Every workspace sends from its own accounts, so one client's mistake is that client's restriction.
- Very specific: line 346
Email: only rows with email_verified = true go into an email campaign.
- Distinctive: line 358
One workspace per chat, named before every write: No row crosses from one workspace into another.
- Distinctive: line 360
Stage, never start: No campaign is started, activated or resumed by this skill.
- Very specific: line 364
Never write into a synced table: Read them; keep the skill's own state in leads.
- Distinctive: line 366
Never invent: No made-up detail in a first line, and every number in a report or a dashboard comes from a query.
- Very specific: line 368
Never scrape a post's engagers twice, and never push a linkedin_url already staged.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Agency Outbound
Outbound for one company, start to finish inside that company's own superagnt workspace: a list from the people engaging with competitors' LinkedIn posts and with the hashtags its buyers follow, enriched and cut to the ones who fit, pushed into the sequencer the workspace already runs, then the results read back from the workspace's synced tables and turned into a dashboard view whose link you can send, or a report in chat where dashboards aren't on yet.
For the scoring rubric and the outreach playbook in depth, the
warm-outbound-engine skill goes further on each; the base lead-generation
skill covers enrichment and dedup. This skill stands alone without them.
Start here
Three first runs that work as pasted. When the person sends one of these, run it as written, and ask only for what it leaves open: which competitor, which hashtag, which campaign.
- "Pull everyone who reacted to or commented on the last 3 posts on my top competitor's LinkedIn page, keep the ones who fit my ideal customer, and save them to a table in this workspace. Ask me which competitor first."
- "Find this week's most engaged LinkedIn posts under a hashtag my buyers follow, pull the people who commented, keep the ones who fit my ideal customer, and find verified emails for the best 20. Ask me for the hashtag first."
- "Take the people engaging with my top competitor's LinkedIn page this month, keep the ones who fit my ideal customer, and add them to my campaign in this workspace's sequencer, stopped so I can check the list before it starts."
Each one runs steps 1 to 3 at their smallest, and the third adds step 4. On a first run in a workspace, step 1's questions come first, minus whatever the prompt already answers.
The workspace comes first
Each workspace is one company: its own database, sequencer connections, synced results and dashboard, and nothing in it is readable from another workspace. That separation only holds if the agent keeps it:
- One workspace per chat. If this chat has more than one superagnt connection, ask which workspace it works in before the first call, and use only that connection's tools from then on.
- Name the workspace before every write: the tables in step 1, every push
in step 4, a sync in step 5, the publish in step 6. Use the name the person
knows it by: the connection's name when it is named for its workspace (for
example
superagnt-acme), or the name they used in this chat. A baresuperagntconnection doesn't say which workspace it reaches, so when nothing else does, ask. - A second workspace is a second connection, named for its workspace when
it's added (the server name in the MCP config, or the name field in
Claude's Add custom connector dialog), never a second bare
superagnt. On the consent screen the person picks that workspace before approving: it may open on another of their workspaces. - Never move rows between workspaces. A list built in this workspace goes into this workspace's sequencer and dashboard, never another's.
The platform's own rules on this: agnt_guidance_load({ "name": "workspaces" }).
Step 0: tools
Codex: superagnt's tools can take a few seconds to appear in a new chat. If none are listed, wait about 10 seconds and check again.
agnt_tools_list_enabled. If the LinkedIn tools aren't served, turn them on:
agnt_tools_enable({ "families": ["data:linkedin", "database"] }) (on a
grouped server, agnt_tools_write with action: "enable"). Most servers
serve them already, along with the first-party data_agnt_* people and
company tools, the database, vendor sync, the dashboard and the platform's
guidance (agnt_guidance_load). requires_upgrade means a plan limit: give
the person the confirm_url, wait, then retry.
The sequencer's tools appear once the vendor is connected in this workspace;
no enable step connects one. Not connected yet: ask the person to connect it
on its page in this workspace in the superagnt app, with that company's own
key. A sync call for a vendor that isn't connected returns
INTEGRATION_NOT_CONNECTED with a connect_url to that page.
Tool names on grouped servers
This skill writes flat (per-operation) names. New servers list grouped tools
instead: one tool per resource with an action argument. Flat names resolve
there but are not listed, so most agent apps won't let you call them:
| Flat | Grouped tool, action |
data_linkedin_get_company_s_post | data_linkedin_companies: posts |
data_linkedin_get_company_post_comments | data_linkedin_companies: post_comments |
data_linkedin_search_post_by_hashtag | data_linkedin_search: hashtag_posts |
data_linkedin_get_post_reactions | data_linkedin_posts: reactions |
data_linkedin_get_profile_post_comment | data_linkedin_posts: comments |
data_linkedin_get_profile_data_by_url | data_linkedin_profiles: profile_by_url |
data_agnt_companies_enrich | data_agnt_companies: enrich |
data_agnt_people_email_finder / _email_verifier | data_agnt_contacts: find_email / verify_email |
agnt_db_apply_migration / agnt_db_execute_sql | agnt_db_sql: apply_migration / execute_sql |
agnt_db_select | agnt_db_read: select |
agnt_db_insert / agnt_db_upsert / agnt_db_update | agnt_db_write: insert / upsert / update |
agnt_sync_catalog / agnt_sync_list / agnt_sync_status / agnt_sync_preview / agnt_sync_changes | agnt_sync_read: catalog / list / status / preview / changes |
agnt_sync_enable | agnt_sync_write: enable |
agnt_dashboards_guide / agnt_dashboards_describe_data / agnt_dashboards_preview / agnt_dashboards_list_files | agnt_dashboards_read: guide / describe_data / preview / list_files |
agnt_dashboards_apply_template / agnt_dashboards_write_file / agnt_dashboards_publish | agnt_dashboards_write: apply_template / write_file / publish |
agnt_agents_create / agnt_schedules_create | agnt_agents_write: create / agnt_schedules_write: create |
agnt_tools_enable | agnt_tools_write: enable |
agnt_guidance_load | agnt_guidance_read: load |
agnt_tools_list_enabled keeps its name on both. The sequencer calls are in
step 4's table, both ways.
Grouped data calls return a markdown table that drops fields this skill
keys on. Pass response_format: "json" on every LinkedIn call in steps 2
and 3. Posts then carry urn (the comments call takes it) and url (the
reactions call takes it); reactions carry urn (the reactor's member id) and
url (their profile); comments carry author_url (the commenter's profile)
and url, which links the comment itself, never a person. For a field the
json view leaves out, pass response_format: "raw".
Step 1: the buyer and the sources (first run in a workspace)
Ask once, in one message, and draft what you can from the company's website so the person only corrects it:
- who buys: the roles that own the problem, the kind and size of company, the region, and who looks close but never buys;
- the competitors whose LinkedIn company pages its buyers follow: 2 to 5, each
as the page's slug (the part after
linkedin.com/company/); - 2 to 4 hashtags its buyers post and comment under;
- who never to contact: competitors and their staff, current customers, partners, the company's own team;
- which sequencer and campaign the leads go into (step 4).
Create the tables with agnt_db_apply_migration. If they exist from an earlier
run, add missing columns instead of recreating them:
icp_profile(id, company text, icp_description text, target_geography text,
do_not_contact text, qualify_threshold int default 50,
sequencer text, campaign_id text, updated_at timestamptz)
sources(source text unique, kind text, name text, added_at timestamptz)
scraped_posts(post_url unique, post_urn, source text, author text, snippet,
reactions int, comments int, scraped_at timestamptz)
leads(id, person, linkedin_url unique, linkedin_member_urn unique, title,
company, company_domain, location, email, email_verified boolean,
source text, source_post_url, engagement text, engagement_text text,
fit_score int, fit_reason text, status text, campaign_id text,
staged_at timestamptz, replied_at timestamptz, meeting_at timestamptz,
updated_at timestamptz)sources.kind is competitor_page or hashtag. Write the answers with
agnt_db_insert, the do-not-contact list included, before the first scrape:
engager lists are full of competitors' staff.
Step 2: pull the engagers
Start from competitor pages and hashtags. LinkedIn's keyword post search fails often, so use it only when neither source produces, and say so in the report.
- Competitor pages.
data_linkedin_get_company_s_postwith the page'susernamereturns its recent posts. Take the 1 to 3 most engaged from the last couple of months. - Hashtags.
data_linkedin_search_post_by_hashtagwithhashtag(no#). Take the 1 to 3 most engaged recent posts, and skip the ones the competitors or the company itself wrote. - Keep a run to 3 to 5 posts across all sources. That cap is the run's whole
data cost. Skip posts already in
scraped_posts; record the rest as you take them. - Reactors:
data_linkedin_get_post_reactionswith the post'surl. Commenters:data_linkedin_get_company_post_commentswith the post'surnfor a company page's post,data_linkedin_get_profile_post_commentfor a person's post. Take a reactor's profile fromurland member id fromurn, and a commenter's profile fromauthor_url. - Record
engagement(reactedorcommented) andengagement_text(the comment, or the reaction type) on each lead, andsource(the page slug or the hashtag) withsource_post_url. - Drop anyone whose
linkedin_urlorlinkedin_member_urnis already inleads, the competitors' own staff and anyone on the do-not-contact list. Thenagnt_db_upsertthe rest withstatus = 'new'.
Filter on the enriched profile, never on the post payload: post data leaves out the location of plenty of real buyers.
Step 3: enrich, then keep the fits
The score gates the spend: a lead under the bar never costs an email lookup.
data_linkedin_get_profile_data_by_urlfor the real title, company, location and canonical profile URL. Dedupe again on both keys.- Gate: an empty or fake profile, or a location outside the target region,
gets
fit_score = 3with the reason, and stops. data_agnt_companies_enrichfor the domain, size and industry.- Score against
icp_profile.icp_description:fit_score0 to 100 with a two or three sentencefit_reasonthat cites the evidence. Overqualify_threshold(default 50) is a fit. A fit getsstatus = 'fit'; anyone under the bar getsstatus = 'disqualified'and nothing more, ever. - Only for fits, and only when the campaign sends email:
data_agnt_people_email_finderfrom name and domain, then alwaysdata_agnt_people_email_verifier. A LinkedIn-only campaign (HeyReach always, Expandi or La Growth Machine when it sends no email) skips both calls. An unverified email staysemail_verified = falseand never goes into an email campaign. agnt_db_updateeach row as it moves:new, thenfitordisqualified, thenenrichedonce it is ready to push (its email checked, or straight away for a LinkedIn-only campaign). A failed run then resumes instead of starting over.
Above about 500 leads a run, the per-lead work belongs in a data job rather than a longer chat.
Step 4: push the fits to the sequencer
Name the workspace, the sequencer and the campaign, and show the person the first ten rows before the first push. Then stage, never start: the campaign stays stopped, and pressing start is the person's.
- Read the campaign first with the vendor's campaign read tool: its status,
and every variable its steps use. A lead missing a variable gets the
step's fallback text, silently. Fill every one, plus first name, last
name, company and title. For HeyReach, also load the platform's doctrine
once:
agnt_guidance_load({ "name": "linkedin-outreach" }). - If a step uses a personal first line, write one per lead from what they engaged with: the topic, never the competitor whose post it was. Never invent a detail. Show the person five before the push.
- Push only rows with
status = 'enriched'and, for an email campaign,email_verified = true, into a campaign that isn't running:
| Sequencer | Flat | Grouped tool, action | Stays stopped when |
| HeyReach | connection_heyreach_list_add_leads_v2 | connection_heyreach_list_members_write: add_leads | you add to the campaign's lead list; never enroll into a running campaign |
| Instantly | connection_instantly_bulkaddleads | connection_instantly_leads_write: add | the campaign is paused or a draft; pass skip_if_in_workspace: true |
| Smartlead | connection_smartlead_add_campaign_leads | connection_smartlead_leads_write: add | the campaign isn't started; a running one emails new leads on its schedule |
| lemlist | connection_lemlist_add_campaign_lead | connection_lemlist_campaign_leads_write: add | the campaign isn't running and has autoReview off (with it on, an added lead launches at once) |
| EmailBison | connection_emailbison_add_leads_to_campaign | connection_emailbison_campaign_leads_write: add | the campaign is paused; the leads must exist in EmailBison first |
| Expandi | connection_expandi_add_campaign_contact | connection_expandi_campaign_contacts_write: add | the campaign is paused |
| La Growth Machine | connection_lagrowthmachine_upsert_lead | connection_lagrowthmachine_leads_write: upsert | the audience feeds no running campaign |
| Apollo | connection_apollo_io_add_contacts_to_sequence | connection_apollo_io_sequences_write: add_contacts | the sequence is inactive; enroll saved contacts only |
- A vendor can skip a lead silently (already in the list or the campaign).
Stamp
staged_atandcampaign_id, and setstatus = 'staged', only on rows you can see in the vendor afterwards. - Report what is waiting, where, and that nothing sends until the person starts the campaign.
Step 5: read the results from the synced tables
The workspace syncs its sequencer into its own database, so replies, accepts and meetings are a database query: no vendor rate limit and nothing that fails halfway.
agnt_sync_listshows this workspace's syncs. No sync for the sequencer yet:agnt_sync_previewshows the tables and rows it would add against the plan's limits. Name the workspace, get the person's yes, thenagnt_sync_enable. The first backfill takes a while;agnt_sync_statusshows when each stream is current.agnt_sync_cataloglists each stream's table and columns. Read it before writing a query, and never guess a column. The tables are named<vendor>_<stream>, for exampleheyreach_campaign_leads,heyreach_messages,instantly_replies,instantly_campaign_daily_stats,smartlead_campaign_daily_statsand, for meetings,calendly_scheduled_eventsandcalendly_invitees.- Query with
agnt_db_execute_sql(read_only: true), filtered to_deleted_at is null, and join the synced rows toleadson the LinkedIn URL or the email. Write what you learn (replied_at,meeting_at,status) intoleads, the skill's own table. Never write into a synced table: the sync owns it and rewrites it. - A later run reads only what changed:
agnt_sync_changeswith the cursor the last run stored, kept in async_cursorstable with one row per stream.
For the sync's own rules: agnt_guidance_load({ "name": "sync" }).
Step 6: build the dashboard and share its view link
Each workspace has one dashboard, and its views query this workspace's
database, so the view you build here shows this company's numbers and no
one else's. No agnt_dashboards_* tools listed? The dashboard isn't on for
this workspace yet: report from the tables in the chat instead, and don't
build a share link some other way.
agnt_dashboards_guideonce per chat, before the first write. Thenagnt_dashboards_describe_datafor the tables and their columns.- One view for the outbound: leads staged, accepts or opens, replies,
interested replies and meetings booked, each compared with the period
before; replies per day by channel; and a table of the interested replies
(person, title, company, campaign, date). Build it with
agnt_dashboards_apply_templatefrom a template the guide lists, oragnt_dashboards_write_file. agnt_dashboards_previewand read the report: every query returns rows and none errors. Then name the workspace andagnt_dashboards_publishwith a plain summary.- A view only goes public if it reads no platform data (credits, usage), has no images, lists every table's columns, has no column named like a secret and no web address written in its text. Keep emails and phone numbers out of a view you'll share: anyone with the link can open it.
- You can't make the link, and no tool reads it back. Give the person the
view's
open_in_app_urlfrom the publish result (a page in the superagnt app, for people in the workspace, never the link to send), and ask them to press Share there and copy the link the dialog shows: that is the link to send. It shows live numbers, read only, with no login. Afterwardsagnt_dashboards_list_filesshowspublic_linkon the view, which says a link is live and since when but carries no URL.
Step 7: the report
From the tables, never from memory: posts scraped and from which sources, new leads, how many fit, how many staged and into which campaign, and once results sync back, replies, interested replies and meetings, by source. Every report carries the score line ("scored N: X fit, Y under the bar, Z gated"). When nothing fit, list the five closest calls with their reasons. Name the sources that produced fits and the ones that didn't: the next run reads that paragraph.
Once a run has worked end to end, offer the standing version: the same
sweep (steps 2 to 5) every day or two, for this workspace only. Run it on
this agent's own scheduler when its scheduled runs see the superagnt tools,
or as a deployed superagnt agent (agnt_agents_create, then
agnt_schedules_create; enable the lifecycle and schedules families
first if they aren't listed). A scheduled run has nobody to approve a tool
call, so give it an allowlist: this skill's reads and its one staging call,
and nothing that starts, activates or resumes a campaign or sends a message.
Confirm the cadence and the expected data spend with the person before
scheduling anything.
Account safety and deliverability
Every workspace sends from its own accounts, so one client's mistake is that client's restriction. Check these before the first push into a workspace, and say which one a request would break:
- LinkedIn senders: about 200 connection requests a week per account is the ceiling, and an account that hasn't been sending gets locked if it suddenly sends 100 or more. Ramp a new sender slowly and leave the daily numbers to the sequencer's per-account limits. Withdraw pending requests after about 10 days.
- Never rented or "avatar" profiles, stacked or resold Sales Navigator seats, or tools that get around LinkedIn's caps. A restriction costs the client their network.
- Email: only rows with
email_verified = truego into an email campaign. The first email carries no link, no emojis and no word "free", and stays short: 35 to 80 words. - Every follow-up adds something new, never "just bumping this", and any reply stops the sequence.
These are the warm-outbound-engine skill's outreach playbook rules,
distilled from practitioners' videos and posts and our own production
outbound; that file has the sources.
Hard rules
- One workspace per chat, named before every write. No row crosses from one workspace into another.
- Stage, never start. No campaign is started, activated or resumed by this skill. That is the person's call, in the vendor's app.
- The do-not-contact list is absolute. Nobody on it is looked up, staged or messaged. A lead under the bar never costs an email lookup.
- Never write into a synced table. Read them; keep the skill's own state
in
leads. - Never invent. No made-up detail in a first line, and every number in a report or a dashboard comes from a query.
- Never scrape a post's engagers twice, and never push a
linkedin_urlalready staged.
<!-- skill_id: agency-outbound · source: https://github.com/superagnt/plugins -->Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

