Skip to main content
Glama

Create LinkedIn Outreach Campaign

li_create_campaign

Create a two-stage campaign from prefilled targets under a sending strategy. Each target needs a profileUrl, an optional connectionNote sent with the invite, and text for every post-acceptance strategy step. Accepts a JSON targets array, CSV text, or both. Rows missing step text are rejected. Does not send anything. With NO targets it creates an EMPTY campaign (strategy, ICP and account as usual) to fill later with li_ingest_prospects(campaignId) or li_move_contact.

AT MOST 50 TARGETS PER CALL, counting the JSON array and the CSV rows together. Split a longer list across calls under the same strategy, or use li_ingest_prospects, which takes 500 at a time and buckets them into a managed campaign for you.

result.collisions reports who among these targets had already been approached from the accounts you can see, measured before the campaign was created. It is information and refused nothing — every target was imported — but each person already invited or connected on another campaign is attempted again at send time, spending a browser session even when it sends nothing. You do NOT need li_check_targets before this — the same check runs here and the same report comes back. That tool is for deciding whether to import at all, before you have a batch.

Targets on the do-not-contact list are NOT imported: they come back in result.suppressed with the reason, and the campaign is refused outright when every target is on it.

Engage-before-invite: give a target engagePostUrl (one of their own posts, the one your note mentions) and the account reacts to it first; the invite follows about a day later, inside the window it goes out in (the person's own morning when the account times invites to each prospect, otherwise its sending hours; 18-30 hours with no window to aim at). Reactions are capped per account per day (15-25, our policy) and run in sending hours. To react to a post right now instead, use li_react_to_post.

Input Schema

NameRequiredDescriptionDefault
nameYesCampaign name, shown on the dashboard and in li_list_campaigns
icpIdNoOptional ICP label for analytics
csvTextNoCSV text with profile_url plus optional connection_note and step text columns (first_message, follow_up_1, follow_up_2, follow_up_3, closer), and optional engage_post_url / engage_reaction (a post of theirs the account reacts to about a day before the invite, never less than 16 hours; see engagePostUrl on targets). At most 50 rows, shared with `targets`.
icpNameNoOptional ICP name to resolve
targetsNoPrefilled targets, at most 50 per call (CSV rows count toward the same limit). For a longer list, split it across calls under the same strategy, or use li_ingest_prospects, which takes 500.
sessionIdNoOptional saved LinkedIn session ID to use for all campaign sends
strategyIdNoStrategy to follow (or use strategyName)
sessionLabelNoOptional saved LinkedIn session label to resolve and use for campaign sends
strategyNameNoStrategy name to resolve and follow
confirmCreateYesMust be true to write the campaign to the database

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed2 schema fields changed
    • changedInput schema / properties / csvText / description
      Previous value: -"CSV text with profile_url plus optional connection_note and step text columns (first_message, follow_up_1, follow_up_2, follow_up_3, closer), and optional engage_post_url / engage_reaction (a post of theirs the account reacts to a day or more before the invite; see engagePostUrl on targets). At most 50 rows, shared with `targets`."New value: +"CSV text with profile_url plus optional connection_note and step text columns (first_message, follow_up_1, follow_up_2, follow_up_3, closer), and optional engage_post_url / engage_reaction (a post of theirs the account reacts to about a day before the invite, never less than 16 hours; see engagePostUrl on targets). At most 50 rows, shared with `targets`."
    • changedInput schema / properties / targets / items / properties / engagePostUrl / description
      Previous value: -"Engage-before-invite: ONE of this person's own recent posts (https://www.linkedin.com/feed/update/urn:li:activity:<id>/ or a /posts/ link), the one the note and first message mention. The sending account visits their profile and reacts to it a day or more BEFORE the invite goes out (about one in seven is only viewed), and the invite then waits 20-60 hours. Connection stage only: ignored for message-stage prospects. A link that is not a LinkedIn post refuses the row. If the post is gone or turns out not to be theirs, nothing is clicked and the invite goes ahead without it."New value: +"Engage-before-invite: ONE of this person's own recent posts (https://www.linkedin.com/feed/update/urn:li:activity:<id>/ or a /posts/ link), the one the note and first message mention. The sending account visits their profile and reacts to it first (about one in seven is only viewed). The invite follows about a day later, never sooner than 16 hours, inside the window it goes out in: the person's own morning invite window when the account times invites to each prospect, otherwise the account's sending hours (18-30 hours when there is no window to aim at, such as sending hours switched off). Connection stage only: ignored for message-stage prospects. A link that is not a LinkedIn post refuses the row. If the post is gone or turns out not to be theirs, nothing is clicked and the invite goes ahead without it."
  2. Changed1 schema field changed
    • changedInput schema / properties / targets / description
      Previous value: -"Prefilled targets, at most 50 per call (CSV rows count toward the same limit). For a longer list, split it across calls under the same strategy, or use li_ingest_prospects, which takes 200."New value: +"Prefilled targets, at most 50 per call (CSV rows count toward the same limit). For a longer list, split it across calls under the same strategy, or use li_ingest_prospects, which takes 500."
  3. Changed3 schema fields changed
    • changedInput schema / properties / csvText / description
      Previous value: -"CSV text with profile_url plus optional connection_note and step text columns (first_message, follow_up_1, follow_up_2, follow_up_3, closer). At most 50 rows, shared with `targets`."New value: +"CSV text with profile_url plus optional connection_note and step text columns (first_message, follow_up_1, follow_up_2, follow_up_3, closer), and optional engage_post_url / engage_reaction (a post of theirs the account reacts to a day or more before the invite; see engagePostUrl on targets). At most 50 rows, shared with `targets`."
    • addedInput schema / properties / targets / items / properties / engagePostUrl
      Added value: +{
      +  "description": "Engage-before-invite: ONE of this person's own recent posts (https://www.linkedin.com/feed/update/urn:li:activity:<id>/ or a /posts/ link), the one the note and first message mention. The sending account visits their profile and reacts to it a day or more BEFORE the invite goes out (about one in seven is only viewed), and the invite then waits 20-60 hours. Connection stage only: ignored for message-stage prospects. A link that is not a LinkedIn post refuses the row. If the post is gone or turns out not to be theirs, nothing is clicked and the invite goes ahead without it.",
      +  "type": "string"
      +}
    • addedInput schema / properties / targets / items / properties / engageReaction
      Added value: +{
      +  "description": "Which reaction to leave on engagePostUrl: LIKE (default), PRAISE (Celebrate), EMPATHY (Love), INTEREST (Insightful), APPRECIATION (Support) or ENTERTAINMENT (Funny). Needs engagePostUrl.",
      +  "type": "string"
      +}
  4. Changed13 schema fields changed
    • addedInput schema / properties / name / description
      Added value: +"Campaign name, shown on the dashboard and in li_list_campaigns"
    • addedInput schema / properties / targets / items / properties / collaborationAngle / description
      Added value: +"The angle the sequence leads with"
    • addedInput schema / properties / targets / items / properties / company / description
      Added value: +"Current employer"
    • addedInput schema / properties / targets / items / properties / context / description
      Added value: +"Free-text notes on how you found them or why now"
    • addedInput schema / properties / targets / items / properties / fitQuality / description
      Added value: +"Your fit grade, e.g. strong / medium / weak"
    • addedInput schema / properties / targets / items / properties / headline / description
      Added value: +"LinkedIn headline"
    • addedInput schema / properties / targets / items / properties / icpBucket / description
      Added value: +"Your ICP bucket for them (free text)"
    • addedInput schema / properties / targets / items / properties / location / description
      Added value: +"Location as shown on the profile"
    • addedInput schema / properties / targets / items / properties / name / description
      Added value: +"Full name"
    • addedInput schema / properties / targets / items / properties / productUrl / description
      Added value: +"Their product or company URL"
    • addedInput schema / properties / targets / items / properties / role / description
      Added value: +"Current job title"
    • addedInput schema / properties / targets / items / properties / whatTheySell / description
      Added value: +"What their company sells, in a line"
    • addedInput schema / properties / targets / items / properties / whyTheyFit / description
      Added value: +"Why they fit, in a line"
  5. First observed

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare non-read-only, non-destructive, non-idempotent, closed-world. The description goes well beyond: it explicitly says nothing is sent, describes the 50-target cap shared across inputs, the collisions report as informative-but-not-refusing, do-not-contact suppression and outright refusal when all targets are suppressed, and the engage-before-invite timing and per-account reaction caps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core purpose and the two hard constraints (50-target cap, no sending) before routing to alternatives. It is long and the engage-before-invite mechanics are largely repeated in the engagePostUrl schema description, which is the only real redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite no output schema, the description explains returns (result.collisions, result.suppressed with reasons) and covers the key edge cases an agent must handle: rejection on missing step text, suppression, the target cap, and engage-before-invite failure modes. Nothing material is left for the caller to guess.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter semantics the schema cannot: every target needs step text for each post-acceptance strategy step, rows missing step text are rejected, profileUrl is required with connectionNote optional, and the JSON/CSV targets share the row limit.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Create a two-stage campaign from prefilled targets under a sending strategy') and immediately scopes what it is not: 'Does not send anything.' The empty-campaign behavior and the three input formats (JSON, CSV, both) let an agent distinguish it from li_ingest_prospects, li_check_targets, and li_move_contact without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Names alternatives and the exact conditions selecting them: split across calls under the same strategy or use li_ingest_prospects (>50), no need for li_check_targets before this, and li_react_to_post for immediate reactions. It also explains when to create an empty campaign to fill later via li_ingest_prospects or li_move_contact.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources