in
Internal build plan
LinkedIn Outreach SaaS — Aimfox-class platform
Prepared for Inan · self-host Voyager · cloud workers · solo-first ICP
🟢 design locked, awaiting implementation plan 2026-05-18 · v1

The "Telethon for LinkedIn" — and the platform around it

Research synthesized from 10 parallel agents covering library landscape, Aimfox internals, anti-detection, session management, official APIs, unibox patterns, open-source starting points, pricing, and deploy infra.

Executive summary

There is no maintained "Telethon for LinkedIn." The canonical Voyager wrapper — tomquirk/linkedin-api — was pulled private in 2024-25; the best Node equivalent has been abandoned since 2021; the entire modern outreach industry (HeyReach, Lemlist, Salesflow…) quietly runs on Unipile, a paid REST API at ~$5/account/mo.

We're not going that route. Decision locked: self-host Voyager via the nsandman fork + StaffSpy + linkedin-messaging, wrap behind a single LinkedInProvider adapter so we can swap to Unipile as a hot-swap escape hatch if Voyager drift gets unmanageable.

Architecture: Aimfox/Expandi-class pure cloud — server worker per account, dedicated ISP residential proxy per account, Chrome extension exists only to harvest the user's li_at cookie + fingerprint snapshot at link time. ICP: solo-first at $49/seat (Aimfox/Dripify lane). Agency, multichannel, white-label features deferred to v2.

Four locked decisions

🟢 locked Core API
Self-host Voyager
nsandman/linkedin-api (Python fork of tomquirk) + StaffSpy for search + linkedin-messaging for inbox. Wrapped behind a Provider interface. Accept ~0.2 FTE on schema drift.
🟢 locked Execution model
Pure cloud + cookie-handoff extension
24/7 server workers per account on dedicated ISP residential proxies ($2-4/mo each). Extension is ~300 LOC of cookie + fingerprint harvest only. No DOM automation.
🟢 locked Deploy surface
linkedin-report.linktree.bond
Used the existing niche-lead-pipeline mechanism (Traefik + Coolify + Python http.server on 46.4.80.39). Note: trackcloudplayer.com has no DNS — not a configured surface.
🟢 locked ICP / pricing
Solo-first — $49/seat
Aimfox/Dripify lane. Annual $39. White-label, agency bundles, multichannel deferred to v2. Funnel via free 7-day trial.

Key numbers from research

Active outreach SaaS
15+
we benchmarked pricing
Target $/seat/mo
$49
solo tier, matches Aimfox
Invites/week ceiling
~100
dynamic via Trust Score
Proxy cost/account
$2-4
ISP residential, /mo

Architecture

flowchart TB subgraph CB["Client's Chrome"] EXT["Cookie-handoff extension
~300 LOC, MV3"] end subgraph FE["Frontend"] NX["Next.js 15 + Tailwind
Accounts · Campaigns · Unibox"] end subgraph BE["Backend control plane (FastAPI)"] API["REST + WebSocket API"] SCHED["Scheduler
jitter, ramp, weekly caps"] HEALTH["Health monitor
HEALTHY→WARMING→THROTTLED→CHALLENGED→BANNED"] PROV["LinkedInProvider adapter
nsandman + StaffSpy + linkedin-messaging"] end subgraph DATA["Data plane"] PG[("Postgres 16
workspaces, accounts,
campaigns, leads,
conversations, events")] REDIS[("Redis
Arq queues
per-account groups")] end subgraph WK["Workers — one per LinkedIn account"] W1["worker-A
proxy-A + fp-A"] W2["worker-B
proxy-B + fp-B"] W3["worker-N
proxy-N + fp-N"] end subgraph LI["LinkedIn"] VOY["voyager-api.linkedin.com
Voyager + /realtime/connect SSE"] end EXT -- "li_at, JSESSIONID,
x-li-track, UA" --> API NX --> API API --> SCHED API --> HEALTH API --> PG SCHED --> REDIS HEALTH --> PG REDIS --> W1 REDIS --> W2 REDIS --> W3 W1 -- "ISP residential" --> VOY W2 -- "ISP residential" --> VOY W3 -- "ISP residential" --> VOY W1 --> PROV W2 --> PROV W3 --> PROV VOY -- "SSE events" --> W1 W1 --> PG PG -- "WebSocket push" --> NX classDef locked fill:#dcfce7,stroke:#16a34a,color:#166534; classDef compute fill:#dbeafe,stroke:#3b82f6,color:#1e3a8a; classDef data fill:#fef3c7,stroke:#f59e0b,color:#854d0e; classDef ext fill:#e9d5ff,stroke:#a855f7,color:#6b21a8; class EXT ext; class W1,W2,W3 compute; class PG,REDIS data; class VOY locked;

Components

#ComponentPurposeStack
1extension/One-job MV3: read li_at + JSESSIONID + x-li-track + UA, POST to backend over TLS at link time. No DOM automation.Vanilla JS, MV3
2backend/providers/linkedin.pyLinkedInProvider interface: login, send_invite, withdraw_invite, send_message, fetch_inbox, fetch_conversation, get_profile, search_people.nsandman + StaffSpy + linkedin-messaging
3backend/workers/account_worker.pyOne process per LinkedIn account. Owns session, asserts proxy, enforces rate limits. Concurrency=1 per account.Python asyncio
4backend/scheduler.pyPer-account jittered cadence, ramp curve, weekly caps, message uniqueness gate. Auto-throttle on negative signals.Arq + Redis
5backend/realtime.pySSE long-poll on /realtime/connect per account → normalize → write conversations/messages → publish to UI WebSocket.httpx-sse
6backend/health.pyProbes /voyager/api/me every 15min. State machine HEALTHY→WARMING→THROTTLED→CHALLENGED→BANNED.asyncio
7backend/proxies.pyProvisions and binds one ISP residential IP per account at link time. Country-matched to account location.IPRoyal API
8backend/db/workspaces · users · linkedin_accounts (cookies encrypted) · campaigns · steps · leads · campaign_leads · conversations · messages · lead_events · notesPostgres 16 + SQLAlchemy 2
9frontend/Three surfaces: Accounts (link/health), Campaigns (sequences), Unibox (cross-account inbox + lead sidebar + AI draft).Next.js 15 + shadcn/ui

Library landscape (what we evaluated)

LibraryLangApproachStatusVerdict
nsandman/linkedin-api PythonVoyager HTTP fork 🟢 active Our pick — modern successor to tomquirk.
StaffSpy PythonVoyager + Playwright 🟢 active Pair for people search + CAPTCHA fallback.
linkedin-messaging PythonVoyager messaging only 🟡 stale but stable Battle-tested in mautrix-linkedin bridge. Use for inbox.
tomquirk/linkedin-api PythonVoyager HTTP 🔴 repo private PyPI v2.3.1 still installable, but you can't PR or inspect. Don't depend.
eilonmore/linkedin-private-api Node/TSVoyager HTTP 🔴 abandoned 2021 4 years stale. Endpoint drift guaranteed.
Unipile HostedREST SaaS 🟢 production-grade Escape-hatch behind Provider interface. ~$5/account/mo if Voyager drift becomes unmanageable.
joeyism/linkedin_scraper PythonPlaywright 🟢 active 4.1k★ Scrape-only, no messaging. Not useful for outreach.
linkedin-developers/* (official) Py/TSOAuth REST official No connection, no DM, no search. Useless for outreach. Industry-wide.

Competitor landscape

ToolArchitectureEntry $/moSegmentOur angle
AimfoxCloud + cookie-handoff ext + dedicated proxy$49/seatSoloDirect lane match — undercut on AI inbox + Slack pings.
HeyreachCloud, shared infra (caught a 2026 ban wave)$79 → $999 bundleAgencyTheir bundle assumes 50 seats; underserved 5-20 seat agencies = v2 white-space.
ExpandiCloud, dedicated IP, premium safety rep$99/accountPremium soloBeat on price; match on safety.
DripifyCloud, simple UX$39 annualSoloBeat on AI + multichannel.
WaalaxyHybrid free-extension + cloud€25 ProEU soloDetection risk; not direct competition.
Linked HelperDesktop Electron app$15-45BargainDifferent model (must be running).
LemlistCloud, email-first, LI side-feature$79 → $109Multichannelv2 target with native email.
Salesflow / Skylead / ZoptoCloud + dedicated IP, managed-service flavored$99 → $297/seatEnterpriseOut of scope v1.

Safety scheduler — hard rules baked in

1. Per-account rolling 7-day cap
Default 80 invites/week, earnable up to ~100 with high Trust Score. Refused server-side even on manual override.
2. Jittered human cadence
45-90s gaps between actions, account-local business hours only, no weekends in weeks 1-4. 2-3 "sessions"/day with 1-3hr gaps.
3. New-account ramp curve
W1: 0 send / 10-15 views · W2: 3-5/day · W3: 8-10 · W4: 12-15 · W5: 15-18 · W6+: 18-20 (cap at ~100/wk).
4. Auto-throttle on negative signal
Drop to 30% pace on: accept rate <25%, pending >400, 429/CAPTCHA/checkpoint, security email detected. Hard pause + re-auth on dead session.
5. Message uniqueness gate
Reject any send where Levenshtein similarity to last 25 bodies > 0.85. Spintax + per-lead variable substitution required.
6. One account = one fingerprint = one proxy, forever
Captured x-li-track and ISP IP frozen at link time. Never rotate, never share. Re-snapshot only on extension reconnect.

v1 scope

✅ IN — v1
  • • Cookie-handoff Chrome extension (MV3)
  • • 1 LinkedIn account per user
  • • ISP residential proxy per account
  • • Invite + follow-up message campaigns
  • • Unibox: 1st-degree messages + connection accepts
  • • Lead sidebar (enrichment, status, notes)
  • • Manual AI draft (OpenAI, 3 options on button)
  • • Account health monitor + state machine
  • • Auth + workspace skeleton (single-user UX)
  • • Stripe billing at $49/seat (annual $39)
🗓️ DEFERRED — v2+
  • • Sales Navigator search import
  • • Native email channel (multichannel sequences)
  • • CRM webhooks (HubSpot / Salesforce native)
  • • White-label client portal
  • • Agency tier + multi-workspace pricing
  • • Multi-account per user
  • • Voice notes + auto-translate
  • • AI auto-reply (vs draft-only)
  • • Sentiment-based auto-routing
  • • Cross-channel super-threads

Postgres schema sketch

workspaces(id, name, plan)
users(id, workspace_id, email, role)
linkedin_accounts(id, workspace_id, owner_user_id, li_member_urn,
                  li_at_enc, jsessionid_enc, x_li_track, user_agent,
                  proxy_id, status, daily_limits jsonb, last_seen_at)

campaigns(id, workspace_id, linkedin_account_id, name, goal, status,
          settings jsonb, created_at)
steps(id, campaign_id, position, type,  -- invite | message | wait | condition
      template_text, delay_hours, branch_on jsonb)

leads(id, workspace_id, li_urn, full_name, headline, company,
      status, sub_status, owner_user_id, enriched jsonb,
      first_touched_at, last_activity_at)
campaign_leads(campaign_id, lead_id, current_step_id, state, next_run_at,
               PRIMARY KEY(campaign_id, lead_id))

conversations(id, workspace_id, lead_id, linkedin_account_id, channel,
              li_conversation_urn, assigned_user_id, last_message_at,
              unread_count, sentiment, tags text[])
messages(id, conversation_id, li_event_urn UNIQUE, direction,
         sender_urn, body, attachments jsonb, sent_at, delivered_at,
         read_at, is_ai_draft bool)

lead_events(id, lead_id, type, payload jsonb, occurred_at)  -- append-only
notes(id, lead_id, author_user_id, body, mentions uuid[], created_at)
proxies(id, provider, ip, country, assigned_to_account_id, status)

Roadmap

gantt title v1 build — solo founder pace dateFormat YYYY-MM-DD axisFormat %b %d section Foundation Provider adapter (Voyager) :a1, 2026-05-19, 7d Auth + workspace + DB schema :a2, 2026-05-19, 5d Cookie-handoff extension :a3, 2026-05-26, 5d section Core engine Per-account workers + Arq queue:b1, after a1, 7d Proxy manager (IPRoyal) :b2, after a1, 3d Scheduler + ramp + caps :b3, after b1, 5d Health monitor + state machine :b4, after b1, 3d section Surfaces Campaigns UI (steps DAG) :c1, after b3, 7d Unibox UI + WS + lead sidebar :c2, after b3, 10d Realtime SSE fetcher :c3, after b1, 4d AI draft button (OpenAI) :c4, after c2, 3d section Pre-launch 5-account burn test 4-week :d1, after b4, 28d Stripe billing + onboarding :d2, after c2, 4d Landing page :d3, after c2, 5d section Launch Private beta (10 users) :milestone, m1, after d1, 0d Public launch :milestone, m2, after m1, 14d

Open risks

🔴 high Risk #1
Voyager schema drift
LinkedIn rotates Voyager schemas quarterly and silently. Need a synthetic test account hitting every endpoint weekly, paging on breakage. Budget 0.2 FTE ongoing.
🔴 high Risk #2
Pre-launch burn test
Run 5 throwaway accounts at full pace for 4 weeks. If >1/5 die, the cloud architecture needs another pass before exposing real client accounts.
🟡 medium Risk #3
BrowserGate fingerprint
LinkedIn's RSA-encrypted dossier may start being validated on receive. Mitigation: keep a "live extension tab fallback" path warm even if not primary.

Legal & positioning

Every outreach tool — Aimfox, Dripify, Expandi, Waalaxy, Heyreach, Lemlist — runs on Voyager calls that violate LinkedIn's ToS §8.2. There is no official endpoint at any price for connection requests or member-to-member DMs. The 9th Circuit's hiQ ruling protects scraping logged-out public pages, not logged-in automation.

Positioning: "LinkedIn co-pilot that acts on the user's behalf within their own session" — never "LinkedIn API integration", never "LinkedIn-partnered". ToS should state: (i) user authorizes software to act in their session, (ii) user owns compliance with LinkedIn ToS, (iii) no representation that automation is permitted, (iv) liability disclaimer for restrictions.

Diversification: roadmap to native email + X by year-end so a HeyReach-style ban event isn't an extinction risk.

Built from 10-agent parallel research · 2026-05-18 · v1 · awaiting implementation-plan approval