Ads Platform Integrations
UniPulse integrates with Meta Ads (Facebook/Instagram) and TikTok Promote for campaign management, post boosting, and ad performance tracking.
This page describes the intended design. No Marketing API code exists:
- No
ads_read/ads_managementscopes are requested. Facebook OAuth asks forpages_*andbusiness_managementonly. - There is no ad-account connection, and no campaign create, activate, pause or resume calls.
boostPost(apps/api/src/services/ads.service.ts) writes a localPENDINGrow. Itstriggeris an explicitBoostTrigger(MANUAL/AUTO_RULE/WORKFLOW), and the publication and account must belong to the calling workspace.ad-sync.worker.tsonly marks campaignsCOMPLETEDafterendsAt.AdCampaign.lastSyncedAtis always null, so spend is always 0 and ROAS is null.
Every /ads campaign, performance and ROAS response carries meta.integration = { source: 'UNAVAILABLE', reason: 'MARKETING_API_NOT_IMPLEMENTED' } (ADS_MARKETING_API_PROVENANCE in @omnisocial/shared), and the Ads page renders a banner from it. Change that constant only when real provider calls ship.
The Ad Library (POST /competitors/search-ads) returns meta.source: LIVE, MOCK (sample ads, only without credentials; badged and not saveable) or UNAVAILABLE (Meta refused; no results). Release-gate status: Pulse/docs/remediation/25-meta-e2e-release-gate.md.
Supported Platforms (planned)
| Platform | API | Features | Sync Queue |
|---|---|---|---|
| Meta Ads | Marketing API v19+ | Create campaigns, boost posts, read insights | ad-sync |
| TikTok Promote | TikTok Ads API | Boost TikTok videos, read performance | ad-sync |
Meta Ads Manager
Authentication
OAuth 2.0 via Facebook Login with the ads_management and ads_read permissions. Uses the same token as the social account connection.
Capabilities
| Feature | API Call | Description |
|---|---|---|
| Create campaign | Marketing API - Campaign creation | Full campaign setup with targeting |
| Boost post | Marketing API - Promote existing post | Turn organic post into ad |
| Read insights | GET /act_{ad-account-id}/insights | Spend, reach, conversions, CPM, CPC |
| Manage ad sets | Marketing API - Ad set CRUD | Audience targeting, budget, schedule |
Auto-Boost Rules
The AutoBoostRule model enables automatic post promotion when performance thresholds are met:
| Rule Condition | Example | Action |
|---|---|---|
| Engagement rate above threshold | engagementRate > 5% | Boost with specified budget |
| Reach above threshold | reach > 10000 in first 2 hours | Boost for wider reach |
| Specific content type | classification.category == 'promotional' | Boost with conversion objective |
Configuration
// AutoBoostRule example
{
workspaceId: 'ws_123',
conditions: {
metric: 'engagementRate',
operator: 'gt',
value: 5,
timeWindow: '2h',
},
budget: {
amount: 50,
currency: 'USD',
duration: '3d',
},
targeting: {
audiences: ['existing_followers', 'lookalike'],
},
enabled: true,
}
TikTok Promote
Authentication
TikTok Ads OAuth with promote.create and promote.read scopes. Linked to the existing TikTok social account.
Capabilities
| Feature | Description |
|---|---|
| Boost videos | Promote existing TikTok videos to wider audience |
| Read performance | Campaign views, clicks, spend, conversions |
| Auto-boost | Same rule engine as Meta, targeting TikTok content |
Ad Performance Sync
The ad-sync queue periodically fetches ad performance metrics from both platforms:
Synced Metrics
| Metric | Meta Ads | TikTok Promote |
|---|---|---|
| Spend | Yes | Yes |
| Reach | Yes | Yes |
| Impressions | Yes | Yes |
| Clicks | Yes | Yes |
| Conversions | Yes | Yes |
| CPM | Yes | Calculated |
| CPC | Yes | Calculated |
| ROAS | Yes (with e-commerce) | Yes (with e-commerce) |
Database Models
| Model | Purpose | Key Fields |
|---|---|---|
AdCampaign | Ad campaign records | workspaceId, platform, externalId, budget, status, metrics |
AutoBoostRule | Automatic boost rules | workspaceId, conditions, budget, targeting, enabled |
API Endpoints
| Endpoint | Method | Description |
|---|---|---|
/ads/boost | POST | Create a manual boost campaign |
/ads/campaigns | GET | List campaigns (supports ?page, ?limit, ?status, ?platform) |
/ads/campaigns/:id | GET | Get a single campaign |
/ads/campaigns/:id/pause | PUT | Pause an active campaign |
/ads/campaigns/:id/resume | PUT | Resume a paused campaign |
/ads/suggestions | GET | AI boost suggestions for high-engagement unboosted posts |
/ads/rules | POST | Create an auto-boost rule |
/ads/rules | GET | List all rules |
/ads/rules/:id | PUT | Update a rule |
/ads/rules/:id | DELETE | Delete a rule |
All ads routes require authenticate, requireWorkspace('EDITOR'), and requireFeature('ads').
triggeredBy Split (Migration 20260311010000)
The AdCampaign model now has explicit trigger fields:
| Field | Type | Description |
|---|---|---|
triggeredBy | String? | Legacy field (kept for backwards compat) |
triggerType | AdTriggerType | MANUAL or AUTO_RULE (default: MANUAL) |
triggeredByRuleId | String? | FK to AutoBoostRule (null for manual campaigns) |
The migration backfills existing data: any campaign with a non-null triggeredBy gets triggerType = AUTO_RULE and triggeredByRuleId set to the same value.
AI Boost Suggestions
The getBoostSuggestions() service function analyzes recent publications (last 7 days) and returns up to 10 unboosted posts that outperform the workspace average by 50%+.
Each suggestion includes:
postPublicationIdandplatformreason:viral_potential(>3x avg),high_engagement(>2x avg), orabove_average(>1.5x avg)suggestedBudget: Scaled by engagement multiplier, capped at $500confidence: 0.5-0.95 based on how far above average
Revenue Predictions + Ad Spend
The predictRevenue() function now aggregates ad spend data (totalAdSpend, avgAdSpendPerCampaign, activeCampaignCount) and includes it in the Gemini prompt for more accurate revenue predictions that account for paid distribution.
- Social APIs -- underlying platform auth tokens
- Queue System -- ad-sync queue
- Analytics -- unified organic + paid analytics
- Schema Overview -- AdCampaign and AutoBoostRule models