Feed Configuration Guide
This guide clarifies the three distinct feed management areas in GP3MixMedia TrailMix and when to use each.
Quick Reference
| Location | Purpose | Who Uses It | When to Use |
|---|---|---|---|
/admin/sources | Define available sources | System admins | Setting up new source libraries |
/ingestion/feeds | Monitor feed execution | Analysts & admins | Checking feed health, triggering runs |
/campaigns/[id]/settings → Feeds | Campaign-specific config | Campaign analysts | Enabling feeds for a campaign |
1. Admin Sources (/admin/sources)
Purpose: Define the library of available data sources that campaigns can use.
Who should use this: System administrators only.
What you configure here:
Pre-Configured Sources Tab
- Global Political Sources: National-level news sources available to all campaigns
- State-Specific Sources: Regional news sources filtered by campaign state
- Source types: newspapers, X/Twitter lists
- Ad-buy keywords for content matching
Newsletter Sources Tab
- Newsletter ingestion endpoints
- Email addresses and webhook configurations
- Parser settings for different newsletter formats
Example use cases:
- Adding a new national news outlet to the source library
- Setting up state-specific news sources for a new market
- Configuring a newsletter subscription for the system
Key point: Sources configured here become available for campaigns to enable. They don't automatically run for any campaign until enabled in campaign settings.
2. Ingestion Feeds (/ingestion/feeds)
Purpose: Monitor and control system-wide data ingestion pipelines.
Who should use this: Campaign analysts and system administrators.
What you see here:
- All registered feed types with status indicators
- Last run time and duration for each feed
- Registration status and scheduled cadence
- Error details for failed runs
- Lock status (prevents duplicate runs)
Available feed types:
| Feed Type | Description | Schedule | Source |
|---|---|---|---|
| FCC Political Files | Broadcast advertising disclosures | Every 6 hours | FCC Public Files API |
| FEC 24-Hour Filings | Independent Expenditure filings | Every 4 hours | OpenFEC API |
| AdImpact | Industry advertising intelligence (dual-feed) | Hourly | AdImpact API |
| Google Political Ads | Google Search/YouTube ads | 3x daily (6am, 2pm, 10pm) | BigQuery |
| Meta Ads Library | Facebook/Instagram political ads | Every 8 hours | Meta Ad Library API |
| Newsletters | Rep emails and newsletter polling | Continuous | Gmail API |
| Mailbox Routing | Campaign-specific email routing | Real-time | Gmail API |
Actions available:
- Trigger manual feed runs ("Run Now" button)
- View run history and error details
- Monitor feed health metrics
Key point: This page shows the system-wide execution status of all feeds. Individual campaigns must still enable and configure feeds in their settings for data to be routed to them.
3. Campaign Feeds (/campaigns/[id]/settings → Feeds Tab)
Purpose: Configure which data sources are enabled for a specific campaign and set campaign-specific parameters.
Who should use this: Campaign analysts assigned to the campaign.
What you configure here:
FCC Political Files
- DMAs (Markets): Select which markets to monitor
- Advertiser Keywords: Names as they appear in FCC filings
- Regex Patterns: Custom matching rules for advertiser names
- Station Roster: Override automatic station detection
AdImpact
- Endpoint URL: AIDE workflow endpoint
- API Token: Authentication credentials
- Campaign ID Filters: Limit to specific AdImpact campaigns
- Market ID Filters: Limit to specific markets
AIDE Pull/Post Integration
The AIDE/AdImpact integration uses a two-sided webhook model:
- Outbound "pull" request: GP3MixMedia sends a request to AIDE telling it what data to fetch
- Inbound "post" response: AIDE delivers results back to our endpoint
Configuration Fields:
| Field | Type | Description |
|---|---|---|
aide_webhook_url | string | Outbound URL to trigger AIDE (required) |
aide_webhook_token | string | Token for outbound webhook authentication (required) |
aide_post_token | string | Token AIDE uses to authenticate when POSTing to us (required) |
aide_date_mode | enum | timestamp (ISO) or date (YYYY-MM-DD) |
aide_election_year | int | Election year (e.g., 2024) |
aide_state_code | string | 2-letter state code (e.g., MA) |
aide_office_type | enum | See office types below |
aide_district_number | int | Required for district-based races |
Office Types:
governorlt_governorattorney_generalus_senateus_house(requires district_number)state_senate_district(requires district_number)state_house_district(requires district_number)
Race String Format:
The outbound webhook body includes a race field with one of these formats:
[ST] Governor [YYYY][ST] Lt. Governor [YYYY][ST] Attorney General [YYYY][ST] Senate [YYYY][ST] CD-[##] [YYYY](Congressional District, e.g., "MA CD-07 2024")[ST] SD-[##] [YYYY](State Senate District)[ST] HD-[##] [YYYY](State House District)
District numbers are left-padded to 2 digits (e.g., 7 becomes "07").
Outbound Webhook Body:
{
"race": "MA Governor 2024",
"date_of_last_run": "2024-01-15T10:30:00.000Z",
"look_back_date": "2024-01-14"
}
The date_of_last_run is derived from the last successful ingest. If no prior success, defaults to now minus 7 days. The look_back_date is always date_of_last_run minus 1 day.
Inbound POST Endpoint:
- URL:
/api/v1/aide/inbound - Authentication: Bearer token or
tokenquery parameter - Required:
campaignIdquery param ORcorrelationIdOR matching race/date fields
Token Placement: By default, tokens are sent in both:
- Query parameter:
?token=... - Authorization header:
Authorization: Bearer <token>
This can be configured via aide_token_placement: query_param, bearer_header, or both.
OpenFEC
- FEC Committee ID: Required (format: C00XXXXXX)
- FEC Candidate ID: Optional
- Filing Types: Schedule E, Electioneering Communications, 24-Hour IE Reports
Meta Ads Library
The Meta Ads Library feed pulls political and issue ads from Facebook and Instagram via the Meta Ad Library API.
Prerequisites (System-Level)
Before the Meta feed can work, an admin must configure these environment variables (or Fly.io secrets):
| Variable | Required? | Purpose |
|---|---|---|
META_ADS_API_TOKEN | Yes | Facebook access token with ads_read permission |
META_ADS_CRON | No (default: 0 */8 * * *) | Feed schedule (every 8 hours) |
META_ADS_API_VERSION | No (default: v20.0) | Graph API version |
META_ADS_PER_PAGE | No (default: 100) | Results per API page (max 250) |
META_ADS_MAX_PAGES | No (default: 10) | Max pages per search term |
META_ADS_SEARCH_COUNTRIES | No (default: US) | Country filter |
META_ADS_REQUEST_TIMEOUT_MS | No (default: 60000) | Request timeout in ms |
META_ADS_LOOKBACK_DAYS | No (default: 21) | Minimum lookback window in days |
How to get a Meta access token:
- Go to Meta for Developers and create a developer account
- Create a new app (type: Business) or use an existing one
- Add the "Marketing API" product to your app
- Apply for Ad Library API access (requires identity verification)
- Once approved, generate a long-lived access token:
- Go to the Graph API Explorer
- Select your app
- Add
ads_readpermission - Generate a User Access Token
- Exchange it for a long-lived token (valid 60 days):
GET https://graph.facebook.com/v20.0/oauth/access_token? grant_type=fb_exchange_token& client_id={app-id}& client_secret={app-secret}& fb_exchange_token={short-lived-token}
- Set the token:
fly secrets set META_ADS_API_TOKEN=<token> --app gp3mixmedia-web
Token Renewal: Long-lived tokens expire after 60 days. Set a calendar reminder to regenerate and rotate the token before expiry. If the token expires, Meta feed runs will fail with auth errors visible in /ingestion/feeds.
Campaign-Level Configuration
Once the system-level token is configured, enable Meta Ads for individual campaigns:
- Go to Campaign Settings → Feeds tab
- Find the Meta Ads Library section
- Configure:
| Field | Required? | Description |
|---|---|---|
| Search Terms | Recommended | Keywords to find relevant ads. If left empty, terms are auto-derived from the campaign's candidate names and advertiser keywords |
| Advertiser IDs | Optional | Specific Facebook Page IDs to monitor (use if you know the exact advertiser pages) |
| Geographic Scope | Optional | Override the default country filter |
Tips for search terms:
- Use candidate names, PAC names, and committee names
- The system will also auto-derive terms from campaign metadata if search terms are empty
- If
META_ADS_ALLOW_KEYWORD_FALLBACKistrue(default), the system falls back to keyword search when page-based search yields few results
How the Feed Works
- Runs on the
META_ADS_CRONschedule (default: every 8 hours) - For each campaign with Meta Ads enabled:
- Builds search queries from configured or auto-derived terms
- Queries
graph.facebook.com/v20.0/ads_archivefor political/issue ads - Paginates through results (up to
META_ADS_MAX_PAGESpages) - Applies inter-request delay (
META_ADS_INTER_REQUEST_DELAY_MS) to avoid rate limiting - Falls back to keyword search if primary search returns few results
- Matching ads are converted to buy line candidates with fields: advertiser, spend range, impressions, platform, creative URL
- Buy line candidates go through deduplication before appearing in the campaign Matrix
- Circuit breaker protects against API failures; partial results are saved on timeouts
Troubleshooting Meta Feed
| Issue | Solution |
|---|---|
| Feed not running | Check META_ADS_API_TOKEN is set (not expired) |
| No results | Verify search terms match actual ad content; try broader terms |
| Rate limited | Increase META_ADS_INTER_REQUEST_DELAY_MS (default 1000ms) |
| Timeouts | Increase META_ADS_REQUEST_TIMEOUT_MS (default 60s, max 120s) |
| Token expired | Regenerate long-lived token (see steps above) |
Key Files
- Feed worker:
src/workers/feeds/meta-ads-library.ts - API client:
lib/services/social-creative/meta-ads-library.client.ts
Google Political Ads
- Advertiser Discovery: Auto-discovers advertisers from campaign keywords
- Geographic Filtering: Enhanced geo matching by state/region
- Date Range: Historical data lookback period
- Data Source: Google BigQuery public dataset
- Relevance Validation: Automatic detection of unrelated advertisers (wrong-campaign guardrail)
Advertiser Relevance Guardrail: The system validates that ingested advertisers are relevant to the campaign's state. If more than 30% of advertisers appear unrelated to the campaign state/office:
- An anomaly is logged in the feed run metadata
- A recommendation is provided (
proceed,fallback_to_keywords, orabort) - This helps detect and prevent wrong-campaign ingestion issues
Date Acceptance Rules for Buy Lines
When creating buy lines from ingested data, the system accepts records with various date fields in priority order:
start_date/end_date- Primary date fieldsspend_period- Combined date range (e.g., "9/28/25 - 10/4/25")flight_start_date/flight_end_date- Flight date fieldsair_date- When the ad actually airedspend_date- When spend was recordedbooking_date- When the ad was bookedupdate_date- Last update timestamp
Key rules:
- Records are NOT rejected if
start_date/end_dateare missing, as long as at least one alternative date field exists - When only a single date is available (e.g., just
air_date), it's used for both start and end date - The minimum required fields for a valid buy line are:
- Advertiser (org_name, advertiser_name, or similar)
- Amount/Spend (one of: gross_cents, spend_cents, amount, etc.)
- At least one date field from the priority list above
Mailbox Routing
- Define email pattern matching rules
- Route emails from specific senders to this campaign
- Toggle rules on/off
Key point: This is where you connect global data sources to your specific campaign. Without configuration here, data won't be routed to your campaign even if the feed is running.
Data Flow Overview
┌─────────────────────────────────────────────────────────────────┐
│ 1. ADMIN: Define Sources (/admin/sources) │
│ → Creates library of available data sources │
└──────────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 2. MONITOR: Ingestion Feeds (/ingestion/feeds) │
│ → View all feed execution status │
│ → Trigger manual runs │
│ → Monitor health and errors │
└──────────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 3. CONFIGURE: Campaign Settings (/campaigns/[id]/settings) │
│ → Enable feeds for specific campaign │
│ → Set campaign-specific parameters (DMAs, keywords, etc.) │
│ → Create routing rules │
└──────────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 4. RESULT: Data appears in campaign │
│ → Raw Inputs, Matrix, Dedupe, etc. │
└─────────────────────────────────────────────────────────────────┘
Common Questions
"I added sources in Admin but don't see any data"
Sources in /admin/sources define available sources. You must:
- Go to the campaign's Settings → Feeds tab
- Enable the specific feeds you want
- Configure campaign-specific parameters (keywords, DMAs, etc.)
"My feed shows 'Error' in the Ingestion Feeds page"
- Click on the feed to see error details
- Check if it's a temporary network issue (will auto-retry)
- For persistent errors, check configuration in
/admin/sources - Contact system admin if credentials or endpoints need updating
"What's the difference between global and state news sources?"
- Global: Available to all campaigns (national political news)
- State: Filtered by campaign state (local/regional news)
Both are configured in
/admin/sourcesand enabled per-campaign in Settings.
"How often do feeds run?"
Each feed type has a default schedule (see table above). You can:
- View last run time in
/ingestion/feeds - Trigger immediate runs with "Run Now" button
- Note: Global schedule cannot be changed per-campaign
Permissions by Role
| Action | Viewer | Analyst | Admin |
|---|---|---|---|
| View Ingestion Feeds | Yes | Yes | Yes |
| Trigger manual runs | No | Yes | Yes |
| Configure campaign feeds | No | Yes | Yes |
| Configure admin sources | No | No | Yes |
Related Documentation
Last Updated: 2026-03-26 (Meta Ads Library setup guide)
Was this helpful? If you have feedback or questions, please contact your administrator.