<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Kamal</title>
    <description>The latest articles on Forem by Kamal (@kamskans).</description>
    <link>https://forem.com/kamskans</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3526397%2F717fe3e6-61a9-4caf-b80f-b6add599783d.jpeg</url>
      <title>Forem: Kamal</title>
      <link>https://forem.com/kamskans</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kamskans"/>
    <language>en</language>
    <item>
      <title>Build an Intent-Based LinkedIn Outreach System in an Afternoon</title>
      <dc:creator>Kamal</dc:creator>
      <pubDate>Wed, 06 May 2026 18:21:23 +0000</pubDate>
      <link>https://forem.com/kamskans/build-an-intent-based-linkedin-outreach-system-in-an-afternoon-d95</link>
      <guid>https://forem.com/kamskans/build-an-intent-based-linkedin-outreach-system-in-an-afternoon-d95</guid>
      <description>&lt;p&gt;I got tired of cold-outreach tools that scrape lists and spray sequences. The reply rates are awful (~0.3% on cold), the recipients hate it, and as a builder it feels like the wrong shape of problem.&lt;/p&gt;

&lt;p&gt;So I built the inverse: a system that &lt;strong&gt;waits for someone to signal they're in-market&lt;/strong&gt; — a comment on a competitor's launch post, a job change, a public post saying "open to senior backend roles" — and fires a webhook the moment a match shows up. Your agent reaches out within hours, while the context is still fresh.&lt;/p&gt;

&lt;p&gt;This tutorial walks through building the whole loop in an afternoon, on top of the &lt;a href="https://github.com/kamskans/myagentmail-outreach-starter" rel="noopener noreferrer"&gt;myagentmail-outreach-starter&lt;/a&gt; (MIT-licensed). I'll be honest upfront: the starter calls a paid backend (the one I built — &lt;a href="https://myagentmail.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=intent-outreach-tutorial" rel="noopener noreferrer"&gt;myagentmail&lt;/a&gt;). The repo's code is yours forever; running it end-to-end uses an API key (7-day free trial, no card). Same shape as Stripe sample apps or Resend examples.&lt;/p&gt;

&lt;p&gt;Here's what we're building:&lt;/p&gt;

&lt;p&gt;Most outreach is broken because it ignores intent. You build a list, you blast a sequence, and you hope. The reply rate is whatever it is.&lt;/p&gt;

&lt;p&gt;There's a more useful version of outreach: wait for someone to &lt;em&gt;signal&lt;/em&gt; that they're in-market, then reach out within hours, while the context is still fresh. The signal might be a complaint about their current vendor, a comment on a competitor's launch post, or a job change into a role with budget. Each of those is a moment where a thoughtful, specific connection note has 5–20× the response rate of a cold blast.&lt;/p&gt;

&lt;p&gt;This tutorial walks through building that system — the whole loop, from signal capture to drafted message in your approval queue — on top of the &lt;a href="https://github.com/kamskans/myagentmail-outreach-starter" rel="noopener noreferrer"&gt;myagentmail-outreach-starter&lt;/a&gt; repo. About an afternoon's work, three accounts (myagentmail, OpenAI, your LinkedIn), zero infrastructure to operate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you're building
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────┐    ┌──────────────────┐    ┌──────────────────┐
│   LinkedIn       │    │   MyAgentMail    │    │  Your starter    │
│   - posts        │───▶│   - polls every  │───▶│   - HMAC-verify  │
│   - reactions    │    │     N hours      │    │   - GPT drafts   │
│   - comments     │    │   - LLM filters  │    │     a note       │
│   - job changes  │    │     by your rule │    │   - queues for   │
│                  │    │   - HMAC webhook │    │     approval     │
└──────────────────┘    └──────────────────┘    └─────────┬────────┘
                                                          │
                                                          ▼
                                                ┌──────────────────┐
                                                │  You: 1-click    │
                                                │  Approve →       │
                                                │  Connection sent │
                                                └──────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things make this loop different from generic outreach tools:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Three signal types&lt;/strong&gt;, not just keyword search. Watch for posts matching a phrase (&lt;em&gt;keyword&lt;/em&gt;), watch for engagers on a specific person or company's posts (&lt;em&gt;engagement&lt;/em&gt;), watch for job changes on a list of profiles (&lt;em&gt;watchlist&lt;/em&gt;). All three feed the same approval queue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A plain-English firing rule&lt;/strong&gt; decides what fires, not just the keyword. The classifier treats your rule as authoritative — &lt;em&gt;"flag founders complaining about cold email; skip vendors selling outbound tools, agencies, content marketers"&lt;/em&gt; — and cites a one-sentence reason on every match.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your real LinkedIn account does the work.&lt;/strong&gt; No proxy farms, no rotating residential IPs, no shared scraping pools. Connection requests arrive from your actual profile, which is what makes the response rate non-trivial in the first place.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setup — three accounts, ten minutes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Sign up at myagentmail.com&lt;/td&gt;
&lt;td&gt;&lt;a href="https://myagentmail.com?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=intent-outreach-tutorial" rel="noopener noreferrer"&gt;https://myagentmail.com?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=intent-outreach-tutorial&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Free signup.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Subscribe to the LinkedIn add-on&lt;/td&gt;
&lt;td&gt;Dashboard → Billing&lt;/td&gt;
&lt;td&gt;Solo tier ($29/mo) is enough for this tutorial.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Grab your master API key&lt;/td&gt;
&lt;td&gt;Dashboard → API Keys&lt;/td&gt;
&lt;td&gt;One key authorizes inbox, LinkedIn, and signals.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Get an OpenAI key&lt;/td&gt;
&lt;td&gt;platform.openai.com&lt;/td&gt;
&lt;td&gt;The starter uses &lt;code&gt;gpt-4o-mini&lt;/code&gt; to draft notes — pennies a day.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Clone the starter&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git clone https://github.com/kamskans/myagentmail-outreach-starter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~3,000 lines of TypeScript. Fork it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Connect a LinkedIn account&lt;/td&gt;
&lt;td&gt;The starter's &lt;code&gt;/accounts&lt;/code&gt; page&lt;/td&gt;
&lt;td&gt;Either email + password (PIN or mobile-app push) or paste your &lt;code&gt;li_at&lt;/code&gt; + &lt;code&gt;JSESSIONID&lt;/code&gt; cookies. AES-256-GCM encrypted at rest.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Configure your &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;MYAGENTMAIL_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tk_...
&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk-...
&lt;span class="c"&gt;# Webhook secret comes after you create your first signal, in the next section.&lt;/span&gt;
&lt;span class="nv"&gt;MYAGENTMAIL_WEBHOOK_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;npm install &amp;amp;&amp;amp; npm run dev&lt;/code&gt;. The starter runs on &lt;code&gt;localhost:3000&lt;/code&gt;. Use &lt;a href="https://ngrok.com" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt; or &lt;a href="https://github.com/cloudflare/cloudflared" rel="noopener noreferrer"&gt;cloudflared&lt;/a&gt; to expose your laptop to public webhooks if you want signals to fire from the cloud cron — or use the &lt;strong&gt;Run now&lt;/strong&gt; button on each signal to trigger polls manually while you develop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick the right signal kind for what you're tracking
&lt;/h2&gt;

&lt;p&gt;The single most important decision in setting this up is which kind of signal matches your use case. Here's the table I'd want pinned to my desk on day one:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;If you want to find…&lt;/th&gt;
&lt;th&gt;Use this signal kind&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;People publicly describing a pain you solve&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Keyword&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;"outbound is broken"&lt;/em&gt;, &lt;em&gt;"our CRM is killing us"&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ICPs warming up to a competitor&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Engagement&lt;/strong&gt; on the competitor's company page&lt;/td&gt;
&lt;td&gt;Watch &lt;a href="https://www.linkedin.com/company/myagentmail/" rel="noopener noreferrer"&gt;&lt;code&gt;linkedin.com/company/competitor/&lt;/code&gt;&lt;/a&gt; → fire on every commenter who's a head of sales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ICPs your champion influencer's audience&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Engagement&lt;/strong&gt; on a profile&lt;/td&gt;
&lt;td&gt;Watch a niche author whose audience IS your ICP → fire on engagers matching role/seniority&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Champions who just got budget at a new company&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Watchlist&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;List of 30 past customers' profile URLs → fire when their new role matches an ICP company&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Engagement and watchlist signals are the ones that meaningfully differentiate from generic intent providers. Keyword search is everywhere; engagement-on-a-tracked-actor and job-change-on-a-curated-list are not. Both feed the killer personalization angle: your connection note can quote the engager's own comment back at them, or congratulate the specific role transition — neither of which requires guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signal 1: keyword — the warm-up
&lt;/h2&gt;

&lt;p&gt;Open the starter at &lt;code&gt;/managed-signals&lt;/code&gt;, click &lt;strong&gt;New signal&lt;/strong&gt;, pick &lt;strong&gt;Keyword&lt;/strong&gt;, and fill it in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;em&gt;Founders complaining about cold email&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query:&lt;/strong&gt; &lt;code&gt;outbound is broken&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firing rule:&lt;/strong&gt; &lt;em&gt;"Flag founders or operators complaining about cold email or outbound burnout. Skip vendors selling outbound tools, agencies pitching their services, and content marketers writing thought-leadership posts."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cadence:&lt;/strong&gt; Daily&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook URL:&lt;/strong&gt; &lt;code&gt;https://your-ngrok-url.ngrok.io/api/webhook&lt;/code&gt; (or &lt;code&gt;http://localhost:3000/api/webhook&lt;/code&gt; if you're triggering polls manually)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter:&lt;/strong&gt; Medium and above&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create signal&lt;/strong&gt;. MyAgentMail returns a &lt;code&gt;whsec_...&lt;/code&gt; secret — paste it into &lt;code&gt;MYAGENTMAIL_WEBHOOK_SECRET&lt;/code&gt; in &lt;code&gt;.env&lt;/code&gt; and restart the dev server. From here on, every match arrives signed; the starter's webhook handler verifies the signature before accepting it.&lt;/p&gt;

&lt;p&gt;How it works under the hood: every poll runs a two-pass pipeline. Pass 1 is a cheap text-only triage that drops vendor pitches and content marketers. Pass 2 looks up the author's actual role + company on LinkedIn, then runs your firing rule with that verified context. The classifier returns &lt;code&gt;{engage: bool, intent: low|medium|high, reason: "one sentence"}&lt;/code&gt;. The reason is shown on every match in your queue — every fire is auditable end-to-end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signal 2: engagement — the high-intent one
&lt;/h2&gt;

&lt;p&gt;This is the kind that earns its keep. The premise: someone who &lt;em&gt;engages&lt;/em&gt; with a post by an actor you've chosen to track is, by their action, a hand-raised lead. The post is the trigger; the engager is the target.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MyAgentMail&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myagentmail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MyAgentMail&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MYAGENTMAIL_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;linkedin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEngagement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Acme company-page engagers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;company&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.linkedin.com/company/acme/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;intentDescription&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Flag engagers who are heads-of-sales or RevOps at SaaS companies &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;between Series A and C. Skip Acme employees, direct competitors, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;and recruiters.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;webhookUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://your-ngrok-url.ngrok.io/api/webhook&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;filterMinIntent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;medium&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When it fires, the starter receives a &lt;code&gt;signal.engagement&lt;/code&gt; payload like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"signal.engagement"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"signal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Acme company-page engagers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"engagement"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"foundAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-28T14:22:11Z"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.linkedin.com/company/acme/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Acme Inc."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"post"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.linkedin.com/feed/update/urn:li:activity:.../"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"excerpt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"We shipped real-time replay this morning..."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"engager"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jane Smith"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"profileUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.linkedin.com/in/jane-smith/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"headline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VP RevOps at Beta"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VP RevOps"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Beta"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commented"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"commentText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"We're hitting this exact problem at Beta — DM'd you."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"classification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"engage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The starter's webhook handler dispatches on &lt;code&gt;event.type&lt;/code&gt; and routes engagement matches to a kind-specific drafter (&lt;code&gt;draftEngagementConnectMessage&lt;/code&gt; in &lt;code&gt;src/lib/agent.ts&lt;/code&gt;). This drafter quotes the engager's verbatim comment back at them — the killer personalization angle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Jane — saw your comment on Acme's replay launch. We hit the same problem at Beta last quarter; happy to share what worked. Open to connecting?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That note has a 3–5× higher acceptance rate than a generic "saw your post, would love to connect" — because it proves you actually read what they wrote.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A note on cadence:&lt;/strong&gt; engagement signals fan out hard (multiple posts × reactions + comments per poll), so sub-daily cadence is gated by tier. Solo (2 sessions) clamps to &lt;strong&gt;daily&lt;/strong&gt;; team (15 sessions) unlocks &lt;strong&gt;every_12h&lt;/strong&gt;; agency (60) unlocks &lt;strong&gt;every_6h&lt;/strong&gt;. Connect more accounts to run more frequent polls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signal 3: watchlist — the slow burn
&lt;/h2&gt;

&lt;p&gt;Job changes are warm-intro gold for the first 30–60 days. A VP of Sales who just moved from Beta to Acme has new budget, new tools to evaluate, and zero loyalty to incumbent vendors at Acme yet. If you've kept a list of ex-customers, ex-coworkers, or champions at past accounts, this signal converts that list into a perpetual "tell me when one of these people gets new budget" feed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;linkedin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createWatchlist&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Past champions on the move&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;profileUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.linkedin.com/in/ex-champion-one/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.linkedin.com/in/ex-champion-two/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// ...up to 500 entries per signal&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;intentDescription&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fire when this person's NEW role is at a SaaS company between $5M and &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$100M ARR and the title implies budget over outbound or sales tooling. &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Skip moves into agencies and consultancies.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;webhookUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://your-ngrok-url.ngrok.io/api/webhook&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;filterMinIntent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;medium&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each entry is polled weekly through MyAgentMail's profile cache. The first poll is a snapshot — it records the role/company without firing. From the second poll onward, role/company diffs trigger the firing rule with the &lt;em&gt;new&lt;/em&gt; role + company, so your rule decides whether the move is one you care about.&lt;/p&gt;

&lt;p&gt;When it fires:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"signal.job_change"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"signal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Past champions on the move"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"job_change_watchlist"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"foundAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-28T..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"person"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jane Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"profileUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.linkedin.com/in/jane-doe-eng/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"headline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VP Engineering at Acme"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"change"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"oldRole"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Director of Engineering"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"oldCompany"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OldCo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"newRole"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VP Engineering"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"newCompany"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Acme"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"classification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"engage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The starter routes this to &lt;code&gt;draftJobChangeConnectMessage&lt;/code&gt;, which writes a specific congratulatory opener — &lt;em&gt;"Jane — saw the move from OldCo into the VP Eng seat at Acme. Curious what your first 90-day priorities look like; happy to share what worked when [past customer] made the same jump"&lt;/em&gt; — instead of the generic &lt;em&gt;"congrats on the new role!"&lt;/em&gt; every recruiter sends.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the queue looks like
&lt;/h2&gt;

&lt;p&gt;Every match — regardless of kind — lands as one row in your approval queue at &lt;code&gt;/queue&lt;/code&gt; with three fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The drafted connection note (280 chars, GPT-4o-mini, ready to send)&lt;/li&gt;
&lt;li&gt;The reasoning line (&lt;em&gt;"Engaged on Acme Inc. (commented) — VP RevOps at Beta matches our ICP — intent: high"&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;A one-click &lt;strong&gt;Approve&lt;/strong&gt; that calls myagentmail's &lt;code&gt;/v1/linkedin/connections&lt;/code&gt; and sends the request from your actual LinkedIn account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You stay in the loop on every send — that's the point. The agent does the work of finding signal, classifying it, and drafting the note. You spend 30 seconds reviewing. The whole approval queue is in &lt;code&gt;src/app/queue/page.tsx&lt;/code&gt; if you want to swap the UI for something else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wire it for production
&lt;/h2&gt;

&lt;p&gt;A few things to do once the basic loop works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Multi-account routing.&lt;/strong&gt; Connect a second and third LinkedIn account in the starter's &lt;code&gt;/accounts&lt;/code&gt; page. Then create signals with &lt;code&gt;sessionId: null&lt;/code&gt; (the default) — myagentmail's session router automatically distributes polls across all healthy sessions, multiplying your daily quota by the number of accounts you've connected. Sub-daily cadences require this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Public webhook endpoint.&lt;/strong&gt; Deploy the starter to Vercel. Update each signal's webhook URL to point at your deployed domain. The starter reads &lt;code&gt;MYAGENTMAIL_WEBHOOK_SECRET&lt;/code&gt; per signal — if you run multiple signals with different secrets, change &lt;code&gt;src/app/api/webhook/route.ts&lt;/code&gt; to look up the right secret per signal ID.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Tune the firing rules.&lt;/strong&gt; The classifier treats your &lt;code&gt;intentDescription&lt;/code&gt; as authoritative. If you're getting too many false positives, add explicit "skip" examples (&lt;em&gt;"skip recruiters posting job ads", "skip authors at competing vendors"&lt;/em&gt;). If you're getting too few matches, loosen the role/seniority constraints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Tune the drafters.&lt;/strong&gt; The three drafters in &lt;code&gt;src/lib/agent.ts&lt;/code&gt; are short prompts. Edit them to match your voice. Add a &lt;code&gt;productPitch&lt;/code&gt; argument if you want the note to mention what you do; remove the constraint on emoji if your audience uses them. The whole thing is ~40 lines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Watch the parse-rate logs.&lt;/strong&gt; Engagement signals do a small amount of LinkedIn page scraping under the hood (LinkedIn migrated comment text off their public API last year). MyAgentMail emits a structured log line per poll — &lt;code&gt;[engagement-poll] signal=X linkedin_comments=20 parsed_comments=18&lt;/code&gt; — so when their next deploy breaks the parser you'll notice within a day instead of a month. If parsed_comments drops to zero across multiple polls, file a ticket; we re-derive parsers from real customer payloads, fix usually within hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you've shipped
&lt;/h2&gt;

&lt;p&gt;In the time it took to read this, you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Three live LinkedIn signals — one keyword, one engagement, one watchlist — running on your own connected account on a daily cadence&lt;/li&gt;
&lt;li&gt;An HMAC-verified webhook handler that drafts a personalized note per match using GPT, with a different drafter per signal kind&lt;/li&gt;
&lt;li&gt;An approval queue you can review in 30 seconds per match&lt;/li&gt;
&lt;li&gt;A one-click connection-request flow that sends from your real profile&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the whole intent-based outreach loop. The rest is product judgment: which signals to add, how to tune the firing rules, how aggressive to be in approving. The infra is done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open-source starter:&lt;/strong&gt; &lt;a href="https://github.com/kamskans/myagentmail-outreach-starter" rel="noopener noreferrer"&gt;github.com/kamskans/myagentmail-outreach-starter&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn API reference:&lt;/strong&gt; &lt;a href="https://myagentmail.com/skills/myagentmail/references/linkedin.md?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=intent-outreach-tutorial" rel="noopener noreferrer"&gt;myagentmail.com/skills/myagentmail/references/linkedin.md&lt;/a&gt; — full schemas for all three signal kinds, webhook payloads, and the multi-session router&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript SDK:&lt;/strong&gt; &lt;code&gt;npm install myagentmail&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP server:&lt;/strong&gt; &lt;code&gt;npm install myagentmail-mcp&lt;/code&gt; — same operations exposed as tools for Claude Desktop, Cursor, Windsurf, Cline&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discord &amp;amp; support:&lt;/strong&gt; &lt;a href="https://myagentmail.com?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=intent-outreach-tutorial" rel="noopener noreferrer"&gt;myagentmail.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build something interesting on top of this, send it our way. The starter is intentionally minimal — three thousand lines of forkable code — because we'd rather you customize it than wait for us to ship features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things I'd love honest feedback on
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The plain-English firing rule&lt;/strong&gt; — feature or footgun? Letting users describe filtering as &lt;code&gt;"Senior ICs, skip recruiters, skip current employees of &amp;lt;us&amp;gt;"&lt;/code&gt; skipped a whole filter UI but pushes complexity into prompt-quality. I think it's the right call but I'd love to be wrong.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The "open source on top of paid backend" framing&lt;/strong&gt; — does that read as honest or as marketing? Trying to get the wording right. I want the repo to be genuinely useful as reference even if you can't run it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What intent signals do you actually use?&lt;/strong&gt; I shipped three (job change, engagement, keyword) because they're the ones I'd run myself. If you're doing intent-based outreach in production, what am I missing?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you build something on top of this, I'd love to see it. Reply here or DM me on LinkedIn — comment thread on this post is a great place for architecture questions too.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/kamskans/myagentmail-outreach-starter" rel="noopener noreferrer"&gt;https://github.com/kamskans/myagentmail-outreach-starter&lt;/a&gt;&lt;br&gt;
Full writeup: &lt;a href="https://myagentmail.com/blog/intent-based-outreach-tutorial?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=intent-outreach-tutorial" rel="noopener noreferrer"&gt;https://myagentmail.com/blog/intent-based-outreach-tutorial?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=intent-outreach-tutorial&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>typescript</category>
      <category>automation</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
