A framework for the periodic review of a live LinkedIn Ads account. It splits the work into independent parallel workstreams, consolidates and cross-checks their findings, and turns them into ranked, human-approved changes plus manual tasks.
Defaults for BigData Boutique accounts: B2B data/search-infrastructure consulting, technical and engineering-leader audience, long sales cycle, low conversion volume. LinkedIn is the precision-targeting channel (role, seniority, company), with the highest cost per click of our paid channels, so judge it on qualified pipeline, not CTR or platform CPL.
Naming. Campaign Manager’s UI hierarchy is campaign → ad set → ad. The Marketing API and our MCP tools use the older names: campaign group → campaign → creative. In this guide, “ad set” means the API
campaign(where budget, bid, targeting, Audience Network and Audience Expansion live). Always cite API ids in findings.
1. Principles
- Measurement first. Insight Tag + Conversions API (deduplicated), lead gen forms synced to the CRM. Without them every CPL finding is noise. L1 gates the rest.
- Judge on qualified outcomes and economics. Use lead → SQL rate, cost per SQL and pipeline against the max allowable CPL / cost per SQL from Phase 0. Platform CTR and CPL are diagnostics, not verdicts.
- Your own trailing history is the benchmark. Compare to the account’s prior periods and to the other paid channels. Do not compare to third-party “industry averages”.
- View-through is suspect for B2B. Report click and view conversions separately.
- The ad set is the unit of decision. Break it down by creative, demographic (function, seniority, company, country) and serving location (on/off network) before judging it.
- Respect learning. Batch edits per ad set. At most one spend-affecting change per ad set per cycle, in ≤20–30% steps.
- Every finding cites data: entity ids, window, metrics and the tool call. No generic advice.
- Humans approve every change. Reviews only propose (
propose_linkedin_ads_change). LLM agents have no LinkedIn write tools.
2. Cadence: Weekly, Monthly, Quarterly
| Workstream | Weekly | Monthly | Quarterly |
|---|---|---|---|
| L1 Measurement | Conversions still recording, form leads reaching CRM | Platform vs CRM reconciliation, click/view split | Conversion windows, CAPI coverage, Revenue Attribution |
| L2 Structure/governance | Rejected ads, ended ad sets still “active” | Naming, stale objects | Access, structure vs funnel plan |
| L3 Budget/delivery | Pacing, ACTIVE with no spend | Budget vs actual, concentration | Allocation by funnel role and channel |
| L4 Objective/bidding | Learning resets | Strategy vs volume, cap vs actuals | Objective fit per funnel role |
| L5 Targeting | Off-target demographics on top spenders | Full demographic and on/off-network breakdown | Audience map, exclusions, Predictive tests |
| L6 Creative | Frequency vs CTR on top spenders | Variant ranking, refresh queue | Format/angle strategy, Thought Leader Ads |
| L7 Forms/LPs | Broken URLs, UTMs | Form completion, LP engagement | Offer and form-question tests |
| L8 Outcomes | — | Lead → SQL by ad set | Pipeline, cost per SQL vs other channels |
| L9 Market | — | — | Competitor ads, new segments, seasonality |
Windows: weekly = last 7 days + 28-day context. Monthly = 30 vs prior 30. Quarterly = 90 vs prior 90. Reporting is in UTC. Demographic data lags 12–24 h in the API (24–48 h in the Campaign Manager UI), so end windows 2 days ago. With long conversion windows, recent periods keep gaining conversions; compare equally “matured” windows.
3. Execution Model
Phase 4 Follow-up (runs FIRST) ─ did last cycle's applied changes deliver?
Phase 0 Context pack (sequential) ─ account, economics, windows, entity map, ICP
Phase 1 L1…L9 workstreams (PARALLEL) ─ each pulls its own data → Findings[]
Phase 2 Consolidation (sequential) ─ L1 gate, cross-check matrix, dedupe, sanity
Phase 3 Prioritize & act (sequential) ─ score, ≤N change proposals + manual tasks + report
Workstreams read only the context pack plus their own data and return candidate actions, never proposals. Proposals and tasks are filed once, after consolidation, so duplicates and conflicts are never filed.
4. Phase 0: Context Pack
| Item | Source | Why |
|---|---|---|
account_id, currency, status | task / linkedin_ads_list_ad_accounts, get_account_details | units, scope |
| Funnel role per campaign group / ad set (awareness, consideration, conversion, retargeting) | objective + naming + human | judge with the right metric |
| Max allowable CPL, target cost per SQL, LinkedIn monthly envelope | goals / human | economics |
| ICP map: job functions, seniorities, company sizes/industries, target account lists, geos | human / guides | judge targeting findings |
| Conversion rules (type, windows, enabled, associated ad sets) | linkedin_ads_list_conversions | interpret conversions |
| Entity map: groups → ad sets → creatives (ids, status, objective, bid, budget, targeting, LAN, Audience Expansion) | get_campaign_groups, list_campaigns (all statuses), get_campaign_details, list_creatives | joins |
| Known events: launches, LP or tracking changes, holidays | change log / human | discontinuities |
Last cycle’s linkedin-ads-change tasks (applied, rejected, held) + their expected_impact | Paid Ads board | Phase 4 |
| Open manual tasks from earlier reviews | Paid Ads board, label LinkedIn Ads | avoid re-filing |
5. Phase 1: Parallel Workstreams
Severity: 🔴 critical, 🟠 material, 🟡 hygiene. Thresholds referenced here are defined once in §12.
L1 Measurement & Attribution
Data: list_conversions, get_conversion_performance, get_lead_gen_performance, CRM leads
with utm_source=linkedin, Campaign Manager UI (Insight Tag and CAPI status: manual).
- Insight Tag live on all pages; each key conversion recorded recently. A primary conversion with zero events while clicks continue → 🔴.
- Conversions API sending key events server-side. Missing → 🟠. When the same event is sent by
Tag and CAPI, both must use separate conversion rules sharing an
eventId, or it is counted twice. LinkedIn keeps the Tag event and discards the CAPI duplicate, so the Tag rule normally shows the higher count. Similar counts on both rules with noeventId→ 🔴 double counting. - Association: every Lead Gen / Website Conversion ad set has the right conversions associated. An unassociated or disabled rule on a conversion ad set → 🔴 (proposable fix).
- Windows: options are 1/7/30/90 days click and view (180/365 for some CAPI/CSV lead and purchase categories). The default is 90-day click / 90-day view. Attribution is last touch. Check each rule is deliberate. In efficiency findings, use click-through conversions.
- Lead gen forms → CRM: form leads arrive in the CRM (native sync or integration) with campaign and ad set ids. Missing or lagging → 🔴. Form leads with no CRM record cannot be judged on quality.
- Reconciliation: platform conversions vs CRM leads for the same window. Gap above threshold → investigate (dedup, spam, stripped UTMs, consent).
- UTMs on every destination URL (source/medium/campaign = ad set id/content = creative id). 🟠
- Revenue Attribution Report (Business Manager, needs a connected Salesforce, Dynamics 365 or HubSpot CRM) used for pipeline influence if available. Absent → 🟡 note (manual).
- Self-reported attribution field on demo/contact forms. Missing → 🟡.
Verdict (required): measurement_confidence: high | medium | low.
L2 Account Structure & Governance
Data: get_campaign_groups, list_campaigns (all statuses), list_creatives, UI for access.
- Structure follows funnel role: campaign groups by goal/stage. Ad sets separated by audience type (prospecting vs retargeting vs account list) and by format, so results are attributable. 🟡
- Fragmentation: many small ad sets splitting a low budget, each below the learning floor → consolidate. 🟠
- Rejected or paused-by-review creatives on top spenders → 🔴.
- Stale objects: ended ad sets still “active”, ad sets with no active creative, empty groups. 🟡
- Naming encodes funnel stage, audience, format, offer. 🟡
- Access: least privilege, former vendors removed (manual). 🟡
L3 Budget, Pacing & Delivery
Data: get_campaign_performance (DAILY), get_daily_trends, budgets from get_campaign_details
/ get_campaign_groups.
- ACTIVE with no or near-zero spend for 3+ days → delivery problem: bid/cap below market, audience too small, creative rejected or missing, schedule. 🔴 on key ad sets.
- Under-delivery (spend below the threshold share of daily budget for a week) → budget is not the constraint. Look at bid, audience size and creative (L4/L5/L6). 🟠
- Daily overspend is normal: LinkedIn may spend up to 100% above the daily budget on a given day while managing the total. Flag only sustained over-plan pacing.
- Pacing vs monthly envelope outside the threshold band. 🟡/🟠
- Concentration: one ad set takes most of the spend without matching results. 🟠
- Group budgets / Dynamic Group Budget: if a campaign-group budget is on, ad-set budgets are not the lever. Check which level controls spend before proposing a budget change.
- Schedule: custom day/hour schedules exist only with a lifetime budget, Sponsored Content and Audience Network off. Check that schedules and end dates are deliberate.
L4 Objective & Bidding
Data: get_campaign_details (objective, costType, optimizationTargetType, unitCost), results.
-
Objective and format fit the role. Both are fixed at launch (§12), and an ad set must use its group’s objective, so a misfit is fixed with a new ad set (X15). Formats per objective follow LinkedIn’s eligibility table (B2B objectives only):
Role Objective ( objectiveType)Eligible formats Prerequisites Judge on · red flag Awareness Brand awareness ( BRAND_AWARENESS)single image, carousel, video, document, event, text, spotlight, follower, article/newsletter, Thought Leader, conversation — CPM, reach, frequency · judged on CPL Consideration Engagement ( ENGAGEMENT); Video views (VIDEO_VIEW, video only)single image, carousel, video, document, event, follower, article/newsletter, Thought Leader, conversation Engagement and video-view retargeting audiences to harvest engagement/view rate, cost per engaged member · expected to produce SQLs 🟠 Consideration Website visits ( WEBSITE_VISIT)single image, carousel, video, document, event, text, spotlight, message, conversation UTMs, LP CPC, engaged sessions · funding a demo goal 🟠 Conversion Lead generation ( LEAD_GENERATION)single image, carousel, video, document, event, article/newsletter, message, conversation Lead gen form (or CAPI), CRM sync (L1/L7) CPL → SQL · pre-filled-only form, leads missing in CRM Conversion Website conversions ( WEBSITE_CONVERSION)single image, carousel, video, document, event, text, spotlight, message, conversation Insight Tag/CAPI rule associated, enough events (last bullet) click-through CPA → SQL · unassociated rule, too few events Job applicants and Talent leads (needs a Recruiter contract) are recruiting objectives. In a marketing account → 🟡. Proposable: a new DRAFT ad set via
create_campaign(typeandformat:STANDARD_UPDATE,CAROUSEL,SINGLE_VIDEO,SPONSORED_UPDATE_NATIVE_DOCUMENT,SPONSORED_UPDATE_EVENT,SPONSORED_INMAIL= message,SPONSORED_MESSAGE= conversation,TEXT,DYNAMIC+SPOTLIGHT/FOLLOW_COMPANY). Its ads go throughcreate_inline_ad(an existing image/video URN) orcreate_creative(an existing post, anadInMailContentor a clone). Manual: Thought Leader Ads (member permission), new video/document/carousel assets, message/conversation copy, event pages. -
Bid strategy (LinkedIn offers three: Maximum delivery, Cost cap, Manual bidding; there is no “Enhanced CPC”):
Situation Fitting strategy New ad set, no cost history, want full budget spent Maximum delivery (CPM-charged, no bid input) Known cost per result, need control Cost cap at the real max allowable cost. It’s an average target, and actual cost can differ Small exact audience (account lists, retargeting), strict CPC/CPM control Manual bidding -
Cap or manual bid far below actuals → no delivery (cross-check L3). Maximum delivery with cost per result above the max allowable → test cost cap.
-
Learning resets: changes to audience, budget, bid strategy or creatives restart learning. LinkedIn recommends running cost-cap ad sets ≥15 days. Repeated edits inside that window → 🟠.
-
Optimization goal too deep for volume (e.g. optimizing for a conversion with a handful of events a month) → step up the funnel or consolidate. 🟠
L5 Targeting & Audiences
Data: get_campaign_details (targeting), get_audience_demographics (function, seniority, title,
industry, company size, company, country/region), get_ad_segmentation (SERVING_LOCATION,
IMPRESSION_DEVICE_TYPE), get_audience_reach, estimate_audience_size, list_saved_audiences,
search_targeting_facets.
- Who actually saw/clicked vs the ICP map: share of spend on off-ICP functions, junior/unpaid
seniority, wrong company sizes or companies (
resolve_organization_namesafter MEMBER_COMPANY). High off-ICP share → narrow or exclude. 🟠 - Demographic pivots (
get_audience_demographics demographicType=):MEMBER_JOB_FUNCTION,MEMBER_SENIORITY,MEMBER_JOB_TITLE,MEMBER_INDUSTRY,MEMBER_COMPANY_SIZE,MEMBER_COMPANY,MEMBER_COUNTRY_V2,MEMBER_REGION_V2,MEMBER_COUNTY(prefer the V2 geo values). Filter withcampaignIds; sort byimpressions/clicks/costInUsd. Its conversions are website conversions only. Lead gen form leads by demographic come from the Campaign Manager UI (manual). - Reading demographics: rows with < 3 events are dropped, and so are ads with too few engaged
members. Only the top 100 values per creative per day are returned, so long-tail titles undercount.
Values are approximate (within ~3 units) and kept 2 years. Reach and carousel metrics aren’t
available on these pivots. Compute off-ICP share over the returned rows and state their coverage
of the ad set total.
MEMBER_COMPANYlegitimately includes non-targeted companies (members with several employers or who changed jobs, Audience Expansion, parent companies), so that alone isn’t a targeting error. - Selection logic: values within a facet are OR, while facets and “Narrow audience further” are AND. Exclude removes members. LinkedIn recommends job function + seniority over a short list of job titles, skills where titles miss the expertise, and no more than 2–3 facets. It suggests testing titles vs skills, and industry vs function, in separate ad sets rather than layering them. Internal rule of thumb: the ICP base is function × seniority × company size (+ geo); titles only for account-list or test ad sets; ≤ 3 AND layers before exclusions.
- Audience size: LinkedIn’s minimum is 300 members. It recommends ~50,000+ in general and 300,000+ for Sponsored Content and Sponsored Messaging. Account lists and retargeting are legitimately smaller; flag small audiences only when delivery is constrained (L3).
- Audience Expansion is on by default where available. Expansion metrics are blended into the ad set’s reporting, so it can’t be split out. On ICP-precise or account-list ad sets with weak quality → turn off (proposable). 🟠
- LinkedIn Audience Network is on by default for new single image, carousel, document and video
ads. Compare on vs off network (
SERVING_LOCATION). Off-network spend with poor engagement or conversion quality → disable, or apply a block list/IAB exclusions (UI, manual). 🟠 - Predictive Audiences replaced Lookalikes (Lookalikes discontinued Feb 29, 2024; existing ones are static). They need a source of 300+ members, and Audience Expansion is off on ad sets that use them. Static lookalikes still in use → 🟡 replace with a Predictive Audience test.
- Exclusions: customers, open opportunities, recent converters, employees and competitors excluded from prospecting. Missing → 🟠.
- Matched audiences (contact/company lists, website/video/form retargeting): status ACTIVE, sizes growing, lists refreshed (stale > 90 days → 🟡). Retargeting should beat prospecting on CVR. If not, look at frequency/creative (L6).
- Overlap: the same audience in several active ad sets → self-competition. 🟡
- Geo & language (folded-in): location options are permanent, recent, or “recent or permanent” (IP-based recent visits widen reach to travellers). Use permanent for standard B2B. Geo targeting is by member location, not company HQ. Multi-country ad sets let expensive markets absorb the budget (no per-geo bid adjustments), so split by country when cost per SQL differs materially. Profile language can’t be changed after launch (new ad set needed). Creative and LP must match it.
L6 Creative & Fatigue
Data: get_creative_performance, list_creatives (age, status), get_audience_reach
(penetration), get_ad_segmentation (CARD_INDEX, CONVERSATION_NODE).
- Variants: active creatives per ad set within the threshold band. < 2 → no learning. 🟠
- Fatigue: CTR down beyond the threshold vs the creative’s first two weeks while frequency or audience penetration rises → refresh. 🟠
- Winners/losers: rank within an ad set on CTR and CVR (click-through). Pause the bottom performer once it has the minimum data (§10).
- Format fit: judge video on view metrics, carousels per card (
CARD_INDEX), Conversation Ads per node (CONVERSATION_NODE). Don’t judge awareness formats on CPL. - Copy truncation: single-image intro text truncates after ~150 characters and headlines after ~70. Key message and CTA must sit before the cut. 🟡
- Thought Leader Ads (sponsored member posts): with the member’s permission, from employees or other members. Objectives: brand awareness or engagement (and video views for video). Documents, polls, multi-image, celebrations and reposts are ineligible. If not tested for awareness/engagement, suggest a test (manual: needs the member’s permission).
- Sponsored Messaging (Message / Conversation Ads): charged per send. LinkedIn strictly limits how often a member receives them, so judge on open/click/lead per send, not frequency. EU/EEA/CH members receive them only if they opted in.
L7 Lead Gen Forms & Landing Pages
Data: get_lead_gen_performance, list_lead_forms, destination URLs from list_creatives,
HTTP checks, GA4 for utm_source=linkedin.
- Form completion rate (opens → submits) per form vs its own history. Drop → form change or audience shift. 🟠
- Qualifying questions vs volume: forms with only pre-filled fields produce easy, low-intent leads (cross-check L8). Too many custom questions cut completion.
- Shared form edits: updating a PUBLISHED form changes every ad set that uses it. Prefer a new form when testing.
- LPs: broken/redirecting URLs, stripped UTMs → 🔴/🟠. Message match ad ↔ LP. Mobile speed.
- Offer fit by role: cold audiences → content/benchmark/guide. Retargeting and account lists → consultation/assessment. Cold traffic straight to “book a call” with poor CVR → 🟠.
L8 Business Outcomes & Lead Quality
Data: CRM (HubSpot) leads by ad set/creative ids or UTMs, lifecycle stages, pipeline; Revenue Attribution Report if connected; self-reported attribution.
- Lead → SQL rate and cost per SQL per ad set vs max allowable and vs Google/Reddit for the same window and definitions. 🟠/🔴
- Junk patterns: students, job seekers, vendors, wrong geos, personal emails concentrated in an ad set, form or expansion/LAN traffic → exclusions.
- Account-level influence: target accounts engaged (MEMBER_COMPANY), opportunities opened at engaged accounts. LinkedIn often influences without last-click credit; record that evidence before cutting.
- No CRM access → say so. Efficiency findings are then platform-based and lower confidence.
L9 Competitive & Market (quarterly)
Data: LinkedIn Ad Library (public, manual), search_targeting_facets (“similar” companies),
own cost trends.
- Competitor messaging and offers in the Ad Library. Do our angles differentiate?
- New ICP segments or account-list additions (similar companies, new titles/skills).
- Cost trend per ad set over 12 months (own data) to separate seasonality from performance drift.
6. Finding Format
- id: L5-03
workstream: L5
title: "Audience Network takes 41% of 'Eng Leaders - Prospecting' spend, 0 SQLs"
entities:
campaign_group_ids: ["…"]
campaign_ids: ["…"] # API campaign = UI ad set
creative_ids: []
evidence:
window: "2026-09-04..2026-10-01"
metrics: {spend: 1240, offsite_share: 0.41, offsite_ctr: 0.0011, sqls_offsite: 0}
source: "linkedin_ads_get_ad_segmentation segmentationType=SERVING_LOCATION"
severity: material # critical | material | hygiene
confidence: high # inherits L1 measurement_confidence
est_monthly_impact: 1300 # account currency
root_cause_hint: "LAN enabled by default on a precision ad set"
candidate_actions:
- kind: proposable # proposable (propose_linkedin_ads_change) | manual
tool: update_campaign
args_hint: {campaignId: "…", offsiteDeliveryEnabled: false}
summary: "Disable Audience Network on ad set …"
depends_on: []
conflicts_with: []
No finding without metrics and a window. Click-through conversions unless stated.
7. Phase 2: Consolidation & Cross-Checks
7.1 Measurement gate (L1)
| L1 verdict | Effect |
|---|---|
high | findings stand |
medium | downgrade CPL/CVR-based findings one confidence level |
low (no conversions recording, Tag/CAPI double counting, forms not reaching CRM, no UTMs) | suspend bid/budget/pause-on-CPL proposals. Report L1 fixes plus non-conversion findings (delivery, rejections, broken LPs, fatigue by CTR, off-ICP demographics, LAN share) |
7.2 Cross-check matrix
| # | If… | …and… | Then |
|---|---|---|---|
| X1 | L3 under-delivery | L4 cap/manual bid below actuals | Root cause = bid. Propose bid/cap change, not budget |
| X2 | L3 under-delivery | L5 small audience | Broaden targeting (or accept for account lists). Don’t raise budget |
| X3 | L3 ACTIVE no spend | L2 creative rejected / none active | Fix creative. No bid/budget change |
| X4 | L5 off-ICP demographics high | L5 Audience Expansion or LAN on | Turn those off before rewriting targeting |
| X5 | L6 CTR falling | L6 frequency/penetration rising | Fatigue. Refresh creative, not targeting |
| X6 | L6 CTR low everywhere | L5 off-ICP share high | Targeting first, then creative |
| X7 | L7 high CTR, low form completion / LP CVR | L7 form or LP changed | Form/LP fix (L7) |
| X8 | Good CPL | L8 low SQL rate | Quality problem: qualifying questions, exclusions, AE/LAN off. Don’t scale |
| X9 | Ad set expensive on platform CPL | L8 strong SQL rate / target-account engagement | Don’t pause on CPL. Judge on cost per SQL |
| X10 | Any bid-strategy switch | L1 ≤ medium or too few conversions | Defer. Fix measurement or build volume first |
| X11 | Any spend change | ad set in learning or changed < 72 h ago | Defer to next cycle |
| X12 | Several findings on one ad set | — | ≤1 spend-affecting change per ad set per cycle. Exclusions and creative swaps don’t count |
| X13 | Budget increases | sum | Must fit the LinkedIn envelope or be funded by proposed cuts |
| X14 | Budget change on an ad set | group budget / Dynamic Group Budget controls spend | Propose at group level (update_campaign_group) |
| X15 | Fix needs a changed objective, format or language | — | Not editable: propose a new DRAFT ad set + pause of the old one, or a manual task |
| X16 | Phase 4: last change didn’t deliver | — | Revert or adjust before any new proposal on that entity |
7.3 Dedupe & sanity
- Merge same-entity findings across workstreams (L3 under-delivery + L4 low cap → one finding).
- Collapse leaf findings into one cause (12 junior job titles → “exclude Entry/Unpaid seniority”).
- Totals reconcile with
get_campaign_performancefor the account (±2%). - Ids exist and are in the stated status. Savings ≤ actual spend. No proposal contradicts a recorded human decision, an open change task, or an open manual task.
8. Phase 3: Prioritization, Change Proposals & Manual Tasks
Score: impact × confidence ÷ effort (confidence 1.0 / 0.6 / 0.3; effort 1 = one API call,
2 = new creative/form/LP, 3 = restructure/tracking). Critical items jump the queue: broken
tracking, double counting, forms not reaching CRM, rejected top creatives, runaway spend.
Buckets: Fix now → Quick wins (exclusions, AE/LAN off, pauses, cap/bid fixes, one budget step) → Optimizations (creative refresh, form questions, ad-set splits by geo/audience) → Strategic (CAPI, CRM loop, Revenue Attribution, Predictive/TLA tests, channel role).
8.1 (a) Proposable changes → linkedin-ads-change tasks
Call propose_linkedin_ads_change(tool_name, arguments, summary, expected_impact, account_id, rationale), one call per change, highest priority first, capped (weekly ≤10). Each call creates
a task on the Paid Ads board (todo, labels linkedin-ads-change + LinkedIn Ads,
requires_human=true) assigned to the Ad Change Applier. After a human clears Requires human,
the applier runs the call exactly once and records applied_at / apply_result /
applied_resources (urn:li:…). A duplicate open proposal returns status: duplicate. Don’t re-file
it. Use the tool’s own argument names (accountId, campaignId, …). An error lists them.
| Finding type | Tool (allowlisted) | Notes |
|---|---|---|
| Pause/resume ad set, budget, bid/cap, cost type, targeting, AE / LAN on-off, end date | update_campaign | spend-affecting. targetingCriteria replaces targeting entirely, so send the full object |
| Group budget / status | update_campaign_group | spend-affecting |
| Pause or archive a creative | update_creative | spend-affecting (it can also activate) |
| New variant from an existing creative or post | create_creative (creativeIdToClone or content reference) | created DRAFT. Activation is a later update_creative proposal |
| New text/image ad with an existing media URN | create_inline_ad | created DRAFT |
| New ad set (new objective/format/language/geo split) | create_campaign (create_campaign_group if needed) | created DRAFT. One call per proposal, so a new group + ad set takes two cycles or a manual build |
| Exclusion / retargeting / matched audience | create_audience, update_audience | 24–48 h processing |
| Conversion association | associate_campaign_conversions, disassociate_campaign_conversions | |
| Lead gen form | create_lead_form, update_lead_form | updating a PUBLISHED form affects all its ad sets |
| Delete anything | delete_campaign_group, delete_campaign, delete_audience | destructive. Prefer pause/archive |
upload_image is allowlisted but takes a file path on the MCP server, so it isn’t practical from a
review. New image assets are manual.
Each proposal carries summary (becomes the title), expected_impact (quantified from the finding)
and rationale (evidence + cross-checks passed).
8.2 (b) Manual changes → human tasks
Anything not on the allowlist, or UI-only, becomes a human task. If the agent has the tasks
output, emit one task per action: board_id: paid-ads, labels LinkedIn Ads + linkedin-ads-review,
requires_human: true, fields.source_task_id = the review task (a pointer only, no copied prose).
Use a short title and description holding the action, entity ids and evidence. Otherwise, list
them in the report’s “Manual actions” section. Typical manual items: Insight Tag / CAPI / eventId
dedup, conversion window changes, CRM form sync, Revenue Attribution setup, LAN block lists,
Thought Leader Ad permissions, new image/video creative, LP work, A/B tests, access cleanup.
8.3 Experiments over guesses
For uncertain changes (Maximum delivery vs cost cap, AE or LAN on/off on a converting ad set, Predictive Audience vs ICP targeting, form vs LP), recommend a Campaign Manager A/B test (ad, audience or placement variable; runs 2 weeks to 90 days) or a duplicated ad set with one variable changed, with cost per SQL as the agreed metric (manual task).
9. Phase 4: Follow-up of Last Cycle’s Changes
Run first, before new findings. For each linkedin-ads-change task since the last review:
| Task state | Action |
|---|---|
applied_at set | Compare the entity’s metrics for an equal window before vs after applied_at (excluding the first 3 days) against expected_impact. Verdict: delivered / partial / no effect / harmful |
| Harmful or no effect | Finding with a revert or adjust candidate (X16). Revert = the inverse call, proposed again |
apply_error / held (apply_started_at without applied_at) | Report it. Check the live state with get_campaign_details / list_creatives. Don’t re-propose blindly |
Still requires_human | List as pending. Don’t duplicate |
| Rejected (cancelled/archived) | Record the human decision. Don’t re-propose without new evidence |
| DRAFT entities created but never activated | Remind (manual), or propose activation if still wanted |
Also check that manual tasks from the last review were done (e.g. CAPI live, exclusion list uploaded). Unfinished critical manual items stay at the top of the report.
10. Decision Frameworks
Minimum data before acting
| Decision | Minimum evidence |
|---|---|
| Pause an ad set or segment for no conversions | spend ≥ 2× max allowable CPL (click-through), L1 ≥ medium, no SQL/account-engagement evidence (X9) |
| Judge CTR of a creative | ≥ 5k impressions |
| Pause a creative | ≥ 5k impressions and bottom of its ad set on CTR and CVR |
| Judge a new ad set or bid strategy | ≥ 15 days (LinkedIn’s cost-cap guidance) at a funded budget |
| Judge lead quality | ≥ 10 leads with CRM stage, or ≥ 30 days of CRM follow-up |
| Judge channel value | ≥ 90 days with CRM pipeline |
Pause vs optimize (ad set)
Cost per SQL ≤ target (or CPL ≤ max allowable with OK SQL rate)? ── yes → keep; scale if budget-limited
│ no
Enough data (above)? ── no → watch; fix only obvious delivery/creative gaps
│ yes
Tracking healthy (L1)? ── no → fix tracking; don't pause on CPL
│ yes
Off-ICP delivery / AE / LAN (L5)? ── yes → narrow/exclude, AE/LAN off
│ no
Creative fatigued (L6)? ── yes → refresh, re-evaluate next cycle
│ no
Form/LP/offer mismatch (L7)? ── yes → fix offer/form
│ no
→ lower bid/cap, or pause
Scale when all hold
- Cost per SQL ≤ target, SQL rate OK, L1 ≥ medium
- Spend ≈ budget (budget-limited) and audience penetration not yet high
- Not in learning; last change ≥ 72 h ago
- Step ≤ 20–30%; re-check after 7 days. Or clone into an adjacent ICP segment, one variable at a time
Refresh creative when
- CTR down beyond threshold vs the creative’s first two weeks with frequency/penetration rising
- Creative live past the age threshold at meaningful spend
- Comments or sales feedback raise objections the ad doesn’t address
11. Symptom → Root-Cause Diagnosis
| Symptom | Check first | Likely causes |
|---|---|---|
| Ad set active, no spend | L2, L4, L5 | rejected/no active creative, bid/cap below market, audience too small, schedule |
| Spend ≪ budget | L4, L5 | tight cap/manual bid, narrow audience, low engagement lowering auction relevance |
| CPM/CPC spiking | L5, L9 | narrow audience, competitive season, expensive geo absorbing budget |
| CTR low | L6, L5 | generic creative, fatigue, off-ICP delivery, LAN placements |
| CTR fine, few leads | L7 | form/LP friction, offer too deep for cold audience |
| Leads up, SQLs flat | L8, L5, L1 | easy forms, AE/LAN/junior delivery, double counting |
| Conversions vanished | L1 | Tag removed, CAPI auth expired, rule disabled or unassociated |
| CPL rising slowly | L6, L5 | fatigue, saturated audience (high penetration), stale retargeting lists |
| Volatile daily results | L4 | repeated edits resetting learning, tiny budgets |
12. Platform Facts & Operating Thresholds
Verified platform facts (LinkedIn help center):
| Fact | Value |
|---|---|
| Bid strategies | Maximum delivery (CPM-charged, no bid), Cost cap (average target, can differ), Manual |
| Cost cap learning | Resets on audience, bid strategy, budget or creative changes. Run ≥ 15 days |
| Conversion windows | 1/7/30/90 days click and view (180/365 for some CAPI/CSV categories). Default 90/90. Last touch |
| Tag + CAPI dedup | Separate rules, shared eventId. CAPI duplicate discarded |
| Audience size | Min 300 members. Recommended ≥ 50k generally, ≥ 300k for Sponsored Content / Messaging |
| Predictive Audiences | Source ≥ 300 members. Audience Expansion off on those ad sets. Lookalikes discontinued 2024-02-29 |
| Audience Expansion | On by default where available. Blended in reporting |
| Audience Network | On by default for single image, carousel, document and video. Split with On/Off Network breakdown |
| Location | Permanent / recent / recent-or-permanent. Member location, not company HQ |
| Language | Not editable after launch |
| Objective / format | Not editable after launch |
| Daily budget | Min $10/day. Daily spend can run up to 100% over on a given day |
| Custom schedule | Lifetime budget + Sponsored Content + LAN off only |
| Reporting time zone | UTC. Demographics lag 12–24 h (API), 24–48 h (UI) |
| Demographic rows | ≥ 3 events, top 100 values per creative per day, approximate, 2-year retention |
| Targeting logic | OR within a facet, AND across facets. LinkedIn advises ≤ 2–3 facets |
| Single image copy | Intro text truncates ~150 chars (max 600). Headline ~70 (max 200) |
Operating thresholds (internal heuristics, not LinkedIn rules; tune per account):
| Metric | Threshold | Action |
|---|---|---|
| Budget/bid/cap step | ≤ 20–30%, ≥ 72 h apart | step it |
| Under-delivery | spend < 70% of daily budget for 7 days | L4/L5 diagnosis |
| Pacing vs envelope | projected > 110% or < 80% | adjust |
| Active creatives per ad set | < 2 or > 6 | add / consolidate |
| Fatigue | CTR −25% vs first 2 weeks with rising frequency; or age > 6 weeks at material spend | refresh |
| Off-ICP spend share | > 20% of an ad set | narrow / exclude |
| Off-network (LAN) spend share with no SQLs | > 20% | disable LAN |
| Platform vs CRM lead gap | > 20% | L1 reconciliation |
| Matched list age | > 90 days | refresh |
| Lead → SQL (vs account’s own other-channel rate) | < half of it | quality investigation |
13. Report Format
One markdown asset per review: LinkedIn Ads Review: <account_id> <period>.
- Verdict (healthy / needs attention / critical) + measurement confidence (L1).
- Scorecard: spend, impressions, CPM, CTR, CPC, conversions (click / view), CPL, leads → SQLs, cost per SQL, vs previous period and target. Windows stated.
- Last cycle follow-up (Phase 4).
- Critical issues.
- Ranked actions: id, action, entity, expected impact, confidence, proposable/manual, task id.
- Proposed changes (change task ids) and Manual actions (task ids or list).
- Workstream notes, including “checked and fine”.
- Deferred / blocked (cross-check id, revisit date).
- Open questions (targets, ICP, CRM access, tests to run).
Appendix: Data Pulls (LinkedIn Ads MCP)
Tools are prefixed linkedin_ads_. LLM agents can bind only read tools (get_ / list_ / search_ / compare_ / estimate_ / resolve_). Writes go only through propose_linkedin_ads_change. List tools
default to ACTIVE, so pass all statuses for audits.
| Need | Tool |
|---|---|
| Accounts, currency | list_ad_accounts, get_account_details |
| Campaign groups (+ budgets, performance) | get_campaign_groups |
| Ad sets (API campaigns) | list_campaigns |
| Full ad-set config: targeting, bid, AE, LAN, schedule | get_campaign_details |
| Ad-set performance (ALL/DAILY/MONTHLY) | get_campaign_performance |
| Daily trends | get_daily_trends |
| Period / entity comparison | compare_performance |
| Creatives (status, age) / creative performance | list_creatives, get_creative_performance |
| Demographics (function, seniority, title, industry, size, company, geo) | get_audience_demographics, then resolve_organization_names |
| On/off network, device, placement, carousel card, conversation node | get_ad_segmentation |
| Reach and audience penetration (≤ 92-day range) | get_audience_reach |
| Audience size estimates | estimate_audience_size |
| Matched / predictive audiences | list_saved_audiences |
| Targeting facets, similar companies | search_targeting_facets, resolve_company_urns |
| Conversion rules / conversion performance | list_conversions, get_conversion_performance |
| Lead gen forms / performance | list_lead_forms, get_lead_gen_performance |
| Post URL → share URN (for sponsoring an existing post) | resolve_activity_urns |
| Insight Tag / CAPI status, LAN block lists, Ad Library, Revenue Attribution, access | Campaign Manager / Business Manager UI (manual) |