Skip to content

LinkedIn Graph Analysis — Capability Audit

Date: 2026-03-02 Prepared by: Claude (Opus 4.6) Capability Slug: linkedin_graph_analysis Handler: LinkedInGraphCapability.php


Part 1: Current State Assessment

What Exists

The LinkedIn Graph Analysis capability is registered in the CapabilityRegistry as an analysis-category capability with 5 analysis types:

Analysis Type Credits Status
Post Performance 5 Prompt-only (no live data)
Audience Insights 10 Prompt-only (no live data)
Relationship Graph 15 Prompt-only (no live data)
ICP Alignment 10 Prompt-only (no live data)
Content-to-Marketplace Correlation 15 Prompt-only (no live data)

The Critical Gap

The capability does not fetch LinkedIn data. Line 87-88 of LinkedInGraphCapability.php:

// In production: fetch LinkedIn API data
// For now: use Claude to analyze provided data

Every analysis method sends whatever $parameters['data'] the user provides (or nothing) to Claude 3 Sonnet via Bedrock and asks it to analyze. Without actual LinkedIn API data flowing in, this is an AI prompt wrapper — not a pipeline intelligence tool.

LinkedIn OAuth — What's Actually Configured

Current scopes (from config/social-media.php):

openid profile email w_member_social

These are publishing scopes. They allow: - Authenticating a user (openid, profile, email) - Posting to the user's LinkedIn feed (w_member_social)

What's NOT configured (required for graph analysis):

Scope / API Product What It Unlocks Required For
r_organization_social Read org posts & engagement Post Performance
r_organization_followers Follower demographics Audience Insights
r_1st_connections_size Connection count Relationship Graph
Marketing Developer Platform Campaign analytics, audience insights All analysis types
Community Management API Comment management, mentions Engagement tracking
Advertising API Audience demographics at scale ICP Alignment

LinkedIn Developer Access Status: INCOMPLETE

The Linkedin.php helper has a getPostAnalytics() method that calls rest/socialMetadata/{urn} — this returns basic engagement counts (likes, shares, comments) for posts the user published. It does NOT provide: - Audience demographics (title, company, industry of engagers) - Follower analytics - Connection/relationship data - Content attribution to external conversions

What the Existing Helper CAN Do

Method Endpoint What It Returns
publishText() rest/posts Post creation
publishImage() rest/images + rest/posts Image + post creation
publishVideo() rest/videos + rest/posts Video upload + post creation
getPostAnalytics() rest/socialMetadata/{urn} Basic engagement counts
getAccountInfo() v2/userinfo Profile name, email, picture

Missing methods for graph analysis: - getFollowerDemographics() — requires Organization API - getEngagerProfiles() — requires Marketing Developer Platform - getConnectionGraph() — not available via API (LinkedIn restricts this) - getPostImpressionDemographics() — requires Advertising API


Part 2: Architecture Questions

Does the graph become input into an agent?

Current state: No. The capability is a terminal output — it produces analysis text and returns it as execution step results. The output does not persist as a data source that other capabilities can consume.

How it works today:

Agent Execution → LinkedInGraphCapability::execute() → Claude prompt → JSON analysis → step_results (stored in AgentExecution)

The step_results JSON is stored in ext_content_manager_agent_executions.step_results but is not indexed, queryable, or referenceable by subsequent capability executions.

Does it sit alongside KB and Brand Voice?

Current integration is one-directional: - Brand Voice → flows into → LinkedIn Graph Analysis (via buildAnalysisPrompt()) - Knowledge Base → not connected → LinkedIn Graph Analysis - LinkedIn Graph Analysis → does not flow back into → anything

Brand voice context (company name, industry, target audience, ICP) is injected into every analysis prompt. But the analysis output does not enrich the brand voice, KB, or any persistent data store.

Should it go with Brand Voice? Become a brand sentiment input?

This is the most strategically interesting question. Consider three architectural options:

Option A: LinkedIn Graph as Agent Capability (Current)

Brand Voice + User Data → [LinkedInGraphCapability] → One-time analysis report

Strengths: Simple, low maintenance, no LinkedIn API dependency Weakness: No persistent intelligence, no compounding value, output is ephemeral

Option B: LinkedIn Graph as Persistent Data Source (alongside KB)

LinkedIn API (scheduled) → [LinkedIn Data Store] → Queryable by any capability
                                                  → Feeds into Brand Voice enrichment
                                                  → Relationship graph as persistent asset

Strengths: Compounding value, cross-capability intelligence, persistent relationship map Weakness: Requires full LinkedIn API access, ongoing sync, storage costs, LinkedIn API rate limits

Option C: LinkedIn Graph as Brand Sentiment Layer (modernized)

LinkedIn API → [Sentiment Processor] → Brand Voice enrichment
                                      → Audience-ICP delta tracking
                                      → Content effectiveness signals
                                      → Relationship warmth scores

Strengths: Differentiating, feeds the GTM engine, aligns with "schema is the moat" thesis Weakness: Most complex, requires LinkedIn Marketing Developer Platform approval, sentiment analysis adds latency

Recommendation: Start with Option A+, evolve toward Option C

Option A+ (Near-term, no API dependency): - Keep the capability as-is but make the output persist as a knowledge artifact - After execution, store analysis results in a linkedin_insights table or as KB documents - Allow other capabilities (content generation, co-sell matching) to query past LinkedIn insights - This creates compounding value without LinkedIn API access

Option C (Medium-term, requires API access): - Once LinkedIn developer access is complete, build a scheduled data sync - Process engagement data into a sentiment/audience profile that enriches Brand Voice - The relationship graph becomes a persistent warm-path asset for co-sell motions


Part 3: LinkedIn Developer Access Audit

Multi-App Architecture (Confirmed)

LinkedIn enforces app isolation on products that read org/member/page data. Products that read data from others are grayed out and cannot be added alongside any other product.

Isolation-required products (grayed out on existing app): - Community Management API - Member Data Portability API (3rd Party) - Pages Data Portability API - Verified on LinkedIn

Stackable products (blue "Request access" on existing app): - Advertising API - Lead Sync API - Live Events - Events Management API - Conversions API - LinkedIn Ad Library

This means Vell needs two LinkedIn developer apps:


App 1: Vell AI — Publishing + Marketing (existing)

  • Client ID: 86lbyxc5vzdo04
  • Created: April 15, 2025
  • Purpose: Identity, outbound publishing, advertising, conversions, research
Product Tier Status
Sign In with LinkedIn (OpenID Connect) Standard ADDED
Share on LinkedIn Default ADDED
Advertising API Development ADDED (approved 2026-03-03)
LinkedIn Ad Library Default ADDED (approved 2026-03-03, App ID 222715561)
Conversions API Standard SUBMITTED — awaiting review
Lead Sync API Standard Not added — optional
Events Management API Standard Not added — not needed
Live Events Development Not added — not needed

Scopes (current): openid profile email w_member_social Scopes (ready to add — Advertising API added): + r_ads r_ads_reporting


App 2: Vell AI — Community Intelligence (CREATED 2026-03-03)

  • Client ID: 787zcjp7t3l55r
  • Created: March 3, 2026
  • App Name (portal): "Vell AI Conversions API" — rename to "Vell AI — Community Intelligence" in Settings
  • Purpose: Read org posts, follower demographics, engagement analytics
  • CRITICAL: This app must have Community Management API as its only product
Product Tier Status
Community Management API Development Request access next

Scopes: r_organization_social w_organization_social rw_organization_admin w_member_social

Endpoints unlocked: - GET /rest/posts — Read organization posts - GET /rest/socialActions — Engagement data (likes, comments, shares) - GET /rest/organizationalEntityFollowerStatistics — Follower demographics - GET /rest/organizationPageStatistics — Page views, visitor demographics - POST /rest/socialActions — React/comment on behalf of org - GET /rest/socialMetadata — Aggregate engagement metrics


Dual-App OAuth Implications

Two LinkedIn apps = two OAuth authorization flows. This has real architectural impact:

Option 1: Sequential authorization — User connects App 1 (publishing), then separately connects App 2 (analytics). Two tokens stored per user.

Option 2: Single UX, dual redirect — One "Connect LinkedIn" button chains both authorizations. User sees two LinkedIn consent screens back-to-back.

Option 3: Conditional authorization — App 1 connects on social media setup. App 2 connects only when user enables LinkedIn Graph Analysis capability.

Recommendation: Option 3. Most users need publishing (App 1). Only users who want LinkedIn Graph Analysis need the analytics connection (App 2). This avoids unnecessary consent friction and aligns with the capability-based architecture.

Code impact: - Linkedin.php needs to support two sets of credentials - LinkedinController.php OAuth callback needs to route to the correct app - SocialMediaPlatform model may need a purpose or app_type field - social-media.php config needs a second LinkedIn entry (e.g., linkedin_analytics)

Current Product Status (as of 2026-03-03)

LinkedIn API Product Tier App Status Action Needed
Sign In with LinkedIn Standard App 1 ADDED None
Share on LinkedIn Default App 1 ADDED None
Advertising API Development App 1 ADDED Add scopes r_ads r_ads_reporting to OAuth flow
LinkedIn Ad Library Default App 1 ADDED Integration built — test with live token
Conversions API Standard App 1 SUBMITTED Awaiting LinkedIn review
Lead Sync API Standard App 1 NOT ADDED Optional
Live Events Development App 1 NOT ADDED Not needed
Events Management API Standard App 1 NOT ADDED Not needed
Community Management API Development App 2 (787zcjp7t3l55r) CREATED — request access next Request Community Management API as sole product
Member Data Portability API Default N/A SKIPPED Requires exclusive app, low priority
Pages Data Portability API Standard N/A SKIPPED Requires exclusive app
Verified on LinkedIn Development N/A SKIPPED Not needed

Tier Definitions (Corrected)

LinkedIn's tier labels are misleading. "Default Tier" does NOT mean instant self-service:

  • Default Tier: Still requires access request form with organization details, use case description, and LinkedIn review. Bound by API Terms of Use + Research API Program Terms. Not instant.
  • Standard Tier: Requires access request form with use case justification
  • Development Tier: Requires LinkedIn review via Access Request Form, use case justification, and approval

In practice, every product beyond Sign In requires a form submission and review.

Immediate Actions on App 1 (existing)

1. LinkedIn Ad Library (Default Tier) — Enables ad data search - Requires access request form with organization + use case details - Must agree to LinkedIn API Terms of Use + Research API Program Terms - Useful for competitor analysis and content intelligence - Use case to submit: "Enable AWS Marketplace ISV partners to analyze competitive ad positioning and content themes for GTM content strategy"

2. Conversions API (Standard Tier) — Enables attribution tracking - The marketplace correlation differentiator - Use case to submit: "Track conversion events from LinkedIn content engagement to AWS Marketplace listing views, demo requests, and Private Offers to measure social selling ROI"

3. Advertising API (Development Tier) — Enables audience demographics - Use case to submit: "Provide ISV partners with aggregate audience demographic insights to measure ICP alignment of their LinkedIn content strategy"

Create App 2: Community Intelligence

4. App 2 created: 787zcjp7t3l55r (2026-03-03) - Current name: "Vell AI Conversions API" — rename to "Vell AI — Community Intelligence" - LinkedIn Page: Same company page as App 1 - Only product: Community Management API (request access next)

5. Request Community Management API on App 2 - Use case to submit: "Read organization post analytics, follower demographics, and engagement metrics to provide ISV partners with LinkedIn audience intelligence for GTM content strategy and social selling optimization"

Application Requirements (for Development Tier products)

LinkedIn requires: 1. Company Page with active presence 2. App use case description — how you'll use the data 3. Privacy policy URL — must describe LinkedIn data handling 4. Terms of service URL — must cover social data usage 5. OAuth redirect URLs — already configured for App 1; need new redirect for App 2 6. Rate limit justification — expected API call volume 7. Data retention policy — how long you keep LinkedIn data

OAuth Config — Dual App Setup

social-media.php will need to support two LinkedIn configurations:

// App 1: Publishing + Marketing (existing)
'linkedin' => [
    'app_id'       => 'LINKEDIN_APP_ID',
    'app_secret'   => 'LINKEDIN_APP_SECRET',
    'redirect_uri' => '/social-media/oauth/callback/linkedin',
    'scopes'       => ['openid', 'profile', 'email', 'w_member_social'],
    // ...existing config...
],

// App 2: Community Intelligence (new — analytics/reading)
'linkedin_analytics' => [
    'app_id'       => 'LINKEDIN_ANALYTICS_APP_ID',
    'app_secret'   => 'LINKEDIN_ANALYTICS_APP_SECRET',
    'redirect_uri' => '/social-media/oauth/callback/linkedin-analytics',
    'scopes'       => ['r_organization_social', 'w_organization_social', 'rw_organization_admin'],
    // ...
],

Note: Current config has scopes as a single space-delimited string ('openid profile email w_member_social'). This should be updated regardless of dual-app setup.

What LinkedIn Will NOT Provide (API Limitations)

Even with full API access, LinkedIn restricts: - Connection graph traversal — You cannot enumerate a user's connections - Engager identity — You get aggregate demographics, not individual profiles of who liked/commented - Cross-account relationship mapping — You can't map paths between arbitrary users - Real-time webhooks — LinkedIn doesn't push data; you must poll

Impact on capability claims:

Capability Claim Feasible via API? Alternative
"See which posts drive engagement" YES — post analytics available
"Map warm paths to key accounts" PARTIAL — no connection graph traversal Use comment/engagement overlap as proxy
"Understand your real audience" YES (aggregate) — follower demographics available
"Social selling at scale" PARTIAL — individual engager profiles not available Use aggregate patterns + manual data input

The Relationship Graph Mapping (15 credits) capability as described in the docs — "warm introduction paths with relationship strength scores" — cannot be fully implemented via LinkedIn's API. The warm-path examples in the feature docs (e.g., "You → Sarah Chen → Mike Johnson") would require connection graph data that LinkedIn does not expose.

Honest alternative: Relationship intelligence based on engagement overlap — who comments on both your posts and the target account's posts. This is achievable and still valuable, but should be positioned differently than "graph mapping."


Part 4: Recent Executions — Do They Relate to LinkedIn?

Short answer: Probably not.

The executions table (ext_content_manager_agent_executions) tracks ALL agent capability executions — content generation, SEO analysis, competitor analysis, marketplace optimization, etc. The "Recent Executions" UI section shows executions sorted by created_at DESC across all capability types.

To confirm, you'd need to filter executions where the agent's capabilities include linkedin_graph_analysis and the step_results reference that capability. Without live database access, the most likely explanation is that recent executions are from other capabilities (content generation, SEO, marketplace listing optimization are the most commonly used).

To verify: Check the task_description or step_results JSON for any execution to see if it references LinkedIn analysis.


Part 5: Alignment with Portability Audit

The PARTNER_OUTPUT_PORTABILITY_AUDIT.md classifies social media as "Dare to Be Bad" — a supporting capability, not a differentiator:

Dare to Be Bad (Supports): Generic content generation, social media, brand monitoring

And rates the competitive moat: - General AI Content Generation: 2/10 (commodity) - "Deployed on AWS": 3/10 (table stakes)

Meanwhile, the stars — the capabilities to double down on: - GTM Content Schema (10/10) - First Call Enrichment (8/10) - Multi-Persona Dry-Run Simulation (9/10) - Agentic Workflow + MCP (8/10)

Where LinkedIn Graph Fits in This Framework

The LinkedIn Graph capability straddles two categories:

If it's "analyze LinkedIn posts and engagement" → Dare to Be Bad (every social media tool does this)

If it's "correlate LinkedIn activity with AWS Marketplace pipeline" → Potential Star (nobody else does this)

The differentiating value is the correlation — connecting social engagement to marketplace conversions. That's the Content-to-Marketplace Correlation analysis type (15 credits). The other four analysis types are commodity.

Strategic Recommendation

Invest in the correlation, not the social analytics. The post performance and audience insights analysis types are table stakes — Hootsuite, Sprout Social, and LinkedIn's native analytics already do this. What nobody else does is correlate LinkedIn engagement with AWS Marketplace conversion events.

The LinkedIn Graph capability should be repositioned: - De-emphasize: Generic post performance, audience demographics (commodity) - Emphasize: Content-to-Marketplace attribution, co-sell relationship signals - Integrate: Feed correlation insights into GTM Content Schema (the crown jewel from the portability audit)


Part 6: Implementation Priority Checklist

TODAY — App 1 (86lbyxc5vzdo04)

  • Share on LinkedIn product — ADDED
  • LinkedIn Ad Library — ADDED (approved 2026-03-03)
  • Advertising API — ADDED (approved 2026-03-03)
  • Conversions API — SUBMITTED, awaiting LinkedIn review
  • Test that publishing flow works now that Share on LinkedIn is active
  • Test Ad Library endpoint with live bearer token

TODAY — App 2 (787zcjp7t3l55r) — CREATED 2026-03-03

  • Create LinkedIn developer app
  • Rename app from "Vell AI Conversions API" → "Vell AI — Community Intelligence"
  • Request Community Management API as the sole product
  • Configure redirect URI: /social-media/oauth/callback/linkedin-analytics

THIS WEEK (Code changes — no API approval required)

  • Add linkedin_analytics config block to social-media.php for App 2
  • Add linkedin_analytics config block to social-media.php
  • Add linkedin_analytics to PlatformEnum (not in publishing UI toArray()/all())
  • Create LinkedinAnalyticsController.php OAuth callback for App 2
  • Register routes: redirect/linkedin-analytics, callback/linkedin-analytics
  • Add searchAdLibrary() to Linkedin.php
  • Create LinkedInAdLibraryService.php for competitor ad research
  • Add competitor_ad_analysis type (10 credits) to LinkedInGraphCapability.php
  • Add App 2 env vars in admin settings: LINKEDIN_ANALYTICS_APP_ID, LINKEDIN_ANALYTICS_APP_SECRET
  • Persist LinkedIn Graph Analysis output as queryable knowledge artifacts
  • Add linkedin_insights as a context source in BrandVoiceContextBuilder
  • Audit feature docs to align claims with what's actually deliverable via API

AFTER COMMUNITY MANAGEMENT API APPROVAL (App 2)

  • Implement getOrganizationPosts() in Linkedin.php using App 2 tokens
  • Implement getFollowerDemographics() using App 2 tokens
  • Implement getPostAnalyticsBatch() for bulk post analysis
  • Build scheduled data sync for connected LinkedIn org accounts
  • Replace "provided data" prompt pattern with actual API data in LinkedInGraphCapability.php
  • Wire Post Performance and Audience Insights analysis types to real data

AFTER ADVERTISING API APPROVAL (App 1) — APPROVED 2026-03-03

  • Add r_ads and r_ads_reporting to App 1 OAuth scopes
  • Implement audience demographic retrieval from ad analytics
  • Wire ICP Alignment analysis type to real audience data
  • Build aggregate demographic profiles for brand sentiment layer

AFTER CONVERSIONS API APPROVAL (App 1 — The Differentiator)

  • Implement conversion event ingestion from LinkedIn
  • Build Content-to-Marketplace attribution pipeline
  • Correlate LinkedIn post engagement with AWS Marketplace conversion events
  • Wire Content-to-Marketplace Correlation analysis type to real attribution data
  • Feed correlation insights back into GTM Content Schema

MEDIUM-TERM (Persistent Intelligence Layer)

  • Build persistent LinkedIn data store (engagement history, audience snapshots)
  • Implement engagement-overlap relationship intelligence (realistic alternative to graph traversal)
  • Create brand sentiment layer from LinkedIn engagement patterns
  • Feed sentiment signals into Brand Voice context for content generation
  • Build time-series audience-ICP delta tracking

Part 7: File Reference

File Purpose Status
app/Extensions/ContentManager/System/Services/Capabilities/LinkedInGraphCapability.php Capability handler Ad Library wired; other types prompt-only
app/Extensions/SocialMedia/System/Helpers/Linkedin.php LinkedIn API client Publishing + analytics + Ad Library search
app/Extensions/SocialMedia/System/Services/LinkedInAdLibraryService.php Ad Library research service NEW — competitor ad analysis
app/Extensions/SocialMedia/config/social-media.php OAuth config Dual-app: linkedin + linkedin_analytics
app/Extensions/SocialMedia/System/Http/Controllers/Oauth/LinkedinController.php App 1 OAuth flow Publishing (existing)
app/Extensions/SocialMedia/System/Http/Controllers/Oauth/LinkedinAnalyticsController.php App 2 OAuth flow NEW — analytics connection
app/Extensions/SocialMedia/System/Enums/PlatformEnum.php Platform enum Added linkedin_analytics case
app/Extensions/SocialMedia/System/SocialMediaServiceProvider.php Route registration Added analytics OAuth routes
app/Extensions/ContentManager/System/Services/AgentCore/CapabilityRegistry.php Capability registration LinkedIn registered at line ~486
app/Extensions/ContentManager/System/Services/AgentCore/BrandVoiceContextBuilder.php Context builder Feeds into LinkedIn capability, but not reverse
docs/features/linkedin-insights.md Public feature docs Claims exceed current API capability
PARTNER_OUTPUT_PORTABILITY_AUDIT.md Strategic framework Classifies social media as "Dare to Be Bad"