Resources
| Resource | What it is |
|---|---|
| Ad Campaign | Top-level container: objective and (optionally) budget. |
| Ad Group | Targeting, placements, and budget for a set of ads. |
| Ad | The creative unit — copy, assets, destination URL. |
| Audience | A custom audience to target or exclude in ad groups. |
One-time setup
- Scopes:
media:generate,media:read,ad_campaign:create,ad_campaign:update,ad_campaign:basic:read,social_account:read. - Facebook page:
GET /social_accountsmust have a"platform": "facebook"entry. If not:POST /social_accounts{"platform": "meta_business", "scopes": ["advertise"], "redirect_url": "..."}→ send the user to the returnedauthorize_url. - Ads payment method: connected in the dashboard (balance or card). Only launching needs it — drafts work without.
Generate a creative
amount_charged; not enough balance → 402 with a deposit_url). Poll GET /media/{id} until status is ready, then use file.id:failed refunds the charge — tweak the prompt and retry. Video: "type": "video" plus optional duration_seconds (5/10/15), resolution (480p–4k), and up to 4 reference_media file IDs (first one seeds the opening frame).Create and launch the ad — one request
POST /ads with an inline ad_group and ad_campaign creates the whole tree in one transaction and launches it:"status": "draft" to ad_campaign, then launch later with PATCH /ad_campaigns/{id} {"status": "active"}.| Field | Rule |
|---|---|
creatives | One entry with no format is required (the base asset). square / vertical / horizontal are optional crops on top. |
url | Required for website ads. Your whop.com store page works as-is; an external page needs your Whop pixel installed. |
| Budget | One level owns it: budget_amount on the ad group (default), or on the campaign with budget_optimization: "ad_campaign" — never both. |
| Targeting | Omit demographics / placements / devices for automatic optimization. regions uses ISO 3166 ("US", "US-CA"). |
lead_form | Only with an instant-form conversion_location. |
ad_group_id instead of ad_group, or ad_campaign_id instead of ad_campaign.Monitor
Poll
GET /ads/{id}: delivery_status is the live state (a brief in_review for content moderation is normal), and issues[] carries a human-readable message for anything Meta rejected asynchronously.Control delivery with pause / unpause on ads, ad groups, or campaigns. Edit copy or creatives with PATCH /ads/{id} — sending creatives replaces the whole set, so include the base entry.Errors
Every gate is a400 whose message says what to fix:
| Error contains | Fix |
|---|---|
The Whop pixel was not detected on <url> | Use your own whop.com store page, or install the pixel on the landing page. |
A Facebook page is required | Connect one via POST /social_accounts (human OAuth step), pass it in social_accounts. |
Connect an ads payment method before launching | Create with ad_campaign.status: "draft", have the user connect a payment method in the dashboard, then PATCH to active. |
Include a base creative | Add a creatives entry with no format. |
A destination URL is required | Pass url. |
budget_amount is required / can't be set | Move the budget to the right level (see field rules above). |
402 + deposit_url (media) | Send the user to deposit_url to top up, then retry. |

