# Execute Linkedin Outreach

> Review and execute the authenticated user's Flywheel LinkedIn Outreach Actions. Use when asked to list, inspect, send, cancel, reopen, or work through "my LinkedIn tasks," LinkedIn connection requests, messages, InMail, or engagement Actions in Flywheel. This skill is LinkedIn-only and must never process email Actions.

- Skill: `flywheel-ai-hq/execute-linkedin-outreach` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add flywheel-ai-hq/execute-linkedin-outreach`
- Raw SKILL.md: https://api.skillmd.com/api/skills/flywheel-ai-hq/execute-linkedin-outreach/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: Flywheel-AI-HQ (https://skillmd.com/u/flywheel-ai-hq)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/flywheel-ai-hq/execute-linkedin-outreach

---


# Execute Flywheel LinkedIn Outreach

Use Flywheel MCP as the source of truth for Action state, ownership, recipient, proposed content,
schedule, and campaign context. In conversation, a user may call an Outreach Action a "task." In
tools and persisted state, always use **Outreach Action** or **Action**.

## LinkedIn only

Process only these Action types:

- `linkedin_connect`
- `linkedin_message`
- `linkedin_inmail`
- `linkedin_engage`

Never list, inspect for execution, send, cancel, or reopen an `email` Action. If the user asks for
email work, explain that this skill handles LinkedIn only.

## Current user only

This skill is shared across users. The MCP Action tools are scoped to the connector's authenticated
user. Never attempt to inspect or act on another teammate's queue. Before every touch, verify from
`get_outreach_action` that the Action is still assigned to the authenticated user. If ownership is
wrong or ambiguous, stop and report a systemic authorization problem.

## Load the queue

Call `list_outreach_actions` with:

```json
{
  "action_types": [
    "linkedin_connect",
    "linkedin_message",
    "linkedin_inmail",
    "linkedin_engage"
  ],
  "statuses": ["proposed"],
  "limit": 50
}
```

Add `campaign_id` or `account_domain` only when the user narrows the request. Follow `next_cursor`
until it is null when the user asks to process the whole queue. Do not act on a future
`scheduled_for` time.

## Inspect before acting

Before every external touch:

1. Call `get_outreach_action` with the Action id.
2. Confirm its type is one of the four LinkedIn types above.
3. Confirm its status is `proposed`, its schedule is due, and it belongs to the authenticated user.
4. Verify the intended LinkedIn profile, person, account, campaign, rationale, and proposed content.
5. If the profile identity or requested touch is ambiguous, stop instead of guessing.

Treat `not found` as inaccessible, reassigned, or stale. Never work around that boundary.

## Perform the touch

Flywheel records the Action but does not perform LinkedIn outreach. Use the client's authenticated,
interactive browser session. In Codex, prefer the user's signed-in Chrome integration. In Claude,
use Claude for Chrome or the authenticated Cowork browser. Do not use a headless LinkedIn API.

If the browser integration, LinkedIn session, correct profile, or required capability is unavailable,
report the blocker and leave the Action unchanged.

Perform exactly the requested touch:

- `linkedin_connect`: send the connection request. Add no note unless the Action explicitly includes
  one.
- `linkedin_message`: send the proposed message to the verified profile. Do not invent copy.
- `linkedin_inmail`: send the proposed InMail only when the account has the required credit and the
  recipient matches.
- `linkedin_engage`: perform only the exact like, comment, follow, or other engagement described by
  the Action.

Respect LinkedIn warnings and limits. Never bypass restrictions, connection limits, missing InMail
credits, or anti-abuse controls.

## Record lifecycle state

Call `update_outreach_action` with `status: "sent"` only after the LinkedIn UI visibly confirms the
touch succeeded. The state update records the outcome; it does not perform or prove the touch.

Use `status: "canceled"` only when the proposed touch should not happen. Supply the most accurate
supported `cancel_reason` and a concise factual `cancel_note` when useful. A browser failure,
temporary session problem, or unavailable integration is not a cancellation reason; leave the
Action proposed and report the blocker.

Use `status: "proposed"` to reopen canceled work only when the user explicitly asks.

## Report directly observed replies

When the requested browser work directly reveals a response associated with the Action, record the
observed fact with `report_campaign_signal`:

- connection accepted: `channel: "linkedin"`, `activity_type: "connection_accepted"`
- LinkedIn message received: `channel: "linkedin"`, `activity_type: "message_received"`

Pass `campaign_id` and `action_id`, the observed `occurred_at` when known, a stable
`idempotency_key`, and an optional factual note. Do not infer interest, score the response, or report
acceptance without direct evidence. Do not scan unrelated historical Actions unless the user asks.

## Report the result

Summarize:

- Actions sent;
- Actions canceled and why;
- Actions skipped;
- blockers requiring user action.

Lead with the person and account names. Include Action ids for traceability.

