AgentStack
SKILL verified MIT Self-run

Linkedin Comment To Outreach Gsheets

skill-felipesalinasr-fsr-stack-linkedin-comment-to-outreach-gsheets · by felipesalinasr

Use when the user provides a LinkedIn post URL and wants to run the full pipeline from commenters to a live cold outreach campaign on Instantly, storing leads in Google Sheets instead of Airtable. Triggers on phrases like "linkedin comment to outreach gsheets", "scrape and email with sheets", "linkedin pipeline google sheets", or any request combining LinkedIn post scraping with cold email outrea…

No reviews yet
0 installs
17 views
0.0% view→install

Install

$ agentstack add skill-felipesalinasr-fsr-stack-linkedin-comment-to-outreach-gsheets

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Linkedin Comment To Outreach Gsheets? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

LinkedIn Comment to Outreach Pipeline (Google Sheets)

Overview

End-to-end orchestrator: LinkedIn post URL in, paused Instantly cold email campaign out. Leads are stored in Google Sheets (not Airtable), enriched via Apollo + Hunter.io fallback, sequenced, and loaded into Instantly. Every external tool call goes through Composio first when available. Every phase maximizes parallelism to finish faster without cutting corners.

One key difference from the Airtable version: Google Sheets can write hundreds of rows in a single API call, so Phase 2 collapses from "4 parallel batch-loading agents" to one bulk write. That time savings compounds.

Another difference: when total enrichment (Apollo + Hunter.io) returns fewer than 20 leads with verified emails, the sequence-writing phase runs on autopilot -- no interactive review, just write all 3 emails and move straight to Instantly. For 20+ leads, the user co-writes each email as usual.

Hunter.io fallback: After Apollo enrichment, contacts that matched a person but have no email are sent to Hunter.io's Email Finder in parallel (8 concurrent threads). This typically recovers 10-20% of the missing emails, boosting total email coverage by 5-15 percentage points. Only contacts with company info are searched -- Hunter requires a company or domain to work.

When to use

  • User provides a LinkedIn post URL and wants to reach out to its commenters via Google Sheets
  • User asks to "run the linkedin pipeline with sheets", "scrape and email", or "linkedin comment to outreach gsheets"
  • User wants the complete path from a LinkedIn post to a live Instantly campaign, stored in Google Sheets
  • User needs commenters scraped, stored in Sheets, enriched with Apollo, sequenced, and loaded into Instantly in one flow

When NOT to use

  • Targeting people who reacted to a post (use linkedin-reaction-to-outreach instead)
  • User specifically wants Airtable (use linkedin-comment-to-outreach instead)
  • Just scraping commenters with no outreach (use linkedin-comment-scraper directly)
  • Just enriching an existing list (use apollo-enrichment directly)
  • Just writing a cold email sequence without a lead source (use cold-email-sequence directly)
  • Just creating an Instantly campaign from leads you already have (use instantly-campaign directly)

Prerequisites

  • Composio toolkits connected: googlesheets, apollo, hunter, instantly
  • Apify MCP server: Connected with mcp__apify__* tools available (Composio fallback: APIFY_RUN_ACTOR_SYNC)
  • User input needed for Phase 4 (if 20+ leads): Social proof, product description, CTA preferences, sender name

Before starting, verify each Composio toolkit is connected. If any is missing, use composio link --no-wait and present the redirect URL to the user. Do all missing connections in parallel -- don't chain them sequentially.

Core Principles

1. Composio First

For every external tool call, try the Composio tool slug first. Fall back to MCP or direct API only if Composio fails or the toolkit isn't connected. This applies universally:

| Service | Composio slug (primary) | Fallback | |---------|------------------------|----------| | Apify | APIFY_RUN_ACTOR_SYNC | mcp__apify__call-actor | | Google Sheets | GOOGLESHEETS_* (see slug table below) | None -- Sheets IS the chosen platform | | Apollo | APOLLO_PEOPLE_ENRICHMENT (parallel single calls) | APOLLO_BULK_PEOPLE_ENRICHMENT (less reliable) | | Hunter.io | HUNTER_EMAIL_FINDER (parallel single calls) | None -- Hunter IS the fallback | | Instantly | INSTANTLY_* (see slug table below) | Direct REST API via curl |

2. Maximum Parallelism

Within every phase, run independent operations concurrently. Between phases, pre-fetch data for the next phase whenever possible. Specific opportunities are called out in each phase below.

3. Auto-Mode for Small Lists

If Apollo enrichment returns fewer than 20 leads with verified emails, skip the interactive email-writing review. Write all 3 emails using the James Shields framework and the context gathered in Phase 1, then proceed directly to Instantly. The user reviews the final campaign in Instantly's dashboard instead of approving each email in chat.

The Pipeline

LinkedIn Post URL
       |
       v
[Phase 1: SCRAPE]           -- Apify via MCP or Composio, deduplicate
       |
       v
[Phase 2: STORE]            -- Create Google Sheet, bulk write all rows
       |
       v
[Phase 3: ENRICH (Apollo)]  -- Apollo parallel single enrichment, batch-update Sheet
       |                       (pre-fetch Instantly accounts here)
       v
[Phase 3.5: ENRICH (Hunter)]-- Hunter.io fallback for contacts Apollo missed emails on
       |                       (parallel single calls, 8 threads, update Sheet)
       v
[Phase 4: WRITE]            -- Co-write 3 emails (or auto-write if = 50` to avoid low-confidence guesses.

Name splitting: same rule as Apollo -- split `fullName` on the FIRST space only.

**Why parallel single calls:** Same rationale as Apollo. Hunter has no reliable bulk endpoint via Composio. Individual calls at 8 concurrent threads give the best throughput and visibility into per-person results.

Expected recovery rates:
- 10-20% of Apollo no-email contacts will get an email from Hunter
- Overall pipeline email coverage typically jumps 5-15 percentage points
- LATAM/emerging market recovery rates may be lower (5-10%)

Collect results into `./output/hunter_results.json`:
```json
{
  "idx": 15,
  "row": 17,
  "fullName": "Jane Smith",
  "email": "jane@company.com",
  "score": 85,
  "source": "hunter",
  "profileUrl": "https://www.linkedin.com/in/janesmith"
}

Step 3: Merge results and update Sheet + Apollo

3a -- Merge into apollo_contacts.json: For each Hunter result with an email, update the corresponding record in ./output/apollo_contacts.json to add the email (mark email_source: "hunter" to distinguish from Apollo-found emails).

3b -- Batch-update Google Sheet: Same pattern as Phase 3 Step 3 C1. For each newly found email, update:

  • J (Lead Status) = "Enriched (Hunter)"
  • K (Email) = Hunter email

Use GOOGLESHEETS_UPDATE_VALUES_BATCH with camelCase valueInputOption.

3c -- Create Apollo contacts for new emails: Add newly found contacts to Apollo using APOLLO_CREATE_BULK_CONTACTS (max 100 per batch, no label_names at root level -- put inside each contact object).

Run 3b and 3c in parallel.

Step 4: Final count

Re-count total emails (Apollo + Hunter). This combined count determines whether Phase 4 runs in auto-mode or interactive mode.

Checkpoint: "Hunter.io recovered X additional emails. Total: Y out of Z people now have emails (W% combined match rate). Y contacts ready for outreach."


Phase 4: Write Email Sequence

Auto-mode (fewer than 20 leads with emails)

Small lists don't justify a multi-round interactive review. Write all 3 emails yourself using the James Shields framework:

  1. Gather context from earlier phases: the trigger (the LinkedIn post), the author, the audience profile (from headlines/companies), and any product/offer info from the sales context.
  2. If product, social proof, and offer are not in the sales context, ask the user ONE consolidated question with all missing items.
  3. Write all 3 emails (Day 0 opener, Day 3 follow-up, Day 7 breakup) following the rules in cold-email-sequence.
  4. Save to {campaign-name}-sequence.md.
  5. Show the user the complete sequence and say: "Wrote 3 emails for your X leads. Loading into Instantly now. You can tweak the copy in the Instantly dashboard before activating."

Interactive mode (20+ leads with emails)

Follow the cold-email-sequence skill procedure exactly: gather inputs, draft Email 1, get approval, draft Email 2, get approval, draft Email 3, get approval, save.

James Shields rules (both modes):

  • Personalize subject (not body), lowercase, 2-4 words
  • 3 sentences + PS per email
  • Low-friction CTA ("Reply 'I'm in'"), never a booking link
  • No em dashes, no exclamation points, no formatting
  • NEW social proof for each email (never repeat)
  • Follow-up subjects: empty string (same thread)

Checkpoint (interactive only): "Sequence locked. Ready to load into Instantly."


Phase 5: Launch on Instantly

Step 1: Resolve credentials

Try Composio first:

  1. Is the instantly Composio toolkit connected? (If you already ran INSTANTLY_LIST_ACCOUNTS in Phase 3, you know the answer.)
  2. If not connected, use composio link instantly --no-wait and present the redirect URL.
  3. If Composio unavailable, check INSTANTLY_API_KEY env var.
  4. Last resort: ask for the API key in chat (ephemeral, session-only, never written to disk).

Step 2: Create campaign

Sanitize ALL email bodies first -- replace every & character with "and" (Instantly silently drops bodies containing &).

CRITICAL Composio/Instantly field notes:

  • Do NOT include delay_unit in sequence steps -- the Composio schema doesn't support it. The delay field is already in days.
  • Timezone: pick one appropriate for the audience. Not all IANA timezone strings work with Composio's Instantly integration. Known working: America/New_York, America/Chicago, America/Denver, America/Los_Angeles, America/Bogota, America/Sao_Paulo, Europe/London, Europe/Berlin. Known rejected: America/Vancouver. When in doubt, use the major city timezone closest to the audience.
  • Follow-up email subjects: use empty string "" to thread under the original subject.
composio execute INSTANTLY_CREATE_CAMPAIGN -d '{
  "name": "LinkedIn - {Author} {Topic}",
  "campaign_schedule": {
    "schedules": [{
      "name": "Weekday Schedule",
      "timing": { "from": "08:00", "to": "17:00" },
      "days": { "0": true, "1": true, "2": true, "3": true, "4": true, "5": false, "6": false },
      "timezone": "America/New_York"
    }]
  },
  "sequences": [{
    "steps": [
      { "type": "email", "delay": 0,
        "variants": [{ "subject": "subject line", "body": "Email 1 body" }] },
      { "type": "email", "delay": 3,
        "variants": [{ "subject": "", "body": "Email 2 body" }] },
      { "type": "email", "delay": 4,
        "variants": [{ "subject": "", "body": "Email 3 body" }] }
    ]
  }],
  "email_list": ["sender@domain.com"],
  "stop_on_reply": true,
  "text_only": true,
  "link_tracking": false,
  "open_tracking": true,
  "daily_limit": 50,
  "daily_max_leads": 50,
  "email_gap": 5,
  "random_wait_max": 10
}'

Delay math: Delays are RELATIVE to the previous step, not absolute from Day 0. So for Day 0 / Day 3 / Day 7: step 1 delay=0, step 2 delay=3, step 3 delay=4 (because 3+4=7).

Save the returned campaign id. Verify all step bodies are non-empty (ampersand check).

Step 2.5: Verify the campaign was created correctly

composio execute INSTANTLY_GET_CAMPAIGN -d '{
  "id": "{CAMPAIGN_ID}"
}'

CRITICAL: This endpoint uses id as the parameter name, NOT campaign_id. Check that all 3 email step bodies are populated and the schedule/timezone look correct.

Parallel block D (run together):

D1 -- Bulk load leads:

composio execute INSTANTLY_ADD_LEADS_BULK -d '{
  "campaign_id": "{CAMPAIGN_ID}",
  "leads": [
    { "email": "j@co.com", "first_name": "Jane", "last_name": "Smith", "company_name": "Co" },
    ...
  ]
}'

Max 1000 leads per call. Check leads_uploaded and invalid_email_count.

D2 -- Attach all sending accounts: If the campaign was created with only one account, patch to include all:

composio execute INSTANTLY_UPDATE_CAMPAIGN -d '{
  "campaign_id": "{CAMPAIGN_ID}",
  "email_list": ["email1@...", "email2@...", "email3@..."]
}'

Step 3: Leave paused

NEVER auto-activate. Campaign starts paused.

Final report to user:

  • Campaign name and status (paused)
  • Number of leads loaded
  • Sending accounts attached
  • Schedule: Mon-Fri, 8:00-17:00 in the audience's timezone
  • Google Sheet URL for reference
  • "Review in Instantly dashboard. Say 'go' when ready to launch."

To activate when user approves:

composio execute INSTANTLY_ACTIVATE_CAMPAIGN -d '{ "campaign_id": "{CAMPAIGN_ID}" }'

Composio Tool Slug Reference

| Service | Action | Composio slug | Key gotchas | |---------|--------|---------------|-------------| | Apify | Get actor info | APIFY_GET_ACTOR | | | Apify | Run actor (sync) | APIFY_RUN_ACTOR_SYNC | Input field is posts not urls | | Sheets | Create spreadsheet | GOOGLESHEETS_CREATE_GOOGLE_SHEET1 | | | Sheets | Get tab names | GOOGLESHEETS_GET_SHEET_NAMES | Always call after create -- tab name is locale-dependent | | Sheets | Write/update values | GOOGLESHEETS_VALUES_UPDATE | Uses snake_case: value_input_option | | Sheets | Batch update | GOOGLESHEETS_UPDATE_VALUES_BATCH | Uses camelCase: valueInputOption | | Sheets | Append rows | GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND | | | Sheets | Read values | GOOGLESHEETS_VALUES_GET | | | Apollo | Single enrichment | APOLLO_PEOPLE_ENRICHMENT | Preferred. Uses organization_name. No reveal_phone_number. | | Apollo | Bulk enrichment | APOLLO_BULK_PEOPLE_ENRICHMENT | Unreliable in parallel -- use single instead. Uses company_name. | | Apollo | Bulk create contacts | APOLLO_CREATE_BULK_CONTACTS | | | Apollo | Check credits | APOLLO_VIEW_API_USAGE_STATS | | | Hunter | Email finder | HUNTER_EMAIL_FINDER | Uses company (not domain) when only name known. max_duration 3-20s. Score >= 50 to accept. | | Hunter | Check credits | HUNTER_ACCOUNT_INFORMATION | searches.available/used for quota. | | Instantly | List accounts | INSTANTLY_LIST_ACCOUNTS | | | Instantly | Get campaign | INSTANTLY_GET_CAMPAIGN | Uses id (NOT campaign_id) | | Instantly | Create campaign | INSTANTLY_CREATE_CAMPAIGN | No delay_unit in steps. Timezone must be a major city. | | Instantly | Update campaign | INSTANTLY_UPDATE_CAMPAIGN | | | Instantly | Bulk add leads | INSTANTLY_ADD_LEADS_BULK | | | Instantly | Activate campaign | INSTANTLY_ACTIVATE_CAMPAIGN | |


Timing Expectations

| Phase | Duration | Notes | |-------|----------|-------| | 1. Scrape | 2-5 min | MCP Apify handles async polling automatically | | 2. Store | ~30 sec | One bulk write -- include tab name discovery | | 3. Enrich (Apollo) | 5-15 min | 8 concurrent single Apollo calls. Sheet batch-update adds ~30s. | | 3.5. Enrich (Hunter) | 3-8 min | 8 concurrent Hunter calls on Apollo no-email contacts. Typically 10-20% recovery. | | 4. Write | 1-2 min (auto) / 10-30 min (interactive) | Auto-mode skips interactive review for small lists | | 5. Launch | 1-3 min | Instantly accounts pre-fetched during Phase 3 | | Total | ~13-30 min (auto) / ~23-60 min (interactive) | |


Common Mistakes

| Phase | Failure mode | Fix | |-------|--------------|-----| | 1 | Zero results: used urls instead of posts | Apify actor field is posts. Always check actor input schema. | | 1 | Only 20 results returned | Actor defaults maxItems to 20. Always override to 1000. | | 1 | Field names don't match expected | Actor returns name/position/linkedinUrl, not fullName/headline/profileUrl. Map them. | | 1 | Invalid LinkedIn URL | Validate format, resolve redirects via WebFetch. | | 2 | "Range not found" error | Tab name is locale-dependent ("Hoja 1", "Feuille 1", etc.). Discover with GOOGLESHEETS_GET_SHEET_NAMES. | | 2 | value_input_option error | GOOGLESHEETS_VALUES_UPDATE uses snake_case. GOOGLESHEETS_UPDATE_VALUES_BATCH uses camelCase. They're inconsistent. | | 2 | values rejected | Must be a native JSON array, not a stringified array. | | 2 | 403 on sheet creation | Google Sheets Composio scopes may be insufficient. Re-link with composio link googlesheets --no-wait. | | 2 | Values array not rectangular | Pad empty cells with "". Every row must have 15 columns. | | 3 | Bulk Apollo returns 0 matches

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.