openai-ads
Plan and manage OpenAI Ads (ChatGPT ads) campaigns end-to-end via the Hyper MCP — API-key auth, account discovery, geo targeting, image upload, chat_card and product-feed creatives, custom audiences, conversion tracking, status flow, and insights, with integer-micros money values
Install
npx skills add https://github.com/hyperfx-ai/marketing-skills/tree/main/skills/openai-ads
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install hyperfx-ai-marketing-skills@llmmart
git clone https://github.com/hyperfx-ai/marketing-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole hyperfx-ai/marketing-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
OpenAI Ads Campaigns
Strategic guide for managing the OpenAI Advertiser API. All operations go
through https://api.ads.openai.com/v1.
Requirements
- Hyper MCP installed and connected. https://app.hyperfx.ai/mcp
- OpenAI Ads integration connected (an OpenAI Ads API key, scoped to one ad account) at https://app.hyperfx.ai/apps.
If search("openai_ads_ad_accounts_get") does not find openai_ads_ad_accounts_get, stop and tell the user to enable Hyper MCP and connect OpenAI Ads. After connecting, openai_ads_health_check() verifies the key — if connected=false, the API key is missing, invalid, or expired.
How to run the tools in this skill
Every tool in this skill is named by its canonical tool name. Run it with the call your surface gives you:
| Surface | Find a tool | Run it |
|---|---|---|
| MCP client (Claude, Cursor, Codex, ChatGPT) | search("<what you want to do>"), then describe("<name>") |
call("<name>", {...}) |
| Hyper CLI | hyperai search "<what you want to do>", then hyperai describe <name> |
hyperai call <name> --json '{...}' |
If a tool is not found, its integration is not connected or not enabled for the workspace: stop and tell the user which integration to connect.
Out of scope — defer to other skills
- Creative generation (ad imagery, copy) →
ad-creative-generation/image-generation. - Cross-platform campaign launches → use this skill for OpenAI Ads, then invoke
meta-ads/google-adsseparately.
Critical Rules
CRITICAL: Auth is bearer API-key auth, not OAuth. One Ads API key is scoped to exactly one ad account. There is no
list ad accountsendpoint;openai_ads_ad_accounts_getreturns the connected account for that key.
CRITICAL: All money values on inputs are integer micros.
$1.00 = 1_000_000micros.$50/day = 50_000_000. Thedaily_spend_limit_microsminimum is1_000_000($1). The ad groupmax_bid_microsis capped at100_000_000($100). Insights responses use plain floats in account currency, not micros.
CRITICAL: Always create campaigns, ad groups, and ads with
status="paused". Surface what was created to the user, then activate using the dedicated activate endpoint after approval.
CRITICAL: Ads have
review_status. New ads enterin_reviewand will not serve untilreview_status="approved", even ifstatus="active".
CRITICAL:
chat_cardcreatives requiretarget_urlandfile_id. Upload the image first viaopenai_ads_images_upload, then pass the returnedfile_idtoopenai_ads_create. PNG, 1024x1024, <= 1 MB.
IMPORTANT: Creative types are
chat_cardandproduct_ad_template. Product-ad templates get image and destination URL from the selected product feed item, so they do not requirefile_idortarget_url.
IMPORTANT: Campaign
bidding_typecan beimpressionsorclicks. Ad groupbilling_event_typecan beimpressionorclick.
Tool surface
| Job | Tools |
|---|---|
| Account | openai_ads_ad_accounts_get, openai_ads_update_ad_account, openai_ads_activate_ad_account, openai_ads_pause_ad_account, openai_ads_health_check |
| Campaigns | openai_ads_campaigns_create, openai_ads_campaigns_get, openai_ads_campaigns_list, openai_ads_campaigns_update, openai_ads_campaigns_pause, openai_ads_campaigns_activate, openai_ads_campaigns_archive |
| Ad groups | openai_ads_ad_groups_create, openai_ads_ad_groups_get, openai_ads_ad_groups_list, openai_ads_ad_groups_update, openai_ads_ad_groups_pause, openai_ads_ad_groups_activate, openai_ads_ad_groups_archive |
| Ads | openai_ads_create, openai_ads_get, openai_ads_list, openai_ads_update, openai_ads_pause, openai_ads_activate, openai_ads_archive |
| Images & targeting | openai_ads_images_upload, openai_ads_search_geo_locations |
| Audiences | openai_ads_create_custom_audience, openai_ads_create_custom_audience_upload, openai_ads_get_custom_audience, openai_ads_list_custom_audiences, openai_ads_archive_custom_audience |
| Conversions | openai_ads_create_conversion_pixel, openai_ads_create_conversion_api_key, openai_ads_create_conversion_event_setting, openai_ads_list_conversion_event_settings, openai_ads_get_conversion_insights |
| Insights | openai_ads_account_insights_get, openai_ads_campaign_insights_get, openai_ads_ad_group_insights_get, openai_ads_insights_get |
| Cache snapshot | openai_ads_cache, openai_ads_caches_get, openai_ads_caches_refresh |
Phase 1: Account Discovery
Run these after connect:
openai_ads_ad_accounts_get()
openai_ads_campaigns_list(limit=100)
openai_ads_list_custom_audiences(limit=100)
openai_ads_list_conversion_event_settings(limit=100)
The connect-time context builder may have already populated a cached snapshot. Prefer:
openai_ads_caches_get()
If success=False because the cache is empty, refresh once:
openai_ads_caches_refresh()
Phase 2: Plan and Confirm
Before creating anything, confirm with the user:
- Objective in plain language.
- Daily and/or lifetime budget in account currency.
- Target geos: simple country codes like
["US", "GB"], or location IDs fromopenai_ads_search_geo_locations. - Any custom audiences to include or exclude.
- Whether this is a normal
chat_cardcampaign or a product-feed campaign. - Headline (
title, <= 50 chars), body (<= 100 chars), and click-through URL forchat_card. - One image asset for
chat_card, either a public URL or a base64 blob. - Max bid in micros (
max_bid_micros; for example2_000_000). - Optional
context_hints, short natural-language phrases that describe when the ad should show. - Optional conversion event setting IDs to attach to the campaign.
If anything is missing, ask. Do not invent budgets, geos, or copy.
All reference files live in
references/. Read them atreferences/<file>(e.g.references/campaign-creation.md).
Routing table
| The user wants to… | Read these files first |
|---|---|
| Launch a chat card or product-feed campaign | Phases 1–2 above → references/campaign-creation.md |
| Target regions / DMAs / custom geo | references/campaign-creation.md — Geo Targeting |
| Create or manage custom audiences | references/audiences-and-conversions.md |
| Set up conversion tracking (pixel, API key, event settings) | references/audiences-and-conversions.md |
| Activate after review | references/campaign-creation.md — Activation |
| Pull insights / manage status / refresh the cache snapshot | references/insights-and-operations.md |
Safety Rules
Never:
- Pass dollar amounts directly. All money inputs are micros.
- Activate a campaign, ad group, ad, or account without explicit user approval.
- Skip image upload for a
chat_card; it needs a realfile_id. - Use geo exclusions; use included geos and audience exclusions instead.
- Assume an ad is delivering just because
status="active". Always checkreview_status. - Treat
archiveas reversible. - Promise paid traffic on a new ad. New ads sit in
review_status="in_review"until OpenAI approves them.
Files (marketing-skills)
-
references
-
audiences-and-conversions.md 1.2 KB
# OpenAI Ads: Custom Audiences & Conversion Tracking ## Custom Audiences Use custom audiences for inclusion, exclusion, and bid multipliers. ```text openai_ads_create_custom_audience( name="Newsletter leads", members=[ {"identifier_type": "email_sha256", "value": "HASHED_EMAIL"}, ], ) ``` Then attach audience IDs to a campaign: ```text openai_ads_campaigns_create( name="Lead retargeting", status="paused", daily_spend_limit_micros=25_000_000, custom_audience_ids=["aud_123"], excluded_custom_audience_ids=["aud_456"], ) ``` Use `custom_audience_bid_multipliers` on ad groups when the user explicitly wants audience-level bid adjustments. ## Conversions Set up conversion measurement before attaching conversion settings to a campaign: ```text openai_ads_create_conversion_pixel(name="Website pixel") openai_ads_create_conversion_event_setting( name="Signup", event_type="custom", custom_event_name="signup", attribution_window_days=30, source_ids=["src_123"], ) ``` Attach conversion event setting IDs on campaign create/update: ```text openai_ads_campaigns_update( campaign_id="CAMPAIGN_ID", conversion_event_setting_ids=["ces_123"], ) ``` -
campaign-creation.md 3 KB
# OpenAI Ads: Campaign Creation (Chat Card & Product Feed) ## Geo Targeting Use `targeting_country_codes` for country-level targeting. For regions and DMAs, search locations first and pass the returned IDs: ```text openai_ads_search_geo_locations(q="San Francisco", limit=5) openai_ads_campaigns_create( name="Hyper - West Coast Test", status="paused", daily_spend_limit_micros=50_000_000, targeting_location_ids=["2000043", "3000194"], bidding_type="clicks", ) ``` Do not pass geo exclusions. The current API supports included locations and custom audience exclusions, but not location exclusions. ## Standard Chat Card Flow ```text openai_ads_campaigns_create( name="Hyper - US Test", status="paused", daily_spend_limit_micros=50_000_000, targeting_country_codes=["US"], description="Initial pilot", idempotency_key="hyper-us-test-2026-06-25", ) ``` ```text openai_ads_ad_groups_create( campaign_id="CAMPAIGN_ID", name="US - Marketing Operators", status="paused", max_bid_micros=2_000_000, context_hints=[ "user is asking about ad automation", "user wants to scale paid acquisition", ], idempotency_key="hyper-us-test-adgroup-2026-06-25", ) ``` ```text openai_ads_images_upload(image_url="https://cdn.example.com/asset.png") ``` ```text openai_ads_create( ad_group_id="AD_GROUP_ID", name="US - Marketing - Variant A", title="Run ads on autopilot", body="Hyper builds, ships, and optimizes ads for you.", target_url="https://example.com?utm_source=openai_ads", file_id=FILE_ID_FROM_UPLOAD, status="paused", creative_type="chat_card", idempotency_key="hyper-us-test-ad-a-2026-06-25", ) ``` Surface the returned `review_status`. ## Product Feed Flow For product-feed ads: - Create the campaign with `mode="product_feed"`. - Create an ad group with `product_feed_id` and optional `product_set_filters`. - Create one `product_ad_template` ad in that ad group. ```text openai_ads_campaigns_create( name="Catalog retargeting", status="paused", daily_spend_limit_micros=100_000_000, mode="product_feed", bidding_type="clicks", ) ``` ```text openai_ads_ad_groups_create( campaign_id="CAMPAIGN_ID", name="High intent catalog", status="paused", max_bid_micros=3_000_000, product_feed_id="feed_123", product_set_filters=[ {"field": "availability", "operator": "in", "values": ["in_stock"]}, ], ) ``` ```text openai_ads_create( ad_group_id="AD_GROUP_ID", name="Catalog template", title="{{product.title}}", body="Shop {{product.brand}} today.", price="{{product.price}}", status="paused", creative_type="product_ad_template", ) ``` ## Activation After the user reviews the paused tree: ```text openai_ads_campaigns_activate(campaign_id=CID) openai_ads_ad_groups_activate(ad_group_id=AGID) openai_ads_activate(ad_id=AID) ``` Activation only takes effect at the ad level once `review_status="approved"`. Poll `openai_ads_get(ad_id=...)` if you need to confirm review state. -
insights-and-operations.md 2.9 KB
# OpenAI Ads: Insights, Status Flow, Cache & Health ## Insights Insights endpoints are server-aggregated. Use current API query names: - `time_granularity`: `"hourly"`, `"daily"`, `"monthly"`, `"none"`. - `aggregation_level`: `"ad_account" | "campaign" | "ad_group" | "ad"`. - `time_ranges`: JSON objects such as `{"type":"date_range","since":"2026-05-01","until":"2026-05-07"}`. The toolkit also accepts legacy `"YYYY-MM-DD..YYYY-MM-DD"` strings and converts them. - `fields`: dot-style fields, e.g. `["metadata.readable_time", "campaign.id", "campaign.name", "campaign.spend"]`. - `filters`: JSON objects such as `{"field":"campaign.id","operator":"IN","value":["cmp_xxx"]}`. - `sort`: JSON objects such as `{"field":"campaign.spend","direction":"desc"}`. - `segments`: optional `product`, `country`, or `device`. - `includes`: optional `zero_impression_items` or `zero_impression_products`. ```text openai_ads_account_insights_get( time_granularity="daily", aggregation_level="campaign", time_ranges=[ {"type": "date_range", "since": "2026-05-01", "until": "2026-05-07"} ], fields=[ "metadata.readable_time", "campaign.id", "campaign.name", "campaign.clicks", "campaign.impressions", "campaign.spend", ], sort=[{"field": "campaign.spend", "direction": "desc"}], ) ``` For a single campaign use `openai_ads_campaign_insights_get(campaign_id=...)`; similar for ad groups and ads. For conversion totals: ```text openai_ads_get_conversion_insights( aggregation_level="campaign", time_ranges=[ {"type": "date_range", "since": "2026-05-01", "until": "2026-05-07"} ], entity_ids=["cmp_123"], ) ``` ## Status Flow `paused` <-> `active` -> `archived` (irreversible) at campaign, ad group, ad, and custom audience level. Account-level tools support activate and pause. Use the dedicated tools for status changes. Ads also have: - `review_status="in_review"`: not delivering yet. - `review_status="approved"`: eligible to deliver. - `review_status="rejected"`: needs a new creative. ## Cache Snapshot The connect-time context builder writes a snapshot of the account into `integration.toolkit_settings["openai_ads_cache"]`. Use: - `openai_ads_caches_get()` for cheap read-only access. - `openai_ads_caches_refresh()` to refresh account, campaigns, ad groups, ads, and insights. Refresh sparingly: once per session is plenty unless something feels stale. ## Pagination All list endpoints use cursor pagination: - `limit` (default 20, max 500). - `after` / `before`: pass the previous response's `last_id` / `first_id`. - `order`: `"asc"` | `"desc"`. Responses include `has_more`. Keep paging only when true and only when you actually need everything. ## Health Check After connecting, run `openai_ads_health_check()`. If `connected=false`, the API key is missing, invalid, or expired. Tell the user to regenerate it from the Ads Manager Settings API area.
-
-
SKILL.md 8 KB
--- name: openai-ads description: Plan and manage OpenAI Ads (ChatGPT ads) campaigns end-to-end via the Hyper MCP — API-key auth, account discovery, geo targeting, image upload, chat_card and product-feed creatives, custom audiences, conversion tracking, status flow, and insights, with integer-micros money values. Use when the user wants to launch OpenAI ads, ChatGPT ads, chat card ads, manage OpenAI ad groups or audiences, or pull OpenAI Ads insights. requires_toolkits: - openai_ads icon: openai_ads short_description: Plan and manage OpenAI Ads with chat cards, product feeds, audiences, and insights. --- # OpenAI Ads Campaigns Strategic guide for managing the OpenAI Advertiser API. All operations go through `https://api.ads.openai.com/v1`. ## Requirements - **Hyper MCP installed and connected.** [https://app.hyperfx.ai/mcp](https://app.hyperfx.ai/mcp) - **OpenAI Ads integration connected** (an OpenAI Ads API key, scoped to one ad account) at [https://app.hyperfx.ai/apps](https://app.hyperfx.ai/apps). If `search("openai_ads_ad_accounts_get")` does not find `openai_ads_ad_accounts_get`, stop and tell the user to enable Hyper MCP and connect OpenAI Ads. After connecting, `openai_ads_health_check()` verifies the key — if `connected=false`, the API key is missing, invalid, or expired. ### How to run the tools in this skill Every tool in this skill is named by its canonical tool name. Run it with the call your surface gives you: | Surface | Find a tool | Run it | | --- | --- | --- | | MCP client (Claude, Cursor, Codex, ChatGPT) | `search("<what you want to do>")`, then `describe("<name>")` | `call("<name>", {...})` | | Hyper CLI | `hyperai search "<what you want to do>"`, then `hyperai describe <name>` | `hyperai call <name> --json '{...}'` | If a tool is not found, its integration is not connected or not enabled for the workspace: stop and tell the user which integration to connect. ## Out of scope — defer to other skills - **Creative generation** (ad imagery, copy) → [`ad-creative-generation`](../ad-creative-generation) / [`image-generation`](../image-generation). - **Cross-platform campaign launches** → use this skill for OpenAI Ads, then invoke `meta-ads` / `google-ads` separately. ## Critical Rules > **CRITICAL**: Auth is bearer API-key auth, not OAuth. One Ads API key is > scoped to exactly one ad account. There is no `list ad accounts` endpoint; > `openai_ads_ad_accounts_get` returns the connected account for that key. > **CRITICAL**: All money values on inputs are **integer micros**. > `$1.00 = 1_000_000` micros. `$50/day = 50_000_000`. The > `daily_spend_limit_micros` minimum is `1_000_000` ($1). The ad group > `max_bid_micros` is capped at `100_000_000` ($100). Insights responses use > plain floats in account currency, not micros. > **CRITICAL**: Always create campaigns, ad groups, and ads with > `status="paused"`. Surface what was created to the user, then activate > using the dedicated activate endpoint after approval. > **CRITICAL**: Ads have `review_status`. New ads enter `in_review` and will > not serve until `review_status="approved"`, even if `status="active"`. > **CRITICAL**: `chat_card` creatives require `target_url` and `file_id`. > Upload the image first via `openai_ads_images_upload`, then pass the returned > `file_id` to `openai_ads_create`. PNG, 1024x1024, <= 1 MB. > **IMPORTANT**: Creative types are `chat_card` and `product_ad_template`. > Product-ad templates get image and destination URL from the selected product > feed item, so they do not require `file_id` or `target_url`. > **IMPORTANT**: Campaign `bidding_type` can be `impressions` or `clicks`. > Ad group `billing_event_type` can be `impression` or `click`. ## Tool surface | Job | Tools | | --- | --- | | Account | `openai_ads_ad_accounts_get`, `openai_ads_update_ad_account`, `openai_ads_activate_ad_account`, `openai_ads_pause_ad_account`, `openai_ads_health_check` | | Campaigns | `openai_ads_campaigns_create`, `openai_ads_campaigns_get`, `openai_ads_campaigns_list`, `openai_ads_campaigns_update`, `openai_ads_campaigns_pause`, `openai_ads_campaigns_activate`, `openai_ads_campaigns_archive` | | Ad groups | `openai_ads_ad_groups_create`, `openai_ads_ad_groups_get`, `openai_ads_ad_groups_list`, `openai_ads_ad_groups_update`, `openai_ads_ad_groups_pause`, `openai_ads_ad_groups_activate`, `openai_ads_ad_groups_archive` | | Ads | `openai_ads_create`, `openai_ads_get`, `openai_ads_list`, `openai_ads_update`, `openai_ads_pause`, `openai_ads_activate`, `openai_ads_archive` | | Images & targeting | `openai_ads_images_upload`, `openai_ads_search_geo_locations` | | Audiences | `openai_ads_create_custom_audience`, `openai_ads_create_custom_audience_upload`, `openai_ads_get_custom_audience`, `openai_ads_list_custom_audiences`, `openai_ads_archive_custom_audience` | | Conversions | `openai_ads_create_conversion_pixel`, `openai_ads_create_conversion_api_key`, `openai_ads_create_conversion_event_setting`, `openai_ads_list_conversion_event_settings`, `openai_ads_get_conversion_insights` | | Insights | `openai_ads_account_insights_get`, `openai_ads_campaign_insights_get`, `openai_ads_ad_group_insights_get`, `openai_ads_insights_get` | | Cache snapshot | `openai_ads_cache`, `openai_ads_caches_get`, `openai_ads_caches_refresh` | ## Phase 1: Account Discovery Run these after connect: ```text openai_ads_ad_accounts_get() openai_ads_campaigns_list(limit=100) openai_ads_list_custom_audiences(limit=100) openai_ads_list_conversion_event_settings(limit=100) ``` The connect-time context builder may have already populated a cached snapshot. Prefer: ```text openai_ads_caches_get() ``` If `success=False` because the cache is empty, refresh once: ```text openai_ads_caches_refresh() ``` ## Phase 2: Plan and Confirm Before creating anything, confirm with the user: - Objective in plain language. - Daily and/or lifetime budget in account currency. - Target geos: simple country codes like `["US", "GB"]`, or location IDs from `openai_ads_search_geo_locations`. - Any custom audiences to include or exclude. - Whether this is a normal `chat_card` campaign or a product-feed campaign. - Headline (`title`, <= 50 chars), body (<= 100 chars), and click-through URL for `chat_card`. - One image asset for `chat_card`, either a public URL or a base64 blob. - Max bid in micros (`max_bid_micros`; for example `2_000_000`). - Optional `context_hints`, short natural-language phrases that describe when the ad should show. - Optional conversion event setting IDs to attach to the campaign. If anything is missing, ask. Do not invent budgets, geos, or copy. > **All reference files live in `references/`.** Read them at `references/<file>` (e.g. `references/campaign-creation.md`). ## Routing table | The user wants to… | Read these files first | |---|---| | Launch a chat card or product-feed campaign | Phases 1–2 above → [references/campaign-creation.md](references/campaign-creation.md) | | Target regions / DMAs / custom geo | [references/campaign-creation.md](references/campaign-creation.md) — Geo Targeting | | Create or manage custom audiences | [references/audiences-and-conversions.md](references/audiences-and-conversions.md) | | Set up conversion tracking (pixel, API key, event settings) | [references/audiences-and-conversions.md](references/audiences-and-conversions.md) | | Activate after review | [references/campaign-creation.md](references/campaign-creation.md) — Activation | | Pull insights / manage status / refresh the cache snapshot | [references/insights-and-operations.md](references/insights-and-operations.md) | ## Safety Rules **Never:** - Pass dollar amounts directly. All money inputs are micros. - Activate a campaign, ad group, ad, or account without explicit user approval. - Skip image upload for a `chat_card`; it needs a real `file_id`. - Use geo exclusions; use included geos and audience exclusions instead. - Assume an ad is delivering just because `status="active"`. Always check `review_status`. - Treat `archive` as reversible. - Promise paid traffic on a new ad. New ads sit in `review_status="in_review"` until OpenAI approves them.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.