Skip to content

feat(cloud): add LinkedIn Ads provider - #11769

Merged
lalalune merged 1 commit into
developfrom
feat/11663-linkedin-ads-provider
Jul 3, 2026
Merged

lalalune merged 1 commit into
developfrom
feat/11663-linkedin-ads-provider

Conversation

@lalalune

@lalalune lalalune commented Jul 3, 2026

Copy link
Copy Markdown
Member

Closes #11663. Refs #11361.

What

Adds linkedin as a cloud advertising platform, wired end-to-end into the existing AdProvider contract — same shape as the sibling X/Twitter (#11662) and Reddit (#11661) lanes:

  • AdPlatform (DB typing + organization_urn metadata), AdPlatformSchema, spend markup (1.1), provider registry, and the app-promotion route platform enum.
  • New packages/cloud/shared/src/lib/services/advertising/providers/linkedin.ts against the versioned LinkedIn Marketing API (api.linkedin.com/rest, LinkedIn-Version + X-Restli-Protocol-Version: 2.0.0, OAuth2 bearer):
    • Account discovery/validation via the adAccounts search finder (Rest.li structured query, raw-query builder so parens/colons stay unencoded).
    • Campaign create = campaign group + paused campaign (x-restli-id extraction), objective mapping, daily/total budget mapping (lifetime requires an end date — validated fail-fast), geo targeting (urn:li:geo URNs pass through, numeric ids wrapped, free-text fails loudly before any platform call, Worldwide default).
    • feat(advertising): add campaign bid controls #11621 bid controls → costType/optimizationTargetType per the documented allowable-combinations table (cpm→CPM, cpc→CPC, cpa→CPM+MAX_CONVERSION; reach/clicks/conversions→MAX_IMPRESSION/MAX_CLICK/MAX_CONVERSION; objective-default auto-bid otherwise; never a manual unitCost).
    • Update/pause/activate via Rest.li PARTIAL_UPDATE patches (budget patch targets whichever of dailyBudget/totalBudget the live campaign uses); delete via PENDING_DELETION for campaign then group.
    • Media upload through the Images/Videos APIs (initializeUpload → byte PUT → finalizeUpload for multipart video), owner resolved from the ad account's organization reference; getMediaStatus maps AVAILABLE→ready.
    • Creative create as an inline dark post (creatives?action=createInline) with CTA label + landing-page mapping; requires a prior provider asset (fail-closed otherwise).
    • Analytics via the adAnalytics analytics finder (CAMPAIGN pivot, explicit fields, costInLocalCurrency/externalWebsiteConversions/oneClickLeads mapping).
    • OAuth2 refresh_token grant (LINKEDIN_ADS_CLIENT_ID/LINKEDIN_ADS_CLIENT_SECRET).

Tests

linkedin.test.ts — 16 pass / 0 fail (78 asserts), fixtures lifted from the Microsoft Learn LinkedIn Marketing API sample responses (account search, ad account fetch, image GET, adAnalytics rows, urn:li:sponsoredCreative:120491345), not invented. Includes a service-integration block that drives advertisingService.createCampaign with the real registered provider (mocked fetch):

linkedin.real.test.ts — credential-gated live lane; loud-skips ([LinkedInAdsRealTest] SKIPPED: set LINKEDIN_ADS_ACCESS_TOKEN ...) without credentials.

Verification (local = merge gate)

  • bun test .../providers/linkedin.test.ts — 16 pass / 0 fail (re-run green after rebase onto latest develop)
  • Existing ad suites: 6 service files — 58 pass / 0 fail; 3 API route files — 14 pass / 0 fail
  • bun run --cwd packages/cloud/shared typecheck ✅ · bun run --cwd packages/cloud/api typecheck ✅ · biome clean on touched files
  • Evidence: .github/issue-evidence/11663-linkedin-ads-provider.md (docs reviewed, fixture provenance, N/A items with reasons)

Live-credential evidence is deferred until operator LinkedIn Ads API credentials are provisioned (the real lane is in place), matching the issue's post-merge live-lane acceptance criterion.

🤖 Generated with Claude Code

Adds `linkedin` to AdPlatform across schemas, DB typing, credit markup,
the provider registry, and app-promotion validation, plus a real
LinkedIn Marketing API provider (versioned REST gateway):

- adAccounts search finder for account discovery/validation
- campaign group + paused campaign creation with objective, budget,
  geo-targeting (urn:li:geo pass-through, worldwide default, loud
  failure on free-text locations), and #11621 bid-control mapping to
  costType/optimizationTargetType per the documented combinations
- Rest.li PARTIAL_UPDATE for update/pause/activate and
  PENDING_DELETION deletes
- Images/Videos API media upload (initializeUpload -> PUT ->
  finalizeUpload) owned by the account's organization reference
- inline dark-post creative creation (creatives?action=createInline)
- adAnalytics analytics-finder metrics mapping
- OAuth2 refresh_token grant support

Unit tests use fixtures lifted from the Microsoft Learn LinkedIn
Marketing API reference pages and drive advertisingService with the
real provider to prove the #11619 approval gate, #11621 bid metadata,
and the fail-closed refund path apply to LinkedIn automatically.
A credential-gated linkedin.real.test.ts live lane loud-skips without
LINKEDIN_ADS_ACCESS_TOKEN.

Closes #11663. Refs #11361.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d85bf889-fc21-4b95-a528-b2409c7d2451

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/11663-linkedin-ads-provider

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lalalune

lalalune commented Jul 3, 2026

Copy link
Copy Markdown
Member Author

Reviewed and validated locally.

What I checked:

  • Compared the new LinkedIn provider against the current Microsoft Learn LinkedIn Marketing API docs for versioned Rest.li headers, account discovery, campaign create/update, creative createInline, media status, and adAnalytics request shape.
  • Confirmed the issue acceptance explicitly allows live network evidence once operator LinkedIn Ads credentials are provisioned; the branch includes the credential-gated linkedin.real.test.ts lane.

Validation in /home/shaw/eliza-worktrees/pr-11769-linkedin-ads:

  • bun test packages/cloud/shared/src/lib/services/advertising/providers/linkedin.test.ts packages/cloud/shared/src/lib/services/advertising/providers/linkedin.real.test.ts - pass (16 provider/service tests, 2 live tests skipped loudly due missing LINKEDIN_ADS_ACCESS_TOKEN)
  • bun run --cwd packages/cloud/shared typecheck - pass
  • bun run --cwd packages/cloud/api typecheck - pass
  • targeted bunx @biomejs/biome check ... - pass
  • git diff --check origin/develop...HEAD - pass

Residual: no live LinkedIn account/campaign/creative/media artifact was captured in this environment because approved LinkedIn Ads API credentials are not provisioned. That matches the issue's post-merge live-lane acceptance note.

@lalalune
lalalune merged commit e718ef5 into develop Jul 3, 2026
37 of 45 checks passed
@lalalune
lalalune deleted the feat/11663-linkedin-ads-provider branch July 3, 2026 03:10
@claude

claude Bot commented Jul 3, 2026 •

Copy link
Copy Markdown
Contributor

Claude encountered an error —— View job


I'll analyze this and get back to you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cloud advertising: LinkedIn Ads provider (#11361)

2 participants