Agent-Driven LinkedIn Outreach
Build an agent that reads enrichment data, researches a prospect’s LinkedIn activity, crafts a message that references their recent work, and sends it via the team member with the closest connection.
Overview
Generic LinkedIn messages get ignored. The ones that work reference something specific—a recent post, a shared connection, a role change. An agent can do this research in seconds and craft a message that feels genuinely personal.
This guide shows how to build a LinkedIn outreach agent that pulls enrichment data, researches the prospect’s LinkedIn profile, generates a personalised message, sends it through the right team member, and tracks the conversation.
Step 1: Get Enrichment Data
Start from an existing enrichment. This gives you the prospect’s name, title, company, email, LinkedIn URL, and more.
# fetch existing enrichment data
import requests
API_KEY = "abm_live_..."
BASE = "https://api.abm.dev/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def get_enrichment(enrichment_id: str) -> dict:
r = requests.get(
f"{BASE}/enrichments/{enrichment_id}",
headers=HEADERS,
)
r.raise_for_status()
return r.json()
# example response
# {
# "id": "enr_abc123",
# "full_name": "Sarah Chen",
# "title": "VP of Revenue",
# "company": "Acme Corp",
# "email": "sarah@acme.com",
# "linkedin_id": "sarah-chen-12345",
# "confidence": 0.95
# }
Step 2: Deep LinkedIn Research
Pull the full LinkedIn profile: headline, summary, experience, recent posts, mutual connections. This is the raw material your agent uses to personalise.
# deep LinkedIn profile lookup
def get_linkedin_profile(linkedin_id: str) -> dict:
r = requests.get(
f"{BASE}/linkedin/profile/{linkedin_id}",
headers=HEADERS,
)
r.raise_for_status()
return r.json()
# example response
# {
# "headline": "VP Revenue @ Acme | Ex-Stripe | PLG enthusiast",
# "summary": "Building the next generation of B2B sales...",
# "experience": [
# {"title": "VP of Revenue", "company": "Acme Corp", "years": 2},
# {"title": "Director Sales", "company": "Stripe", "years": 3}
# ],
# "recent_posts": [
# {"text": "Just shipped our new PLG motion...", "likes": 142},
# {"text": "Hot take: outbound is not dead...", "likes": 89}
# ],
# "mutual_connections": 3
# }
Step 3: Craft the Message
Feed the enrichment and LinkedIn data into a prompt template. The key is referencing something specific—a recent post, a role change, a shared interest.
# prompt template for outreach message
OUTREACH_PROMPT = """
Write a LinkedIn connection request message.
Prospect:
- Name: {name}
- Title: {title} at {company}
- Headline: {headline}
- Recent post: "{recent_post}"
- Previous company: {prev_company}
Rules:
1. Open by referencing their recent post (be specific)
2. Connect it to a shared interest or challenge
3. End with a low-commitment ask (no pitch)
4. Under 280 characters total
5. Sound like a real person, not a sales bot
"""
def craft_message(enrichment: dict, profile: dict) -> str:
recent = profile.get("recent_posts", [])[0]
prev = profile.get("experience", [])[-1] if len(profile.get("experience", [])) > 1 else {}
prompt = OUTREACH_PROMPT.format(
name=enrichment["full_name"],
title=enrichment["title"],
company=enrichment["company"],
headline=profile.get("headline", ""),
recent_post=recent.get("text", "N/A"),
prev_company=prev.get("company", "N/A"),
)
return call_llm(prompt)
Example Output
“Sarah, your post on shipping PLG at Acme really resonated—we’re navigating the same shift from outbound-first. Would love to hear how you’re measuring the transition. Open to connecting?”
Step 4: Send the Message
Send via the LinkedIn messages API. The key feature: multi-account routing. The API automatically picks the team member who has the closest connection to the prospect—mutual connections, shared groups, or second-degree proximity.
# send via closest team connection
def send_message(linkedin_id: str, text: str) -> dict:
r = requests.post(
f"{BASE}/linkedin/messages",
headers=HEADERS,
json={
"recipient_linkedin_id": linkedin_id,
"text": text,
# optional: force a specific sender
# "sender_account_id": "acc_xyz",
},
)
r.raise_for_status()
return r.json()
# response includes which team member sent it
# {
# "message_id": "msg_789",
# "sent_via": "Alex Kim (3 mutual connections)",
# "status": "delivered"
# }
Step 5: Track Conversations
After sending, monitor replies so your agent can follow up. The conversations endpoint returns all messages in a thread.
# track conversations for follow-up
def check_conversations(linkedin_id: str) -> list:
r = requests.get(
f"{BASE}/linkedin/conversations",
headers=HEADERS,
params={"participant": linkedin_id},
)
r.raise_for_status()
return r.json()["conversations"]
# check for replies and auto-follow-up
def follow_up_if_needed(enrichment_id: str, linkedin_id: str, days_wait: int = 3):
convos = check_conversations(linkedin_id)
if not convos:
print(f"No reply yet — scheduling follow-up in {days_wait} days")
return "pending"
latest = convos[0]
if latest["last_message"]["sender"] == "prospect":
print(f"Got a reply: {latest['last_message']['text'][:50]}")
return "replied"
return "waiting"
Best Practices
Message Length
Keep connection requests under 280 characters. InMail can be longer (up to 1,900) but shorter messages consistently outperform. Aim for 2-3 sentences max.
Personalisation Quality
Reference something from the last 30 days—a recent post, a job change, a company announcement. Generic compliments (“I’m impressed by your background”) don’t count. The prospect should feel like you actually read their profile.
Timing
Send messages Tuesday through Thursday, 8-10am in the prospect’s local timezone. Monday mornings and Friday afternoons have the lowest response rates.
Follow-up Cadence
Wait 3-5 business days before following up. Your follow-up should add new value—share an article, reference a new post, or mention a relevant event. Never just “bumping this up.” After 2 follow-ups with no reply, move on.
Multi-Account Strategy
Connect multiple team members’ LinkedIn accounts. The API routes each message through the person with the strongest connection to the prospect. This dramatically increases acceptance rates compared to cold outreach from a single account.
Ready to send smarter LinkedIn messages? Start with the enrichment API to build your prospect list, then wire up the outreach flow.