Meta Ads via Plai
Manage Meta (Facebook & Instagram) advertising through the Plai API.
Setup
Requires two environment variables:
PLAI_API_KEY — your Plai API key (from your Plai account dashboard)
PLAI_USER_ID — your Plai user ID
How to Run
All operations are run via:
node scripts/plai-meta.js <command> [options]
Options are passed as --key value pairs. JSON array/object values must be
passed as quoted JSON strings, e.g. --locations '[{"id":"2537","type":"city","countryCode":"AE"}]'.
Account Connection
This must be done before any ad operation. If an ad tool returns
"User has no connected Facebook account" or similar, generate a connection
link and share it with the user so they can complete the OAuth flow.
Generate an account connection link
# Connect Facebook / Instagram Ads account
node scripts/plai-meta.js create-connection-link --platform FACEBOOK
# Connect Google Ads account
node scripts/plai-meta.js create-connection-link --platform GOOGLE
# With a custom redirect after connection
node scripts/plai-meta.js create-connection-link \
--platform FACEBOOK \
--redirectUri "https://yourapp.com/connected"
Required: --platform (FACEBOOK | GOOGLE)
Optional: --redirectUri — URL the user lands on after completing OAuth
Returns a link the user must open in their browser to connect their account.
Share this link directly; do not attempt to open it automatically.
Campaign Management
List all campaigns
node scripts/plai-meta.js list-campaigns
Create a campaign
node scripts/plai-meta.js create-campaign \
--campaignName "My Campaign" \
--campaignType LEAD_GENERATION \
--budget 50 \
--url "https://example.com" \
--locations '[{"id":"2537","type":"city","countryCode":"AE"}]' \
--primaryText '[{"text":"Join us today!"}]' \
--images '["https://example.com/image.jpg"]' \
--leadsFormId "1234567890"
Required params: campaignName, campaignType, budget, locations
Required by type:
LEAD_GENERATION → also requires --leadsFormId
CONVERSIONS / OUTCOME_SALES → also requires --pixelId and --conversionGoal
Optional params: --url, --primaryText, --headlines, --images, --videos,
--interests, --gender ("1" male / "2" female), --minAge, --maxAge,
--targetLocales, --audienceId
Notes:
--images takes a JSON array of URL strings — they are automatically transformed to {url} objects.
--minAge and --maxAge must both be provided together.
callToAction is always LEARN_MORE (set automatically).
campaignType values: LEAD_GENERATION | LINK_CLICKS | CONVERSIONS | OUTCOME_SALES
Create a message engagement campaign
Drives conversations via messaging apps (Messenger, Instagram DM, WhatsApp).
node scripts/plai-meta.js create-message-engagement \
--campaignName "Chat with Us" \
--budget 50 \
--url "https://example.com" \
--locations '[{"id":"2537","type":"city","countryCode":"AE"}]' \
--messagingApps '["MESSENGER","WHATSAPP"]' \
--multipleCreatives '[{
"url": "https://example.com",
"primaryText": "Chat with us today!",
"greetingText": "Hi {{user_first_name}}! How can we help you?",
"conversations": [{"title": "Learn more about our products"},{"title": "Get a quote"}],
"image": "https://example.com/image.jpg"
}]'
Required params: campaignName, budget, url, locations, messagingApps, multipleCreatives
messagingApps — JSON array of: MESSENGER | INSTAGRAM | WHATSAPP — always ask the user; never default.
multipleCreatives — JSON array of 1–10 creative objects. Each creative:
- Required:
url, primaryText, greetingText, conversations (array of {title} objects)
- Optional:
headlines, phoneNumber, image (URL), video ({video_id, thumbnail_url})
Optional campaign params: --interests, --gender, --minAge, --maxAge, --targetLocales, --audienceId
Get campaign insights
node scripts/plai-meta.js get-insights \
--campaignId "123456789" \
--startDate "2024-01-01" \
--endDate "2024-01-31"
Update campaign status
node scripts/plai-meta.js update-status --campaignId "123456789" --status ACTIVE
node scripts/plai-meta.js update-status --campaignId "123456789" --status PAUSED
node scripts/plai-meta.js update-status --campaignId "123456789" --status DELETE
status values: ACTIVE | PAUSED | DELETE
Update campaign budget
node scripts/plai-meta.js update-budget --campaignId "123456789" --amount 100
Lead Forms
Check Lead Ads Terms of Service
Always run this before creating a lead form. If TOS is not accepted, the user
must accept at https://www.facebook.com/ads/leadgen/tos first.
node scripts/plai-meta.js check-leadform-tos
List existing lead forms
node scripts/plai-meta.js list-leadforms
Create a lead form
Requires user to provide all values — never assume or fabricate them.
node scripts/plai-meta.js create-leadform \
--leadsFormName "My Lead Form" \
--privacyPolicyUrl "https://example.com/privacy" \
--privacyPolicyName "Privacy Policy" \
--website_url "https://example.com/thank-you" \
--questionPageCustomHeadline "Tell us about yourself"
Targeting
Search targeting locations (always use before creating a campaign)
node scripts/plai-meta.js search-locations --query "Dubai"
node scripts/plai-meta.js search-locations --query "United States"
Returns {id, type, countryCode} objects. Pass these directly to create-campaign --locations.
Search targeting interests
node scripts/plai-meta.js search-interests --query "fitness"
node scripts/plai-meta.js search-interests --query "real estate" \
--specialAdCategories '["HOUSING"]' \
--specialAdCountries '["US","GB"]'
specialAdCategories values: HOUSING | FINANCIAL_PRODUCTS_SERVICES | EMPLOYMENT
Returns {id, name, type} objects. Pass to create-campaign --interests.
Get targeting locales (language targeting)
node scripts/plai-meta.js get-locales
Returns numeric locale IDs. Pass as --targetLocales '[65,24]' to create-campaign.
Audiences
List custom audiences
node scripts/plai-meta.js list-audiences
Create page engagers audience
node scripts/plai-meta.js create-page-audience \
--name "FB Page Engagers" \
--type FACEBOOK
node scripts/plai-meta.js create-page-audience \
--name "IG Engagers" \
--type INSTAGRAM
Create lead form audience
All fields are required — ask the user for each one explicitly.
node scripts/plai-meta.js create-leadform-audience \
--name "Lead Form Openers" \
--description "People who opened our summer campaign form" \
--leadFormIds '["12345","67890"]' \
--type open
type values: open | close | submit
Create lookalike audience
node scripts/plai-meta.js create-lookalike \
--sourceType AUDIENCE \
--sourceId "120231334546180123" \
--audienceName "Lookalike - US" \
--countryCode US
sourceType options: AUDIENCE (requires --sourceId) | PAGE (no sourceId needed)
Pixels & Conversions
List pixels
node scripts/plai-meta.js list-pixels
List custom conversions for a pixel
node scripts/plai-meta.js list-conversions \
--pixelId "123456789" \
--campaignType CONVERSIONS
campaignType options: CONVERSIONS | OUTCOME_SALES
Media
List ad account media
node scripts/plai-meta.js list-media --type IMAGE
node scripts/plai-meta.js list-media --type VIDEO
Returns {video_id, thumbnail_url} for videos. Use these in create-campaign --videos.
List Facebook page media
node scripts/plai-meta.js list-fb-media --type IMAGE
List Instagram page media
node scripts/plai-meta.js list-ig-media
Upload a video
node scripts/plai-meta.js upload-video --videoUrl "https://example.com/video.mp4"
Returns a videoId. Then poll until ready:
node scripts/plai-meta.js get-video-info --videoId "123456789"
Poll until status is READY, then use video_id + thumbnail_url in create-campaign --videos.
Product Catalogs
List catalogs
node scripts/plai-meta.js list-catalogs
Create a catalog
node scripts/plai-meta.js create-catalog --name "My Store Catalog"
Create a product feed (imports products from a URL)
node scripts/plai-meta.js create-catalog-feed \
--catalogId "123456" \
--name "Daily Feed" \
--fileUrl "https://example.com/products.csv" \
--currencyCode USD \
--timezone "America/New_York" \
--interval DAILY
Get catalog feed status
node scripts/plai-meta.js get-catalog-feed --catalogFeedId "123456"
List products in a product set
node scripts/plai-meta.js list-catalog-products --productSetId "123456"
Create a product set
node scripts/plai-meta.js create-product-set \
--catalogId "123456" \
--name "Summer Collection" \
--productIds '["prod1","prod2","prod3"]'
Workflow Guide
Before creating any campaign
- Run
create-connection-link --platform FACEBOOK if the Meta account is not yet connected — share the returned link with the user.
- Run
search-locations to get valid location IDs — never guess IDs.
- Optionally run
search-interests for interest targeting.
- For
LEAD_GENERATION: run check-leadform-tos, then list-leadforms or create-leadform.
- For
CONVERSIONS/OUTCOME_SALES: run list-pixels then list-conversions.
- Run
list-media if using existing account media for creatives.
Campaign type decision
| Goal | Command | campaignType |
|---|
| Collect leads via a form | create-campaign | LEAD_GENERATION |
| Drive traffic to a website | create-campaign | LINK_CLICKS |
| Optimize toward a pixel event | create-campaign | CONVERSIONS |
| Optimize toward purchases | create-campaign | OUTCOME_SALES |
| Drive messaging conversations | create-message-engagement | OUTCOME_ENGAGEMENT |
After creating a campaign
Campaigns are created paused by default. Run:
node scripts/plai-meta.js update-status --campaignId "<id>" --status ACTIVE
when the user confirms they are ready to go live.
Error Handling
| Error message | Cause | Fix |
|---|
Unauthorized | Invalid PLAI_API_KEY | Check the key in your Plai dashboard |
User has no connected Facebook account | Meta account not linked in Plai | Run create-connection-link --platform FACEBOOK and share the link with the user |
User has no connected Google account | Google Ads account not linked | Run create-connection-link --platform GOOGLE and share the link with the user |
| TOS not accepted | Lead Ads TOS pending | User must accept at facebook.com/ads/leadgen/tos |