MCP App Store
by KeenEthics
Opus Growth icon

Opus Growth

By Opus Growth

Get New App Alerts

Overview

Opus Growth connects your advertising accounts to ChatGPT so you can run them by talking. Ask for a performance report, change a budget, pause a campaign, add negative keywords, or build a new search campaign, and it happens in your own ad account. Works with Google Ads, Meta, Microsoft Advertising, TikTok and LinkedIn. Search Console, Google Analytics, Tag Manager, Business Profile and YouTube connect alongside them, so you can ask why a campaign is underperforming and get an answer that accounts for the traffic and conversions behind it. You connect each account once with your own credentials, and access stays limited to what you granted. Every change is previewed and confirmed by you before it is applied, and each action is recorded in an audit log you can review.

Tools

account_audit

ChatGPT
Account hygiene audit: finds wasteful keywords and low Quality Score. DATE: period ('today','last_30_days'… or 'YYYY-MM-DD[:...]') overrides days; days=0 = today.

account_snapshot

ChatGPT
ONE-CALL ACCOUNT PICTURE for an ad platform — use it before any account review, health check or "what is going on in my account" answer. For every campaign it joins: what it is (status, whether it is actually serving, type, daily budget, bidding and the platform's own learning status where exposed), WHEN it was created, started and last changed, its age in days, its performance SINCE IT STARTED (lifetime: spend, impressions, clicks, conversions, CPC, CTR, cost per conversion, average daily spend) and over the last recent_days full days, plus account totals. platform: google_ads | meta | tiktok | linkedin | microsoft | chatgpt_ads. account = that platform's account id (empty = connected default). include_inactive=false lists serving/active campaigns and any campaign that spent in the lifetime window; true lists every non-removed campaign. Lifetime starts at the earliest campaign start/creation date, capped by how far each platform's reporting API goes back (stated in lifetime_window). Meta conversions are reported per action type (leads, messaging_conversations, purchases, registrations, link_clicks) because Meta has no single conversion count. LinkedIn rows are API campaigns = what Campaign Manager calls ad sets. Microsoft's report has no campaign id, so its rows are joined by campaign name. Costs the credits of the underlying list_entities and get_report calls. Read-only.

add_keywords

ChatGPT
ADD POSITIVE KEYWORDS to a Google Ads ad group (bulk) — the keywords your ads should SHOW for. This is NOT the negative-keyword tool: use add_negative_keyword only to BLOCK traffic. match_type: PHRASE | EXACT | BROAD, applied to every item that does not carry its own. PER-KEYWORD MATCH TYPE: suffix an item with '|EXACT', '|PHRASE' or '|BROAD' and that one overrides the default — keywords=['yeminli tercume|EXACT', 'noter onayli tercume|PHRASE', 'tercume burosu'] adds a mixed set in ONE call, the same suffix syntax add_negative_keyword already uses. Without it a mixed set needs one call per match type. To MOVE keywords between ad groups, add them here on the target ad group and then remove them from the old one with manage_keywords(action='remove'). To pause/enable/re-bid an EXISTING keyword use manage_keywords. confirm=false → dry-run.

add_location_assets

ChatGPT
Attaches Google Business Profile LOCATION assets (store address, map pin, "Get directions", call-from-map) to the account's ads. In Google Ads, location assets are created AUTOMATICALLY once the Business Profile is LINKED to the Google Ads account, then serve account-wide. This tool: (1) detects whether a linked location asset set (LOCATION_SYNC / Business-Profile location group) exists in the account; (2) if it exists AND a campaign_id is given, pins that asset set to the campaign (otherwise Google already serves it account-wide — nothing to do); (3) if none exists, returns the exact steps to link the Business Profile. confirm=false → dry-run. AD GROUP LEVEL: pass ad_group_id to pin the location asset set to ONE ad group (AdGroupAssetSet) instead of the whole campaign, e.g. a branch ad group that should show only that branch's address. asset_set_id picks a specific location asset set (a location group such as "Bağcılar", from the location_asset_sets list this tool returns); without it the Business Profile sync set is used. NOTE: creating the Business-Profile↔Ads link itself needs Business Profile API access (pending Google approval); until then the customer links it once in the Google Ads UI (~2 minutes).

add_microsoft_ad_extensions

ChatGPT
Adds SITELINK and/or CALLOUT ad extensions to a Microsoft (Bing) campaign — the extra links and highlights that make the ad taller and lift CTR. sitelinks: lines in 'Display text|https://url' or 'text|url|description1|description2' format (display text ≤25 chars). callouts: short highlight phrases like ['Free shipping', 'Same-day support'] (≤25 chars). Creates them in the account library and attaches them to the campaign in one call. confirm=false = preview, confirm=true applies.

add_microsoft_ad_schedule

ChatGPT
Sets WHEN a Microsoft (Bing) campaign is allowed to serve — e.g. business hours only. Without an ad schedule the campaign runs 24/7 and burns budget at night when nobody converts. days = comma-separated: Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday (or 'weekdays' / 'weekend' / 'all'). from_hour / to_hour = 0-24 in the account's time zone. bid_adjustment = optional % bid modifier for those hours (e.g. 20 = bid 20% more). confirm=false previews.

add_microsoft_demographic_target

ChatGPT
Adds a demographic bid adjustment. criterion_type: Age | Gender. value: age range (e.g. TwentyFiveToThirtyFour) or gender (Male | Female). bid_multiplier: bid adjustment (%). confirm=false = preview, confirm=true applies.

add_microsoft_device_target

ChatGPT
Adds device bid adjustments (all THREE TOGETHER — required by Microsoft). Values are % bid adjustments (0 = no change for that device). confirm=false = preview, confirm=true applies.

add_microsoft_keywords

ChatGPT
Adds keywords to an ad group. match_type: Broad | Phrase | Exact. confirm=false = preview, confirm=true applies.

add_microsoft_location_target

ChatGPT
Adds a geographic location target to a campaign using Bing LocationIds. location_ids are numeric Microsoft LocationIds from microsoft_location_search (e.g. 'İstanbul' → 136887). If bid_multiplier>0, applies a bid adjustment (%). confirm=false = preview, confirm=true applies.

add_microsoft_negative_keywords

ChatGPT
Adds negative keywords to a campaign or ad group. entity_type: Campaign | AdGroup. keywords: a list, or a comma-separated string ("staj, kurs"). match_type: Exact | Phrase (negatives support only these two). confirm=false = preview, confirm=true applies.

add_negative_keyword

ChatGPT
Adds negative keywords at CAMPAIGN or AD GROUP level (to stop wasted spend) — ONE keyword or a WHOLE BATCH in a single call. BULK (use this whenever there is more than one keyword — one call, one confirmation, one credit): keywords=['ucuz|BROAD', 'iş ilanı|EXACT', 'kariyer'] — each item is 'text' (takes match_type, default PHRASE) or 'text|EXACT' / 'text|PHRASE' / 'text|BROAD', exactly like manage_shared_negative_list. A comma- or newline-separated string is accepted too. Up to 5000 per call. NEVER loop this tool one keyword at a time: the batch goes to Google as a single mutate with partial failure ON, so one bad or duplicate keyword can no longer take the rest of the batch down, every keyword comes back with its own ok/criterion_id/error, and nothing can be silently dropped between hand-made batches. skip_existing=true (default) first reads the negatives already live on that campaign/ad group and skips exact (text + match type) duplicates — repeat daily runs then stay idempotent and do not burn the 10.000-negatives-per-campaign limit. Set false to write them anyway. Give campaign_id to block the term across the whole campaign, or ad_group_id to block it in ONE ad group only — the latter is what cross-negation needs (each ad group serves only its own theme, negating the other groups' core terms inside a single campaign). If both are given, ad_group_id wins. Undo: remove_negative_keyword (bulk: criterion_ids=[...] or keywords=[...]) at campaign level; manage_keywords(action='remove') at ad group level — list_negative_keywords returns the ids. If confirm=false, returns a dry-run preview of the WHOLE batch.

add_pmax_images

ChatGPT
Attaches IMAGES to a Performance Max asset group (required for the campaign to be eligible to run). Each parameter is a list of asset resource_names returned by upload_image_asset. marketing_image (1.91:1 landscape), square_image (1:1 square), logo (1:1). PMax requires at least 1 landscape + 1 square image + 1 logo. confirm=false → dry-run.

add_site

ChatGPT
Adds ANOTHER website to the user's existing server, on its own domain. Use this instead of telling them to buy a second server: one server hosts up to 15 sites, at no extra cost. domain is the new site's own domain (e.g. ikincisite.com); key is an optional short name used to address the site in later tool calls (defaults to a slug of the domain). confirm=false previews, confirm=true creates it. We take the domain onto our own DNS: the only thing the customer does is change the nameservers (NS) at their registrar to the ones this tool returns — never hand them CNAME/A records. If the domain's zone is already ours (e.g. blog.theirsite.com) there is nothing for them to do at all. Then write the pages with site_write_file(site='<key>', …).

animate_creative

ChatGPT
ANIMATE AN IMAGE INTO A VIDEO CLIP (AI motion video): turns one still image into a 5 or 10 second video with real motion (camera push-in, steam rising, liquid pouring, fabric moving, people moving naturally), then sets the headline, logo and call-to-action button on top in real fonts. Use it when the user wants a moving video ad from a product photo or a creative, not just a slideshow (hareketli video, görseli canlandır; animiertes Video; animar imagen; animer une image). For a slideshow of several images use create_video_creative, which is faster and cheaper. Source: creative_id from create_ad_creative (its image without text is used) or image_url (public image URL, e.g. a product photo or upload_image URL). motion: describe only the movement in English or the user's language, e.g. "slow camera push-in, steam rising from the cup" (keep it simple and physical; the product should stay recognizable). duration 5 or 10 seconds. format: story 1080x1920 (default) | portrait 1080x1350 | square 1080x1080; the image is cropped to that shape. headline, cta, logo_url, brand_color, font: optional text layer as in create_ad_creative; leave empty for a clean clip. This runs in the background and takes about 1-4 minutes. It returns a job_id immediately: tell the user it is being made, then call creative_status(job_id) after about a minute and again until status is done; the result has video_url (MP4) for upload_meta_video or upload_tiktok_video. Uses more credits than images and counts toward the plan's daily and monthly motion-video limits (10 seconds counts twice); not available on the Basic plan. If the job fails, the credits are refunded.

apply_recommendation

ChatGPT
Applies or dismisses Google recommendations. resource_names: comma-separated values from list_recommendations. action: apply | dismiss. apply can make costly changes such as budget increases (subject to the same budget guardrails as set_campaign_budget). confirm=false → dry-run.

attach_linkedin_conversion

ChatGPT
Attaches an existing conversion tracking rule to a LinkedIn campaign so it reports/optimizes for that conversion. conversion_id: the numeric id from list_linkedin_conversions. A MAX_CONVERSION (automated 'Maximum Delivery') campaign REQUIRES a conversion attached before it can go ACTIVE. confirm=false = preview.

block_ip_addresses

ChatGPT
Excludes IP addresses from a Google Ads campaign by adding them as IP exclusion (negative IP_BLOCK) campaign criteria, for example addresses that produce repeated invalid clicks. ips = comma-separated IPv4/IPv6 addresses, at most 200 per call; Google Ads allows up to 500 IP exclusions per campaign. confirm=false returns a preview, confirm=true applies. Use unblock_ip_addresses with the criterion ids from list_ip_exclusions to undo.

budget_pacing_alerts

ChatGPT
BUDGET PACING — projects this month's spend against the daily budget; flags campaigns that will OVERspend (over) or UNDERspend (under) by month-end. threshold_pct: deviation threshold (%). pace_pct = actual daily average / daily budget. READ-ONLY.

chatgpt_ads_ad_preview

ChatGPT
Renders how an existing ChatGPT Ads ad looks, plus the delivery blockers OpenAI reports for it. The preview shows appearance only — OpenAI is explicit that it does not prove the ad is eligible to serve — so this also requests the ad's serving issues and review status, which is what actually answers "why is this not running". Read-only.

chatgpt_ads_conversion_setup

ChatGPT
Shows ChatGPT Ads conversion tracking: pixels (data sources), conversion event settings with the campaigns they are attached to, and events received in the last ~15 minutes. Use it to answer "is my pixel firing" and "which campaigns measure which action". Recent events come from OpenAI's test stream (up to 50 events from roughly the last 15 minutes, per pixel; browser events show channel pixel_sdk, server events server_to_server) — an empty list only means nothing arrived in that window. If OpenAI has not enabled the stream for the account it answers 404 and this tool says so. Standard events can be the goal of a conversions-objective campaign; custom events can only be measured. Read-only.

chatgpt_ads_details

ChatGPT
Full details of one ChatGPT Ads campaign, ad group or ad by id, including everything that decides delivery. Campaign (cmpn_…): budget, schedule, objective, full location/platform/audience targeting, attached conversion events, serving issues. Ad group (adgrp_…): bid strategy and bid in the account currency, bid_too_low (with the 24-hour caveat: OpenAI does not raise it within 24 hours of an ad-group edit, so the time it becomes evaluable is returned), serving issues, its parent campaign and the context hints (paged by hints_limit/hints_offset). Ad (ad_…): the full creative — title, body, destination URL, image — plus review status, the review reason (for example a regulated health category) and serving issues with plain explanations. Read-only.

chatgpt_ads_geo_lookup

ChatGPT
Finds ChatGPT Ads location ids (countries, regions, markets, postal codes) to use in campaign location targeting. Returns id, name, type, country and region code. Pass the id (or a plain ISO country code such as TR) to update_chatgpt_ads_campaign include_locations / exclude_locations. Search in English or the local name ('Istanbul', 'England'); a country name can also match postal codes elsewhere, so check country_code. A valid location does not mean ads are allowed in that country — OpenAI's pilot country list and category policies still apply. Read-only.

chatgpt_ads_insights

ChatGPT
ChatGPT Ads delivery report with every option OpenAI offers: hourly/daily/monthly buckets, platform/country/device breakdowns, zero-impression rows and conversion metrics. scope_id narrows to one campaign/ad group/ad (cmpn_/adgrp_/ad_); empty = whole account. level: account | campaign | ad_group | ad — one row per entity at that level. granularity: none (period total) | hourly (about 30 days of history) | daily | monthly; times are in the account time zone. segment: platform (android_app, android_web, desktop_web, ios_app, ios_web, web) | country | device | product — one at a time, not with hourly, not with conversions. include_zero=true adds campaigns/groups/ads that got no impressions (unsegmented only), which is how you see what is NOT delivering. conversions=true (granularity none or daily only) adds conversions, cpa, post_click_cvr, attributed sales and ROAS (null means not computable, not zero; conversions settle over about a day). Spend, CPC and CPM are in the account currency. Dates are YYYY-MM-DD (end inclusive); without dates the last days days. Rows are paged with limit/offset. Read-only.

chatgpt_ads_spend_limits

ChatGPT
Shows the ChatGPT Ads account-level spending limits: date-range windows and the recurring daily limit, with spend so far. An account limit caps spend across ALL campaigns; a campaign can have budget left and still stop because the account limit is used up. Amounts are in the account currency; end dates are exclusive (the limit ends at midnight of that day, account time zone). OpenAI only exposes these to an ad-account ADMIN key with billing permission — with an ordinary key the tool says exactly that instead of failing. Account limits exist only on postpaid (invoice) billing. Read-only.

client_report

ChatGPT
CLIENT REPORT DATA (Google Ads only) — returns the numbers of one Google Ads account, not a finished report; when the user asks for a report, marketing_report_pdf returns the designed report across every platform as a direct download link. This tool aggregates an account's period performance in a CLIENT-READY format: overall KPIs (cost/clicks/conversions/ROAS/CPA/CTR) + % change vs. the PREVIOUS period + top-spending campaigns. brand_name appears in the report title. For a shareable page, use client_report_link. READ-ONLY.

competitor_ads

ChatGPT
Competitor AD intelligence — fetches a brand's/domain's LIVE ads from public ad libraries. platform: google (Ads Transparency Center) | meta (Meta Ad Library; requires a connected Meta account) | linkedin. Returns only publicly available ad creatives/copy; competitors' ORGANIC keywords are NOT available in Google's APIs.

connect_business_profile

ChatGPT
Generates an authorization link to connect Google Business Profile (My Business) — editing business info/hours/website, publishing posts, and replying to reviews. Stored with a separate encrypted token; does not touch the Google Ads connection.

connect_ga4

ChatGPT
Generates a one-time authorization link to connect Google Analytics 4. READS reports (traffic, users, conversions, channels, top pages) AND lets you set up GA4: mark conversions (key events), link the property to Google Ads, and create custom dimensions. Stored with a separate encrypted token; does not touch the Ads / Search Console / Business Profile connections.

connect_google_ads

ChatGPT
Generates a one-time authorization link to connect a Google Ads account. Once the user clicks the link and approves with Google, the refresh token is stored encrypted.

connect_linkedin_ads

ChatGPT
Generates a one-time authorization link to connect a LinkedIn ad account. Once the user clicks the link and approves via LinkedIn, the access/refresh token is stored encrypted.

connect_meta_ads

ChatGPT
Generates a one-time authorization link to connect a Meta (Facebook/Instagram) ad account. Note: Meta issues a 60-day token (no refresh token) — when it expires, reconnect with the same tool.

connect_microsoft_ads

ChatGPT
Generates a one-time authorization link to connect a Microsoft Advertising (Bing Ads) ad account. After authorizing, you can query the accessible ad accounts.

connect_search_console

ChatGPT
Generates a one-time authorization link to connect Google Search Console (organic search queries/clicks/impressions/position + sitemap management + URL index inspection). Stored with a separate encrypted token; does not touch the Google Ads connection.

connect_tag_manager

ChatGPT
Generates a one-time authorization link to connect Google Tag Manager (full A-to-Z management: accounts/containers/workspaces, tags/triggers/variables, versioning + PUBLISH). Stored with a separate encrypted token; does NOT touch the Google Ads / Search Console / Business Profile / Analytics / YouTube connections.

connect_tiktok_ads

ChatGPT
Generates a one-time authorization link to connect a TikTok (TikTok for Business) ad account. After authorization you can query the accessible ad accounts (advertisers).

connect_youtube

ChatGPT
Generates a one-time authorization link to connect YouTube (channel + video performance: views, watch time, subscribers, traffic sources — read-only). Separate encrypted token; does not touch the Ads / Search Console / Business Profile / Analytics connections.

conversion_tracking_health

ChatGPT
Conversion tracking health — audits conversion actions: paused/removed, and zero recorded data over the lookback window. It reports include_in_conversions_metric (the REAL Primary/Secondary flag in goal-based accounts — biddable = counted in the Conversions column); the legacy primary_for_goal is also returned but can be misleading (it is true on hidden auto-goals and false on the genuinely Primary action in goal-based accounts). A zero-conversion alert is raised ONLY for Primary (included) actions — a Secondary/observation action recording 0 is expected, not broken. The UI 'Tracking status' diagnostic (Active / Needs attention / No recent conversions) is computed by Google and is NOT a single API field; for the full per-action table use list_conversion_actions. days: lookback window for the conversion-volume check (default 30). Read-only.

copy_ad_group

ChatGPT
Copies one or more Google Ads AD GROUPS, with their contents, into another campaign of the same account (or duplicates them inside the same campaign) — instead of rebuilding each group with create_ad_group + add_keywords + create_responsive_search_ad + manage_extension_assets. Copied per group: the ad group settings (default CPC / target CPA / target ROAS, ad rotation, tracking template, final URL suffix, custom parameters, targeting setting), keywords with match type, status, bid and URLs, ad-group negative keywords, responsive search ads with headlines, descriptions, pins, paths, final URLs and tracking, linked ad-group assets (sitelinks, callouts, snippets, calls, images...), audience and demographic criteria with bid modifiers, and device bid adjustments. ad_group_ids: one id or several comma-separated (up to 25 per call). target_campaign_id must be in the same account and of the same campaign type. status: PAUSED (default, nothing serves until you enable it), ENABLED, or KEEP (same as the source). new_name renames a single copy; name_suffix is appended to every copy name (default: same name, or ' (copy)' when copying inside the same campaign). cpc_bid > 0 overrides the copy's default max CPC. Ad types other than responsive search ads are listed as skipped. The group, its keywords and its ads are created in ONE atomic request (all or nothing); assets, audiences and device adjustments follow and any that fail are reported one by one. The source is never changed. confirm=false returns a per-group preview of what will be copied.

copy_meta_entity

ChatGPT
Copies a Meta campaign/ad set/ad (the 'Duplicate' action in Ads Manager). kind: campaign | adset | ad. deep_copy=true copies a campaign (or ad set) along with all of its child items. The copy comes out PAUSED. If rename_prefix is provided, a prefix is added to the copy. Meta only allows 3 objects per deep-copy call (error 100 / subcode 1885194), so a campaign/ad set with more children used to fail outright; now the tool detects that limit and finishes the deep copy automatically by copying each child ad set and ad one at a time — the result reports how many were copied (deep_copy:"chunked"). confirm=false = preview.

core_web_vitals

ChatGPT
Real Core Web Vitals & performance for a page — FREE, no credits. Runs Google PageSpeed Insights (Lighthouse lab data + real-user CrUX field data): LCP, CLS, INP/TBT, a 0–100 performance score, and the top opportunities to make the page faster. Use it as a quality gate when building or fixing a website. strategy: 'mobile' (default) or 'desktop'. resources=true adds Lighthouse's RAW RESOURCE LISTS, which is what turns a score into actual work: which stylesheets block rendering, which files carry unused CSS or JS and how many bytes of each are wasted, which images are oversized or not in a modern format, the full network request list, and the LCP element itself. Without it you know a page needs "less unused CSS" but not which file, so critical CSS cannot be targeted and an oversized image cannot be found. limit caps rows per audit (default 20).

create_ad_creative

ChatGPT
CREATE AN AD IMAGE (AI creative generator): generates a new advertising image and returns it ready in every ad size, with the headline, supporting line, call-to-action button and logo set in real fonts on top. Use it when the user asks to create, design or make an ad image, banner, post visual or creative (reklam görseli, kreatif, banner; Werbebild; imagen de anuncio; visuel publicitaire), or to place their product photo in a new scene. How it works: an image model creates the scene from prompt (describe the scene, subject, mood and setting; no need to describe text). The text is NOT drawn by the image model: headline, subtext, cta and logo are typeset by Opus Growth on top, so spelling is always exact and brand colors are kept. The image model is told to leave calm space for the text. product_image_url: the user's product photo (a public image URL, or the URL from upload_image). The product is kept as it is and placed in the scene described by prompt. logo_url: logo image (PNG with transparency is best). brand_color: #RRGGBB used for the button and panels. font: montserrat | inter | poppins | playfair | dmsans | oswald. layout: overlay (text on the image, default) | panel (image on top, brand-colored text panel below) | clean (image only, logo and button kept). text_position: top | bottom (overlay layout). style: photo | studio | lifestyle | illustration | 3d. formats (comma separated): portrait 1080x1350 (4:5, Meta/Instagram feed), square 1080x1080 (1:1), story 1080x1920 (9:16, Reels, Stories, TikTok), landscape 1200x628 (1.91:1, Google Display / PMax, Meta link ads). Default: all four, from one generated image. variants 1-3 = that many different scenes. Write headline, subtext and cta in the language of the ad's audience; keep the headline short (about 3-7 words) and do not repeat it in subtext. Returns a preview image of the sizes plus files with a public URL per size. To use them in an ad, pass the URL to upload_meta_image (Meta), upload_image_asset (Google Ads), upload_tiktok_image (TikTok) or to create_video_creative / animate_creative for video. Show the preview to the user and ask for approval before uploading anything to an ad account. Each variant uses credits and counts toward the plan's daily and monthly creative limits; not available on the Basic plan. Takes about 10-30 seconds.

create_ad_group

ChatGPT
Creates a new AD GROUP inside a Google Ads campaign — the layer between the campaign and its keywords/ads. This is the missing middle step when building a search campaign end to end: create_search_campaign (campaign + budget) → create_ad_group (this, one per theme) → add_keywords (positive keywords for that group) → create_responsive_search_ad (the RSA, which needs the ad_group_id this returns). cpc_bid = the group's default max CPC in the account currency. In a DISPLAY campaign the group is created as a Display ad group; when that campaign bids on viewable CPM (create_display_campaign bid_strategy=MANUAL_CPM) give cpm_bid = the max viewable CPM (cost per 1,000 viewable impressions) instead of cpc_bid, then add create_responsive_display_ad and an audience with manage_audience_targeting. confirm=false → dry-run.

create_app_campaign

ChatGPT
Creates an App (UAC) campaign (PAUSED). app_id = store app ID. app_store: GOOGLE_APP_STORE | APPLE_APP_STORE. confirm=false → dry-run.

create_chatgpt_ads_ad

ChatGPT
Creates a ChatGPT Ads ad (chat card) inside an ad group. title and body are the text a person sees in ChatGPT; target_url is where the click goes; image_url is uploaded for you, so you pass a normal URL rather than a file id. An image is REQUIRED for a chat card — the API refuses the ad without one, even though the schema marks the field optional. Creating an ad submits its creative for review, so the text, the image and the destination should be final. Created paused unless activate=true. confirm=false returns a dry run showing the exact card.

create_chatgpt_ads_ad_group

ChatGPT
Creates an ad group under a ChatGPT Ads campaign and sets its maximum bid and context hints. max_bid is the most you will pay for one click (or one thousand impressions when billing_event is impression), in the account currency. It is checked against two rules: a bid can never exceed the campaign's own daily budget — one click cannot consume the whole day — and there is a sanity ceiling that catches a decimal slip like 3500 for 3.5. A deliberate high bid passes with allow_high_bid=true. context_hints decides which ChatGPT conversations the ads can match: short natural sentences describing the product, the use case or the need, in the language your buyers actually chat in, up to 2000 of them. They are not exact-match keywords and they do not replace country or platform targeting. Created paused unless activate=true. confirm=false returns a dry run.

create_chatgpt_ads_campaign

ChatGPT
Creates a ChatGPT Ads campaign in your connected ChatGPT Ads account. Give a daily_budget or a lifetime_budget (or both) in the account currency — not micros, the conversion is handled here. objective: clicks | reach | conversions. The campaign is created PAUSED; pass activate=true only when you mean spending to start the moment it is created, because it does. A campaign alone never serves: it needs an ad group (create_chatgpt_ads_ad_group) and an ad (create_chatgpt_ads_ad) under it. Budgets pass the same safety cap as every other platform, so a mistyped amount is refused rather than spent. confirm=false returns a dry run of exactly what will be created.

create_chatgpt_ads_event_setting

ChatGPT
Creates a ChatGPT Ads conversion event setting (which tracked action counts as a conversion) on one pixel source. event_type: one of OpenAI's standard events — appointment_scheduled, lead_created, order_created, registration_completed, subscription_created, trial_started, checkout_started, items_added, contents_viewed, page_viewed, app_installed, app_opened — or custom with custom_event_name (lowercase letters, digits, _ or -, up to 64 characters, not a built-in name). source_id is the pixel source id (cds_…) from chatgpt_ads_conversion_setup; left empty it uses the account's only source. The attribution window is 30 days, as OpenAI requires. A setting does not send events by itself — the site's pixel or server must send that event — and only a STANDARD event can be the goal of a conversions-objective campaign. Attach it to campaigns with update_chatgpt_ads_campaign. confirm=false returns a dry run.

create_customer_match_audience

ChatGPT
Customer Match audience — creates an EMPTY list shell. upload_key_type: CONTACT_INFO | CRM_ID | MOBILE_ADVERTISING_ID. NOTE: member (hashed email/phone) UPLOAD is a separate step and requires Customer Match eligibility; this tool only creates the list. confirm=false → dry-run.

create_demand_gen_ad

ChatGPT
Creates an ad inside a Demand Gen ad group — a VIDEO ad (give youtube_video) or an IMAGE multi-asset ad (give image asset ids instead). This is the last step that makes a Demand Gen campaign able to serve, and the only way to run a click-oriented YouTube campaign now that Google moved Video Action campaigns into Demand Gen. VIDEO ad: youtube_video accepts a bare video id, a watch URL, a youtu.be link or a /shorts/ link; the matching video asset in the account is reused if it already exists and created once if it does not. Headline <=40 chars, long_headline <=90, description <=90, up to 5 each. call_to_action is optional (LEARN_MORE, SIGN_UP, GET_QUOTE, CONTACT_US, SUBSCRIBE, DOWNLOAD, BOOK_NOW, SHOP_NOW, BUY_NOW, DONATE_NOW, ORDER_NOW, APPLY_NOW). IMAGE ad (DEMAND_GEN_MULTI_ASSET_AD): leave youtube_video empty and give marketing_image_asset_ids (landscape 1.91:1, min 600x314) and/or square_image_asset_ids (1:1, min 300x300) — at least one of the two — plus optional portrait_image_asset_ids (4:5, min 480x600); 20 images at most in total. Ids come from list_assets or upload_image_asset (use aspect '1.91:1' / '1:1' / '4:5'). Up to 5 headlines <=30 chars and up to 5 descriptions <=90 chars; there is no long headline. call_to_action_text is the optional button text (e.g. 'Learn more'). headlines and descriptions take a list, or a string with one item per line (comma-separated also works when no item contains a comma). business_name (<=25) and a square logo are REQUIRED by Google for both types: pass logo_asset_id from an image asset you already have, or leave it empty and the account's largest square image asset is used; if none exists, upload one first with upload_image_asset. The ad is created PAUSED so nothing spends before you look at it. To edit the copy of an existing image ad later use update_rsa with its ad id. confirm=false -> preview.

create_demand_gen_campaign

ChatGPT
Creates a Demand Gen campaign (PAUSED) + a daily budget. Ads/assets are added separately. bid_strategy: MAXIMIZE_CONVERSIONS (default) | MAXIMIZE_CLICKS (for a traffic/click objective — this is what a migrated Video Action campaign wants) | TARGET_CPA (target_cpa required, account currency). A CPC ceiling is NOT accepted on Demand Gen: Google rejects it with OPERATION_NOT_PERMITTED_FOR_CONTEXT on this channel (measured), so there is no cpc_bid_ceiling parameter — the cap only exists on Search/Display. To change the strategy after creation use manage_bidding_strategies. GEOGRAPHY IS NOT SET HERE AND THE DEFAULT IS EVERY COUNTRY. Demand Gen keeps location and language on the AD GROUP, not the campaign, so this call cannot take them: a campaign created here and enabled without a further step runs worldwide. That happened on a live account — 714 clicks in six hours, one of them from the target continent. After creating the ad group, set the targeting with set_ad_group_targeting(ad_group_id=..., locations=...) and verify it with get_campaign_targeting(ad_group_id=...). Connected TV inventory is also on by default; for a traffic objective exclude it with manage_device_targeting(device='CONNECTED_TV', bid_modifier_pct=-100). confirm=false → dry-run.

create_display_campaign

ChatGPT
Creates a standard Google DISPLAY NETWORK campaign (PAUSED) + daily budget — e.g. a remarketing campaign shown only to site visitors, bid on viewable CPM. Unlike Demand Gen it runs on the Display Network only and can bid per 1,000 viewable impressions. bid_strategy: MANUAL_CPM (default — viewable CPM / vCPM; set the amount per ad group with create_ad_group cpm_bid) | MANUAL_CPC | MAXIMIZE_CLICKS | MAXIMIZE_CONVERSIONS | TARGET_CPA (target_cpa required). Target CPM is NOT available for Display campaigns in the Google Ads API (Google rejects it), viewable CPM is the CPM option. exclude_youtube=true (default) adds youtube.com as an excluded placement so the ads stay off YouTube. Build order: create_display_campaign → set_campaign_targeting (locations + languages; WITHOUT it the campaign targets every country) → create_ad_group (cpm_bid for MANUAL_CPM) → upload_image_asset (landscape 1.91:1 and square 1:1 images, optional logo) → create_responsive_display_ad → manage_audience_targeting(ad_group_id, audience=<remarketing list>, mode=TARGETING) for a visitors-only campaign. confirm=false → dry-run.

create_google_ads_account

ChatGPT
Creates a NEW Google Ads client account under a manager (MCC) account — for agencies onboarding a client without leaving the chat. manager_customer_id is the MCC that will own the account (see list_accounts). The new account is linked to that manager automatically and can be used straight away for campaigns. CURRENCY AND TIME ZONE ARE PERMANENT: Google does not allow changing them after the account exists, so getting them wrong means starting over with a new account. Confirm both with the user before creating. confirm=false → dry-run showing exactly what will be created.

create_gtm_container

ChatGPT
Creates a new web CONTAINER in a GTM account — a fresh, isolated space (its own tags/triggers/variables + 'GTM-XXXX' id). account = a list_gtm_accounts value or name/id (empty if only one). SAFETY: confirm=false → preview; confirm=true → creates it.

create_gtm_tag

ChatGPT
Creates a TAG in a workspace. tag_type = GTM type string: 'html' (Custom HTML → parameters='{"html":"<script>...</script>"}'), 'gaawc' (GA4 Configuration → '{"measurementId":"G-XXXX"}'), 'gaawe' (GA4 Event → '{"measurementIdOverride":"G-XXXX","eventName":"purchase"}'), 'sp' (Google Ads Conversion), 'img' (Custom Image Pixel → '{"url":"https://..."}'). parameters is JSON: an object {key:value} (each wrapped as a template param) OR a raw GTM parameter list for complex types. firing_trigger_ids = comma-separated triggerId(s) from list_gtm_triggers (e.g. the All-Pages trigger). SAFETY: confirm=false → preview; confirm=true → creates it (staged in the workspace; goes live only after publish_gtm_container).

create_gtm_trigger

ChatGPT
Creates a TRIGGER in a workspace. trigger_type = GTM type string: 'pageview' (All Pages), 'domReady', 'windowLoaded', 'click' (All Elements), 'linkClick', 'customEvent', 'formSubmission', 'scrollDepth', 'timer', 'historyChange'. For 'customEvent' just pass event_name='cf7_success' and the event filter is built for you. If you build it by hand, customEventFilter items are CONDITIONS, not maps: parameters='[{"type":"list","key":"customEventFilter","list":[{"type":"EQUALS","parameter":[{"type":"template","key":"arg0","value":"{{_event}}"},{"type":"template","key":"arg1","value":"cf7_success"}]}]}]' Condition types (filter/customEventFilter/autoEventFilter): equals, contains, startsWith, endsWith, matchRegex, greater, greaterOrEquals, less, lessOrEquals, cssSelector, urlMatches; to negate add {"type":"boolean","key":"negate","value":"true"}. SAFETY: confirm=false → preview only; confirm=true → creates it (staged in the workspace until you publish_gtm_container).

create_gtm_variable

ChatGPT
Creates a user-defined VARIABLE. variable_type = GTM type string: 'c' (Constant → parameters='{"value":"..."}'), 'v' (Data Layer Variable → parameters='{"name":"ecommerce.value"}' plus optional dataLayerVersion), 'jsm' (Custom JavaScript → parameters='{"javascript":"function(){return ...}"}'), 'u' (URL), 'k' (1st-party Cookie → '{"name":"_ga"}'). parameters is JSON: an object {key:value} (wrapped as template) or a raw GTM parameter list. SAFETY: confirm=false → preview; confirm=true → creates it (staged until publish).

create_gtm_workspace

ChatGPT
Creates a new WORKSPACE in a container — an isolated draft area to stage tag/trigger/variable changes before publishing (GTM lets you run several in parallel). container = 'GTM-XXXX' / container name / full path (empty if only one). SAFETY: confirm=false → preview; confirm=true → creates it.

create_lead_form

ChatGPT
Creates a Google Ads LEAD FORM — the form that opens INSIDE the ad, so someone can leave their name and phone number without ever reaching the website — and optionally attaches it to a campaign or ad group in the same call. business_name, headline, description and privacy_policy_url are required by Google; a form without a privacy policy link is rejected. LENGTH LIMITS, checked before the call so a long name costs no round trip: business_name ≤25, headline ≤30, description ≤200 characters. The 25 for business_name is the one that surprises people — it is shorter than the RSA headline limit, so a company name that fits an ad may not fit the form. cta is the button on the ad: LEARN_MORE | GET_QUOTE | APPLY_NOW | SIGN_UP | CONTACT_US | SUBSCRIBE | DOWNLOAD | BOOK_NOW | GET_OFFER | REGISTER | GET_INFO | REQUEST_DEMO | JOIN_NOW | GET_STARTED. fields = the boxes the person fills in, comma separated: FULL_NAME, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, WORK_EMAIL, WORK_PHONE, COMPANY_NAME, JOB_TITLE, CITY, POSTAL_CODE, COUNTRY, STREET_ADDRESS and more. questions = your own QUALIFYING questions, one string each as "question|answer|answer|...", e.g. "Hangi sınava hazırlanıyorsunuz?|YKS|LGS|Diğer". They are what separate a real prospect from a curious click, so a form aimed at a specific programme usually wants one or two. post_submit_headline / post_submit_description are the thank-you message, post_submit_cta is VISIT_SITE | DOWNLOAD | LEARN_MORE | SHOP_NOW. webhook_url (with webhook_secret) pushes each lead to your own endpoint the moment it arrives; without it, read them with list_lead_form_submissions. ONE-TIME STEP GOOGLE DOES NOT ALLOW OVER THE API: the account must accept the Lead Form Terms of Service once, in the Google Ads interface. Until that is done every create is refused, and this tool says so plainly rather than returning a vague error. confirm=false → preview.

create_linkedin_campaign

ChatGPT
Creates a LinkedIn campaign. campaign_type: TEXT_AD | SPONSORED_UPDATES | SPONSORED_INMAILS | DYNAMIC | EVENT_AD. cost_type: CPC | CPM | CPV | CPS. Targeting: country ('TR','US','DE'… 36 verified country codes) sets the geo target; for finer targeting pass comma-separated 'urn:li:geo:...' values to geo_urns (found via linkedin_targeting_search). language sets the interface language (en/tr/de/fr/es). Budget is processed in the account's own currency (TRY on a TRY account). If total_budget>0 the total budget is used, otherwise daily_budget. associated_entity: the Company Page URN required for SPONSORED_UPDATES / SPONSORED_INMAILS / DYNAMIC / EVENT_AD campaigns, e.g. 'urn:li:organization:1234567'; if left empty it is auto-filled from the account's organization reference, otherwise an error is returned. Not needed for TEXT_AD. objective_type (LinkedIn's enum is SINGULAR — live-verified): WEBSITE_CONVERSION | WEBSITE_VISIT | WEBSITE_TRAFFIC | LEAD_GENERATION | BRAND_AWARENESS | ENGAGEMENT | VIDEO_VIEW | JOB_APPLICANT | CONVERSATION_STARTER | TALENT_LEAD | CREATIVE_ENGAGEMENT. Plural spellings (WEBSITE_CONVERSIONS / VIDEO_VIEWS / JOB_APPLICANTS) are auto-corrected. The objective CANNOT be changed after creation, so pick it now. ad_format is a SEPARATE axis from campaign_type and decides what the ad looks like: STANDARD_UPDATE (single image) | SPONSORED_UPDATE_NATIVE_DOCUMENT (document ad) | CAROUSEL | SINGLE_VIDEO | SPOTLIGHT | FOLLOW_COMPANY | JOBS | SPONSORED_UPDATE_EVENT | TEXT_AD | SPONSORED_INMAIL | SPONSORED_MESSAGE. Leave it empty and LinkedIn falls back to a single-image ad set, which can then only be corrected in the UI — and that edit wipes the targeting, so set it here. Friendly spellings are accepted (DOCUMENT, CAROUSEL, SINGLE_IMAGE, VIDEO). RUN SCHEDULE: start_date / end_date ('YYYY-MM-DD'; end date runs to the end of that day, UTC). Empty start = now. With total_budget and no end_date LinkedIn's 90-day default end applies and spend is paced over 90 days — set end_date for event or short campaigns. NOTE: the 'I certify this is not a political ad' declaration (politicalIntent=NOT_POLITICAL) is sent automatically — political ads are not supported by this tool. confirm=false = preview.

create_linkedin_campaign_group

ChatGPT
Creates a LinkedIn campaign group — a 'Campaign' in the Campaign Manager UI. status: DRAFT | ACTIVE. If total_budget>0 is given, a total budget is assigned in the account's currency. RUN SCHEDULE: start_date / end_date ('YYYY-MM-DD'; end date runs to the end of that day, UTC). Empty start = now. With a total budget and no end_date LinkedIn's 90-day default end applies, so the budget is paced over 90 days — give end_date for event or short campaigns. LEAVE objective_type EMPTY unless you really want the whole group locked to one objective: an objective set at GROUP level constrains EVERY ad set inside it, so a group created with ENGAGEMENT can never host a Lead Generation or Website Visit ad set, and LinkedIn normally refuses to change it afterwards. Empty is the default and keeps the group open to every objective — set the objective on the individual campaign instead. objective_type (LinkedIn's enum is SINGULAR — live-verified): WEBSITE_CONVERSION | WEBSITE_VISIT | WEBSITE_TRAFFIC | LEAD_GENERATION | BRAND_AWARENESS | ENGAGEMENT | VIDEO_VIEW | JOB_APPLICANT | CONVERSATION_STARTER | TALENT_LEAD | CREATIVE_ENGAGEMENT. Plural spellings (WEBSITE_CONVERSIONS / VIDEO_VIEWS / JOB_APPLICANTS) are auto-corrected. Objective CANNOT be changed after creation. If confirm=false, only a preview is returned; to apply, use confirm=true.

create_linkedin_post_ad

ChatGPT
Turns an existing Company Page post into an ad (Sponsored Content) for a SPONSORED_UPDATES campaign. THIS IS THE WAY TO RUN AN IMAGE AD ON LINKEDIN: there is no direct image upload (LinkedIn needs an organization permission this connector does not have), so the image is posted on the Company Page first and then sponsored here. post_urn: 'urn:li:share:<id>' or 'urn:li:ugcPost:<id>' — the numeric id from the post's LinkedIn URL. The campaign must be tied to an organization (associatedEntity). intended_status: leave EMPTY (auto) and the ad follows its ad set like Campaign Manager does — under a DRAFT ad set it is created ACTIVE, so it starts serving the moment you activate the campaign and ad set, while nothing spends before that; under an ad set that is already ACTIVE or PAUSED it is created DRAFT so it never goes live unasked. Pass ACTIVE / DRAFT / PAUSED to force a status. A DRAFT ad does NOT serve even when its ad set is ACTIVE — activate it with update_linkedin_creative_status. confirm=false = preview.

create_linkedin_text_ad

ChatGPT
Creates a text ad (creative) for a TEXT_AD campaign — does not require an organization page. headline ≤25, description ≤75 characters. intended_status: leave EMPTY (auto) and the ad follows its ad set like Campaign Manager does — under a DRAFT ad set it is created ACTIVE, so it starts serving the moment you activate the campaign and ad set, while nothing spends before that; under an ad set that is already ACTIVE or PAUSED it is created DRAFT so it never goes live unasked. Pass ACTIVE / DRAFT / PAUSED to force a status. A DRAFT ad does NOT serve even when its ad set is ACTIVE — activate it with update_linkedin_creative_status. confirm=false = preview.

create_lookalike_audience

ChatGPT
Creates a Lookalike audience from seed audiences. seed_user_list_ids and country_codes are comma-separated lists (e.g. '9343436766' and 'TR,DE'). expansion_level: NARROW | BALANCED | BROAD. Run list_audiences first for valid seed ids. confirm=false → dry-run.

create_meta_ab_test

ChatGPT
Creates a Meta SPLIT TEST (A/B) that compares two existing campaigns 50/50 on non-overlapping audiences and reports a statistically fair winner. campaign_a / campaign_b from list_meta_campaigns. start_time / end_time in ISO 8601 (2026-08-15T09:00:00+03:00) or unix seconds. For a clean read the two campaigns should differ in ONE variable (creative, audience, or placement). confirm=false = preview.

create_meta_custom_conversion

ChatGPT
Creates a custom conversion on a pixel. Give url_contains (visitors whose page URL contains this, e.g. '/tesekkurler' — the classic thank-you / lead-page conversion), or event (a standard pixel event name), or BOTH (that event fired on that page). event must be a STANDARD Meta pixel event and is matched case-sensitively; any casing you pass is normalized: PageView, ViewContent, Search, AddToCart, AddToWishlist, InitiateCheckout, AddPaymentInfo, Purchase, Lead, CompleteRegistration, Contact, CustomizeProduct, Donate, FindLocation, Schedule, StartTrial, SubmitApplication, Subscribe. custom_event_type categorizes it for reporting/optimization (PURCHASE | LEAD | COMPLETE_REGISTRATION | ADD_TO_CART | CONTENT_VIEW | SEARCH | OTHER); leave it out and it is derived from the event. pixel_id from list_meta_pixels. confirm=false = preview. An EVENT rule always carries a URL condition, because Meta rejects an event-only rule: if you pass no url_contains, an all-pages condition is added so the conversion means "this event anywhere on the site". custom_event_type must match the event, so leave it out and it is set correctly for you. DELETING IS NOT POSSIBLE THROUGH THE API: Meta refuses custom-conversion deletion for this app (OAuthException 200), so a wrong one can only be removed by the user in Meta Events Manager → Custom conversions. Confirm the name and rule with the user BEFORE calling with confirm=true, and do not create throwaway test conversions.

create_meta_dynamic_ad

ChatGPT
Creates a dynamic product ad (DPA) — one creative that automatically pulls products from the catalog. The ad set must be catalog-bound (create_meta_adset with product_set_id). page_id from list_meta_pages, product_set_id from list_meta_product_sets. message/description may use macros like {{product.name}}, {{product.price}}; leave empty to let Meta fill product fields. call_to_action: SHOP_NOW | LEARN_MORE | BUY_NOW... confirm=false = preview.

create_meta_engagement_audience

ChatGPT
Builds a Meta retargeting audience from ENGAGEMENT instead of website traffic — people who watched your video, interacted with your Facebook Page or Instagram profile, or opened your lead form. NO PIXEL AND NO WEBSITE TRAFFIC NEEDED, which makes it the only realistic retargeting option for accounts with a small or brand-new site (and the best lookalike seed you can get early on). source: video | page | ig | lead_form source_id: the video id (list_meta_page_posts), page id (list_meta_pages), Instagram account id, or lead form id. event (optional, sensible default per source): • video → ThruPlay | 10s | 25% | 50% | 75% | 100% (Meta'da %95 eşiği YOKTUR) • page → page_engaged | page_visited | page_messaged | page_cta_clicked | post_engaged • ig → ig_business_profile_engaged | ig_business_profile_visited | ig_business_profile_all • lead_form → lead_form_opened | lead_form_submitted | lead_form_not_submitted retention_days: 1-365 (how long someone stays in the audience). confirm=false previews.

create_meta_instagram_post_ad

ChatGPT
Turns an EXISTING Instagram post/Reel into an ad (Ads Manager's "Use existing post → Instagram"). No new creative — the organic Instagram media runs as-is. This WORKS TODAY and needs no Instagram content permission: it is a pure Marketing API write (AdCreative source_instagram_media_id + instagram_user_id, no object_story_id). ig_media_id is the Instagram media id — from list_meta_instagram_posts (media_id, including its advertised_media list) or from Ads Manager → Use existing post → Instagram. The connected Instagram account is resolved automatically from page_id, or from the ad account when neither page_id nor ig_user_id is given; pass ig_user_id directly (list_meta_instagram_accounts) when the account has no Facebook Page. CLICK-TO-WHATSAPP / MESSENGER FROM AN ORGANIC POST: welcome_message = the ready-written text that auto-populates the person's WhatsApp/Messenger compose box (without it the thread opens empty, so it looks like the business started the conversation and the business's auto-reply never fires), greeting = optional in-thread greeting, ice_breakers = up to 4 tappable quick replies (≤80 chars each; they replace the auto-fill). Engagement still accrues on the ORGANIC post. Leave cta_type empty and the button is derived from the ad set's destination_type — that is the normal path; only pass cta_type (WHATSAPP_MESSAGE / SEND_MESSAGE) together with link when you want to override it, because Meta rejects a call-to-action with no link on this creative. To add a welcome message to an ad that ALREADY exists, use update_meta_instagram_post_ad — it keeps the organic post, the ad id and its learning. The ad is created PAUSED and goes through Meta review. confirm=false = preview.

create_meta_lead_form

ChatGPT
Creates an INSTANT FORM (lead form) on a Facebook Page, with custom questions, and returns the form id to attach to a lead ad. questions: each entry is EITHER a standard field Meta pre-fills from the user's profile — FULL_NAME, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CITY, COMPANY_NAME, JOB_TITLE, DOB, GENDER … — OR a custom question written as 'Question text?|Option 1|Option 2' (with options it renders as a choice list, without options it is a free-text answer). privacy_policy_url is REQUIRED by Meta on every form. Optional: intro_headline / intro_description (the card shown before the questions), thank_you_text, follow_up_url (where the thank-you button goes). locale is the form's language (default TR) — give it in Meta's full form, e.g. EN_US, EN_GB, DE_DE, AR_AR; it sets BOTH Meta's own interface strings AND the three texts this tool supplies for you (the privacy-policy link text and the intro-card / thank-you buttons), so an EN_US form reads "Privacy Policy" / "Continue" / "Go to site". confirm=false shows those three under form_button_texts, so you can check the language before the form exists. higher_intent=true creates a 'Higher intent' Instant Form (Meta's is_optimized_for_quality — it adds a review/confirm step before submission, giving fewer but more qualified leads); the default is 'More volume'. Meta does NOT allow the form type to change after the form is published, so choose it at creation, and note Meta's API cannot create a form as an unpublished draft — use confirm=false to preview the fields (and see form_type) before it exists. After creating it, build the ad with create_meta_link_ad(..., lead_gen_form_id=<returned id>) on an ad set whose optimization_goal is LEAD_GENERATION and whose promoted_object page_id is this Page. confirm=false = preview.

create_meta_lookalike_audience

ChatGPT
Creates a lookalike audience from a source custom audience. source_audience_id from list_meta_audiences. country ISO alpha-2. ratio 0.01-0.20 (1%-20%; smaller = more similar). confirm=false = preview.

create_meta_post_ad

ChatGPT
Turns an existing Facebook/Instagram Page post into an ad (boosts an organic post). object_story_id: '{page_id}_{post_id}' — found via list_meta_page_posts. Keeps the post's own text and media. link + cta_type = the "Boost post AND add a button" option in Ads Manager: a website button is attached on top of the organic post (e.g. cta_type=LEARN_MORE, link=https://…). Give link and the button appears; leave both empty and the post runs exactly as posted. A link is also what makes boosting possible in sales/leads campaigns, where Meta requires a destination URL. confirm=false = preview.

create_meta_video_ad

ChatGPT
Creates a Meta video ad. Give EITHER video_id (already uploaded via upload_meta_video — reusable) OR video_url (Meta fetches and processes it asynchronously). Thumbnail: image_hash, image_base64 or image_url — Meta REQUIRES a cover image; if you omit it, one is auto-generated from the video's own frame. To reuse a video already published on the Page (including Reels), take video_url from list_meta_page_posts and pass it here. page_id REQUIRED. cta_type = the button (LEARN_MORE | SHOP_NOW | SIGN_UP | BUY_NOW | WHATSAPP_MESSAGE | SEND_MESSAGE | INSTAGRAM_MESSAGE | ...) — leave EMPTY to auto-pick from the ad set's messaging destination, else the campaign objective. INSTAGRAM DM ADS: an ad set with destination_type=INSTAGRAM_DIRECT only delivers with cta_type INSTAGRAM_MESSAGE; LEARN_MORE creates the ad but Meta kills delivery with 1487891, and MESSAGE_PAGE/SEND_MESSAGE is rejected on Instagram (2875003). For Click-to-WhatsApp/Messenger/Instagram-DM video ads (cta_type WHATSAPP_MESSAGE / SEND_MESSAGE / INSTAGRAM_MESSAGE) you can pre-fill the chat: welcome_message = the ready-written text that auto-populates the user's compose box, greeting = optional in-thread greeting, ice_breakers = up to 4 tappable quick replies (≤80 chars each; if given they replace the auto-fill). MUSIC / AUDIO / SOUNDTRACK: a video ad already carries the audio embedded in the video file — that is how you ship a SPECIFIC song. enhancements=['ai_music','subtitles','video_auto_crop',...] (or music=true) enrol the video in Meta's Advantage+ creative features; the response reports enhancements_applied vs enhancements_not_applied. INSTAGRAM IDENTITY: by default Meta derives the Instagram account from page_id, which silently picks one when the Page has several — pass instagram_user_id (from list_meta_instagram_accounts) to choose it explicitly. Either way the response returns the instagram_user_id the ad ACTUALLY runs from, so you can confirm the identity without opening Ads Manager. LEAD ADS (Instant Form): pass lead_gen_form_id and the button opens Meta's instant form instead of a website — pair it with a LEAD_GENERATION ad set (promoted_object page_id) and cta_type SIGN_UP / APPLY_NOW / GET_QUOTE. You can build the form itself here too (create_meta_lead_form) or reuse one made in Meta Ads Manager (Page > Instant Forms) by passing its ID. If Meta answers (#200) the account was connected before this permission was granted — reconnect Meta from the panel once. confirm=false = preview.

create_meta_website_audience

ChatGPT
Creates a website retargeting audience from a pixel. pixel_id from list_meta_pixels. retention_days 1-180. event: which pixel event a visitor must fire to enter the audience — PageView (default, all visitors), ViewContent (product viewers), AddToCart / InitiateCheckout (cart/checkout abandoners — the core e-commerce retargeting audience), Purchase (buyers, e.g. to exclude or seed a lookalike), also Search/AddToWishlist/AddPaymentInfo/Lead/CompleteRegistration. confirm=false = preview.

create_microsoft_ad_group

ChatGPT
Creates an ad group in a campaign (starts PAUSED by default). cpc_bid = default click bid. language = ad group language (e.g. English, Turkish) — required by Microsoft. confirm=false = preview, confirm=true applies.

create_microsoft_campaign

ChatGPT
Creates a campaign in Microsoft Advertising (starts PAUSED). campaign_type: Search | Audience | Shopping | DynamicSearchAds. daily_budget is in the account's currency. Subject to the absolute budget cap (parity with Google/Meta). confirm=false = preview, confirm=true applies.

create_microsoft_conversion_goal

ChatGPT
Creates a URL-based conversion goal. uet_tag_id is REQUIRED (first run create_microsoft_uet_tag or list_microsoft_uet_tags). operator: Contains|BeginsWith|Equals|RegularExpression. goal_category: Purchase|AddToCart|Signup|Subscribe|Lead|Other. url_expression e.g. 'opus-growth.com/tesekkur'. confirm=false = preview, confirm=true applies.

create_microsoft_remarketing_list

ChatGPT
Creates a remarketing audience — visitors of a specific page (URL). uet_tag_id is REQUIRED (list_microsoft_uet_tags / create_microsoft_uet_tag). url_contains e.g. '/products'. operator: Contains|Equals|BeginsWith. membership_duration = membership duration (days). confirm=false = preview, confirm=true applies.

create_microsoft_responsive_search_ad

ChatGPT
Creates a Responsive Search Ad (RSA). headlines: 3-15 headlines, descriptions: 2-4 descriptions, final_url required. path1/path2 are optional display URL paths. confirm=false = preview, confirm=true applies.

create_microsoft_uet_tag

ChatGPT
Creates a UET (conversion tracking) tag. Once its script is installed on the site, conversion goals + remarketing become available. confirm=false = preview, confirm=true applies.

create_pmax_campaign

ChatGPT
Creates a Performance Max campaign (PAUSED): budget + campaign + asset group + text and image assets. Google REJECTS an asset group that is missing anything, so all of these are required: at least 3 headlines (≤30 chars), 1 long headline (≤90), 2 descriptions (≤90), plus at least one landscape image (1.91:1), one square image (1:1) and one square logo. Upload the images first with upload_image_asset and pass the returned resource_names in marketing_image_assets / square_image_assets / logo_assets.

create_responsive_search_ad

ChatGPT
Adds a Responsive Search Ad (RSA) to an ad group. status: 'PAUSED' (default) | 'ENABLED'. NOTE: the ad's own status is INDEPENDENT of the campaign — a PAUSED ad stays paused even after the campaign is switched on, so a draft handed over with paused ads serves nothing until someone also enables them. When you are building a campaign for review, pass status='ENABLED': the campaign itself is created PAUSED, which already guarantees nothing spends, and the reviewer then needs a single campaign-level switch to go live. Use update_ad_status later to change it. At least 3 headlines, 2 descriptions. DISPLAY URL PATHS: path1 / path2 are the two segments shown after the domain — ftapi.com/sicherer/datenraum comes from path1="sicherer", path2="datenraum". Up to 15 characters each, no slashes, and they do NOT have to exist as real pages. They are not decoration: the display URL is an expected-CTR signal, so an ad left on a bare domain is measurably weaker than the rest of the account. Optional: final_mobile_url (separate mobile landing page) and tracking_url_template (ad-level tracking template). PINNING: headline_pins / description_pins are optional lists in the SAME ORDER as headlines/descriptions; each entry pins that text to a fixed position — headline 1/2/3, description 1/2 (0 or empty = unpinned, Google rotates it). E.g. headlines=["Free Shipping","B","C"], headline_pins=[1,0,0] always shows "Free Shipping" as headline 1. Assets pinned to the same position compete only for that position; Google cannot fill the others. confirm=false → dry-run.

create_search_campaign

ChatGPT
Creates a new Search campaign (starts PAUSED for safety) + daily budget. locations: comma-separated location names/geo IDs (e.g. 'Türkiye'); languages: language codes (tr,en...). search_partners=False → Search Partners OFF (set True to enable). Display expansion always starts off. presence_only=True → location type PRESENCE (people PRESENT in the target area; Google's default is PRESENCE_OR_INTEREST i.e. "people interested too", set False to revert to that). You can also change these 3 settings later via manage_campaign_settings. IMPORTANT: if no locations are given the campaign targets the ENTIRE WORLD — you can add them later via set_campaign_targeting. BRANDING: business_name (≤25 chars) and logo_asset_id (a square logo ≥128x128 already uploaded with upload_image_asset) are linked to the campaign so the ad shows your brand name and logo on the SERP, which also lifts Ad Strength. Leave them empty and the campaign inherits whatever is set at ACCOUNT level; if the account has none either, the result says so explicitly instead of launching an unbranded campaign silently. confirm=false → dry-run.

create_tiktok_ad

ChatGPT
Creates an ad inside a TikTok ad group. identity_id is required (list_tiktok_identities); the ad runs under that TikTok account identity. Give exactly one creative source: tiktok_item_id (promote an existing TikTok post as a Spark ad; the identity must own the post), or video_id (an uploaded video) with ad_text and landing_page_url, or image_ids. For click-to-WhatsApp ad groups pass whatsapp_number (with country code) and optionally whatsapp_message; for Direct Message ad groups pass auto_message_id. call_to_action e.g. LEARN_MORE, SHOP_NOW, SIGN_UP, CONTACT_US, BOOK_NOW, SEND_MESSAGE. The ad is created paused. TikTok requires an AI-generated content disclosure: ai_generated=auto turns it on for media made with Opus Growth; pass yes for media from another AI tool. confirm=false returns a preview, confirm=true creates it.

create_tiktok_adgroup

ChatGPT
Creates a TikTok ad group — targeting/budget/billing event. Starts DISABLED (paused). location_ids REQUIRED (comma-separated; from tiktok_region_search). optimization_goal: CLICK|CONVERT|REACH|VIDEO_VIEW|LEAD_GENERATION|CONVERSATION|... billing_event: CPC|CPM|OCPM|CPV. Use optimization_goal=CONVERSATION for TikTok Direct Message (messaging) ad groups — promotion_type defaults to LEAD_GEN_CLICK_TO_TT_DIRECT_MESSAGE, billing_event to OCPM and optimization_event to MESSAGE automatically; no pixel is needed. age_groups: AGE_18_24,AGE_25_34... genders: GENDER_MALE|GENDER_FEMALE. For CONVERT, pixel_id + optimization_event are required. placements (optional, comma-separated): PLACEMENT_TIKTOK,PLACEMENT_PANGLE,PLACEMENT_GLOBAL_APP_BUNDLE — leave empty for TikTok's automatic placement. schedule_start / schedule_end: 'YYYY-MM-DD HH:MM:SS' in the ad account's timezone — leave schedule_start empty to start now, schedule_end empty to run open-ended. search_result_enabled=true also serves on TikTok Search results. promotion_type (what is being advertised — TikTok requires it): WEBSITE|APP_ANDROID|APP_IOS|LEAD_GEN_INSTANT_FORM|LEAD_GEN_CLICK_TO_CALL|LEAD_GEN_CLICK_TO_TT_DIRECT_MESSAGE|LEAD_GEN_CLICK_TO_SOCIAL_MEDIA_APP_MESSAGE|LEAD_GEN_EXTERNAL_WEBSITE|FOLLOWERS|LIVESTREAM. INSTANT MESSAGING APPS (WhatsApp / Messenger / LINE / Zalo, per TikTok's official API guide "Create a Lead Generation ad with optimization location as instant messaging apps"): campaign objective LEAD_GENERATION + promotion_type=LEAD_GEN_CLICK_TO_SOCIAL_MEDIA_APP_MESSAGE, then messaging_app_type (cannot be changed later): IM_URL (default) = a custom chat link set on each ad, e.g. https://wa.me/<number> via create_tiktok_ad(whatsapp_number=...), works with no extra setup, CLICK only; WHATSAPP = TikTok fills the chat from messaging_phone (international format, e.g. +14155550123; phone_region_code only when the calling code is shared, like +1), but TikTok requires that number to be connected to its Messaging Management Tool (MMT); MESSENGER = messaging_app_account_id is the Facebook Page ID; LINE = messaging_app_account_id is the LINE Business ID (CLICK only); ZALO = messaging_phone (CLICK only). optimization_goal CONVERSATION is allowlist-only at TikTok and only for MESSENGER/WHATSAPP. BUDGET MINIMUMS: TikTok sets the ad group daily minimum per currency (base 20 USD, converted with each currency's budget ratio); when a budget is too low TikTok's error states the exact minimum for that account's currency — leave empty for website campaigns (TRAFFIC/REACH/VIDEO_VIEWS/ENGAGEMENT/WEB_CONVERSIONS) and it is derived from the campaign objective; set it explicitly for app/catalog/lead-form campaigns. bid_type: BID_TYPE_NO_BID (automatic bidding, default) | BID_TYPE_CUSTOM (manual bid — bid_price required, in the account currency, e.g. manual CPC). pacing: PACING_MODE_SMOOTH (standard delivery, default — spreads the budget over the day) | PACING_MODE_FAST (accelerated; only valid with BID_TYPE_CUSTOM, TikTok rejects it with automatic bidding). automation: AUTO (default) reads the campaign and uses the matching endpoint — TikTok refuses classic ad-group creation inside a Smart+ campaign, so this is what makes an ad group possible there at all; MANUAL or SMART_PLUS force one path. In a Smart+ ad group TikTok requires a real placement list (automatic placement is rejected, so PLACEMENT_TIKTOK is used when none is given) and treats your targeting as a starting point it can widen; pacing and search_result_enabled do not apply. confirm=false previews, confirm=true applies.

create_tiktok_auto_message

ChatGPT
Creates a TikTok WELCOME-CHAT (automated message) template: the greeting a user sees the moment they tap a Direct Message ad, plus optional tappable 'suggested question → automatic answer' pairs (e.g. "Services and prices?" → your price list). TikTok requires such a template on every messaging ad, so without this no DM campaign can go live. title = internal template name. content = the greeting message. suggested_questions = [{"question": "...", "answer": "..."}] — both fields are mandatory in each pair. TikTok reviews the template (audit_status); once it PASSes, pass its auto_message_id to create_tiktok_ad. confirm=false previews.

create_tiktok_campaign

ChatGPT
Creates a TikTok campaign. objective_type: TRAFFIC | REACH | VIDEO_VIEWS | ENGAGEMENT | LEAD_GENERATION | WEB_CONVERSIONS | PRODUCT_SALES | APP_PROMOTION. FOUR OF THOSE OBJECTIVES HAVE A HARD EXPIRY: TikTok stops accepting campaign, ad group and ad creation for PRODUCT_SALES, WEB_CONVERSIONS, APP_PROMOTION and LEAD_GENERATION on 1 January 2027 (legacy manual flow; the request will simply error and nothing is created). TRAFFIC, REACH, VIDEO_VIEWS and ENGAGEMENT are not affected, and neither is anything you do to a campaign that already exists — updates, status changes and reporting keep working. budget_mode: BUDGET_MODE_DAY | BUDGET_MODE_TOTAL | BUDGET_MODE_INFINITE. budget = MAIN unit of the account currency (e.g. 50 = 50.00). Delivery is enabled at the ad-group level — a campaign alone does not spend. automation: MANUAL (default, the classic flow) or SMART_PLUS — TikTok's Smart+ (Upgraded Smart Plus) campaign, the replacement for the expiring manual flow. Smart+ needs a campaign-level budget (TikTok enforces a per-account minimum), accepts only a daily or lifetime budget, and does not support PRODUCT_SALES. sales_destination (Smart+ WEB_CONVERSIONS only): WEBSITE | APP | WEB_AND_APP. First returns a preview with confirm=false; to confirm, use confirm=true.

create_tiktok_customer_file_audience

ChatGPT
Creates a TikTok custom audience from a CUSTOMER LIST (retarget your CRM contacts / build a lookalike seed). identifiers = comma- or newline-separated emails or phone numbers; id_type: EMAIL | PHONE. Identifiers are SHA-256 hashed ON THE SERVER before upload — raw PII never leaves. TikTok needs roughly 1000+ matching users for the audience to be usable. confirm=false previews, confirm=true applies.

create_tiktok_lookalike_audience

ChatGPT
Creates a TikTok lookalike audience from a source custom audience. location_ids is comma-separated (from tiktok_region_search). audience_size: NARROW | BALANCED | BROAD. confirm=false previews, confirm=true applies.

create_tiktok_pixel

ChatGPT
Creates a TikTok conversion PIXEL. Without a pixel you cannot optimise for conversions (CONVERT) or build website audiences — on a fresh account this is the first thing to set up. After creating it, install the pixel code on the site (or send events server-side with send_tiktok_conversion_event). confirm=false previews, confirm=true creates.

create_video_ad

ChatGPT
MAKE A COMPLETE, ORIGINAL VIDEO AD FROM A BRIEF (AI creative director): writes the concept and script, designs every scene from scratch (no templates), generates the photography, a professional voiceover and music, animates it with real typography, checks it and delivers a finished MP4. Use it when the user wants a real video ad / Reels / TikTok / YouTube Shorts ad / promo video for their business or product (reklam videosu, tanıtım filmi, Reels reklamı; Werbevideo; vídeo publicitario; vidéo publicitaire). For a quick slideshow from existing images use create_video_creative; to animate one image use animate_creative. Give: product (what is sold and what makes it different, real facts only), goal (e.g. sign-ups, calls, store visits, WhatsApp orders), audience, cta (the call to action, e.g. "Sign up free"), and the website if there is one: the brand's real colours, fonts, logo and page text are read from it, and a screenshot of the site can appear in the video. Optional: brand_name, offer (only a real, current offer), tone, must_say, avoid, product_image_urls (comma-separated public image URLs of the real product, used as reference so the product stays recognisable), logo_url. format: story 1080x1920 (default, Reels/TikTok/Shorts) | portrait 1080x1350 | square 1080x1080 | landscape 1920x1080. seconds: 15-60 (default 20). language: tr, en, de, es, fr, pt (Brazilian Portuguese) or ar (Arabic; on-screen text is set right-to-left) (default: the account's language). voiceover and music on by default. voice: the narrator voice by name from list_voices (e.g. "Brian", "Charlotte"); let the user listen and choose with list_voices first when they care about the voice. Choosing a voice turns narration on. Empty = the director picks a fitting voice. style: "reels" (default) makes a filmed-looking, organic Instagram/TikTok Reel built the way winning Reels ads are built (real-looking moments, hard cuts, native platform text; vertical 9:16 only); "motion" makes a designed motion-graphics explainer (any format). The ad follows the proven rules for short video ads: a hook in the first 3 seconds, the brand within 5 seconds, platform safe areas, one clear call to action. It never invents statistics, prices, reviews or claims that are not in the brief or on the site, and shows no real people. Every file carries a C2PA "made with AI" credential; when uploaded to TikTok through upload_tiktok_video, create_tiktok_ad turns on TikTok's AI disclosure automatically. Runs in the background for about 6-12 minutes. Returns a job_id immediately: tell the user it is being made, then call creative_status(job_id) every 2-3 minutes until status is done; the result has video_url (MP4). The video also appears in the Creatives page of the Opus Growth panel. Counts toward the plan's video ad limits; not available on the Basic plan. If it fails, the credits are refunded.

create_video_creative

ChatGPT
CREATE A SHORT AD VIDEO FROM IMAGES (template video): turns 1-6 images into a 6-20 second ad video with a gentle zoom on each scene, the scene text fading in, crossfades, and an end card with the logo and call-to-action button. No AI video model is used, so it is fast and inexpensive. Use it when the user asks for a video ad, Reels/Stories/TikTok video or slideshow from images or from creatives made with create_ad_creative (reklam videosu, Reels videosu; Werbevideo; vídeo de anuncio; vidéo publicitaire). For real motion inside the image (camera move, steam, pouring), use animate_creative instead. Sources: creative_id = one or more ids from create_ad_creative (comma separated; the image without text is used), and/or image_urls = public image URLs (comma separated, e.g. product photos or upload_image URLs). Up to 6 scenes in that order. texts = one short line per scene separated by | (e.g. "Fresh every morning|Delivered in 30 minutes|10% off your first order"); write them in the audience's language. cta = button text on the end card. logo_url, brand_color (#RRGGBB), font (montserrat | inter | poppins | playfair | dmsans | oswald) as in create_ad_creative. format: story 1080x1920 (Reels, Stories, TikTok; default) | portrait 1080x1350 | square 1080x1080. seconds_per_scene 2-5. end_card true adds a brand-colored closing scene with logo and cta. The video is silent; add music in Meta or TikTok when creating the ad if wanted. Returns a preview frame and video_url (MP4, H.264). Pass it to upload_meta_video (video_url) or upload_tiktok_video. Show the preview to the user before uploading. Uses credits and counts toward the plan's daily and monthly video limits; not available on the Basic plan. Takes about 15-60 seconds.

create_website_audience

ChatGPT
Creates a rule-based remarketing audience from website visitors (by page URL). string_operator: CONTAINS | EQUALS | STARTS_WITH | ENDS_WITH. membership_days = membership duration, lookback_days = look-back window. confirm=false → dry-run preview.

creative_status

ChatGPT
CHECK A MOTION VIDEO JOB OR LIST RECENT CREATIVES. With job_id (from animate_creative): returns status queued | running | done | error; when done, video_url (MP4) for upload_meta_video or upload_tiktok_video. Poll about once a minute; most jobs finish in 1-4 minutes. Failed jobs are refunded automatically. Without job_id: lists the account's most recent creatives (images and videos with their public URLs, newest first, up to limit) and the plan's creative limits used today and this month. Free of credits.

delete_gtm_container

ChatGPT
DELETES an entire GTM container permanently (IRREVERSIBLE — removes all its tags/triggers/variables/versions). SAFETY: confirm=false → preview only (nothing deleted); confirm=true → deletes. container = 'GTM-XXXX' / container name / full path (list it first with list_gtm_containers).

delete_gtm_entity

ChatGPT
DELETES a GTM tag / trigger / variable / workspace by its full path (IRREVERSIBLE). kind = tag|trigger|variable|workspace. Get the path from list_gtm_tags/triggers/variables/workspaces. SAFETY: confirm=false → preview; confirm=true → deletes. (For a whole container use delete_gtm_container; the container's Default Workspace can't be deleted.)

delete_linkedin_campaign

ChatGPT
Deletes a LinkedIn campaign (permanent if DRAFT; otherwise marked for deletion; reporting data is preserved). confirm=false = preview.

delete_linkedin_campaign_group

ChatGPT
Deletes a LinkedIn campaign group (permanent if DRAFT; otherwise marked for deletion). confirm=false = preview.

delete_linkedin_creative

ChatGPT
Deletes a creative (ad) — permanent if DRAFT, otherwise marked for deletion. confirm=false = preview.

delete_meta_ad

ChatGPT
Deletes a Meta ad. confirm=false = preview.

delete_meta_adset

ChatGPT
Deletes a Meta ad set. confirm=false = preview.

delete_meta_audience

ChatGPT
DELETES a Meta custom/lookalike audience — the cleanup for lists built by mistake, duplicates, or audiences you must remove on a privacy request. We could create audiences but never delete them, so bad ones piled up permanently. NOTE: an audience still attached to an ad set cannot be deleted; detach it first. audience_id from list_meta_audiences. confirm=false previews.

delete_meta_campaign

ChatGPT
Deletes a Meta campaign (reporting data remains readable in Meta). confirm=false = preview.

delete_microsoft_audience

ChatGPT
Deletes a Microsoft audience (remarketing list, etc.). confirm=false = preview, confirm=true applies.

delete_microsoft_campaign

ChatGPT
Deletes a Microsoft campaign (irreversible). confirm=false = preview, confirm=true applies.

deploy_site_zip

ChatGPT
Deploy an ENTIRE website from a .zip onto the user's AI Server in one step. Give the zip as zip_base64 (a file the user dropped into chat — base64-encode it; only practical up to a few MB) OR zip_url (a public link, fetched SERVER-SIDE with no chat cost — the way to do LARGE files up to 300 MB). zip_url accepts a Google Drive or Dropbox SHARE link directly (the file must be shared 'anyone with the link'); it is auto-converted to a direct download and Drive's large-file confirmation is handled — so the user just uploads to Drive and pastes the link. It uploads the zip to the user's own isolated server, unzips it and PUBLISHES it live at /data/site over HTTPS. If the zip is ALREADY a static site (index.html at the root) it is published as-is — instant, no build. If it is a source project it is BUILT on the server and the static output is published: Next.js → STATIC EXPORT (output:'export' is injected automatically) from out/; Vite → dist/; Create-React-App → build/. Sites are served statically: NO Node/SSR process runs at runtime, so Next.js features that REQUIRE a live server are NOT supported (getServerSideProps/SSR, API routes /api/*, ISR/revalidate, middleware, Server Actions, next/image default optimization). If the project needs those, the static build fails and this returns a clear explanation; the fix is to make the app statically exportable or use a runtime host. Everything under /data/site is REPLACED (a version snapshot is taken first, so site_restore can undo it). Build cap ~10 min, zip ≤ 300 MB (for anything above a few MB use zip_url — a 300 MB base64 will not fit the chat context).

describe

ChatGPT
Explains what the unified read tools can do: with no argument it lists every platform get_report / list_entities support; with a platform it returns that platform's account-id field, the row levels and the breakdown dimensions it accepts, plus the entity types list_entities can list. Call this whenever you are unsure which level/breakdown/entity name to pass. Read-only, costs nothing.

detach_linkedin_conversion

ChatGPT
Removes a conversion tracking rule from a LinkedIn campaign (ad set) — the inverse of attach_linkedin_conversion. The rule itself is NOT deleted; only this campaign stops reporting/optimizing for it. This is how you clean up a badly scoped rule (e.g. a site-wide 'All Pages' conversion that fires on every page and inflates conversion counts). WARNING: on a MAX_CONVERSION ('Maximum Delivery' automated bidding) campaign, detaching the LAST conversion can stop delivery — LinkedIn requires an associated conversion for that bid strategy. Check list_linkedin_conversions → associated_campaigns first. conversion_id: the numeric id from list_linkedin_conversions. confirm=false = preview.

diagnose_chatgpt_ads_delivery

ChatGPT
Explains why ChatGPT Ads are not getting impressions: ranked findings with the evidence, the fix and the tool that applies it. Follows OpenAI's own troubleshooting order: account (status, brand review, account integrity review, account spend limit), campaigns (status, schedule, budget left vs spend, locations and platforms, conversion events, serving issues), ad groups (status, bid strategy vs budget type, the bid_too_low warning — which OpenAI does not raise within 24 hours of an ad-group edit, so a fresh 'false' is reported as not yet evaluable with the exact time it becomes meaningful —, context-hint count and language vs the targeted country, orphan groups), ads (status, review status and reason, serving issues explained, landing page reachability and cross-domain redirects, generic homepage destinations), then a 7-day hourly delivery timeline that detects every campaign stopping in the same hour (an account-level cause such as a spend limit or prepaid balance). Health and clinic advertisers get a policy finding: those categories need manual advertiser approval and are limited to pilot countries, which no bid, budget or hint change fixes. Severity order: blocker > policy > warning > info. Read-only; campaign_id narrows it to one campaign.

dismiss_recommendations

ChatGPT
Dismisses one or more Google Ads recommendations. resource_names = comma-separated list of recommendation resource_names (obtained from list_recommendations). Reversible (the recommendation may reappear later). confirm=false → dry-run preview.

download_linkedin_leads

ChatGPT
Downloads the leads submitted through LinkedIn Lead Gen Forms (name, email, phone and every other answer), so they can be worked or exported. form_id narrows it to one form (from list_linkedin_lead_forms); since/until are YYYY-MM-DD dates; test_only=true returns only the test leads generated from Campaign Manager. Needs LinkedIn's Lead Sync API access — if the account has not been reconnected since that permission was granted, the tool says so.

download_meta_leads

ChatGPT
Downloads the answers people submitted through a Meta instant form — name, phone, email and every custom question, with the ad, ad set and campaign each lead came from. form_id comes from list_meta_lead_forms. since (YYYY-MM-DD) returns only newer leads, which is how you pull just what arrived since the last check. This is the Meta counterpart of download_tiktok_leads. Read-only.

download_tiktok_leads

ChatGPT
Downloads the actual leads collected by TikTok lead-gen or messaging ads, with each lead's answered fields. lead_source: INSTANT_FORM (form ads — page_id REQUIRED, from list_tiktok_lead_forms) or DIRECT_MESSAGE (messaging ads). Optional ad_id narrows to one ad; start_date/end_date are YYYY-MM-DD. Returns up to limit leads (hard cap 1000) with a truncated flag — leads carry PII so the result is bounded.

enable_gtm_builtin_variables

ChatGPT
ENABLES GTM built-in variables (Click URL, Form ID, Scroll Depth, Video Percent, Page Path…). These are OFF by default and a trigger/tag that references a disabled built-in variable silently never fires — this is the usual cause of "my click/scroll/form trigger doesn't work". types = comma-separated, e.g. 'clickUrl,clickClasses,formId,scrollDepthThreshold,videoPercent'. Common: pageUrl,pageHostname,pagePath,referrer,clickElement,clickClasses,clickId,clickTarget,clickUrl,clickText,formElement,formClasses,formId,formTarget,formUrl,formText,scrollDepthThreshold,scrollDepthUnits,scrollDirection,videoProvider,videoStatus,videoUrl,videoTitle,videoPercent,videoDuration,videoCurrentTime,errorMessage,errorUrl,errorLine,historySource,newHistoryFragment,oldHistoryFragment. confirm=false previews. Publish afterwards with publish_gtm_container.

extension_asset_performance

ChatGPT
Reports how each ad EXTENSION performs: sitelinks, callouts, the call button, images, structured snippets, business name and logo — impressions, clicks, cost and conversions per asset. This answers "which sitelink actually gets clicked" and "how often is the call button used", which no other tool can: list_extension_assets returns configuration only, and rsa_asset_performance covers headlines and descriptions, not extension types. Returns two views — by_field_type is the account summary (one row per extension kind, so you can see at a glance that sitelinks earn clicks while structured snippets do not) and assets is the per-asset detail with each sitelink's own link text or the phone number of a call asset. days is the lookback window. campaign_id narrows to one campaign. field_type filters to one kind: SITELINK | CALLOUT | CALL | STRUCTURED_SNIPPET | AD_IMAGE | BUSINESS_NAME | BUSINESS_LOGO | PRICE | PROMOTION | MOBILE_APP. Read-only.

fetch

ChatGPT
Expands a search result (id = 'platform:account_id') into details: account name, id, and fields like currency/status (OpenAI/ChatGPT connector fetch contract). Read-only. Returns: {id, title, text, url, metadata}.

find_instagram_media_id

ChatGPT
Turns an Instagram POST or REEL LINK into the media id needed to advertise it, so nobody has to dig the id out by hand. Paste the URL — instagram.com/p/…, /reel/…, /reels/…, /tv/…, with or without the username prefix, query strings ignored — and get back: shortcode, media_pk (Instagram's INTERNAL key, decoded from the link), owner_username, permalink, thumbnail_url, is_reel, and when it can be resolved ig_media_id: the real ads-usable id you pass straight to create_meta_instagram_post_ad. READ THIS BEFORE PROMISING ANYTHING: media_pk is NOT the ads id. Meta's ad API accepts only the "media V2" id and that id CANNOT be computed from a link — verified live with ads_management: /{ig_user_id}/media, the media field expansion, business_discovery, /{media_pk} and instagram_oembed all return permission errors, and the public page source does not contain it either. So ig_media_id comes back only when this post has ALREADY been advertised from the ad account (the id is then read off that account's ad creatives) — pass account_id to enable that lookup. When it cannot be resolved, usable_for_ads=false and note/manual_path say exactly what to do instead, including the create_meta_video_ad fallback (new creative, faster, but the organic post's likes and comments do not carry over). The tool also checks OWNERSHIP: owned_by_connected_account=false means the link belongs to somebody else's Instagram account, which is the most common reason an Instagram post ad cannot be created at all.

fraud_scan

ChatGPT
Scans the ad-click traffic captured by your fraud tag for click fraud / invalid traffic and returns the most suspicious IP addresses, each scored 0-100 with reasons (repeat/velocity from one IP, datacenter/hosting, Tor exit, known-malicious list, bot user-agent, high volume with zero conversions). READ-ONLY — it does not block anything. Feed recommended_block_ips to block_ip_addresses (approval-gated) to exclude them. Needs the tag installed first (fraud_setup). CLICK LOOKUP (click log / click events / lead lookup, tıklama kaydı): pass gclid (from a form lead or CRM), ip, or at (time of a suspicious lead, Europe/Istanbul, 'YYYY-MM-DD HH:MM', with ±window_minutes) to get the individual captured clicks — time (Istanbul + UTC), GCLID, IP, user-agent, referrer, landing page — plus that IP's fraud score, so a fake lead can be matched to its ad click and the IP excluded. Form contents are not captured by the tag.

fraud_setup

ChatGPT
Sets up click-fraud (invalid traffic) tracking for a website. Returns a one-line tracking tag and a site key bound to the given domain. The tag records clicks that arrive from ads (landing URLs with gclid/gbraid/wbraid) so suspicious IP addresses can be reviewed later with fraud_scan. If the site is hosted on Opus Growth, install=true writes the tag into every page automatically; otherwise add the snippet to the <head> of each page or through a Tag Manager Custom HTML tag. The reply includes how many ad clicks were captured in the last 24 hours and 7 days.

ga4_create_conversion

ChatGPT
Marks a GA4 event as a CONVERSION (key event) — e.g. 'generate_lead', 'purchase', 'sign_up'. Once marked, GA4 counts it as a conversion and it can be imported into Google Ads for bidding. counting: ONCE_PER_EVENT (default) | ONCE_PER_SESSION. property: see ga4_list_properties. SAFETY: confirm=false → preview; confirm=true → creates. Needs the analytics.edit scope (if you get a permission error, reconnect Google Analytics to grant edit access).

ga4_create_custom_dimension

ChatGPT
Creates a GA4 custom dimension → makes an event/user parameter reportable in GA4. parameter_name = the event param key (e.g. 'plan', 'lead_source'); display_name = shown in reports; scope: EVENT | USER. property: see ga4_list_properties. SAFETY: confirm=false → preview; confirm=true → creates. Needs the analytics.edit scope (reconnect Analytics if you get a permission error).

ga4_list_conversions

ChatGPT
Lists the GA4 conversions (key events) of a property — event name + counting method. property: see ga4_list_properties.

ga4_list_data_streams

ChatGPT
Lists a GA4 property's DATA STREAMS with their MEASUREMENT ID (G-XXXXXXX) — the value you need to build a GA4 configuration tag in Tag Manager, and the one piece that used to be unreachable, so a GA4 setup could never be finished from chat. Per stream: stream_id, type (web, iOS, Android), display name, measurement_id and the site URL it is bound to. property = a value from ga4_list_properties (a number or 'properties/123' both work); leave it empty and the connected/only property is used. Read-only.

ga4_list_properties

ChatGPT
Lists the GA4 properties in the connected Google Analytics account. Pass a returned property to the other ga4_* tools — or you can give the property name/number directly (auto-matched).

ga4_overview

ChatGPT
GA4 overview KPIs (last N days): sessions, users, new users, page views, conversions, engagement rate (%), avg. session duration (sec). property = from ga4_list_properties (property name/number also works).

ga4_realtime

ChatGPT
GA4 REALTIME: the number of users currently active on the site + a country breakdown (last ~30 min). property = from ga4_list_properties (name or number also works).

ga4_report

ChatGPT
GA4 breakdown report: sessions / users / key events (conversions) / total events by a dimension. dimension: channel (default) | source | medium | source_medium | page | landing_page | country | device | date | event. The channel/source breakdown answers 'which traffic converts'. dimension='event' splits by EVENT NAME, which is the only way to isolate ONE key event. Without it conversions is the SUM of every key event in the property, so a property with purchase + form_sent + mql reports a single number that cannot be reconciled against what an ad platform counted — the most common check in any paid media audit. Use ga4_list_conversions to see which event names are key events. dimension2 crosses a SECOND dimension with the first, e.g. dimension='event' + dimension2='source_medium' gives form submissions per traffic source. Rows then carry both dimension fields. events is the raw event count and conversions counts only key events, so on an event row the two differ whenever that event is not a key event.

gbp_create_location

ChatGPT
CREATES a NEW Google Business Profile listing (a new business on Maps/Search). title = the business's REAL name exactly as on its signage (adding keywords/city to the name violates Google's guidelines and gets listings suspended). address_lines = street address; city = town/district; region = province/state; country = 2-letter code (TR). primary_category: a category name ('Diş Kliniği') or id — see gbp_search_categories. hours = plain text ('Pzt-Cum 09:00-18:00; Pazar kapalı'). ELIGIBILITY (Google's rules, ignore them and the listing is suspended): the business must be real, must physically operate at that address (no PO box / virtual office), you must be authorised by the owner, and it must not already exist on Maps (search first — duplicates are a violation). The new listing is created UNVERIFIED and stays invisible on Maps until verified: run gbp_verification_options then gbp_start_verification. SAFETY: confirm=false (default) sends the data to Google for VALIDATION ONLY and creates nothing — it reports whether Google would accept it; confirm=true actually creates it.

gbp_create_post

ChatGPT
Publishes an UPDATE post (local post) to the Business Profile — visible on Maps/Search. summary = post text. Optional call-to-action: cta_type (LEARN_MORE|BOOK|ORDER|SHOP|SIGN_UP|CALL) + cta_url; CALL takes no url (Google dials the business phone number), every other type requires one. The button is read back from Google's reply and returned as cta_type. image_url attaches a PHOTO to the post itself (not the gallery): it must be a publicly reachable direct image link — Google downloads it server-side — JPG or PNG, at least 250px on the short edge and at least 10 KB. If you do not have a public link, call upload_image first: give it source_url when the photo already lives somewhere on the web (their site, WordPress media, Drive) and it is re-published with no work for the user, or call it with no arguments to get a drop link that takes up to 10 photos at once. language = post language code (tr|en|…); leave it empty and the post is tagged with the business location's OWN language, read from the profile (falls back to en if the profile does not declare one). SAFETY: confirm=false → preview, confirm=true → publish. location = a gbp_list_locations value or a business name/domain (auto-matched).

gbp_insights

ChatGPT
Google Business Profile PERFORMANCE insights — how customers find and act on the listing on Google Search & Maps: impressions (search + maps), phone CALLS, direction requests, website clicks and bookings, totalled over the last days (default 30, max 540). by_day=true also returns daily: one row per day with search_impressions, maps_impressions, calls, direction_requests, website_clicks and bookings, for trend charts. Uses Google's current Business Profile Performance API. messages is ALWAYS 0 and is kept only for backward compatibility: Google shut Business Profile messaging down on 31 July 2024, so it is a dead metric, NOT a feature waiting to be switched on — never advise the user to "turn messaging on". bookings is different: Reserve with Google is still live, so a 0 there IS a real gap. location = a gbp_list_locations value OR a business name/domain ('Altes Diş' / 'altesklinik.com') — auto-matched.

gbp_list_locations

ChatGPT
Lists all business locations across the connected Google Business Profile accounts (name, address, phone, website). Pass the returned location value to other gbp_* tools.

gbp_list_posts

ChatGPT
Lists already-published Google Business Profile posts for one owned location, newest first. Returns each post's text, state, timestamps, call-to-action and media/public URLs. This public OpenAI operation is read-only and cannot delete posts.

gbp_location_details

ChatGPT
Reads all information for a business location: name, address, phone, website, description, categories, business hours, THE SERVICES THE BUSINESS OFFERS, the Maps link, the Google place ID (place_id) and the direct "write a review" link (review_uri, ready for a QR card or a message to customers). location = a value from gbp_list_locations OR a business name/domain ('Altes Diş' / 'altesklinik.com') — auto-matched to the correct location. services is the business's OWN service list (Google calls it serviceItems): each entry has a name, an optional description and an optional price, plus kind = "structured" (one of Google's predefined service types for the category) or "free_form" (a custom service the business typed itself). services_count is the true total; the list is capped at services_limit. NOTE ON EMPTY RESULTS: services: [] means the business really has NOT entered any services — it is NOT a tool limitation. Do NOT infer the service list from categories: the category objects carry a Google CATALOG of what is available for that category, which is why the raw catalog fields are deliberately not returned here. google_updates is the "Google updates" badge you see in the Business Profile Manager: when Google (or a member of the public) proposes a change to the listing, pending is true and changes shows each affected field with the CURRENT value next to the one Google suggests — so you can tell a harmless tweak from a name or address change the owner must react to. Accepting or rejecting a suggestion has no API endpoint; it is done in the Business Profile Manager, though re-writing the correct value with gbp_update_location also overrides the suggestion.

gbp_manage_services

ChatGPT
Manages the SERVICE LIST on a Google Business Profile (the "Services" block shown on Maps/Search) — the field gbp_location_details returns as services. This is what Google's Ask Maps AI reads when it answers "does this clinic do X?", so a complete list with descriptions is worth real visibility. action: • add (default) — adds the given services; names that already exist are SKIPPED (idempotent, safe to re-run) • update — sets the DESCRIPTION of services that already exist; adds nothing • remove — deletes the given services by name • replace — makes the list EXACTLY what you pass (anything not listed is deleted) services accepts any of: a list of names (["Fissür Örtücü", "Flor Uygulaması"]), "Name|description" entries, a newline/semicolon separated string, or dicts [{"name": …, "description": …}]. Descriptions are capped at 300 characters by Google — longer text is rejected with the offending service named. Names are matched loosely (case, Turkish characters and text in parentheses are ignored), so "Diş Beyazlatma (Bleaching)" and "diş beyazlatma" count as the same service. If a name matches one of Google's predefined service types for the category it is written as a STRUCTURED item, otherwise as a FREE-FORM item under the primary category. Existing prices on services you do not touch are preserved. SAFETY: confirm=false (default) returns a PREVIEW listing exactly what would be added/updated/removed and changes nothing; confirm=true applies. location = a gbp_list_locations value or the business name/domain (auto-matched).

gbp_reply_review

ChatGPT
Writes/updates a public REPLY to a Google review. review = the full name from gbp_reviews (accounts/…/locations/…/reviews/…). SAFETY: confirm=false → preview, confirm=true → publish.

gbp_reviews

ChatGPT
Lists a business location's Google reviews (stars, reviewer, text, date, reply status, and when the reply was written) + the average rating and how many are still UNANSWERED. Each answered review carries replied_at, so reply SPEED (created → replied_at) is measurable. gbp_reply_review posts a reply. Large profiles: use summary_only=true for just the counts, only_unanswered=true to see just the ones needing a reply, and offset+limit to page (the answer tells you next_offset). Order is most recently UPDATED first and a reply updates a review, so unanswered reviews collect at the END of the list; use only_unanswered=true instead of paging to find them. When the list holds MORE reviews than the count Google publishes, the summary says how many are still unpublished (unpublished_estimate): those cannot be replied to yet, and a reply attempt comes back as review_not_replyable, so do not retry them.

gbp_search_categories

ChatGPT
Searches Google Business Profile CATEGORIES by name (e.g. 'diş kliniği', 'restaurant') and returns each category's id (categories/gcid:…) plus its display name. Use this to find the id needed by gbp_update_location's primary_category / additional_categories. Searching in the listing's own language works, and the search RELAXES when your word is not the one Google used: a Turkish word with suffixes is shortened to its stem, and a handful of business terms whose catalogue name has a different root are mapped (searching tercüme finds Çevirmen, which is what Google calls a translation service in Turkish). When that happens the reply says which word actually matched, so you can tell the user the category exists under a different name rather than claiming their word is a category. A relaxed search only keeps categories whose name STARTS with the matched word — an unrelated query returns nothing rather than a plausible-looking wrong category. Read-only.

gbp_search_keywords

ChatGPT
The actual SEARCH TERMS people typed on Google Search & Maps before the business listing appeared ("searches used to find your business" in the Business Profile dashboard) — the only source that shows WHICH queries a location ranks for locally, so you can see which service or category is missing. Returns the terms ranked by impressions plus a summary. Google gives an exact count only for high-volume terms; everything else is reported as "fewer than N" and is counted, not guessed. Data lags ~2 months and goes back ~18 months. location = a gbp_list_locations value OR a business name/domain — auto-matched. For impressions/calls/directions totals use gbp_insights instead.

gbp_update_location

ChatGPT
UPDATES the business listing on Google Maps/Search: phone, additional_phones (the extra numbers shown under the main one; a list REPLACES the current extras, clear_additional_phones=true removes them; changing only phone keeps the extras), website, description, business NAME (title), the ADDRESS, opening HOURS and CATEGORIES. hours = plain text like 'Pzt-Cum 09:00-18:00; Cmt 10:00-14:00; Pazar kapalı' (Turkish or English day names, day ranges, multiple slots per day, 'closed' days are simply left out). special_hours = one-off DATE overrides such as a public holiday, separate from the weekly hours: '2026-10-29 closed; 2026-12-31 10:00-14:00' (date YYYY-MM-DD or DD.MM.YYYY; value closed/kapalı, one or more time ranges, or 'remove' to drop that date's override). Only the dates you give change, other special days already on the listing are kept. primary_category / additional_categories accept a category id (categories/gcid:…) OR a plain name ('Diş Kliniği') which is auto-resolved — use gbp_search_categories to look ids up. additional_categories REPLACES the whole secondary list; to REMOVE every secondary category (keep only the primary) pass clear_additional_categories=true (an empty list alone changes nothing). ADDRESS: address_lines (street line, use ' | ' between lines), city (town/district), region (province), postal_code, country (2-letter code). Give address_lines OR city to rewrite the address; the parts you leave empty keep their current value, so fixing only a misspelled street is one call. Google RE-VERIFIES a listing whose address changes and may ask for proof, so correct wording rather than moving the pin: a real relocation belongs in the Business Profile Manager. SAFETY: confirm=false (default) returns a PREVIEW and changes nothing; confirm=true applies. Only the fields you pass are touched. location = a gbp_list_locations value or the business name/domain (auto-matched).

gbp_update_photo_category

ChatGPT
Changes the category of a photo that is already on a Google Business Profile, keeping the same photo. media_id = an id or full name from list_business_profile_photos. category = EXTERIOR | INTERIOR | PRODUCT | AT_WORK | FOOD_AND_DRINK | MENU | TEAMS | ADDITIONAL. COVER, PROFILE and LOGO cannot be set through the API. confirm=false returns a preview, confirm=true applies. location = a gbp_list_locations value or the business name/domain.

gbp_update_post

ChatGPT
Edits an ALREADY PUBLISHED Google Business Profile update post in place — the text, or the call-to-action button — without deleting it. Use this when a live post states something that is no longer true (an old service count, a phone number, a branch count): deleting and re-posting would reset the post's original date, so a 2018 post would reappear dated today and the profile would show a burst of "new" posts. post is the post name from gbp_list_posts; the full accounts/…/locations/…/localPosts/… form and the bare id both work. Only the fields you pass are changed — leave summary empty to keep the current text and only swap the button. cta_type: LEARN_MORE | BOOK | ORDER | SHOP | SIGN_UP | CALL, with cta_url (CALL takes no url). The current text is read before the change and returned next to the new one, and the post is read back afterwards, so a silent no-op is reported instead of a false success. SAFETY: confirm=false previews the edit. Google decides what is editable on a live post; if it refuses, the reason is passed through so you can decide whether to delete and re-create instead.

gbp_upload_photo

ChatGPT
Adds one or SEVERAL PHOTOS to the Google Business Profile listing (visible on Maps/Search) in a single call. Give image_url for one photo, or image_urls for a batch of up to 10 — a batch is the normal case after upload_image, which hands back one public URL per photo the user dropped. Each URL must be a publicly reachable direct image link, because Google downloads it server-side. category applies to every photo in the call: ADDITIONAL (default) | PROFILE (profile picture) | COVER | LOGO | EXTERIOR | INTERIOR | PRODUCT | AT_WORK | FOOD_AND_DRINK | MENU | TEAMS — upload different categories in separate calls. LOGO does not work: measured 22 Sep 2026, Google accepted the request, processed it as PROFILE and left every slot unchanged, so the logo has to be set in the Business Profile dashboard by hand. Whatever category you ask for, the reply is now checked against the slot Google actually wrote to; if they differ the photo is reported as failed with both categories and the credit is refunded, instead of a success that changed nothing. A batch is charged PER PHOTO, exactly as separate calls would be, and a photo Google rejects is not charged. If some photos go up and others fail you get both lists back with the reason for each, so a partial result is never reported as full success. This adds photos to the listing's PHOTO GALLERY; it does NOT attach them to an Update post — to publish a post WITH a photo use gbp_create_post(image_url=...). COVER ASPECT RATIO IS CHECKED BEFORE THE UPLOAD: Google rejects a cover photo whose ratio is wrong with a bare "Request contains an invalid argument." that names no cause and still burns the credit, so the image is measured first and you get the actual pixel size, the ratio and what Google expects (16:9). Pass allow_any_ratio=true to skip that check. SAFETY: confirm=false → preview, confirm=true → publish. location = a gbp_list_locations value or the business name/domain.

gbp_upload_video

ChatGPT
Adds a VIDEO to a Google Business Profile (shown on Maps/Search) — the video counterpart of gbp_upload_photo, which only takes images. video_url must be a publicly reachable direct video link (mp4/mov); Google downloads it server-side. If the file is only on a computer, host it somewhere public first and pass that URL. Google's limits: up to ~30 seconds, ~100 MB, at least 720p; processing can take a few minutes before the video appears on the profile. category = ADDITIONAL (default) | EXTERIOR | INTERIOR | PRODUCT | AT_WORK | FOOD_AND_DRINK | MENU | TEAMS. SAFETY: confirm=false → preview, confirm=true → publish. location = a gbp_list_locations value or the business name/domain (auto-matched).

gbp_verification_options

ChatGPT
Shows which verification methods Google offers for a business listing (postcard to the address, phone call, SMS, email, or instant) and the current verification state. Starts nothing — read-only. An already-verified listing returns no options.

get_ad_details

ChatGPT
GOOGLE ADS ONLY — reads the full content of Google Ads ads (RSA headlines/descriptions, URL, status) together with each ad's AD STRENGTH and Google's own action items for improving it. Filtered by ad_group_id or ad_id. ad_strength is the value Google shows in the interface: PENDING, NO_ADS, POOR, AVERAGE, GOOD or EXCELLENT. It answers "is this ad done or is one step left", which a weak-ads list cannot — a list of weak ads tells you what is bad, not whether the rest reached EXCELLENT. action_items is what Google itself says is missing on that specific ad ("try including more keywords in your headlines"), so the remaining gap is read rather than guessed; an ad can have all 15 headlines filled and still score low because they repeat each other. customer_id is a 10-digit Google Ads id; for Meta/TikTok/LinkedIn ads use list_meta_ads / list_tiktok_ads / list_linkedin_creatives instead.

get_ad_schedule

ChatGPT
Reads a campaign's CURRENT ad schedule (day/hour blocks + minutes + bid modifier + criterion_id). If campaign_id is empty, covers all campaigns. The 'block' field is ready to paste into set_ad_schedule. Read-only.

get_billing_status

ChatGPT
Shows your current plan, remaining monthly operation quota and trial dates.

get_campaign_targeting

ChatGPT
Reads a campaign's CURRENT targeting: location (geo), radius/proximity targeting (radius + units + lat/lng), language, device, and audience (user_list) criteria + negative (excluded) ones — plus the campaign's geo_target_type (PRESENCE = people physically in the area, vs PRESENCE_OR_INTEREST). If campaign_id is empty, all campaigns. ad_group_id reads the AD GROUP's own criteria instead — location, language, audience and user list. This is the only way to see targeting on Demand Gen, where Google keeps geo, language and audience at ad group level, not on the campaign: for those campaigns the campaign-level read returns device criteria and nothing else, which made it impossible to verify what a Demand Gen campaign actually targets (measured on a live campaign that had 27 ad-group locations the campaign read could not see). Location values come back as geo IDs; pass one to geo_target_search to get its name. READ-only.

get_change_history

ChatGPT
GOOGLE ADS ONLY — lists Google Ads account changes over the last N days IN DEPTH: who (user_email), from where (client_type), when, operation (CREATE/UPDATE/REMOVE), resource, CHANGED FIELDS, and old→new values. Max 30 days. Filter with resource_type (e.g. 'CAMPAIGN','AD_GROUP','AD_GROUP_CRITERION') and operation ('CREATE'|'UPDATE'|'REMOVE'). For the same audit trail on Meta (who changed which campaign/ad set/ad and when) use meta_change_history.

get_conversion_settings

ChatGPT
Reads the SETTINGS detail of conversion actions: id, category, counting type (every/one), click/view-through lookback window (days), attribution model, default value/currency, and whether it's primary for the goal. Also reports the ACCOUNT-level conversion setup under account: the conversion tracking id, who manages tracking, whether the customer-data terms have been accepted and whether ENHANCED CONVERSIONS FOR LEADS is on. That last flag is the one to check when a tag sends hashed e-mail/phone in user_data but Google appears to ignore it — the tag can be perfect while the account switch is off, and there is no way to see this from the tag itself. The switch lives in the Google Ads interface (Goals > Settings > Customer data terms / Enhanced conversions for leads); the API exposes it read-only, so this tool tells you the state and the interface changes it. account.call_reporting answers the question set_call_reporting cannot: whether call reporting and call CONVERSION reporting are currently on, and which conversion action calls are counted against. Check this before switching it on, and read it before you change it if you may need to put it back. Read-only (get_conversion_tags only returns the tag).

get_conversion_tags

ChatGPT
Reads the Google Ads tag info for conversion actions: Conversion ID (AW-...) + label — the 'send_to' value needed for GTM / gtag (Google tag) setup. conversion_action_ids is a comma-separated id list (e.g. '7680551900,7680991420'); if empty, returns all conversions in the account. Read-only.

get_meta_ad_creative

ChatGPT
Reads an EXISTING Meta ad's creative: headline, primary text, description, image or video, the button, the destination link, the Page and Instagram actor, and WHICH Advantage+ creative enhancements are currently switched on. Use it before duplicating an ad (copy_meta_entity) or rewriting it (update_meta_ad_creative), and to answer "did Meta add music / touch up my video". Meta turns several enhancements on by default; enhancements_on shows exactly what is live.

get_pmax_assets

ChatGPT
Reads a Performance Max ASSET GROUP: its creative content (headlines/descriptions, images, videos) AND its SIGNALS — the audience signal and the search themes that actually steer the campaign. Every asset comes with its resource_name — pass those to update_pmax_assets(remove_resource_names=...) to remove or replace one. Each group also reports its id, landing page, ad strength and status. signals=true (default) adds, per asset group: the search themes, and the audience signal expanded to the ACTUAL first-party lists behind it — each remarketing or customer-match list with its name, type and MATCH SIZE. That size is the point: a customer list below Google's 1,000-member minimum is attached but does nothing, and from the interface it looks like it is working. This is the single most important configuration question in a Performance Max campaign — which of your own data is feeding it — so it is on by default; pass signals=false to skip the extra lookups. Read-only.

get_report

ChatGPT
ONE report tool for EVERY connected platform — use this for any "how did X perform" question instead of hunting for a platform-specific report tool. platform: google_ads | meta (facebook/instagram) | tiktok | linkedin | microsoft | chatgpt_ads (OpenAI ChatGPT Ads) | ga4 | search_console | youtube | business_profile. account = that platform's account id (Google Ads customer_id, Meta account_id, GA4 property, Search Console site url, Business Profile location…); leave empty to use the connected/default one. days = lookback window (or give start_date/end_date as YYYY-MM-DD). For the ad platforms (google_ads, meta, tiktok, linkedin, microsoft) days=N means the last N FULL days, excluding today, on every platform alike (chatgpt_ads: the last N days up to now in the ad account's time zone; its rows include conversions and conversion_value when conversion tracking is set up), and every reply carries period {start, end} = the exact range that was reported: quote that range, not the one you assumed. For performance SINCE EACH CAMPAIGN STARTED (lifetime) next to a recent window, call account_snapshot instead of guessing a start date. For metrics or breakdowns this report does not expose, use the platform's free query tool (describe lists it). level = row granularity for ad platforms (campaign | ad_group/adset | ad). breakdown = split rows by a dimension (device, day, country, age, gender, publisher_platform… for GA4/Search Console this is the dimension: channel, source, page, query…). Call describe(platform) to see exactly which levels and breakdowns that platform supports. For search_console the breakdown also takes a COMBINATION and a SORT: 'page,query' answers which page ranks for which term, and 'query by impressions' sorts by impressions instead of clicks — Search Console itself only ever returns rows by clicks, so high-impression low-click terms are invisible without it. Returns the platform's metric rows.

get_shared_negative_list

ChatGPT
Reads the actual NEGATIVE KEYWORDS inside a shared negative keyword list (shared set). Get the shared_set_id from list_shared_negative_lists. limit caps how many keywords are returned (default 500; large lists can hold thousands — raise limit or call again to see more). Read-only.

get_tiktok_lead_form_fields

ChatGPT
Returns the question/field schema of a TikTok lead instant form (field key, label and type) so the answers in download_tiktok_leads can be interpreted. page_id comes from list_tiktok_lead_forms.

google_ads_query

ChatGPT
Runs ANY read-only Google Ads Query Language (GAQL) SELECT against a connected Google Ads account and returns the raw rows — the escape hatch for data no other tool exposes: any resource (campaign, ad_group, ad_group_ad, ad_group_criterion, keyword_view, search_term_view, change_event, change_status, asset, conversion_action, customer_client, bidding_strategy, geographic_view, user_location_view, landing_page_view, recommendation, ...), any metric and any segment the Google Ads API supports. GAQL can only READ; it cannot change the account. Reference: https://developers.google.com/google-ads/api/docs/query/overview and the field list per resource at https://developers.google.com/google-ads/api/fields (use the version this server runs; field names occasionally change between API versions, e.g. campaign.start_date_time). Rules: start with SELECT; metrics need a date filter or segments.date range for meaningful periods (e.g. WHERE segments.date DURING LAST_30_DAYS); money fields are in micros (divide by 1,000,000, account currency). Prefer list_entities / get_report when they already answer the question — they normalise statuses (is_serving) and currencies. limit = rows returned to you (max 2000); total_rows tells you if more exist — narrow the query instead of paging through huge results. customer_id empty = the default connected account.

gsc_add_site

ChatGPT
Adds a website as a property to the connected Search Console account, so a site that was never added (gsc_list_sites does not show it) can be set up from chat. site_url is either a domain property ('sc-domain:example.com', covers every subdomain and http/https; recommended) or a URL-prefix property ('https://www.example.com/'). A newly added property is UNVERIFIED: Google only shows its data after ownership is proven, and the verification code is shown only to the signed-in owner in Search Console. Next step after adding: the user opens the property in Search Console (link in the result), picks the DNS TXT method, and gives you the 'google-site-verification=...' value; if the domain's DNS is hosted on Opus Growth, write it with site_dns_records (mode='add', type='TXT') and the user presses Verify. confirm=false previews, confirm=true adds.

gsc_delete_sitemap

ChatGPT
Removes a submitted sitemap from Search Console. Submitting was possible, removing was not, so a stale or wrongly submitted entry stayed in the property forever and kept reporting errors — a page accidentally submitted as a sitemap, or an old WordPress sitemap_index.xml left behind after a rebuild. Deleting the entry only tells Google to stop reading that URL as a sitemap; it does not touch the file or deindex anything. sitemap_url must be the complete URL exactly as gsc_sitemaps lists it. confirm=false previews.

gsc_inspect_urls

ChatGPT
Inspects the Google index status of MANY URLs in one call — a batch wrapper over gsc_url_inspect for site-wide crawl-freshness audits (which URLs Google recrawled since a content change, which are indexed, which were never crawled). Returns a compact row per URL — page_url, verdict, coverage (coverageState), last_crawl, indexing, google_canonical — deliberately WITHOUT the rich-results / mobile detail so the whole table stays within the result-size limit. page_urls is a list of full URLs (a comma/newline-separated string is also accepted). At most limit URLs per call, hard-capped at 20, because Google's URL Inspection API is quota-limited (~2000/day and 600/min per property); if a quota or rate error is hit mid-batch the tool STOPS and reports how many rows it managed. site_url must be a verified property. Same limit as gsc_url_inspect: requesting (re)indexing is not possible through any API.

gsc_list_sites

ChatGPT
Lists the verified properties (site / sc-domain) of the connected Search Console account; properties that were added but not yet ownership-verified are listed separately under unverified.

gsc_performance

ChatGPT
Search Console ORGANIC search performance: queries, pages, countries or devices with clicks, impressions, CTR (%) and average position. Organic data, independent of ads. dimension takes ONE dimension or a COMBINATION separated by commas: query, page, country, device, date (daily series, returned in date order), or page,query. The combination is what answers "which page ranks for which term", so it is the one to use for cannibalisation work — two pages competing on the same query show up as two rows with the same query (issue #295). sort = clicks | impressions | ctr | position. This matters more than it looks: Search Console ALWAYS returns rows by clicks, and its own orderBy parameter is accepted and then ignored (measured). So a query with 520 impressions and 2 clicks sits far below the cut and CTR work — which is by definition about high-impression, low-click terms — was impossible. The sort is therefore done here, over every row fetched, and rows_scanned tells you how wide that base was (issue #296). page / query filter to one page or one query (substring match), so "which queries does THIS page get" is one call. days: lookback days (Search Console data lags 2-3 days, so the newest days may be empty). start_date / end_date (YYYY-MM-DD) report an exact window instead of days; the reply's period is the range actually used.

gsc_sitemaps

ChatGPT
Lists the sitemaps submitted to a property + their status: last submitted, last downloaded by Google (last_downloaded — whether Google actually fetched it after submission), type, whether it is a sitemap index, errors/warnings, whether pending, and contents (URL count per content type as submitted). Google no longer reports an indexed count per sitemap; check indexing of specific URLs with gsc_inspect_urls.

gsc_submit_sitemap

ChatGPT
Submits/resubmits a sitemap to Google. sitemap_url must be complete (e.g. https://site.com/sitemap.xml). Enables faster crawling of new/updated content.

gsc_url_inspect

ChatGPT
Inspects a URL's Google index status: whether it's indexed, last crawl time, robots/indexing state, chosen canonical, mobile usability, and rich results. site_url must be a verified property. REQUESTING (RE)INDEXING IS NOT POSSIBLE: Google has no API behind the "Request Indexing" button, and its Indexing API only covers JobPosting and BroadcastEvent, so no tool here can do it. To get a stale address recrawled: resubmit a sitemap whose lastmod is the real edit date, link to it from often-crawled addresses, or press "Request Indexing" by hand (about 10 a day).

gtm_workspace_status

ChatGPT
Shows what is WAITING TO BE PUBLISHED in a GTM workspace — every pending tag/trigger/variable change (added, updated, deleted) plus any merge conflicts. Use this before publish_gtm_container to see exactly what would go live, or to answer "why isn't my tag firing yet?" (unpublished changes never run on the site). workspace = a list_gtm_workspaces path (empty → Default Workspace); container = 'GTM-XXXX'/name when workspace is empty.

impression_share_analysis

ChatGPT
Impression share analysis for Google Ads Search campaigns: impression share, top of page and absolute top share, exact match share, and how much share was lost to budget or to rank (bid and quality), plus disapproved ads. level = campaign (default) | ad_group | keyword; budget loss is reported only at campaign level. Competitor auction insights (overlap rate, outranking share per competitor) are not available through the Google Ads API and are not returned. Read-only.

instagram_follower_growth

ChatGPT
Reports how many Instagram FOLLOWERS an account gained per day, so follower/profile-visit campaigns can finally be measured. WHY THIS EXISTS: Meta's insights actions breakdown has NO follower row for OUTCOME_TRAFFIC/PROFILE_VISIT campaigns (only link_click, video_view, post_engagement), and the true follower_count metric lives behind Instagram Insights permissions we do not hold. So we snapshot followers_count daily (it IS available with ads_management) and diff it. Returns per Instagram account: series [{day, followers, delta}], first, last, gained, days_tracked — plus today's live count. IMPORTANT: history starts the first day tracking ran; Meta exposes no historical follower numbers, so ask the user to wait for at least two days of data before judging a campaign. Combine gained with the campaign's spend from meta_campaign_insights for a cost-per-follower estimate; state clearly that this is account-level growth, not click-attributed.

instant_audit

ChatGPT
INSTANT AUDIT (activation) — immediately surfaces the most critical findings in a connected Google Ads account: WASTED SPEND (₺) going to search terms with no conversions + impressions lost due to rank + conversion-tracking issues. Auto-selects the active account. Read-only.

issue_status

ChatGPT
Checks what happened to a problem the user reported with report_issue: whether it is still open or resolved, when it was reported, and — once it is closed — what was actually done about it. Without this the only signal was the resolution e-mail, so the honest answer to "is that fixed yet?" was a guess. Pass issue_id for one report, or leave it out to list the user's own recent reports newest first. Only the user's own reports are visible. Read-only and free — checking never costs a credit.

keyword_historical_metrics

ChatGPT
Google Ads Keyword Planner HISTORICAL METRICS for keywords you already have (not new ideas — use keyword_planner for ideas). For each keyword returns the month-by-month search volume (monthly_searches: [{month: 'YYYY-MM', searches}]), average monthly searches, competition (LOW/MEDIUM/HIGH + 0-100 index), the top-of-page bid range (low/high, account currency) and average CPC, plus the close variants Google grouped under it. Use it to see seasonality and trend before judging a keyword ("searches fell" vs "it is seasonal"). keywords: up to 10,000 per call. language_id: Google language constant id (e.g. 1000 English, 1037 Turkish, 1001 German); empty = all languages. geo_ids: Google geo target constant ids (find them with geo_target_search); empty = all locations. start_month / end_month: 'YYYY-MM'; Google keeps roughly the last 4 years, default is the last 12 months. Read-only.

keyword_opportunities

ChatGPT
Keyword OPPORTUNITIES — from the customer's OWN search-term + keyword data (does NOT require Keyword Planner approval, works with existing access): (1) search terms that converted but aren't added as keywords → ADD (expansion); (2) keywords spending money without conversions → pause/negative; (3) low quality-score keywords. days: number of days to look back.

keyword_planner

ChatGPT
Generates keyword ideas from seed keywords with average monthly searches and competition (Google Ads Keyword Planner). Set the market explicitly: language_id = Google language constant id (e.g. 1000 English, 1001 German, 1002 French, 1003 Spanish, 1037 Turkish); geo_id = Google geo target constant id, several allowed comma-separated (find ids with geo_target_search). Empty language_id = all languages, empty geo_id = all locations worldwide — volumes are then global, so always pass the advertiser's market when you know it. The reply echoes the targeting that was used. For month-by-month history of keywords you already have, use keyword_historical_metrics.

keyword_quality_report

ChatGPT
Google Ads keyword QUALITY SCORE OVER TIME — for diagnosing WHEN a Quality Score dropped and WHICH component caused it. Returns, per keyword and per day, the historical Quality Score (1-10) plus its three components: expected CTR, ad relevance and landing page experience (each BELOW_AVERAGE / AVERAGE / ABOVE_AVERAGE). Pass ad_group_id to scope to one ad group (recommended on large accounts). days = lookback window; rows are segmented by date so the trend is visible. This is the HISTORICAL view; list_entities(entity='keywords') gives the CURRENT snapshot (current quality_score / ad_relevance / landing_page_experience / expected_ctr). Read-only.

keyword_report

ChatGPT
Keyword performance + Quality Score; flags low QS and zero-conversion spend. DATE: period ('today','last_7_days'… or 'YYYY-MM-DD[:...]') overrides days; days=0 = today. NOTE: each row carries an active flag for whether the keyword is serving right now; the report also includes REMOVED/PAUSED keywords that had impressions during the period, as historical data. PAGING: keywords is ONE PAGE, sorted by cost DESC — limit rows starting at offset (limit max 1000). The summary fields (total_rows, active_count, qs_hist, active_match_types, low_quality_score, zero_conversion_spenders) are always computed over the WHOLE account, not just the page. When truncated is true there are more rows: call again with offset=next_offset and merge. NEVER report the page as the full keyword list — quote total_rows. Asking for a huge limit in one call can overflow the client's tool-result budget and the body will not reach you at all. include_zero_impressions=true also returns keywords with NO impressions in the period (they are excluded by default) — needed when the user wants the complete keyword inventory.

linkedin_ad_copy

ChatGPT
Reads the actual AD COPY of specific LinkedIn ads — built for "which messaging performs best" analysis. Give it a shortlist (comma-separated, max 60): creative_ids (urn:li:sponsoredCreative:N or bare N — e.g. the top pivotValues from linkedin_campaign_analytics pivot=CREATIVE) and/or post_urns (urn:li:share:N / urn:li:ugcPost:N). Per creative you get: ad_copy (the ACTUAL ad text of post-based ads), author_type / is_thought_leader_ad, format, name, headline, description, landing_page, post_urn, post_permalink, ad_library_url and — where LinkedIn supports it — official preview_url / preview_url_mobile. HOW THE COPY IS OBTAINED (so you can be precise with the user): LinkedIn's Posts API is NOT open to this connector (needs r_organization_social / Community Management API), so the copy is read from two PUBLIC sources, in order — LinkedIn's Ad Library (ad_copy_source: "linkedin_public_ad_library") and, when that is rate-limited or has no text, the post's own public page (ad_copy_source: "linkedin_public_post_page"). Both are verified live for Document Ads and Sponsored Content. RATE LIMIT — READ THIS BEFORE BATCHING: the Ad Library throttles hard. Measured live: 10 lookups fine, 60 in one go → most return HTTP 429 and the penalty window lasts minutes. So ask for the TOP rows (10–20 creative_ids), not the whole account. A throttled row comes back with ad_copy_status: "ad_library_rate_limited" — that means TEMPORARY, retry smaller in a few minutes; it does NOT mean the ad has no copy. is_thought_leader_ad / author_name describe the POST'S AUTHOR (a person = Thought Leader Ad, vs the company page); advertiser_name is the paying advertiser and is almost always the company. When the author cannot be read the field is null (never false) and thought_leader_ad_status explains why — do not report "not a Thought Leader Ad" on a null. When a row has no ad_copy, say so plainly and give post_permalink / ad_library_url. NEVER fabricate ad copy and never pass the internal creative name off as the ad text. Text-based formats (textAd, spotlight, follow/jobs ads) carry their copy inline, so for those the headline/description here IS the real ad copy.

linkedin_audience_forecast

ChatGPT
Asks LinkedIn HOW MANY MEMBERS a targeting setup reaches — the pre-flight check to run BEFORE create_linkedin_campaign / update_linkedin_campaign_targeting. LinkedIn will not deliver to an audience under ~300 members, so without this a campaign can be built successfully and then silently fail to serve. Two modes: pass campaign_id to forecast an EXISTING campaign's current targeting exactly as it is (including facets this connector cannot author), or pass the same URN arguments you would give update_linkedin_campaign_targeting to forecast a targeting you are about to apply. Returns audience_size plus below_delivery_minimum. A result of 0 almost always means an ARCHIVED matched audience (check list_linkedin_audiences) or mutually exclusive facets. Read-only.

linkedin_bid_recommendations

ChatGPT
Asks LinkedIn what it actually costs to advertise to a given audience — the SUGGESTED BID and the allowed bid floor/ceiling in the account's own currency, plus the minimum daily budget. Run this BEFORE create_linkedin_campaign and use the returned number as unit_cost. Why it matters: LinkedIn's minimum bid varies by country, currency and targeting. A bid below the floor makes the campaign fail to deliver (or get rejected outright) — which is exactly what happens on TRY accounts when a generic default is used. campaign_type: TEXT_AD | SPONSORED_UPDATES | SPONSORED_INMAILS | DYNAMIC. bid_type: CPC | CPM | CPV. geo_urns = comma-separated urn:li:geo:… (from linkedin_targeting_search; empty = country default). Read-only.

linkedin_campaign_analytics

ChatGPT
LinkedIn performance report. pivot: CAMPAIGN | CREATIVE | CAMPAIGN_GROUP | ACCOUNT | MEMBER_JOB_TITLE | MEMBER_JOB_FUNCTION | MEMBER_INDUSTRY | MEMBER_SENIORITY | MEMBER_COMPANY_SIZE ... Up to THREE pivots at once, comma-separated (e.g. pivot='CAMPAIGN_GROUP,CAMPAIGN,CREATIVE') via LinkedIn's Statistics finder; combinable pivots are ACCOUNT, CAMPAIGN_GROUP, CAMPAIGN, CREATIVE, CONVERSION. Professional demographic pivots (MEMBER_*) work one at a time. campaign_ids = comma-separated campaign ids to narrow the report. start_date/end_date (YYYY-MM-DD) override days. Performance data is kept 10 years, professional demographic data 2 years (https://learn.microsoft.com/en-us/linkedin/marketing/integrations/ads-reporting/ads-reporting). time_granularity: ALL | DAILY | MONTHLY. fields: comma-separated metric list (empty = default set; up to 20). Demographic pivots have a 12-24h delay and privacy thresholds. Each row carries pivot_names — the HUMAN-READABLE labels for pivotValues, resolved for you at EVERY level: campaign / ad set / campaign group / creative names as well as demographic labels. Report from pivot_names; never show raw URNs like urn:li:title:95 to the user and do NOT try to reverse them with typeahead search (that only goes name→ID). Anything that genuinely could not be resolved is listed in unresolved_pivot_urns. With pivot=CREATIVE each row also carries creative_content (headline, description, landing_page, format), so for TEXT-BASED formats (textAd, spotlight, follow/jobs ads) you can correlate MESSAGING with performance directly, without a second call. POST-BASED formats are the exception and in many accounts they are the MAJORITY of spend (Sponsored Content, Document Ads, Thought Leader Ads). There the copy lives in the referenced post, so by default creative_content carries post_urn + post_permalink and ad_copy_status: "not_fetched" instead of the text — the report stays fast. The copy IS retrievable: rank the rows first, then call linkedin_ad_copy with the top creative ids (or re-run with include_ad_previews=true) — it reads the real ad text from LinkedIn's PUBLIC Ad Library, plus author_type / is_thought_leader_ad (Thought Leader Ads) and preview_url. Both paths are capped at 60 creatives, so narrow down first. Never invent copy and never present an internal creative name as the ad text. Rows also carry computed ctr, engagement_rate, cpc, cpm (ratios, not %) — LinkedIn returns only raw counts; use these instead of recomputing. pivotValues is always requested even if you pass a custom fields list (without it rows have no identity).

list_accounts

ChatGPT
Lists the user's connected Google Ads account(s). Returns only the account(s) the user selected at connect time; the response carries default_account_id. all_accessible=true returns every account the credential can reach, which also expands client accounts under MCC (manager) accounts. platform is optional and defaults to Google Ads; naming another connected platform (meta | tiktok | linkedin | microsoft | chatgpt_ads | ga4 | search_console | youtube | business_profile) returns the name of that platform's own account-listing tool instead.

list_ad_extensions

ChatGPT
Reads the extension assets LINKED to a campaign (sitelink / callout / structured snippet / call / promotion / price). If campaign_id is empty, returns extensions for all campaigns in the account. by_type: how many of each type are attached. READ-ONLY.

list_ad_policy_issues

ChatGPT
Google Ads ad POLICY / DISAPPROVAL report — which ads were DISAPPROVED or limited, and WHY. Returns per ad: campaign + ad group, ad id and type, approval_status (APPROVED | APPROVED_LIMITED | DISAPPROVED | AREA_OF_INTEREST_ONLY), review_status (REVIEWED | UNDER_REVIEW | REVIEW_IN_PROGRESS), and the policy topics that triggered it (topic name + type, e.g. PROHIBITED / RESTRICTED / LIMITED) WITH Google's evidence for each topic: the exact triggering ad text (evidence.triggering_texts), the flagged websites / non-working destination URL, and the constraints (which countries the ad is limited in, missing certification). When Google sends no detail for a topic the entry carries detail_available=false. Use this whenever a campaign or ad group reports HAS_ADS_DISAPPROVED in its status_reasons: that flag says something is disapproved but not WHICH ad or WHY, and this tool answers both without opening the Google Ads UI. only_problems=true (default) returns just the ads that are not fully approved; false returns every ad with its approval state. Read-only.

list_assets

ChatGPT
Lists the account's ASSET LIBRARY ("Öğeler" / Assets) — ALL asset types across the whole account (not just one campaign): sitelinks, callouts, structured snippets, calls, IMAGES, promotions, prices, lead forms, and text assets. Shows each asset's type, a readable label, id, and source (ADVERTISER = you created it, AUTOMATICALLY_CREATED = Google auto). Optionally filter with asset_type (e.g. IMAGE, SITELINK, CALLOUT, PROMOTION). The reply always reports the TRUE total in the account and says when the list was cut short by limit, so a capped page is never mistaken for the whole library. include_usage=true also says, per asset, whether it is used by a running ad, only by paused/removed ones, or by nothing at all — that is what to check before any clean-up. NOTE: Google Ads has NO API to delete library assets (the API supports create and update only), so unused assets cannot be removed from here or in bulk; they can only be unlinked from campaigns (remove_extension_assets) or hidden in the Google Ads interface. LOCATION is NOT an asset-library type — location assets come from the Google Business Profile link and are handled by add_location_assets. BUSINESS NAME / LOGO are NOT asset types in Google Ads — they are FIELD TYPES of a TEXT or IMAGE asset, so look for them with list_extension_assets (which reports campaign- and account-level BUSINESS_NAME / BUSINESS_LOGO / LANDSCAPE_LOGO links) and attach them with manage_extension_assets(link_asset_ids=..., link_field_type=...). include_approval=true adds, per asset, its LINK approval status across campaigns and the account — approval_status is ELIGIBLE / PENDING / LIMITED / NOT_ELIGIBLE, with reasons such as ASSET_DISAPPROVED — plus a top-level approval_summary of the disapproved/limited ones. That is how you answer "which sitelinks were not approved" (the ad-level policy report, list_ad_policy_issues, covers ads only and never shows extension/sitelink assets). Read-only.

list_audiences

ChatGPT
Lists the account's audiences (remarketing / rule-based / customer-match / lookalike): id, name, type, membership status, membership duration (days), Display/Search network size. definition=true also returns WHAT DEFINES each list, which the name alone does not tell you and which you need before attaching or — far more dangerous — EXCLUDING one: for a RULE_BASED list the URL/event rule items with their operators and lookback window; for a LOGICAL/COMBINED list the constituent lists resolved to NAMES with their ANY/ALL/NONE operator; for a SIMILAR list the seed list; for CRM_BASED the upload key and data source. Excluding a list that turns out to be "anyone who reached any thank-you page" silently suppresses real prospects, so check the rule rather than trusting the name. Google does NOT store a readable rule for every list — older ones come back with definition_available=false, and then the Google Ads interface (Tools, Audience manager) is the only place to read it. Read-only.

list_business_profile_photos

ChatGPT
Lists the photos on a Google Business Profile location: each photo's category, Google URL, thumbnail and id, counts per category, and which key categories (such as COVER or LOGO) have no photo yet. category filters to one kind; limit shortens the photo list but the counts always cover the whole gallery. location = a gbp_list_locations value or the business name/domain. Read-only.

list_conversion_actions

ChatGPT
Lists Google Ads CONVERSION ACTIONS with the columns of the Goals > Conversions > Summary table: name, id, category (Request quotes / Submit lead forms / Sign-ups / Contacts / Page views…), status, type, whether it is INCLUDED in the Conversions metric (included_in_conversions — the REAL Primary/Secondary flag in goal-based accounts, NOT the legacy primary_for_goal), counting (ONE_PER_CLICK / MANY_PER_CLICK), click-through and view-through lookback windows in days, default value, and recorded conversions + value over the last days. Use this to see which action sits in which category and which one is biddable, then join with list_conversion_goals (which says which CATEGORIES are biddable). NOTE: the UI 'Tracking status' diagnostic (Active / Needs attention / No recent conversions) is computed by Google and is not exposed as a single Google Ads API field, so it is not returned here. Read-only.

list_conversion_goals

ChatGPT
Lists Google Ads CONVERSION GOALS — the modern lever that decides which conversion CATEGORY is PRIMARY (biddable: counted in the Conversions column + feeds Smart Bidding) vs SECONDARY. In goal-based accounts THIS governs bidding, not a single action's primary_for_goal flag — so it is the correct way to demote/promote 'local actions' too. Each goal is identified by category + origin (e.g. ENGAGEMENT/GOOGLE_HOSTED = Google Business Profile 'other engagements' / map-profile signals; GET_DIRECTIONS/GOOGLE_HOSTED = directions; DEFAULT/WEBSITE = website leads; PHONE_CALL_LEAD/CALL_FROM_ADS = calls from ads). Pass campaign_id to also see that campaign's overrides. Read-only.

list_custom_conversion_goals

ChatGPT
Lists the CUSTOM conversion goals of a Google Ads account — the named bundles of conversion actions that campaigns can optimise for, with the actions inside each one and the campaigns using it. This is what to check when Google refuses to delete a conversion action with "cannot be removed because it is used by one or more of your custom goals": the blocking goal is in this list, and the action has to leave the goal (update_custom_conversion_goal) before the action can be deleted. Read-only.

list_entities

ChatGPT
Lists the entities (campaigns, ad groups/ad sets, ads, keywords) of ANY connected ad platform with their IDs — use this instead of looking for a platform-specific list tool. platform: google_ads (default) | meta (facebook/instagram) | tiktok | linkedin | microsoft. entity: campaigns | ad_groups (adsets on Meta, adgroups on TikTok) | ads | keywords (Google Ads) | campaign_groups & creatives (LinkedIn). account = that platform's account id (for Google Ads you may also use customer_id); leave empty to use the connected/default account. parent_id narrows children to one parent (e.g. the campaign id when listing ad groups/ad sets, the ad set id when listing ads). Call describe(platform) to see which entity names a platform accepts. DATES — read them before you judge anything: rows carry when each item was created, last changed and is scheduled to run, so "new / still learning / untouched for months / changed yesterday" is measured, not guessed. Google Ads: created_time (exact, Google keeps it 30 days), created_within_90d, updated_time (last 90 days; empty = unchanged for 90+ days), campaign start_time/end_time. Meta: created_time, updated_time, start_time/stop_time (campaign), start_time/end_time (ad set). TikTok: created_time, updated_time, ad group start_time/end_time. LinkedIn: created_time, updated_time, start_time/end_time (ISO, UTC). Microsoft: ad group start_date/end_date only (the API exposes no creation date). Before attributing a result to a change, compare these dates with the report period (get_report with start_date/end_date or a day breakdown). Google Ads specifics (platform='google_ads'): PAGING: rows is ONE PAGE — limit rows from offset (limit max 2000). total_rows is the real count and serving_count / serving_summary are always computed over ALL rows, not the page. truncated=true → call again with offset=next_offset and merge; never present the page as the complete list. Keyword and ad pages are returned as FLAT rows (a keyword inventory can run to thousands of rows — the raw nested GAQL form overflows the client's tool-result budget and then nothing reaches you at all). CURRENT BIDS: ad group rows carry max_cpc (and target_cpa when set), keyword rows carry max_cpc plus effective_max_cpc and bid_source. Read the bid before changing it — a percentage change ("raise this by 30%") is otherwise sent as an absolute number and can LOWER the bid instead. bid_source: AD_GROUP_CRITERION means the keyword carries its own bid and OVERRIDES the ad group: raising the ad group bid then changes nothing for that keyword. Average CPC from a report is not a substitute — it is what was paid, not the ceiling that was set, and it is usually lower. ACTIVE / RUNNING / LIVE = is_serving: true (derived from serving_status), NOT status: "ENABLED". They are different things: a campaign whose end date has passed still reads status=ENABLED while the Google Ads UI shows it as "Ended". When the user asks which campaigns are active, answer from is_serving and account for the rest using serving_status + status_reasons (ENDED, PAUSED, NOT_ELIGIBLE, MISCONFIGURED, PENDING...). Never present a status=ENABLED row as active on its own — the user is comparing your answer to their Google Ads screen. serving_summary gives the counts. For campaigns it ALSO returns the bidding strategy (bidding_strategy), daily budget (daily_budget), targets (target_cpa / target_roas) and the max CPC ceiling (max_cpc, in account currency; null means no ceiling is set AT CAMPAIGN LEVEL). When max_cpc_note is present the campaign uses a portfolio strategy and max_cpc was not read.

list_extension_assets

ChatGPT
Lists a campaign's CURRENT extension assets (sitelinks, callouts, call, structured snippets, promotions, prices, WhatsApp message extensions, app, hotel callout, lead form) with the asset id and the resource_name needed to remove each one. If campaign_id is empty, covers all campaigns. Read-only.

list_google_ads_experiments

ChatGPT
Lists Google Ads campaign experiments for one connected customer account, including status, dates and the campaigns each experiment currently locks. This public OpenAI operation is read-only and cannot end, promote or remove an experiment.

list_gtm_accounts

ChatGPT
Lists the Google Tag Manager accounts accessible by the connected user (path, accountId, name).

list_gtm_containers

ChatGPT
Lists the containers of a GTM account (name, public id 'GTM-XXXX', containerId, usageContext=web/ios/android, path). account = a list_gtm_accounts value or name/id (leave empty if there is only one account).

list_gtm_permissions

ChatGPT
Lists the USER PERMISSIONS of a GTM account — who can access it and at what level (account: noAccess/user/admin; per-container: read/edit/approve/publish). account = a list_gtm_accounts value or name/id (empty if only one).

list_gtm_tags

ChatGPT
Lists the TAGS in a workspace (name, tagId, type e.g. 'gaawe'=GA4 event / 'html'=Custom HTML / 'awct'/'sp'=Google Ads conversion, firingTriggerId). Also returns each tag's PARAMETERS flattened as params (e.g. conversionId + conversionLabel for a Google Ads conversion tag — use this to verify which conversion a tag fires and catch double-counting). workspace = a list_gtm_workspaces path (leave empty → Default Workspace of the resolved container). container = 'GTM-XXXX'/name if workspace is empty.

list_gtm_triggers

ChatGPT
Lists the TRIGGERS in a workspace (name, triggerId, type e.g. 'pageview'/'click'/'customEvent'). Also returns each trigger's conditions — the firing filter flattened as {filter, op, arg0, arg1} (e.g. op='CONTAINS', arg0='{{Page URL}}', arg1='/tesekkur') so you can READ BACK and verify a URL/click filter after creating it, without a live Preview. A tag fires on its trigger(s). workspace/container: see list_gtm_tags.

list_gtm_variables

ChatGPT
Lists the user-defined VARIABLES in a workspace (name, variableId, type e.g. 'c'=constant / 'v'=Data Layer / 'jsm'=Custom JS). Also returns each variable's params flattened (e.g. a constant's value, or a Data Layer variable's name) so you can read back its config. workspace/container: see list_gtm_tags.

list_gtm_versions

ChatGPT
Lists a GTM container's VERSION HISTORY (newest first) and marks which one is currently LIVE. Each version is a published snapshot — use this to see what changed and when, or to pick a version to roll back to with rollback_gtm_version. container = 'GTM-XXXX'/name (empty if only one). DATES: the newest details versions (default 10, max 25, 0 = off) also carry saved_at (when the version was created/saved, UTC) and description (the change notes typed at publish time). Use them to answer 'what changed between date X and Y'. Google's API does not expose who published a version — that is only visible in the GTM web UI.

list_gtm_workspaces

ChatGPT
Lists the workspaces of a container (name, workspaceId, path). Tags/triggers/variables live inside a workspace; changes are staged there until published. container = 'GTM-XXXX' / container name / full path (leave empty if there is only one).

list_ip_exclusions

ChatGPT
Lists the campaign-level IP exclusions (click fraud / fake clicks / invalid clicks / bot IP blocking; TR: sahte tıklama, geçersiz tıklama, IP engelleme listesi) on the account. Give campaign_id to scope to one campaign, or leave empty for the whole account. Returns each excluded IP with its campaign and criterion_id (needed to remove it via unblock_ip_addresses; to ADD/block new IPs use block_ip_addresses). Google Ads allows up to 500 IP exclusions per campaign. Read-only.

list_lead_form_submissions

ChatGPT
Reads the LEADS people submitted through a Google Ads lead form — name, phone, email and the answers to your qualifying questions — so they can be worked without opening the Google Ads interface. asset_id narrows it to one form. Google keeps this data for 30 DAYS and then deletes it, so anything older is gone for good; for a permanent copy set a webhook on the form (create_lead_form webhook_url) and store the leads yourself. Read-only.

list_linkedin_ad_accounts

ChatGPT
Lists the user's connected LinkedIn ad account(s). Returns only the account(s) the user selected at connect time; the response carries default_account_id. all_accessible=true returns every account the credential can reach.

list_linkedin_audiences

ChatGPT
Lists a LinkedIn account's MATCHED AUDIENCES (Dreamdata/CRM company lists, website retargeting, video/document/lead-form engagement, contact lists, predictive) — fully paginated. Per audience: name, urn (urn:li:adSegment:… — the value you pass to update_linkedin_campaign_targeting), status, source, member_count, created/last_modified and usable_for_targeting. CRITICAL: an ARCHIVED or EXPIRED audience CANNOT be used — applied to an ad set it resolves to 0 members and delivery stops. That state is only visible here, so check usable_for_targeting BEFORE building a retargeting/ABM campaign. below_delivery_minimum flags audiences under LinkedIn's ~300-member delivery floor. Read-only.

list_linkedin_campaign_groups

ChatGPT
Lists ALL campaign groups in a LinkedIn ad account (fully paginated). ⚠️ These rows are what the user's Campaign Manager screen calls "Campaigns" — LinkedIn renamed the UI levels in October 2025 without renaming the API. This is the RIGHT tool when someone asks to list their LinkedIn campaigns. A paused group stops every ad set inside it even if those ad sets read ACTIVE. ACTIVE = status ACTIVE and serving_statuses contains RUNNABLE; serving_count is the number actually running. If a row has _truncated, the list is INCOMPLETE — say so.

list_linkedin_campaigns

ChatGPT
Lists ALL API-level campaigns in a LinkedIn ad account (fully paginated). ⚠️ TERMINOLOGY — read before answering. LinkedIn renamed its Campaign Manager levels in October 2025 but did NOT rename the API. The rows this tool returns are what the user's screen calls "Ad sets". What the UI calls a "Campaign" is an API campaign group (list_linkedin_campaign_groups). API campaign group → UI "Campaign" API campaign → UI "Ad set" ← this tool So when the user says "my campaigns" / "aktif kampanyalarım", they almost always mean CAMPAIGN GROUPS. This tool therefore also returns ui_campaigns (the group-level answer) so you can answer at the level the user actually sees. Lead with that; mention ad sets only as the level below. Do not claim the UI is wrong or that the user is confused. Each row also carries DELIVERY + TARGETING settings read-only: audience_expansion, offsite_delivery (LinkedIn Audience Network), story_delivery, connected_tv_only, optimization_target, format, creative_selection and targeting (full targetingCriteria). Use these to answer "is audience expansion on?" or "what is this targeting?" — do NOT call update_linkedin_campaign_targeting to find out: that tool REPLACES targeting and its preview shows what you WOULD set, not what is set. ACTIVE = status is ACTIVE and serving_statuses contains RUNNABLE. Something can be ACTIVE yet not serve (group paused → CAMPAIGN_GROUP_STATUS_HOLD, billing/budget hold). Never answer "none are active" from status alone. If a row has _truncated, the list is INCOMPLETE — say so.

list_linkedin_conversions

ChatGPT
Lists ALL conversion tracking rules on a LinkedIn ad account — fully paginated. Per rule: id, name, type, enabled, attribution type, post-click/view-through attribution windows, conversion value, URL rules, conversion method, created/last modified, and the campaigns (ad sets) it is associated with. _available_fields shows which keys LinkedIn actually returned for that rule. If an entry with _truncated: true appears, the list is INCOMPLETE — never say 'these are all' or 'there are none'. Use an id with attach_linkedin_conversion to tie a conversion to a campaign, and detach_linkedin_conversion to remove one from a campaign (e.g. a site-wide 'All Pages' rule that fires on every page). Read-only.

list_linkedin_creatives

ChatGPT
Lists ALL creatives (ads) in a LinkedIn account (fully paginated); if campaign_id is given, limits to that campaign. Also returns review status. If any row has _truncated, the list is INCOMPLETE — say so. Every row carries a normalized text block next to the raw content: headline, description, landing_page, format — and for POST-BASED ads (Sponsored Content, Document Ads, Thought Leader Ads) post_urn + post_permalink. IMPORTANT — post-based ads: the ad copy lives in the referenced POST, not in the creative, so it is NOT in this list by default (text.ad_copy_status: "not_fetched"). To get the REAL copy call linkedin_ad_copy with a shortlist of creative ids, or set include_previews=true here. NEVER invent copy and never present an internal creative name as the messaging. include_previews=true additionally resolves, per post-based creative: the real ad_copy (read from LinkedIn's PUBLIC Ad Library — no extra permission needed), author_type / is_thought_leader_ad, ad_library_url and official preview_url / preview_url_mobile. Costs ~2 extra requests per creative and is capped at 60, so use it on a shortlist (e.g. the top CTR rows), not on a 1.000-ad account.

list_linkedin_lead_forms

ChatGPT
Lists the LinkedIn Lead Gen Forms of an ad account — id, name, status and question count. Use it to find which form a lead-gen campaign collects into before pulling the leads with download_linkedin_leads. Reading forms works with the normal ads permissions; downloading the leads themselves needs LinkedIn's separate Lead Sync API access.

list_meta_ad_accounts

ChatGPT
Lists the user's connected Meta ad account(s). Returns only the account(s) the user selected at connect time; the response carries default_account_id. all_accessible=true returns every account the credential can reach.

list_meta_ads

ChatGPT
Lists Meta ads in an ad account, optionally limited to one ad set (adset_id). Each ad includes its status, effective delivery status, ad set and campaign ids, creative id, creation and update times, and, when Meta limits or blocks delivery, an issues_info list with Meta's error code and message explaining why. Read-only.

list_meta_adsets

ChatGPT
Lists Meta ad sets; if campaign_id is provided, limits to that campaign. Budgets are in cents.

list_meta_audiences

ChatGPT
Lists the custom audiences (custom/lookalike/website retargeting) in the Meta ad account. The returned audience_id values are used in the custom_audience_ids parameter of create_meta_adset.

list_meta_campaigns

ChatGPT
Lists the campaigns in a Meta ad account. account_id = numeric account ID (with or without the leading act_ prefix). Budgets are in minor units (cents).

list_meta_catalogs

ChatGPT
Lists the product catalogs in the Business Manager that owns a Meta ad account: each catalog's id, name, product count and vertical. Use the catalog id with list_meta_product_sets and as product_catalog_id in create_meta_adset for catalog (dynamic product) ads. Read-only.

list_meta_custom_conversions

ChatGPT
Lists the account's custom conversions (URL- or event-based conversion definitions built on a pixel, e.g. 'visited /thank-you'). Use one for reporting or as an ad set's optimization event. ARCHIVED = DELETED: deleting a custom conversion in Meta Events Manager only archives it, and Meta keeps returning it. Archived ones are left OUT by default (archived_count tells you how many); pass include_archived=true only when the user asks about deleted ones. never_fired lists live conversions whose last_fired_time is empty — they have never recorded a single conversion, which usually means the rule does not match what the pixel actually sends. Say this plainly instead of presenting them as working.

list_meta_instagram_accounts

ChatGPT
Lists the Instagram accounts usable from a Meta ad account. Use this when list_meta_pages shows no Page, or the Instagram account is not linked to a Page. FOUR sources are merged, so an account the Ads Manager 'connected Instagram accounts' list omits is still found: (1) act_{id}/connected_instagram_accounts, (2) the legacy instagram_accounts edge, (3) the ad account's own AD HISTORY (adcreatives → instagram_user_id) — an Instagram account this ad account has already advertised from is usable even when both link edges are empty, (4) the FACEBOOK PAGE BRIDGE — in practice an Instagram business account is linked to a Page, not to the ad account, so the Pages assigned to this ad account (and, when nothing else was found, the user's other Pages) are read for instagram_business_account. Each entry: ig_user_id (pass it to list_meta_instagram_posts or create_meta_instagram_post_ad), username, and source (ad_account_link | previously_advertised | page_linked). For page_linked entries check usable_from_this_ad_account: false means the Instagram account exists and is linked to a Page, but that Page is not assigned to this ad account — the blocker is the Page assignment, not Instagram. If the list is EMPTY, no Instagram account is reachable from this ad account: the user must add it in Meta Business Suite → Settings → Accounts → Instagram accounts, then 'Assign to people and assets' → this ad account. A missing account here usually indicates a sharing/permission gap rather than a non-existent account; check every returned source first, and check list_meta_pages(account_id=...) for the Page-assignment cause. account_id comes from list_meta_ad_accounts.

list_meta_instagram_posts

ChatGPT
Lists the posts and Reels published on an Instagram Business account — Instagram-native content (including Reels not shared to Facebook) that list_meta_page_posts CANNOT see. Each item returns: media_id (feed it to create_meta_instagram_post_ad to run it as an ad), media_product_type (FEED/REELS), is_reel, caption, permalink, thumbnail_url, media_url, like_count, comments_count and — for Reels/videos — views, so you can pick the best-performing content. Identify the account with ANY of: page_id (from list_meta_pages, Page-linked Instagram), ig_user_id (direct, from list_meta_instagram_accounts) or account_id (resolves the ad account's connected Instagram — no Page needed). NOTE: the full ORGANIC listing needs Instagram content permission (instagram_basic), which is pending Meta App Review; when it is missing this tool does NOT fail — it returns organic_listing_available=false plus advertised_media (real, usable media_id values of Instagram posts already advertised in this ad account — pass account_id to get them). Turning an Instagram post into an ad works today either way.

list_meta_lead_forms

ChatGPT
Lists the INSTANT FORMS (lead forms) that belong to a Facebook Page — id, name, status and the questions each one asks. Use it to pick the lead_gen_form_id you pass to create_meta_link_ad / create_meta_video_ad when building a lead ad, or to check what an existing form collects before reusing it. page_id comes from list_meta_pages. Read-only.

list_meta_page_posts

ChatGPT
Lists a Facebook Page's recent posts and THEIR MEDIA, so content already published on the Page can be reused as ad creative. Each post returns: object_story_id (for create_meta_post_ad), media_type, permalink_url, image_url (the photo, or the cover frame for a video) and — for videos and Reels — video_url (a downloadable mp4) plus page_video_id. Feed video_url straight into create_meta_video_ad, and image_url into create_meta_link_ad. page_id comes from list_meta_pages.

list_meta_pages

ChatGPT
Lists the Facebook Pages managed by the connected Meta user (id, name, category). The page_id required when creating ads is selected from here. ALWAYS pass account_id when you are building ads for a specific ad account: only then can this tool tell you which Pages that ad account is actually allowed to advertise for (Meta's own promote_pages), returned as ad_account_pages with assigned_to_ad_account on every entry. A Page appearing in the flat list does NOT mean the ad account can use it — the user's personal Page role and the ad account's asset assignment are two different things, and Ads Manager shows the former. An empty ad_account_pages means Meta could not DISCOVER an assigned Page — it does NOT mean ads cannot be built, and it is NOT a connector fault. Verified live on 2026-09-22: with promote_pages empty, passing page_id explicitly created a WhatsApp messaging ad set without any error. So when ad_account_pages is empty, pick the Page from the pages list and pass page_id directly to create_meta_adset / create_meta_link_ad FIRST. Do not send the user to Meta Business Settings on the strength of this empty list; only treat it as a real asset-assignment problem if Meta itself rejects the explicit page_id. A Page missing here is not an access loss on our side. If the list comes back empty this tool also reports WHY: a missing pages_show_list permission looks identical to "no pages" otherwise.

list_meta_pixels

ChatGPT
Lists the conversion pixels in a Meta ad account (id, name, last fired). The pixel_id for ad sets optimizing OFFSITE_CONVERSIONS is obtained here.

list_meta_product_sets

ChatGPT
Lists the product sets inside a Meta product catalog. A product set is a filtered subset of the catalog (e.g. 'Underwear', 'On sale') that a dynamic product ad targets. catalog_id from list_meta_catalogs. Use the returned product_set_id in create_meta_adset (product_set_id) and create_meta_dynamic_ad.

list_meta_saved_audiences

ChatGPT
Lists the account's SAVED audiences (reusable targeting presets — interests + geo + demographics saved in Ads Manager) with approximate size. Different from list_meta_audiences, which returns CUSTOM/retargeting audiences. Reuse their targeting when building ad sets.

list_microsoft_accounts

ChatGPT
Lists the user's connected Microsoft Advertising ad account(s). Returns only the account(s) the user selected at connect time; the response carries default_account_id. all_accessible=true returns every account the credential can reach.

list_microsoft_ad_extensions

ChatGPT
Lists the SITELINK and CALLOUT extensions attached to campaigns in a Microsoft (Bing) account, with their text, URLs and status — use it before adding more (duplicates are a common cause of disapprovals) or to find the id to remove. READ-ONLY.

list_microsoft_ad_groups

ChatGPT
Lists the ad groups in a Microsoft Advertising campaign.

list_microsoft_ads

ChatGPT
Lists the ads in a Microsoft ad group.

list_microsoft_audiences

ChatGPT
Lists the account's audiences (remarketing / similar / combined) — for audience targeting.

list_microsoft_campaigns

ChatGPT
Lists the campaigns in a Microsoft Advertising ad account (account_id). If customer_id is empty, it is resolved automatically from the connection record.

list_microsoft_conversion_goals

ChatGPT
Lists the conversion goals in the account — required for conversion-based bidding strategies (MaxConversions/TargetCpa).

list_microsoft_keywords

ChatGPT
Lists the keywords in a Microsoft ad group.

list_microsoft_negative_keywords

ChatGPT
Lists the NEGATIVE keywords currently blocking traffic on a Microsoft (Bing) campaign or ad group — run this when impressions drop unexpectedly, or before removing one. entity_type: Campaign | AdGroup. Read-only.

list_microsoft_uet_tags

ChatGPT
Lists the account's UET (conversion tracking) tags — required for conversion goals + remarketing.

list_negative_keywords

ChatGPT
Lists the negative keywords that apply to a Google Ads account: ad group level, campaign level and keywords from shared negative keyword lists attached to campaigns. Leave campaign_id empty for the whole account, or give one campaign to see the negatives that apply to it. Each row has its text, match type and level (ad_group, campaign or shared_list) so you know where it is defined. Read-only.

list_recommendations

ChatGPT
Lists Google's campaign recommendations + optimization score, WITH PROJECTED IMPACT (estimated +conversions / +clicks / +cost if the recommendation is applied). Filter by rec_type (e.g. 'KEYWORD', 'CAMPAIGN_BUDGET','TARGET_CPA_OPT_IN'); include_dismissed=true also returns dismissed ones. The returned resource_names are passed to apply_recommendation / dismiss_recommendations.

list_servers

ChatGPT
Lists the websites you can edit: Opus Growth hosting servers ('Sunucum') AND any WordPress sites connected with the Opus Growth Connector plugin. A hosting server is an isolated container we run for the user (edit with site_write_file / site_run_command). A connected WordPress site is the user's OWN WordPress (edit with the wordpress_* tools). Both are editable — they just use different tool families.

list_shared_negative_lists

ChatGPT
Lists the shared negative keyword lists in a Google Ads account (Shared library > Negative keyword lists): each list's name, id, number of keywords, status and the campaigns it is attached to. Use get_shared_negative_list to read the keywords of one list. Read-only.

list_sites

ChatGPT
Lists the websites hosted on the user's server. One server holds UP TO 15 WEBSITES (the returned limit): there is no per site fee and no need to buy another server for a second site. Returns each site's domain, its short key (used as the site argument on the other website tools) and which one is the primary site, plus how much of the server's disk is in use.

list_tiktok_ad_accounts

ChatGPT
Lists the user's connected TikTok ad account(s). Returns only the account(s) the user selected at connect time; the response carries default_account_id. all_accessible=true returns every account the credential can reach.

list_tiktok_adgroups

ChatGPT
Lists TikTok ad groups; if campaign_id is provided, limits to that campaign.

list_tiktok_ads

ChatGPT
Lists TikTok ads; if adgroup_id is given, limits to that ad group. Each ad also returns THE CREATIVE IT RUNS ON, so a winning creative can be reused in another ad group without re-uploading anything: tiktok_item_id (an organic TikTok post = Spark ad), video_id (an uploaded video) or image_ids, plus identity_id/identity_type. Pass those straight into create_tiktok_ad. Note: the numeric prefix in an ad NAME is not a video_id — use the fields returned here.

list_tiktok_asset_groups

ChatGPT
Lists the asset groups in a TikTok Business Center (groupings of advertisers / pixels / identities used for bulk permissioning). bc_id from list_tiktok_business_centers.

list_tiktok_audiences

ChatGPT
Lists TikTok custom + lookalike audiences (for ad group targeting).

list_tiktok_auto_messages

ChatGPT
Lists the TikTok WELCOME-CHAT (automated message) templates on the account — the auto_message_id that TikTok REQUIRES on every Direct Message / messaging ad. Each template holds the greeting text plus optional tappable 'suggested question → automatic answer' pairs. Only templates with audit_status=PASS can be used in an ad; create new ones with create_tiktok_auto_message.

list_tiktok_bc_advertisers

ChatGPT
Lists the ad accounts (advertisers) under a Business Center.

list_tiktok_bc_pixels

ChatGPT
Lists every pixel across a TikTok Business Center (cross-account measurement view). bc_id comes from list_tiktok_business_centers.

list_tiktok_business_centers

ChatGPT
Lists the accessible TikTok Business Centers (agency multi-account management).

list_tiktok_campaigns

ChatGPT
Lists the campaigns in a TikTok ad account (advertiser_id).

list_tiktok_creative_portfolios

ChatGPT
Lists the account's creative portfolios (named collections of creative assets).

list_tiktok_gmv_max_stores

ChatGPT
Lists the TikTok Shop stores linked to the ad account — these are what GMV Max (TikTok Shop e-commerce) campaigns run against. An empty list means no shop is connected, so GMV Max is not available for this account.

list_tiktok_identities

ChatGPT
Lists TikTok ad identities — the identity_id needed when creating ads is obtained here (the equivalent of Meta's page_id). If the account's identity directory is not readable, the identities already used by existing ads are returned instead (source="existing_ads"): those identity_id values work normally in create_tiktok_ad, only the display name is unavailable.

list_tiktok_lead_forms

ChatGPT
Lists the account's TikTok LEAD-GEN instant forms — each with the page_id that download_tiktok_leads needs, plus the form title, status and creation time. This is how you find which forms a lead-gen campaign is collecting into before pulling the leads.

list_tiktok_pixels

ChatGPT
Lists TikTok conversion pixels (for measurement + WEB_CONVERSIONS/CONVERT optimization). Each pixel includes its configured events (e.g. ON_WEB_REGISTER, SHOPPING, FORM) with optimization_event, plus activity_status (ACTIVE = receiving fires) and setup_mode — so you can verify a conversion (e.g. signup) is set up before optimizing an ad group for it. TikTok's Marketing API does NOT expose raw 7/28-day event fire counts; those live in Events Manager.

list_tiktok_posts

ChatGPT
Lists the ORGANIC TIKTOK POSTS of a connected TikTok account (identity) that can be turned into ads — Spark Ads. Returns item_id (pass it to create_tiktok_ad as tiktok_item_id), caption, duration and cover image, newest first. This is how you promote an existing post — the best-performing organic video — without re-uploading it. identity_id comes from list_tiktok_identities. Works for both kinds of real TikTok account: TT_USER (the account itself) and BC_AUTH_TT (a TikTok account authorised to the Business Center — the usual case for a business's own account). Leave identity_type empty and the identity's own type is read from the account; for BC_AUTH_TT the Business Center id TikTok demands is resolved automatically (pass identity_bc_id only when the user has several Business Centers). CUSTOMIZED_USER identities have no organic posts.

list_tiktok_smart_plus

ChatGPT
Lists the account's Smart+ (TikTok's automated 'smart performance') campaigns, ad groups or ads — these do NOT appear in list_tiktok_campaigns/adgroups/ads, which only cover standard (manual) objects. level: CAMPAIGN | ADGROUP | AD. Use this to see and report on Smart+ delivery.

list_tiktok_videos

ChatGPT
Lists the ad account's VIDEO LIBRARY — every video material uploaded to TikTok Ads, newest first, with the video_id that create_tiktok_ad requires (plus file name, duration, size and a preview link). Use this to pick "the video I uploaded last" instead of guessing an id.

list_voices

ChatGPT
LIST THE NARRATOR VOICES for video ads (create_video_ad voice=...), each with a short listening sample per language (TR, EN, DE, ES, FR, PT, AR). Show the user the voices with their gender, age and tone and the sample link in their language so they can listen and pick one; recommended: false means that voice is less clear in that language. language: tr | en | de | es | fr | pt | ar (empty = all). Free of credits.

list_wordpress_sites

ChatGPT
List the WordPress sites connected to your Opus Growth account, plus your WordPress plan tier, limits, and every WordPress tool you can use on them.

looker_studio_report

ChatGPT
DATA STUDIO (LOOKER STUDIO) REPORT — for when the user asks for Data Studio, Looker Studio or a live, editable dashboard in their own Google account (Data Studio raporu, Looker Studio raporu). Other report requests are answered with marketing_report_pdf, which returns the finished report as a direct download link. This tool creates a professional, chart-based Data Studio report on the user's OWN Google account, fed live by Opus Growth: Google Ads, Meta Ads, TikTok Ads, LinkedIn Ads, Microsoft Advertising and ChatGPT Ads in one data source (daily campaign rows; spend, impressions, clicks, conversions, conversion value, CTR, CPC, CPM, CPA, ROAS). Returns a one-click Data Studio link; opening it copies Opus Growth's designed ten-page template and connects every page to the user's own data. page = the page it opens on: cover (default; title page with the client's name, the period, an executive summary in plain sentences and the report contents) | summary (KPI cards vs the previous period with sparklines, daily spend and conversions, channel mix, platform table, top campaigns) | website (GA4: sessions, users, key events by channel and day, top landing pages) | organic (Search Console: organic clicks, impressions, CTR, average position, top queries and pages) | business_profile (Business Profile: Search and Maps views, calls, direction requests, website clicks per day) | campaigns (budget, budget pacing, projected month spend, impression share and impression share lost to budget and rank, full campaign table, daily spend by platform, spend by campaign type) | audience (device, gender, age, country map, audience table) | ads (ad-level table, spend vs conversions, CTR and cost per conversion over time) | search (top search terms, Quality Score with expected CTR, ad relevance and landing page experience, search terms that spent without converting = negative keyword candidates) | findings (prioritised findings and recommended next steps from Opus Growth's own analysis: wasted search term spend, conversion tracking health, impression share loss, budget pacing, low Quality Score, Google's own recommendations with projected impact, campaigns without conversions or with outlier cost per conversion). The report stays live and editable, can be shared, scheduled by email and downloaded as PDF in Data Studio. platforms = comma-separated subset (google_ads, meta, tiktok, linkedin, microsoft, chatgpt_ads); empty = every connected platform. accounts = comma-separated account ids (or platform:id); empty = every connected account. currency = report currency (ISO code, e.g. TRY, USD, EUR); other currencies are converted with the daily ECB rate; empty = currency with the highest spend. Agencies add their own name and logo with looker_studio_settings and they appear on every report. Beyond ads the report also covers the website and local presence: GA4 (sessions, users and key events by channel over time, landing pages), Search Console (organic clicks, impressions, CTR, average position over time, top queries and pages) and Business Profile (search and Maps views, calls, direction requests, website clicks over time). ga4_property / gsc_site / gbp_location pick which GA4 property, Search Console site and Business Profile location the report reads (values from ga4_list_properties, gsc_list_sites, gbp_list_locations; a name or domain also works). Leave them empty and the only connected one is used, or the one whose name matches the client's ad account; when an agency has several clients and none matches, pass them explicitly. The first call also returns the user's Data Studio key, which is pasted into Data Studio once.

manage_audience_targeting

ChatGPT
ATTACHES an audience to a campaign or ad group (or removes it) — the missing link that makes your audience lists actually do something. Creating a remarketing/customer-match/lookalike list does NOT affect delivery until it is attached to a campaign; until now they were built but never connected. audience = one of THREE different things, and the tool works out which: • YOUR OWN remarketing / customer-match / lookalike list — name or ID from list_audiences. Works at campaign AND ad-group level. • A GOOGLE-DEFINED segment — an in-market, affinity, life-event or detailed-demographic audience, given by its EXACT display name as it appears in reports and in the Google Ads interface (e.g. "Network & Enterprise Security", "Large Employer (250-10k Employees)"), or as customers/…/userInterests/92507. Campaign and ad-group level both work. Two limits come from Google: a detailed-demographic CATEGORY cannot be attached, only a leaf inside it ("Large Employer (250-10k Employees)" works, "Company Size" does not), and life-event segments are refused on Search campaigns — they exist for Display, Video and Demand Gen. NOTE: the criterion_id in an audience report is NOT the segment id — do not pass it here; pass the name. • An AUDIENCE from Audience Manager (the combined audiences Demand Gen uses) — its name, ID, or customers/…/audiences/123. Ad-group level only. DEMAND GEN ad groups accept only an Audience, never a bare list or segment. When you give a list or segment for such an ad group, the tool reuses the audience with the same name or creates one containing it, then attaches that audience. The audience always targets there, so mode does not apply, and exclusions on Demand Gen ad groups are refused by Google. Give campaign_id OR ad_group_id. action: • attach = add the audience as a POSITIVE criterion (observe/bid on it). • exclude = add it as a NEGATIVE criterion (negative=true) → do NOT show ads to this audience (e.g. exclude people who already converted / clicked call/WhatsApp). Works at campaign AND ad-group level. • remove = detach the positive audience criterion. • remove_exclusion = remove a previously excluded audience. (remove/remove_exclusion find the exact criterion by this audience + negative flag, so the right one is removed.) mode (positive attach only): OBSERVATION = don't narrow reach, just report/bid (safe default). TARGETING = restrict reach to only the attached audiences (e.g. a search remarketing / RLSA campaign shown only to site visitors). TARGETING is a setting of the whole campaign or ad group: it narrows delivery to ALL positive audiences attached at that level, and its other targeting restrictions are kept. Empty mode = OBSERVATION for a new audience and leaves the existing setting unchanged. If the audience is already attached, attach with mode=TARGETING just switches the setting, and attach with mode=OBSERVATION switches it back to observation. bid_modifier: optional bid multiplier for a POSITIVE audience, e.g. 1.2 = +20% (invalid for exclude; 0 = none). confirm=false previews.

manage_bidding_strategies

ChatGPT
Changes a campaign's bidding strategy. strategy: MAXIMIZE_CONVERSIONS (optional target_cpa) | TARGET_CPA (target_cpa required) | TARGET_ROAS (target_roas required, e.g. 3.5 = 350%) | MAXIMIZE_CONVERSION_VALUE (optional target_roas) | MAXIMIZE_CLICKS (optional max_cpc cap) | TARGET_IMPRESSION_SHARE (impression_share_percent + max_cpc required; location: ANYWHERE_ON_PAGE | TOP_OF_PAGE | ABSOLUTE_TOP_OF_PAGE) | MANUAL_CPC. max_cpc (CPC ceiling) is ONLY valid for MAXIMIZE_CLICKS and TARGET_IMPRESSION_SHARE — Google Ads does not accept a CPC ceiling on campaign-level MAXIMIZE_CONVERSIONS / TARGET_CPA / TARGET_ROAS / MAXIMIZE_CONVERSION_VALUE / MANUAL_CPC (there it only exists on a PORTFOLIO/shared strategy, which this tool cannot create or assign); passing it with those strategies returns an error instead of being silently ignored. confirm=false → dry-run.

manage_business_name

ChatGPT
Fixes the BUSINESS NAME shown in a campaign's ads — the one place a typo is visible to every person who sees the ad, and until now there was no way to correct it. Text assets in Google Ads are IMMUTABLE: asset.text_asset.text cannot be edited, which is why no update tool could exist. The correct operation is a RELINK, and this tool does it in one atomic request: it finds an asset in the account that already carries the exact text you want (so a name you already use elsewhere is REUSED instead of duplicated), creates one only if none exists, links it to the campaign as BUSINESS_NAME and unlinks the old one in the same mutate — a campaign accepts only one business name, so the swap has to happen together. Call it with text empty to READ what the campaign shows today without changing anything. confirm=false returns a dry run naming the old and the new text; confirm=true applies it and reads the value back from Google. Fixing a name used by several campaigns means calling this once per campaign, because the link is per campaign.

manage_campaign_proximity

ChatGPT
Manage a campaign's PROXIMITY (radius / lat-lng circle) targeting — the radius targets that get_campaign_targeting reads but set_campaign_targeting CANNOT change. Lets you focus a campaign on a specific corridor/area (e.g. a highway stretch) by lat/lng + radius. action: • 'add' → add one radius target (lat, lng, radius, units) — or many at once via points. • 'update' → replace ONE existing radius target (needs criterion_id) with new lat/lng/radius/units. (Proximity fields are immutable in Google Ads, so this removes the old + adds the new in a single atomic mutate.) • 'remove' → delete ONE radius target by criterion_id. • 'replace' → delete ALL existing radius targets on the campaign, then add the given one(s). (Give no point to simply CLEAR all radius targeting.) units: 'KILOMETERS' (default) or 'MILES'. radius e.g. 5. Get criterion_id from get_campaign_targeting. points: optional JSON list for batch add/replace, e.g. '[{"lat":38.92,"lng":27.84,"radius":3,"units":"KILOMETERS"},{"lat":38.95,"lng":27.9,"radius":2}]'. confirm=false → dry-run preview (nothing is changed).

manage_campaign_settings

ChatGPT
Campaign-level settings (networks, location type, tracking template, EU political ads declaration, AI Max for Search), plus reading them: • geo_target_type: 'PRESENCE' (those PRESENT in the target location) | 'PRESENCE_OR_INTEREST' (also those interested). • search_partners: 'on' | 'off' — Search Partners. • display_expansion: 'on' | 'off' — Display expansion on a Search campaign. • tracking_url_template: campaign tracking template (e.g. '{lpurl}?utm_source=google'); send '-' to DELETE. • eu_political_ads: 'no' | 'yes' — the advertiser's declaration whether this campaign has European Union political ads. Google refuses targeting and other edits on a campaign until it is declared (MISSING_EU_POLITICAL_ADVERTISING_SELF_DECLARATION). Ask the user; do not answer for them. AI MAX FOR SEARCH (Search campaigns only). AI Max is Google's switch that lets a Search campaign reach beyond its keywords. With it ON: – search term matching: keywords behave like broad match and Google also matches searches from the landing pages and ads, so the campaign shows for searches you never added (check the search terms report — rows marked "AI Max"); – text customization: Google writes extra headlines/descriptions from your site and ads; – final URL expansion: Google may send a click to a different page of your site than the ad's final URL. It can bring new traffic, but it can also spend on unrelated searches (rentals, parking, hotels, other businesses' names); compare the "AI Max" rows' cost and conversions with your own keywords before deciding. • ai_max: 'on' | 'off' — the main switch. 'off' also turns text customization and final URL expansion off in the same change (Google requires that); keywords then match by their own match type again. • ai_max_text_customization: 'on' | 'off' — needs ai_max on. • ai_max_final_url_expansion: 'on' | 'off' — needs ai_max on AND text customization on. • ai_max_search_term_matching: 'on' | 'off' — applied to EVERY ad group of the campaign: 'off' keeps AI Max on for text/URL features but makes keywords match only by their own match type. Brand lists, locations of interest, URL inclusions and text guidelines are not set here. Any field left empty is NOT changed. confirm=false → if called with no fields provided, returns the campaign's CURRENT values, including the AI Max state and each ad group's search term matching (call with no parameters to READ).

manage_chatgpt_ads_accounts

ChatGPT
Lists, re-checks, disconnects or re-defaults your connected ChatGPT Ads accounts. action: list | check | set_default | disconnect. list shows every connected ad account with its currency and review status. check calls the account endpoint with the stored key and reports whether it still works — use it when a tool starts returning an authorization error, because a revoked key looks exactly like a missing permission from the outside. set_default decides which account the other ChatGPT Ads tools use when you do not name one. disconnect deletes the stored key for that account. Keys are never shown back.

manage_chatgpt_ads_spend_limit

ChatGPT
Creates, changes or removes a ChatGPT Ads account spending limit (date-range window or recurring daily limit). action: create_window | update_window | delete_window | set_daily | remove_daily. Dates are YYYY-MM-DD in the account time zone; end_date is EXCLUSIVE (a window for October 1–7 ends on 2026-10-08). amount is in the account currency (a window's total, or the allowance per day for set_daily); OpenAI rejects lowering it below spend already recorded. Windows cannot overlap each other or the daily limit; an active window's start date cannot move; completed windows cannot change. set_daily creates or edits the daily limit — a new one starts tomorrow at the earliest (start_date defaults to the earliest allowed day), no_end_date=true makes it open-ended. The daily-limit revision OpenAI requires is read for you just before the change. Removing or raising an exhausted limit lets delivery resume, so the dry run states the account-wide effect. Needs an ad-account admin key with billing permission; otherwise the tool says so. confirm=false returns a dry run.

manage_conversion_actions

ChatGPT
Creates a new conversion action. type: WEBPAGE (default) | AD_CALL (phone calls from ad/location call extensions, i.e. Google 'Calls from ads') | WEBSITE_CALL (calls to a number on your site) | CLICK_TO_CALL | UPLOAD_CALLS | UPLOAD_CLICKS. category: LEAD, PURCHASE, SIGNUP, PHONE_CALL_LEAD… (for AD_CALL/WEBSITE_CALL it defaults to PHONE_CALL_LEAD). phone_call_duration = minimum seconds a call must last to count (call types only). PREREQUISITE for AD_CALL: run set_call_reporting(call_conversion='true') first; note Google forwarding numbers (needed for AD_CALL) exist only in supported countries. NOTE: this tool only CREATES; to read use list_conversion_goals, to edit use update_conversion_action / to make it primary/biddable use update_conversion_goal. confirm=false → dry-run.

manage_device_targeting

ChatGPT
Set a campaign's per-DEVICE bid adjustment — the WRITE side of the device criteria that get_campaign_targeting reads. Lets you run e.g. MOBILE-ONLY by excluding desktop + tablet. device: MOBILE | DESKTOP | TABLET | CONNECTED_TV (comma-separated, e.g. 'DESKTOP,TABLET'). bid_modifier_pct: percent adjustment for those device(s): • -100 = EXCLUDE the device (ads won't show there), • 0 = neutral / reset (no adjustment), • +20 = bid 20% more, -50 = 50% less. Range -100..+900. Mobile-only recipe: device='DESKTOP,TABLET', bid_modifier_pct=-100. CONNECTED_TV matters on Demand Gen and Video: that inventory is ON by default and a TV impression rarely turns into a click, so a traffic campaign usually wants it excluded (device='CONNECTED_TV', bid_modifier_pct=-100). On Demand Gen Google accepts ONLY full exclusion (-100) or neutral (0) for EVERY device — a partial adjustment like -50 is rejected by Google on that channel (measured), and the reply says so instead of passing the raw error through. Creates the device criterion if missing, else updates its adjustment. confirm=false previews.

manage_extension_assets

ChatGPT
Adds extension assets to a campaign — or to ONE AD GROUP when ad_group_id is given. AD GROUP LEVEL: pass ad_group_id (campaign_id may then be left empty; it is resolved from the ad group) and every asset created or linked here is attached as an AdGroupAsset to that ad group only. Google serves ad-group assets INSTEAD of the campaign's assets of the same type for that ad group's ads, so a branch ad group can show its own phone number, WhatsApp chat, sitelinks and callouts while the rest of the campaign keeps the campaign-level ones. To reuse assets already created for one ad group on a second ad group, pass link_asset_ids with the other ad_group_id (no duplicates are created). callouts: short highlights ['Free Shipping',...]. sitelinks: lines in 'text|url' or 'text|url|description1|description2' format. phone_number (+phone_country) adds a call extension. snippet_header + snippet_values add a structured snippet (header e.g. 'Services', 'Brands' — from Google's fixed list). whatsapp_number (+whatsapp_country, whatsapp_greeting, whatsapp_cta, whatsapp_cta_text) adds a MESSAGE extension (BUSINESS_MESSAGE) that opens a WhatsApp chat — all five are required by Google: greeting is the pre-filled first message, whatsapp_cta is one of APPLY_NOW, BOOK_NOW, CONTACT_US, GET_INFO, GET_OFFER, GET_QUOTE, GET_STARTED, LEARN_MORE, and whatsapp_cta_text is the button description (max 30 chars). Only WhatsApp is supported (Facebook Messenger and Zalo need provider data Google does not expose here). prices: PRICE extensions, one line per offering in 'Header|Description|1500 TRY|https://url' format (optional extra '|unit:PER_MONTH'); 3 to 8 offerings are required, headers must be unique and max 25 chars, descriptions max 25 chars. price_type groups them (SERVICES, BRANDS, PRODUCT_CATEGORIES, NEIGHBORHOODS, EVENTS, LOCATIONS, PRODUCT_TIERS, SERVICE_CATEGORIES, SERVICE_TIERS), price_qualifier is optional (FROM, UP_TO, AVERAGE) and price_language must be one of de, en, es, fr, it, ja, nl, pl, pt_BR, ru, sv. IMPORTANT, measured against the live API: Google does NOT support Turkish (tr) or the Turkish lira (TRY) for price extensions, so a TRY account cannot use them — the tool says so instead of failing cryptically. promotions: PROMOTION extensions in 'Target text|discount|https://url' format — discount is '%20' (percent) or '150 TRY' (amount); optional extra '|key:value' segments: code:SUMMER25, min:500 (minimum order amount, mutually exclusive with code), start:2026-08-01, end:2026-09-01, occasion:SUMMER_SALE, lang:tr, modifier:UP_TO. link_asset_ids: comma-separated asset IDs ALREADY in the account's asset library (from list_assets) — links them to the campaign INSTEAD of creating duplicates; the field type is derived from the asset's own type (PROMOTION, SITELINK, CALLOUT, CALL, STRUCTURED_SNIPPET, PRICE, MOBILE_APP, HOTEL_CALLOUT, LEAD_FORM, IMAGE). IMAGE assets (from upload_image_asset) are linked as the Search IMAGE EXTENSION (field type AD_IMAGE) — Google requires at least 300x300 px and accepts 1.91:1 and 1:1 crops. At least one extension type must be provided. confirm=false → dry-run. To VIEW or REMOVE existing extensions, use list_extension_assets and remove_extension_assets. LOCATION (business address, map pin, "Get directions") is NOT an extension type here: location assets are generated automatically from the Google Business Profile link and serve account-wide — inspect or pin them with add_location_assets.

manage_gtm_permissions

ChatGPT
GRANTS or REVOKES a user's access to a GTM account/containers. action='grant' → give email access (account_permission: user|admin|noAccess; optionally container_id + container_permission: read|edit|approve|publish). action='revoke' → remove access by permission_path (from list_gtm_permissions). SAFETY: confirm=false → preview; confirm=true → applies.

manage_keywords

ChatGPT
PAUSE / ENABLE / REMOVE an EXISTING Google Ads keyword, or change its max CPC bid. This is the keyword STATUS tool: use it to pause a low-Quality-Score or wasteful keyword without deleting it, to re-enable a paused one, or to move keywords out of an ad group (remove here after adding them to the target group with add_keywords). It works on POSITIVE keywords — negative keywords are handled by add_negative_keyword / remove_negative_keyword. criterion_ids: comma-separated criterion ids (the criterion id from keyword_report / list_entities(entity='keywords') output; that output also carries quality_score so you can decide what to pause). action: pause | enable | remove | set_bid (with cpc_bid, in the main currency) | set_url (keyword-level landing page that overrides the ad's final URL: final_url and/or final_mobile_url; pass an empty string to clear one and fall back to the ad's URL). confirm=false → dry-run.

manage_portfolio_bidding_strategy

ChatGPT
Creates, updates, lists and assigns PORTFOLIO (shared) bidding strategies — Google Ads' Shared library → Bid strategies. This is the ONLY way to put a max/min CPC limit on Maximize Conversions, Maximize Conversion Value, Target CPA or Target ROAS: those strategies carry cpc_bid_ceiling/floor ONLY as a portfolio strategy, never on the campaign itself (that is why manage_bidding_strategies rejects max_cpc for them). action: • list — every portfolio strategy on the account with its limits and how many campaigns use it (read-only, no confirm needed). • create — makes a new portfolio strategy. name + strategy required. max_cpc = CPC ceiling, min_cpc = optional CPC floor, target_cpa / target_roas optional targets. • create_and_assign — create, then attach it to campaign_id in one step (the usual request: "cap Maximize Conversions at 50 TL on this campaign"). • assign — attach an EXISTING strategy (bidding_strategy_id, from action=list) to campaign_id. This replaces the campaign's own bidding settings. • update — change the NAME and/or the limits/targets of an existing strategy (bidding_strategy_id): pass name to rename it, max_cpc / min_cpc / target_cpa / target_roas to change its limits. Affects EVERY campaign using it. The strategy TYPE cannot be changed — create a new strategy for that. • delete — removes a strategy from the Shared library (bidding_strategy_id). Refused while any active campaign still uses it, so it is safe: use it to clean up strategies that ended up attached to nothing. strategy: MAXIMIZE_CONVERSIONS | MAXIMIZE_CONVERSION_VALUE | TARGET_CPA | TARGET_ROAS | MAXIMIZE_CLICKS | TARGET_IMPRESSION_SHARE. Amounts are in the account currency (max_cpc=50 → 50 TL/click ceiling). To move a campaign back to its own (non-shared) bidding, call manage_bidding_strategies — it detaches the portfolio automatically. confirm=false → dry-run preview.

manage_recommendation_auto_apply

ChatGPT
Google Ads 'auto-apply recommendations' (recommendation subscriptions): Google applying recommendations by itself, e.g. removing negative keywords or changing ad rotation without the user. action=list shows every subscription with its type and status (ENABLED = Google applies it automatically). action=pause turns auto-apply OFF: types = comma-separated recommendation types from the list (e.g. 'OPTIMIZE_AD_ROTATION,KEYWORD') or 'all' for every enabled one. Some auto-apply types are not exposed by the Google Ads API and come back as type UNKNOWN; they cannot be changed through the API and must be turned off in the Google Ads UI (Recommendations > Auto-apply); the response says so. Turning auto-apply ON is not offered here. confirm=false → dry-run preview.

manage_shared_negative_list

ChatGPT
Manages SHARED negative keyword lists (Shared Library, account-level — the lists list_shared_negative_lists reads). action: • create (name required) → creates an empty shared negative list, returns its id • add (shared_set_id + keywords required) → adds negatives; each keyword is 'text' (defaults to PHRASE) or 'text|EXACT' / 'text|BROAD' / 'text|PHRASE' • remove (shared_set_id + keywords) → removes matching keywords from the list • attach (shared_set_id + campaign_id) → applies the list to a campaign • detach (shared_set_id + campaign_id) → removes the list from a campaign • rename (shared_set_id + name) → renames the list; keywords and campaign attachments stay as they are confirm=false → dry-run preview.

manage_tiktok_search_negative_keywords

ChatGPT
Manages negative keywords on TikTok SEARCH ads. object_type: CAMPAIGN | ADGROUP; object_id is that campaign/ad group. operation: GET (list current — no confirm needed) | ADD (keywords = comma-separated terms, match_type BROAD|PHRASE|EXACT) | DELETE (keyword_ids = comma-separated, from a GET first). ADD/DELETE preview with confirm=false and apply with confirm=true.

marketing_insights

ChatGPT
CROSS-CHANNEL OPPORTUNITIES — derives prioritized, ACTIONABLE recommendations from connected Google sources: Search Console page-2 quick wins (pos 8-20 with high impressions), Ads money-burning keywords + keywords to add, GA4 pages that get traffic but don't convert, Business Profile unanswered reviews / low ratings. Each recommendation includes the source + what to do. If customer_id is empty, the active Ads account is selected automatically.

marketing_overview

ChatGPT
UNIFIED MARKETING OVERVIEW — in a single call, aggregates ALL connected Google sources as a funnel: Paid (Google Ads: spend/clicks/conversions) + Organic (Search Console: clicks/impressions/position) + Site (GA4: sessions/users/conversions) + Video (YouTube: views/watch time/net subscribers) + Local (Business Profile: locations/rating). Returns only CONNECTED sources; if one errors, the others still come through. SCOPE: the response's scope block states exactly which Google Ads account, Search Console property and GA4 property the numbers cover. customer_id targets a specific Google Ads account (empty = the selected one). site_url targets a specific Search Console PROPERTY on a multi-site account (e.g. 'kenbiltercume.com.tr' or 'https://kenbiltercume.com.tr') — the organic figures are then locked to that property; if the site is not among the connected properties scope.site_matched is false and the available properties are listed rather than silently reporting another site.

meta_campaign_insights

ChatGPT
Meta performance report (impressions/clicks/spend/reach/conversion actions). date_preset: today | yesterday | last_7d | last_14d | last_30d | last_90d | this_month | last_month | maximum. level: account | campaign | adset | ad. breakdown (opt.): age | gender | country | region | publisher_platform | platform_position | device_platform | impression_device — splits rows by that dimension. If since/until (YYYY-MM-DD) are given, that date range is used instead of date_preset.

meta_change_history

ChatGPT
Meta ACTIVITY / CHANGE HISTORY (audit trail) — WHO changed WHAT and WHEN in a Meta ad account, the equivalent of Ads Manager's "Activity history" and of get_change_history on Google Ads. Returns, per event: time (converted to the ACCOUNT's own timezone — Meta serves this endpoint in UTC, and time_utc keeps the raw value), actor (the person's name), the event in plain language, level (campaign | adset | ad | account), the object id/name, and field-level old→new values where Meta provides them (status, budget, bid, targeting, schedule, creative updates). since/until are YYYY-MM-DD — pass the same day in both for an hour-by-hour report of that day. level filters to one entity type. NOTE: Meta keeps roughly 90 days of history and its events are coarser than Google's — a creative edit shows up as an event, but the before/after text of a headline or primary text is not always included.

meta_delivery_estimate

ChatGPT
Estimates an ad set's reachable audience size and expected daily results for its CURRENT targeting/optimization — WITHOUT spending. optimization_goal (optional) overrides the estimate goal (e.g. OFFSITE_CONVERSIONS, LINK_CLICKS). adset_id from list_meta_adsets. Use it to sanity-check targeting before launching.

meta_insights_query

ChatGPT
Free-form READ of the Meta Marketing API Insights edge — any metric, breakdown and filter Meta supports, for data the ready-made reports do not cover. fields: Insights field names (e.g. spend, impressions, reach, frequency, clicks, inline_link_clicks, actions, cost_per_action_type, action_values, purchase_roas, video_p75_watched_actions, campaign_id, campaign_name, adset_id, ad_id), as a list or a comma-separated string; breakdowns and action_breakdowns accept either form too. level: account | campaign | adset | ad. breakdowns: e.g. age, gender, country, region, publisher_platform, platform_position, device_platform, impression_device, hourly_stats_aggregated_by_advertiser_time_zone (Meta allows only certain combinations). action_breakdowns: e.g. action_type, action_destination. time_increment: 1 = one row per day, 'monthly', or a number of days. filtering: Meta filter objects, e.g. [{"field": "campaign.id", "operator": "IN", "value": ["123"]}] or [{"field": "spend", "operator": "GREATER_THAN", "value": 0}]. days = last N full days excluding today, or give start_date/end_date (YYYY-MM-DD); Meta keeps insights for 37 months. Reference: https://developers.facebook.com/docs/marketing-api/insights/parameters. Meta rejects invalid field/breakdown combinations with an explanatory error. Read-only; reply carries the exact period.

meta_reach_estimate

ChatGPT
Returns the estimated reach for a targeting setup (audience size before spending). countries: comma-separated ISO codes. interest_ids: comma-separated ids from search_meta_interests. genders: 1 (men) and/or 2 (women). Demographics & behaviours are estimated too, with exactly the same fields and codes as create_meta_adset (relationship_statuses — 3 = married, education_statuses, family_statuses, life_events, industries, behaviors) — size the audience here BEFORE building the ad set.

microsoft_entity_performance_report

ChatGPT
Microsoft (Bing) performance BROKEN DOWN below campaign level — which ad group, ad, keyword, location, device or age/gender actually produced the clicks and conversions. microsoft_performance_report only gives campaign totals; this is where you find what to fix. level: adgroup | ad | keyword | geo | device | age_gender. Dates 'YYYY-MM-DD'; empty = last 30 days. Read-only.

microsoft_performance_report

ChatGPT
Microsoft Advertising campaign performance report (spend/impressions/clicks/CTR/conversions). Dates are YYYY-MM-DD; if empty, the last 30 days. customer_id is auto-detected if empty.

microsoft_report_query

ChatGPT
Free-form READ of the Microsoft Advertising Reporting API (v13) — any report type and column set. report_type: e.g. CampaignPerformance, AdGroupPerformance, AdPerformance, KeywordPerformance, SearchQueryPerformance, GeographicPerformance, AgeGenderAudience, AudiencePerformance, AssetPerformance, ShareOfVoice, ProductPartitionPerformance (the 'ReportRequest' suffix is added). columns: that report's column names, e.g. ['TimePeriod','CampaignId','CampaignName','Impressions','Clicks','Spend','Conversions','Revenue','ImpressionSharePercent'] — the valid set differs per report type; Microsoft rejects invalid ones with an explanatory error. aggregation: Summary | Hourly | Daily | Weekly | Monthly | Yearly | DayOfWeek | HourOfDay (include TimePeriod for time rows). days = last N full days excluding today, or start_date/end_date (YYYY-MM-DD). Reference: https://learn.microsoft.com/en-us/advertising/reporting-service/reporting-service-reference. Read-only; reply carries the exact period.

microsoft_search_terms_report

ChatGPT
SEARCH TERMS report for Microsoft Advertising (Bing) — the actual queries people typed, with the keyword that matched and its match type. This is where wasted spend hides: terms that take clicks and return no conversions are your negative-keyword candidates (feed them to add_microsoft_negative_keywords). min_waste: only return terms that spent at least this much with ZERO conversions (0 = return everything). Sorted by spend, highest first. Dates YYYY-MM-DD; if empty, the last days days. READ-ONLY.

pause_campaign

ChatGPT
Pauses a campaign. If confirm=false, only a preview is returned (dry-run); to actually pause, call again with confirm=true.

performance_report

ChatGPT
Performance report (cost, clicks, impressions, conversions). level: campaign | ad_group | ad | keyword. Keyword level reports from keyword_view and is where pause / negative-keyword decisions are actually made, because one ad group usually mixes match types and intents; negative criteria are excluded (they have no performance and would read as "keywords with zero results"). Keyword rows come back flattened: campaign, ad_group, keyword, match_type, metrics. PAGING: keyword level can easily exceed ten thousand rows, which is more than a tool result can carry, so rows are capped (500 at keyword level, 2000 elsewhere) and the response carries total_rows / returned / offset / truncated / next_offset — call again with offset to walk the rest. limit/offset are only on this tool; get_report always returns the first page. segment: empty | device | date | day_of_week | hour | network | city | region | country | conversion_action | audience — adds a breakdown ("day" is accepted for date, "geo" for country). network splits Search / Search partners / Display / YouTube. Geo segments (city/region/country) report from geographic_view and always come back at campaign level with a readable location field. conversion_action answers "which conversion actions is each campaign actually producing" (the UI's Segment > Conversions > Conversion action): one row per campaign x conversion action, with primary conversions and all_conversions side by side, which is how you reconcile a campaign total against an account total when offline/CRM uploads are mixed with website tags. That segment returns NO cost, clicks or impressions — Google does not split spend by conversion action and would repeat the campaign total on every row, so anyone summing it would inflate the spend. AUDIENCE: segment="audience" answers "which audiences should I add, bid up or split out" — the UI's Audiences > Segments. One row per audience, at ad group level when level is ad_group/ad/keyword and at campaign level otherwise, with the audience NAME resolved: Google returns internal codes like uservertical::93017 for in-market, affinity and demographic segments, which is unreadable on its own. Each row carries taxonomy (IN_MARKET vs AFFINITY), audience_type, bid_modifier and the usual metrics. targeting_mode is the field the decision rests on: observation means measure only, delivery is NOT narrowed, while targeting means delivery is restricted to that audience. Pair it with manage_audience_targeting, which attaches or excludes an audience, so a change can be measured before and after instead of only made. IMPRESSION SHARE (no segment only): campaign returns all three — impression_share_pct, lost_is_rank_pct, lost_is_budget_pct; ad_group and keyword return impression share and rank-lost but NOT budget-lost, because Google only exposes budget-lost at campaign level (budget is a campaign constraint); the ad level has none at all. Values come as readable percentages next to Google's raw 0-1 fractions, and a missing field means the metric does not exist for that row (Display/PMax, or no impressions) rather than zero. This is what separates "efficient but saturated, no headroom" from "efficient and budget-starved, scale it". DATE: if you provide period it is used instead of days — 'today' (TODAY 00:00–now), 'yesterday', 'this_week', 'last_week', 'this_month', 'last_month', 'last_7_days'/'last_14_days'/'last_30_days', or an explicit 'YYYY-MM-DD' / 'YYYY-MM-DD:YYYY-MM-DD'. If period is empty, days applies (days=0 = today only).

portfolio_report

ChatGPT
PORTFOLIO REPORT (agency) — aggregates ALL connected/selected Google Ads accounts into one report: per-account cost/clicks/conversions/conversion value/ROAS/CPA + grand total + underperformers. Skips inaccessible accounts. days: lookback days. Read-only.

preview_meta_ad

ChatGPT
Renders a visual PREVIEW of an ad creative WITHOUT publishing it — returns a preview URL you can open in a browser to see exactly how the ad will look in a given placement (great for reviewing before launch). creative_id comes from create_meta_link_ad / create_meta_video_ad / create_meta_carousel_ad / get_ad_details. ad_format: MOBILE_FEED_STANDARD | DESKTOP_FEED_STANDARD | INSTAGRAM_STANDARD | INSTAGRAM_STORY | FACEBOOK_STORY_MOBILE | INSTAGRAM_REELS | FACEBOOK_REELS_MOBILE | RIGHT_COLUMN_STANDARD.

publish_gtm_container

ChatGPT
PUBLISHES the workspace's staged changes LIVE: creates a container version from the workspace, then publishes it. This is what makes new/edited tags actually run on the site. SAFETY: confirm=false → preview (nothing goes live); confirm=true → create version + publish. workspace = a list_gtm_workspaces path (empty → Default Workspace of the resolved container). container = 'GTM-XXXX'/name if workspace is empty.

remove_extension_assets

ChatGPT
Removes (unlinks) extension assets from a campaign — the 'remove' counterpart to manage_extension_assets (e.g. delete a sitelink, callout or promotion). Give EITHER resource_names (comma-separated resource names from list_extension_assets — campaign_asset, customer_asset (account level) and ad_group_asset are ALL accepted; asset SET links such as a Business Profile location sync are accepted too: customerAssetSets / campaignAssetSets / adGroupAssetSets resource names from google_ads_query on customer_asset_set / campaign_asset_set / ad_group_asset_set) OR field_type to remove ALL of one kind: 'SITELINK', 'CALLOUT', 'CALL', 'STRUCTURED_SNIPPET', 'PROMOTION', 'PRICE', 'MOBILE_APP', 'HOTEL_CALLOUT', 'LEAD_FORM', 'BUSINESS_MESSAGE' (WhatsApp message extension) or 'AD_IMAGE' (image extensions) — field_type sweeps all three levels. Unlinking leaves the asset itself in the account library, so it can be re-linked later. confirm=false → dry-run (preview what would be removed).

remove_microsoft_ad_extensions

ChatGPT
REMOVES sitelink/callout extensions: detaches them from the campaign and deletes them from the account library. Get the ids from list_microsoft_ad_extensions. Pass campaign_id so the association is cleared first. confirm=false = preview, confirm=true applies.

remove_microsoft_negative_keywords

ChatGPT
REMOVES negative keywords from a Microsoft (Bing) campaign or ad group — the undo for a negative that was added by mistake. A wrong negative silently blocks real searches and, until now, could not be taken back through this connector. keywords = comma-separated texts exactly as they appear (list_microsoft_negative_keywords). entity_type: Campaign | AdGroup. confirm=false previews.

remove_negative_keyword

ChatGPT
Removes negative keywords from one Google Ads campaign. Inputs: customer_id = the Google Ads account id; campaign_id = the campaign whose negatives are removed; then EITHER criterion_ids = the ids of the negative keywords to remove (from list_negative_keywords), OR keywords = their texts, each optionally followed by '|EXACT', '|PHRASE' or '|BROAD' (without a match type every match type of that text is removed). Several keywords can be removed in one call (up to 5000) and each is reported with its own result. Only campaign-level negatives are handled here; ad group negatives are removed with manage_keywords. confirm=false returns a preview of what would be removed; confirm=true removes them.

remove_site

ChatGPT
Removes an EXTRA website from the server (the primary site cannot be removed here). site is the key or the domain. By default the files are KEPT on the server and only the domain stops being served, so the change is reversible; delete_files=true also erases them. confirm=false previews.

report_issue

ChatGPT
Reports a bug or unexpected failure to Opus Growth support: pass a short summary, the failing tool_name, and any error details. The user is emailed when the issue is resolved. title, description and text are accepted as synonyms of summary/details, so a report is never lost to a parameter-name mismatch.

rollback_gtm_version

ChatGPT
ROLLS BACK the live site to an EARLIER GTM container version — the undo for a bad publish. Re-publishes the chosen version so the site immediately serves that older, known-good configuration. version_id from list_gtm_versions. SAFETY: confirm=false → preview (shows which version would go live and which is live now); confirm=true → publishes. Workspace changes are not lost; they stay staged.

rsa_asset_performance

ChatGPT
RESPONSIVE SEARCH AD (RSA) asset report — per HEADLINE and DESCRIPTION: impressions, clicks, cost, conversions, conversion value, cost per conversion, conversion rate and CTR, plus each ad's Ad Strength. This is the evidence for deciding which headlines to keep and which to replace: the one number that decides it is cost per conversion at ASSET level, and it is here. days = 0 (default) reports LIFETIME figures, which is what you want for a keep-or-drop decision — a headline's whole history, not a slice of it. days=7/14/30 narrows to that window; any other number becomes an explicit date range, and the range actually used comes back as period_from / period_to so two calls can be compared. BEWARE of a short window on a paused campaign: the rows still come back but every metric is zero, which looks like missing data and is really just no traffic. ALSO: Google reports asset IMPRESSIONS from an earlier date than asset clicks and cost, so a lifetime impression count can be much higher than a window that already covers all of the spend — both numbers are right, and CTR is only comparable within the same window. The reply flags this when it sees it. Rows are sorted by cost, capped by limit (the true total is always reported), and min_impressions drops assets with too little data to judge. If campaign_id is empty the whole account is covered. Google's own BEST / GOOD / LOW grades are included when they exist, but in most accounts they DON'T: Google only grades an asset when the ad group has enough competing assets and enough traffic, and otherwise reports NOT_APPLICABLE. The reply says how many fall in each grade, so an empty grade list is never mistaken for a broken report. Read-only.

scaffold_site

ChatGPT
Installs a site toolkit onto the hosting server. Sets up: Tailwind (responsive utilities), GSAP + ScrollTrigger + SplitText, Lenis (smooth scroll), Swiper, and Fontshare designer fonts — all loaded; a single-source nav/footer (edit assets/site.config.js once → every page updates, no multi-page nav duplication); and a neutral starter skeleton whose content is {{...}} placeholders. style is only an industry hint (universal, clinic, restaurant, corporate, service, shop); it does not change the files. site = the key or domain of an EXTRA website on that server — the toolkit is then installed into THAT website; leave it empty for the server's main website. confirm=false → preview (this OVERWRITES index.html + assets/*); confirm=true → writes them (live immediately).

search_meta_interests

ChatGPT
Searches Meta detailed-targeting entities by name → returns id + estimated audience size. kind: adinterest (interest) | adworkemployer (employer) | adeducationschool (school) | adgeolocation (location) | adTargetingCategory (DEMOGRAPHICS & BEHAVIOURS catalogue). For adTargetingCategory you MUST also pass category_class: "family_statuses" (parents, child age brackets), "life_events" (newly-wed, recently moved, birthday soon...), "industries" (job sector), "behaviors", "income" — leave query empty to list the whole class. The returned ids feed create_meta_adset / update_meta_adset (family_statuses, life_events, industries, behaviors). Relationship status and education level are NOT in this catalogue: they are fixed numeric codes passed directly as relationship_statuses / education_statuses. Location ids come back in key, not id.

search_terms_report

ChatGPT
Search terms report. Returns terms (the FULL list, one row per search term with impressions, clicks, cost, conversions and ctr) plus three curated buckets: converters, waste_candidates (negative-keyword candidates) and already_negative (terms that an EXISTING negative — ad group, campaign or shared list — already blocks, with the matching negative named; never propose these again). sort: cost | impressions | clicks | conversions. min_clicks: 0 (default) INCLUDES terms that got impressions but no clicks — raise it to 1 to see only clicked terms. limit: max rows (default 1000). Google withholds very low-volume terms behind its privacy threshold, so the list is never exhaustive. DATE: if period ('today','yesterday','this_month','last_7_days'… or 'YYYY-MM-DD[:...]') is provided, it overrides days; days=0 = today. start_date/end_date (YYYY-MM-DD) are accepted as an explicit range too. campaign_id: optional, one id or comma-separated ids, limits the report (and limit) to those campaigns. SMART (Smart/akıllı) campaigns are included in terms with campaign_type='SMART' (Google exposes only impressions, clicks and cost for them, conversions=null; they are left out of the buckets).

select_google_resource

ChatGPT
Switches WHICH single Google resource this account works on: a Business Profile location, a Search Console property, a GA4 property, a YouTube channel or a Tag Manager container. On Free/Pro plans one resource at a time is included, and until now the first one ever used stayed locked in with no way out; this tool moves the selection. Agency plans use several at once and do not need it. service: businessprofile | searchconsole | analytics | youtube | tagmanager. resource is the full id as listed by that service's list tool (for a Business Profile: accounts/…/locations/…). confirm=false previews the change.

send_meta_conversion_event

ChatGPT
SENDS A SERVER-SIDE CONVERSION EVENT to Meta (Conversions API) — the fix for conversions lost to iOS 14+ / ad-blockers / cookie limits, and the only way to report sales that happen off the website (CRM, phone, showroom, POS). pixel_id from list_meta_pixels. event_name: Purchase | Lead | CompleteRegistration | AddToCart | ViewContent | Contact | Schedule ... At least ONE identifier is required: email, phone, external_id, fbc or fbp. Email/phone are SHA-256 hashed locally — raw personal data never leaves the server. event_id: pass the SAME id as the browser pixel and Meta de-duplicates automatically (no double counting). action_source: website | phone_call | chat | email | physical_store | system_generated | other — use phone_call/physical_store for offline sales. test_event_code: routes the event to Meta's Test Events view without touching real reporting — verify first, then send for real. confirm=false previews; confirm=true sends.

send_tiktok_conversion_event

ChatGPT
SENDS A SERVER-SIDE CONVERSION EVENT to TikTok (Events API) — the fix for conversions lost to ad-blockers, iOS restrictions and cookie limits. Use it to report a purchase/lead that happened off-site (CRM, phone, POS) or to back up the browser pixel. pixel_code from list_tiktok_pixels / create_tiktok_pixel. event_name: CompletePayment | CompleteRegistration | SubmitForm | AddToCart | ViewContent | Contact ... At least ONE identifier is required: email, phone, external_id or ttclid. Email/phone are SHA-256 hashed locally — raw personal data never leaves the server. event_id: pass the SAME id you use in the browser pixel and TikTok automatically de-duplicates (no double counting). test_event_code: sends to TikTok's Test Events view without affecting real reporting — use it to verify the setup first. confirm=false previews; confirm=true sends.

seo_ai_overview

ChatGPT
Google AI Overview visibility / GEO (SEO Intelligence add-on). Returns the text of Google's AI answer for a search query plus the sources it cites; pass your domain as target to learn whether your site is cited in that AI answer. Goes beyond mention counts — shows what the AI actually says and who it credits. language takes either a two-letter code ('en', 'tr', 'de') or the language name ('English', 'Türkçe') — a name used to be passed straight to the data provider, which rejected it with a bare "Invalid Field: language_code" that named no cause. location takes a country name ('United States'). Costs SEO add-on credits.

seo_audit

ChatGPT
SEO + speed + measurement-tag AUDIT of a web page. Renders the page in a real Chrome via Google PageSpeed (Lighthouse): performance/SEO/accessibility scores (0-100), Core Web Vitals (LCP/CLS/INP), top-impact improvement opportunities, failed SEO / accessibility / best-practices audits with the elements that fail them (selector, HTML snippet, explanation), the LCP element with its timing breakdown, render-blocking and unused CSS files, and the measurement/ad tags loaded on the page (GA4/GTM/Google Ads conversion/Meta/TikTok/LinkedIn). strategy: mobile|desktop. runs=2 measures the page a second time about a minute later and reports both values, their median and their spread under stability. Lab measurements run on a shared virtual machine and move between runs: the same page has returned 12.5 s, 4.7 s and 12.7 s LCP minutes apart, so a single number can send you in the wrong direction. Real-user (field) data does not move, but pages with little traffic have none, and the reply tells you when that is the case. Use runs=2 before and after a speed change so you can tell a real result from noise. It takes about a minute and a half and the second measurement costs one more call; if PageSpeed returns its cached answer instead of a fresh run, that call is refunded and the reply says so.

seo_content_lint

ChatGPT
AI-footprint linter (FREE — no add-on credits). Scans a draft for the patterns that make content read as AI-generated (tell-words like delve/robust/seamless, filler clichés, the "it's not just X, it's Y" template, em-dash overuse, over-transitions, robotic uniform paragraph rhythm) and returns a human-likeness score (0-100) with specific fixes. Run it on any draft BEFORE publishing. It is a QUALITY signal, NOT a detector-evasion tool — Google rewards value, not 'undetectable' text, so fix by adding real data/experience and editing, not by gaming the score.

seo_history

ChatGPT
Lists the SEO/AEO add-on queries this account has ALREADY run, newest first: which tool, which query, when it ran, and whether the stored result can still be reused for free. Use this BEFORE running an expensive SEO tool so the user does not pay twice for the same data — if a run is still reusable, just call that tool again with the same arguments and it returns the stored result at ZERO credits. Read-only, costs nothing.

seo_keyword_research

ChatGPT
Keyword research for a seed keyword: related keyword ideas with monthly search volume, cost per click, competition and keyword difficulty. language and location select the market. Part of the SEO Intelligence add-on and uses add-on credits.

seo_paid_keywords

ChatGPT
Competitor paid search (SEO Intelligence add-on). For any domain: how many keywords it bids on, estimated monthly Google Ads spend, the keyword list with search volume and CPC, THE ACTUAL AD HEADLINES AND DESCRIPTIONS it is running, and the landing pages those ads point at (grouped, highest demand first). This is competitor ad intelligence for search — what they pay for, what they say, and where they send the click. Use it before writing your own RSAs, and to find keywords a competitor pays for that you could win organically. Empty result means the domain showed no ads in THIS scan of that location/language, not that it never advertises. Costs SEO add-on credits.

seo_rank_history

ChatGPT
Stored rank history — FREE, no credits. Reads back every position measurement this account has already paid for: per keyword the first and latest position, the best ever reached, the total change over the window and the full point series. Call this BEFORE seo_rank_tracker when the question is "are we moving" rather than "where are we right now" — the answer is already bought and stored. Leave target empty for every tracked domain. days is the look-back window. Read-only, costs nothing.

seo_rank_untrack

ChatGPT
Stops daily automatic re-measurement for tracked keywords. Leave keywords empty to stop the whole domain. The stored history is KEPT by default so past movement stays readable in seo_rank_history; pass delete_history: true only if the customer wants the records gone for good. Free, costs no credits.

seo_serp_analysis

ChatGPT
Live SERP analysis (SEO Intelligence add-on). For a keyword, returns the current Google top results — who ranks, their titles/domains/URLs, ratings, and which results are paid ads. Use it for organic competitor analysis AND ad-competition intel. Costs SEO add-on credits.

set_ad_group_targeting

ChatGPT
Sets geographic and language targeting on an AD GROUP — the only place Demand Gen keeps them. Demand Gen campaigns reject location and language at campaign level: Google keeps both on the ad group instead (measured on a live campaign). That left a Demand Gen campaign impossible to limit geographically through Opus, so it went live on Google's default of every country and spent the budget outside the target market. This tool closes that: pass the ad group and the countries, and it writes the criteria where Google actually reads them. locations: comma-separated names or geo IDs ('Germany' or '2276' or 'Almanya, Fransa'). negative_locations: places to exclude. languages: codes (tr, en, de) or numeric IDs. mode='add' keeps what is there and adds; mode='replace' removes the existing criteria of the dimensions you pass and writes yours in the SAME request, so the ad group is never left untargeted. confirm=false previews the resolved locations and what would be removed. Read the current state with get_campaign_targeting(ad_group_id=...).

set_ad_schedule

ChatGPT
Sets a campaign's ad schedule (day/hour) — 'run only during business hours', or 'bid 50% less at night'. schedule: comma-separated 'DAY HH:MM-HH:MM' blocks, e.g. 'MONDAY 09:00-18:00, TUESDAY 09:00-18:00'. Minutes must be 00/15/30/45. Days: MONDAY..SUNDAY. A range crossing midnight cannot be a single block — split it: 'SATURDAY 22:00-24:00, SUNDAY 00:00-02:00'. mode='add' (default) adds the blocks alongside the campaign's existing ones — it can only WIDEN a schedule, and Google rejects blocks that overlap existing ones. mode='replace' first DELETES every existing schedule block of the campaign and writes only the ones given here (remove+create in a single request, so the campaign never briefly falls back to 24/7) — this is the way to shrink/reshape an existing schedule. Read the current state with get_ad_schedule first. Bid modifier (percentage, like Microsoft's bid_adjustment): bid_modifier_pct applies to every block, or put it per block as a trailing percentage — 'SATURDAY 22:00-24:00 -50%'. Range -90%..+900%; -100% is not possible — to stop serving in an hour range, leave it OUT of a mode='replace' schedule. Fully automated bidding (tCPA/tROAS/Maximize conversions) ACCEPTS but IGNORES bid modifiers. Once a schedule exists, the campaign runs ONLY during those hours. confirm=false → dry-run.

set_call_reporting

ChatGPT
Turns account-level CALL REPORTING on/off — the prerequisite for Google to track phone calls from ads as conversions. call_reporting: 'true'/'false' (show a Google forwarding number + report calls); call_conversion: 'true'/'false' (count qualifying calls as conversions). Provide at least one. To switch calls off send call_reporting='false' alone; call_conversion is not sent alongside it because Google rejects that pair and it has no effect while reporting is off. call_conversion_action_id: WHICH conversion action the calls are attributed to (id from list_conversion_actions); send 'default' to clear it back to Google's built-in action. Leave empty to keep the current one. A fresh account points at an internal placeholder that cannot be configured or reported on by name, so binding your own AD_CALL action is what makes call conversions readable. Pair with set_conversion_settings(phone_call_duration=…) to set the minimum call length. NOTE: Google forwarding numbers are only available in supported countries; enabling has no effect where unavailable. Called with NEITHER value it READS the current state instead of failing — use that before you switch anything on, and before you undo a change, because there is otherwise no way to know what the setting was. The same fields also appear under get_conversion_settings > account > call_reporting. confirm=false → dry-run.

set_campaign_budget

ChatGPT
Sets a campaign's daily budget. Safety limits: a single increase above +50% is blocked, the TOTAL increase within 24h may not exceed +100% of the budget at the start of that window, and a single cut deeper than -80% is blocked — pause the campaign instead. There is also an absolute floor, about 5 USD per day converted into the account's currency, because a budget pulled down to near zero stops delivery while the campaign still reads as active in the interface — an invisible way to kill a campaign. A small local campaign can legitimately sit below that floor, so allow_low_budget=true clears it: the flag makes the decision explicit rather than accidental. Say plainly that delivery may be thin or stop at that level, and check the bid — a daily budget below one click's worth of max CPC buys nothing. Everything else (the increase caps, the -80% rule) still applies. If confirm=false, returns a dry-run preview.

set_campaign_targeting

ChatGPT
Sets geographic and language targeting on a campaign. locations: comma-separated location names or geo IDs (e.g. 'Türkiye' or 'İstanbul, Ankara' or '2792'). negative_locations: locations to exclude. languages: comma-separated language codes (tr, en, de...) or numeric IDs. mode='add' (default) KEEPS the campaign's existing targeting and only adds to it — targeting Ankara while Türkiye is still targeted does NOT narrow reach, Google takes the union. mode='replace' overwrites: every existing criterion of the dimensions you pass (positive locations if you pass locations, exclusions if you pass negative_locations, languages if you pass languages) is removed and replaced with your list, in a single request so the campaign is never left untargeted. Use replace to go from 'all of Türkiye' to 'only Ankara'. Other dimensions are untouched. A campaign without targeting targets the entire world. confirm=false → preview of the resolved locations + what would be removed.

set_conversion_settings

ChatGPT
Edits a conversion action's VALUE/COUNTING settings — only the fields you provide change: default_value (+ default_currency_code, e.g. 'TRY'), always_use_default_value ('true'/'false'), counting_type ('ONE_PER_CLICK' | 'MANY_PER_CLICK'), click_lookback_days, view_lookback_days, phone_call_duration (min. seconds a call must last to count — only for call conversion actions, e.g. 30). NOTE: pausing/removing a conversion is disabled in the API — do it in the panel. confirm=false → dry-run.

set_microsoft_bid_strategy

ChatGPT
Sets a campaign's bid strategy. strategy: ManualCpc | EnhancedCpc | MaxClicks (optional max_cpc ceiling) | MaxConversions | TargetCpa (target_cpa REQUIRED) | MaxConversionValue (optional target_roas) | TargetRoas (target_roas REQUIRED). target_roas is a PERCENTAGE: pass 350 for 350%, or 3.5 and it is converted. The four conversion-based strategies need a conversion goal in the account first (list_microsoft_uet_tags → create_microsoft_conversion_goal). confirm=false = preview, confirm=true applies.

share_tiktok_audience

ChatGPT
Shares (or un-shares) TikTok custom audiences with other ad accounts under the SAME Business Center — e.g. an agency reusing one seed list across managed advertisers. audience_ids and shared_advertiser_ids are comma-separated; action: SHARE | CANCEL. Targets must be in the same BC or TikTok rejects it. confirm=false previews, confirm=true applies.

site_command_status

ChatGPT
Reads a background command started by site_run_command(background=true): whether it is still running, what it has printed so far, and the exit code once it ends. job = the id that call returned. Call it again after a while for long installs; the output is the tail of the job's log, so a huge npm log does not flood the conversation. Read-only: it never starts or stops anything.

site_csp_reports

ChatGPT
Reads first-party Content-Security-Policy violation reports collected for one of the user's managed sites. Returns grouped directives, blocked origins, counts and example pages. This public OpenAI operation is read-only and cannot clear stored reports.

site_db_backup

ChatGPT
Takes a consistent backup of the website's database and stores it on the server under /data/backups. Uses a transaction-consistent dump, so the site keeps working while it runs and no table is locked. Leave database empty to back up everything. The reply lists the backups that exist afterwards, newest first — feed one of those file names to site_db_restore. A nightly automatic backup already runs; this tool is for taking one right before a risky change.

site_db_restore

ChatGPT
Restores the website's database from a backup file taken by site_db_backup (found under /data/backups). Requires confirm=true; without it you get a preview and nothing runs. THIS OVERWRITES CURRENT DATA — rows written after that backup are lost, so take a fresh site_db_backup first if the current state matters at all. Pass only the file name, not a path.

site_design_guide

ChatGPT
Returns the web-design playbook for building sites on the hosting server: the design-system component classes available, how to recolor a site per brand, content/writing rules, image handling, and the anti-'AI-generated-template' pitfalls to avoid.

site_dns_records

ChatGPT
Lists and edits DNS records of a domain whose DNS is hosted on Opus Growth (a domain of the user's own hosting server). Use it for verification and mail records: Google Search Console / Bing / Meta domain verification TXT, SPF/DKIM/DMARC TXT, or a CNAME for a third-party service. mode='list' shows the records (records that publish the site are shown as system records without their value); mode='add' creates a TXT or CNAME record (name is the host: '@' or empty for the domain itself, 'www', '_dmarc', 'selector._domainkey'...; value is the record content); mode='delete' removes a TXT or CNAME record by record_id from the list. MX/NS records and the records that publish the website are protected and cannot be changed here (the panel's DNS screen handles mail records with an extra confirmation). confirm=false previews, confirm=true applies; DNS changes are usually visible within a few minutes.

site_edit_file

ChatGPT
Makes a SMALL, surgical edit to an existing text file by replacing an exact string — WITHOUT resending the whole file. Use this for iterative tweaks (a heading, a Tailwind class, a price): it saves a large amount of tokens vs site_write_file, which must resend the entire file every time. old_string must match the file EXACTLY and be UNIQUE (include enough surrounding context), unless replace_all=true. For a brand-new file use site_write_file instead.

site_fetch

ChatGPT
Downloads a file from a public URL DIRECTLY onto the hosting site, and RESIZES or CONVERTS images on the way in (resize, webp, avif, jpg, quality) — this is the only image processing available for a hosting server, because the container itself has no cwebp, no ImageMagick and no sharp. The CORE fetches the bytes (full internet + memory) and writes them to path under /data/site. Use this for the user's OWN images/logos/PDFs (any domain) or stock photos, INSTEAD of base64-encoding or chunking large files through chat (which wastes memory). Also works around the sandbox's limited outbound DNS (shell curl in site_run_command only reaches whitelisted hosts like images.unsplash.com; site_fetch reaches ANY host). path e.g. 'assets/img/hero.webp'. Binary-safe, up to 20MB per file. IMAGE PROCESSING happens here, before the file lands: resize accepts '1200x' (width, height follows the aspect ratio), 'x800' (height), '1200x800' (fit inside that box, never cropped or stretched) or '50%'; image_format converts to 'webp', 'jpg', 'png' or 'avif'; quality is 1-100 for the lossy formats (default 82). The hosting container has no cwebp, no ImageMagick and no sharp, so this is how a photo becomes a right-sized WebP — download a 4000px original and write a 1200px WebP in one call. Images are never enlarged past their real size, because upscaling only adds bytes. Not for SVG: an SVG is written through untouched (it is already scalable), and it cannot be rasterised into a PNG favicon here — for that install sharp with site_run_command(background=true).

site_form_submissions

ChatGPT
Lists the CONTACT FORM SUBMISSIONS (leads) that came in from the user's hosted static site — every message the form relay received, whether or not the e-mail went out. This is the record behind configure_site_smtp: each submission is written down BEFORE the e-mail is attempted, so a lead is never lost when the mail account refuses a send (expired app password, quota, a temporary 4xx). delivered:false with an error means the message is safely here but never reached the inbox — fix the mail account with configure_site_smtp and you still have the lead. Every visible form field is returned in fields, so hidden tracking inputs (gclid, wbraid, gbraid, utm_*) travel with the lead and a form submission can be matched to the click that produced it. days looks that far back (default 30, max 365) and limit caps the rows (default 50, max 200). Read-only; nothing is deleted and no visitor IP is stored.

site_headers

ChatGPT
Manages the HTTP response headers of a website hosted on Opus Growth. mode = list (show the current headers), add (add or update the given fields), remove (delete the named fields) or replace (overwrite the whole set). headers = one 'Field: value' per line, optionally preceded by a path pattern to limit it to some paths (for example '/assets/* Cache-Control: public, max-age=31536000, immutable'; Cache-Control is accepted only with a path). Supported fields include Content-Security-Policy and its Report-Only variant, Reporting-Endpoints, Permissions-Policy, Referrer-Policy, the Cross-Origin policies, X-Frame-Options and Access-Control-Allow-Origin. Headers apply to the whole site and take effect within seconds. server and site select the target when there is more than one. confirm=false previews a change, confirm=true applies it.

site_info

ChatGPT
Shows a hosting server's web address, status and disk usage (used, limit and free). Pass server (its name) if the account has more than one. Caching for these sites is managed by Opus Growth: pages are served without cache, so a published change is visible immediately. Read-only.

site_list_files

ChatGPT
Lists files on the user's hosting server (website directory /data/site). No SFTP/SSH client or credentials are required.

site_preview

ChatGPT
Renders a page of the hosting site and returns a screenshot image. path = which page (e.g. "/", "/hizmetler.html"). view = "mobile" (390px) | "desktop" (1440px) | a pixel width. site = the key or domain of an EXTRA website on that server; leave it empty for the server's main website. Returns the image directly. (Note: right after writing files there can be a brief CDN cache delay; if it looks stale, wait a few seconds and preview again.)

site_read_file

ChatGPT
Reads a text file from the user's hosting website directory. No SFTP/SSH needed. TOKEN-SAVING: for a large file pass offset (1-based start line) and limit (max lines) to read only a slice instead of the whole file — response then includes total_lines so you can page. offset/limit=0 (default) returns the full file (backward-compatible).

site_read_url

ChatGPT
Reads a LIVE web page exactly as a visitor's browser would and returns the HTTP status code, the response headers and the HTML body. Writes NOTHING anywhere — this is the read-only counterpart of site_fetch. outline=true returns the page's STRUCTURE instead of the body: title, meta description, canonical, and the full h1-h6 heading tree with an h1_count. Use it when the question is "did my edit land", "does this page have an H1", "is the heading order right" — a normal WordPress page is 50 KB of HTML and does not fit in one tool result, so asking for the whole body to check one heading wastes the call. The outline is computed from the SAME fetch, so it reflects what the server actually rendered, including whatever a theme or page builder shortcode produced (issue #260: post_content shows the shortcode, not the <h1> the theme emits from it). text=true returns the page's READABLE TEXT instead of the HTML: title, meta description, canonical, H1s, the visible text with scripts/styles/navigation markup stripped (first 6,000 characters, text_truncated says if there was more) and the list of same-site internal_links. Use it for "what does this page say", "is this the right landing page for my CTA / ad / post", "which pages does it link to". A typical page is 50-120 KB of HTML and the raw body does not fit in one tool result; the text view is a few KB. Use it to verify what actually shipped: is the page live, did it come back 200 or 404, which language did the server pick, is the canonical/hreflang/meta-description what you intended, did the redirect land where you meant it to. REDIRECTS ARE REPORTED, NOT HIDDEN: when the URL you asked for redirects, the answer carries redirected: true, requested_url, final_url and a redirects list with every hop (from, status, to), and status/headers describe the FINAL page. So "does www redirect to the apex host" is answerable in one call — read redirects, do not infer it from status alone. Why this exists (issue #257): the other tools with similar names do different jobs and cost a wasted turn. site_fetch is a DOWNLOADER, it requires a path and writes the file into the site. site_headers MANAGES response headers, it does not read a page. And curl inside site_run_command cannot resolve the site's own domain from the hosting container. So verifying a page you just published had no tool at all. max_bytes caps the body (default 200 KB, max 2 MB) and is applied exactly — the body is cut to it and truncated tells you when something was left behind. Pass max_bytes=0 when you only need to know whether a URL answers: the response then carries status and headers and NO body, which is the cheap way to check a batch of internal links one call at a time. The URL passes the same SSRF guard as every other outbound request, so private and internal addresses are refused. Read-only.

site_rebuild

ChatGPT
Re-build and re-publish a Next.js / Vite / CRA site FROM THE SOURCE KEPT ON THE SERVER by a prior deploy_site_zip of a source project. This is the "edit in chat → publish" loop for build frameworks: the source lives under .src/ (kept after the first deploy) — edit it with site_edit_file / site_write_file (e.g. .src/app/page.tsx, .src/pages/index.js, .src/src/App.jsx), then call site_rebuild to compile the static export again and publish it live. Only works when a SOURCE project was deployed (a .src/ exists); a plain static site has no build step — just edit its files directly (they are already live). A version snapshot is taken first (site_restore undoes it). Build cap ~10 min; the server needs enough disk for node_modules (Next.js is large — small/free servers may run out of space; use an adequately-sized AI Server). site = the key or domain of an EXTRA website on that server; leave it empty for the main website.

site_redirects

ChatGPT
Manages REAL 301 redirects for the hosting website — the correct way to move an old URL to a new one after a redesign or a URL change, so link equity and rankings carry over. These are server-level redirects issued at the edge (HTTP 301 + Location), NOT a <meta refresh> or a JavaScript hop: search engines treat them as permanent moves, users never see the old page. mode='list' shows the current rules; 'add' appends; 'remove' deletes the given source paths; 'replace' overwrites the whole list. rules is one rule per line, source then target: /eski-sayfa /yeni-sayfa /urunler/eski.html /urunler/yeni /kampanya https://baska-alan.com/kampanya Source must start with '/', target with '/' or 'http(s)://'. Matching is an exact path (query strings ignored): wildcard or query sources like /dir/* are rejected as invalid, not stored; both the with- and without-trailing-slash forms are handled automatically. Bulk lists are fine (thousands of rules). confirm=false previews the change, confirm=true applies it — live in seconds. For 'remove' only the source paths are read from rules, one per line. CANONICAL HOST: canonical='www' makes the apex redirect to www instead of the other way round; canonical='apex' restores the default. Until now the platform always sent www to the apex, which costs a site whose ad landing URLs, GA4/GTM and pixel history all live on www an extra hop and a host change in its own measurement (issue #298). confirm=false previews the switch. This is a per-site setting and does not touch the redirect rules above.

site_restore

ChatGPT
Restores the website (or a single file/folder) from an automatic snapshot listed by site_versions — this is how you undo an overwritten or lost change. path restores just that file/folder (e.g. 'index.html' or 'blog/'); leaving it empty restores the WHOLE site to that snapshot. confirm=false previews what would change; confirm=true applies it (live immediately). The current state is snapshotted before restoring, so a restore is itself undoable. IMPORTANT: if the site is produced by a generator (a source folder + build script), restore gets the content back but the NEXT rebuild will overwrite it again — port the recovered content into the generator source too, or the loss repeats.

site_seo_guide

ChatGPT
Returns the SEO + JSON-LD playbook for sites built on the hosting server: how to structure the schema.org @id graph, which schema type each page type needs, title/description rules, URL and canonical architecture, internal linking, sitemap/robots, blog architecture, and the rules on what must NEVER be invented in structured data (opening hours, geo, ratings, prices). Universal — not industry-specific. Pair it with site_design_guide: design decides how the site looks, this decides whether it gets found. The toolkit ships two runnable scripts at the site root (installed by scaffold_site): node seo-stamp.mjs stamps the base JSON-LD graph + canonical + OG tags into every page statically from assets/site.config.js, and node seo-check.mjs audits the whole site (missing/duplicate meta, multiple H1, broken internal links, orphan pages, invalid or missing JSON-LD, alt-less images, JS-hidden content; --dup adds a cross-page duplicate-content ratio). Run both via site_run_command before publishing and fix until zero errors.

site_tag_gateway

ChatGPT
Serves a Google tag from the site's OWN domain — Google tag gateway / first-party mode — for a website hosted here. A reserved measurement path on the customer's domain is reverse-proxied at the edge to Google's gateway, so the tag and its requests are first-party: no third-party host, no third-party cookie, and ad blockers and browser tracking protection no longer cut the measurement. WHY THIS TOOL EXISTS: Google's own setup offers two routes. One asks you to sign into Cloudflare from GTM and grant access to the zone — impossible on a hosted site, because the DNS zone lives in the Opus Growth Cloudflare account and handing a customer a role there would expose every other customer's zone. The other is a manual reverse proxy, which is what this does, at the edge, in seconds, with no access to share. action: 'status' (default) shows the current setup and runs Google's own health check; 'set' turns it on; 'remove' turns it off. tag_id is the Google tag or container id the gateway was created for — G-XXXXXXX, GT-XXXXXXX, AW-XXXXXXX, DC-XXXXXXX or GTM-XXXXXXX — taken from GTM > Admin > Google tag gateway. path is the measurement path, one segment, default /metrics; it MUST be a path the site does not already use, and the tool refuses if a real page answers there. After 'set' the tool verifies https://yourdomain<path>/healthy, which Google expects to read "ok", so you know it works before telling anyone. Then enter that same path in GTM. Requests are proxied with Host rewritten to the gateway, cookies and query strings passed through untouched, visitor country forwarded for geo, and caching off. confirm=false previews.

site_versions

ChatGPT
Lists automatic version snapshots of the website. Every file write, edit, download and shell command takes a snapshot of /data/site FIRST, so an overwritten page can always be recovered. Use this when the user says a change "disappeared", "was reverted" or "came back wrong" — find the snapshot from before the change, then call site_restore. Returns {versions:[{id, when, label}]} — newest first; older history is paged with offset (the answer gives next_offset). READ-ONLY FILE HISTORY: pass version_id + path to get that file exactly as it was in that snapshot (show="content") or its difference from the current file (show="diff") without restoring anything; long output is paged with byte_offset (the answer gives next_byte_offset).

site_write_file

ChatGPT
Writes a TEXT file to the user's hosting website (HTML/CSS/JS/JSON/SVG) — creates it or overwrites. Live immediately. No SFTP/SSH needed. IMAGES/BINARIES: do NOT write them here and do NOT paste base64 into HTML. Passing image bytes through chat (as base64) burns huge amounts of tokens and can exhaust the context. Instead use site_fetch(url, 'assets/img/x.webp') — it downloads the file SERVER-SIDE (the bytes never enter the conversation) and you reference it with <img src="/assets/img/x.webp">.

smart_negative_keywords

ChatGPT
SMART NEGATIVE KEYWORD suggestions — finds SEARCH TERMS that trigger the ad and spend MONEY but bring NO CONVERSIONS, ranked by estimated savings. min_waste: minimum spend threshold (₺). You can add the suggestions with a SINGLE bulk add_negative_keyword call — pass them all as keywords=['text|BROAD', ...], never one call per keyword. Read-only.

tiktok_advertiser_qualification

ChatGPT
Returns an advertiser's qualification / verification status under a Business Center (business verification, industry qualification, account standing). bc_id from list_tiktok_business_centers.

tiktok_campaign_report

ChatGPT
TikTok performance report (spend/impressions/clicks/CTR/conversions...). Dates are YYYY-MM-DD; if empty, the last 30 days are used. data_level: AUCTION_CAMPAIGN | AUCTION_ADGROUP | AUCTION_AD. breakdown (optional): age | gender | country_code | province_id | placement | platform — splits rows by that demographic/placement dimension.

tiktok_change_history

ChatGPT
Business-Center-scoped change log — who changed what and when (audit trail) across the BC's ad accounts. advertiser_id optionally narrows to one account; start_date/end_date are YYYY-MM-DD. Returns up to limit entries (cap 500) with a truncated flag. bc_id from list_tiktok_business_centers.

tiktok_recommendations

ChatGPT
TikTok's own automatic optimization suggestions for an ad account (the equivalent of Google Ads recommendations) — budget, bidding, targeting, creative and setup improvements TikTok recommends applying. level: ACCOUNT (account-wide) | CAMPAIGN | ADGROUP | AD.

tiktok_report_query

ChatGPT
Free-form READ of TikTok's integrated report endpoint (/report/integrated/get/) — any metric and dimension TikTok supports. report_type: BASIC (performance) | AUDIENCE (age, gender, country_code, province_id, platform, placement...) | PLAYABLE_MATERIAL | CATALOG | BC | TT_SHOP. data_level: AUCTION_ADVERTISER | AUCTION_CAMPAIGN | AUCTION_ADGROUP | AUCTION_AD. dimensions: e.g. ['campaign_id'], ['adgroup_id','stat_time_day'], ['ad_id','gender']. metrics: e.g. spend, impressions, clicks, ctr, cpc, cpm, reach, frequency, conversion, cost_per_conversion, result, cost_per_result, video_play_actions, video_watched_6s, average_video_play, likes, comments, shares, follows, profile_visits. lifetime=true returns totals since the start of each object (no date range). Otherwise days = last N full days excluding today, or start_date/end_date (YYYY-MM-DD); TikTok limits the range (with stat_time_day it is 30 days) and rejects longer ranges with an explanatory error. filtering: TikTok filter objects, e.g. [{"field_name": "campaign_ids", "filter_type": "IN", "filter_value": "["123"]"}]. Reference: https://business-api.tiktok.com/portal/docs?id=1740302848100353. Read-only; reply carries the exact period or lifetime=true.

unblock_ip_addresses

ChatGPT
Removes IP exclusions from a Google Ads campaign (undo of block_ip_addresses). criterion_ids = comma-separated ids from list_ip_exclusions. confirm=false previews; confirm=true applies.

update_ad_group

ChatGPT
Rename a Google Ads AD GROUP and/or change its default max CPC bid. Restructuring an existing account means renaming groups constantly — a theme gets split, a match type moves out, a naming convention changes — and without this the rename has to happen in the Google Ads interface while everything else is done here. name = the new ad group name (unique within the campaign). cpc_bid = the group's default max CPC in the account currency; leave it at 0 to keep the current bid. status ('ENABLED' | 'PAUSED') is accepted too so a rename and a pause can be one call; update_ad_group_status does the status alone. Only the fields you pass are changed. confirm=false → dry-run.

update_ad_group_status

ChatGPT
Turns a Google Ads AD GROUP on or off, or removes it: status 'ENABLED' to enable / resume / unpause / reactivate the group, 'PAUSED' to pause / stop / turn off a whole ad group at once, or 'REMOVED' to delete it. REMOVED is PERMANENT: the ad group leaves the campaign and CANNOT be re-enabled (its reporting history stays), so pause first if you are unsure. This is the missing middle level — update_campaign_status handles the campaign, update_ad_status a single ad, and this one the ad group. Pausing the group stops every ad and keyword inside it, so this is the right way to switch off a theme that is wasting budget; pausing the keywords one by one does NOT stop the group's ads. confirm=false → dry-run.

update_ad_status

ChatGPT
Turns a single Google Ads AD on or off, or DELETES it: status 'ENABLED' to enable / publish / resume / unpause an ad, 'PAUSED' to pause / stop it, 'REMOVED' to delete it for good. This is how you launch ads (e.g. an RSA) that were created PAUSED for safety. REMOVED matters during an iteration cycle: Google allows only 3 enabled RSAs per ad group, so revising copy repeatedly leaves dead paused ads behind that keep occupying the group — removing them is the only way to clean up. Removal cannot be undone, the ad and its history are gone, so pause first if you are unsure. For the whole ad group use update_ad_group_status, for the campaign use update_campaign_status. confirm=false → dry-run.

update_campaign

ChatGPT
RENAMES a Google Ads CAMPAIGN and/or changes its status — the campaign-level counterpart of update_ad_group. Renaming is the half update_campaign_status cannot do, so restructuring an account (merging two campaigns, changing a naming convention) no longer has to drop back into the Google Ads interface. name = the new campaign name; it must be unique in the account. status: 'ENABLED' | 'PAUSED' | 'REMOVED'. REMOVED DELETES the campaign: it leaves the campaign list and CANNOT be re-enabled — its reporting history stays but the campaign itself is gone for good, so pause first if you are unsure. Only the fields you pass are changed; leave one empty to keep it. Passing REMOVED ignores name, because a removed campaign cannot be renamed. confirm=false → dry-run.

update_campaign_status

ChatGPT
Turns a Google Ads campaign ON or OFF: status 'ENABLED' to enable / resume / unpause / reactivate / start serving a paused campaign again, or 'PAUSED' to pause / stop / turn it off. This is the tool for BOTH directions — there is no separate resume_campaign or enable_campaign. Use it to launch campaigns that were created paused for safety. confirm=false → dry-run.

update_chatgpt_ads_ad

ChatGPT
Changes the copy, destination or image of an existing ChatGPT Ads ad. Only the fields you pass change; the rest of the creative is read first and sent back unchanged, so a title edit cannot wipe the image or the destination. A new image_url is uploaded and the returned file replaces the current one. Editing any creative field starts a NEW OpenAI review of the ad, so it stops serving until that review passes — a name-only change does not. The ad cannot be moved to another ad group. confirm=false returns a dry run showing current values next to proposed ones.

update_chatgpt_ads_ad_group

ChatGPT
Changes the name, bid, bid strategy or context hints of an existing ChatGPT Ads ad group. Context hints are what decides which ChatGPT conversations the ads can match, so this is the main relevance lever on the platform. Write them as short natural sentences describing the product, the use case or the need, in the language the buyers actually chat in: "lightweight trail running shoes for rocky terrain" works, "shoes" does not, and "customers in California" belongs in country targeting instead. Up to 2000 per ad group; they are not exact-match keywords and they do not replace country or platform targeting. hints_mode: add appends to what is already there (the default, and the safe one), replace swaps the whole list, remove deletes the ones you pass, clear empties it. OpenAI's own field is replace-only, so a bare replace with two hints would silently delete the rest; the merge happens here. max_bid is in the account currency and is checked against the parent campaign's real daily budget plus a sanity ceiling that catches a decimal slip; a deliberate high bid passes with allow_high_bid=true. strategy: fixed_bid | maximize_clicks | maximize_conversions. Switching to a maximize strategy drops the manual bid because OpenAI requires it to be absent there. Bid settings you do not pass — including audience bid multipliers — are preserved, not cleared. confirm=false returns a dry run.

update_chatgpt_ads_campaign

ChatGPT
Changes an existing ChatGPT Ads campaign: name, budget, locations, platforms, schedule, conversion events, status. Only what you pass changes. Budgets are in the account currency. daily_budget on a lifetime-budget campaign SWITCHES it to a daily budget, which OpenAI does not let you undo (a lifetime budget would need a new campaign); the dry run says so. A campaign has exactly one budget type, so pass one of the two. include_locations / exclude_locations take ISO country codes (TR, GB) or location ids from chatgpt_ads_geo_lookup (e.g. 2001311 = Istanbul); locations_mode: replace (the list becomes exactly this, default) | add | remove. Changing geography rewrites the whole targeting block, so the current audiences and platforms are read first and sent back unchanged. platforms: any of ios_app, android_app, web, desktop_web, ios_web, android_web, or ['all'] to lift the restriction; a platform-only change leaves locations untouched. start / end: YYYY-MM-DD or YYYY-MM-DDTHH:MM in the account time zone, 'now', or 'none' to remove the date. conversion_event_setting_ids attaches event settings for measurement (conversion_mode add | replace | remove); it is refused on a conversions-objective campaign because OpenAI locks that event at creation. status: active | paused (archiving is chatgpt_ads_status). The objective itself can never change. confirm=false returns a dry run showing each current value next to the proposed one.

update_conversion_action

ChatGPT
Makes a conversion action PRIMARY/SECONDARY (primary_for_goal); to change its default VALUE, counting or lookback window use set_conversion_settings instead. role: 'PRIMARY' (counted in the Conversions column + feeds into smart bidding) | 'SECONDARY' (observed only in 'All conversions', does not feed bidding). NOTE: the Google Ads API does not allow pausing/removing (HIDDEN/REMOVED) a conversion action — that is only done from the panel. confirm=false → dry-run preview.

update_conversion_goal

ChatGPT
Makes a conversion CATEGORY primary or secondary — the CORRECT lever for Smart Bidding in a goal-based account. Unlike update_conversion_action's legacy primary_for_goal flag, this ALSO works on Google-hosted 'local actions' (map/profile signals) that the API otherwise refuses to change. category+origin identify the goal (run list_conversion_goals first), e.g. category='ENGAGEMENT' origin='GOOGLE_HOSTED' → demote Google Business 'other engagements'; category='PHONE_CALL_LEAD' origin='CALL_FROM_ADS' → keep calls primary. role: 'PRIMARY' (biddable) | 'SECONDARY'. Pass campaign_id to override just that campaign; omit for account-wide. confirm=false → dry-run.

update_custom_conversion_goal

ChatGPT
Edits a CUSTOM conversion goal: change which conversion actions it contains, rename it, or delete it. Use it to unblock deleting a conversion action that Google says is used by a custom goal — pass conversion_actions with the ids that should REMAIN (comma separated), leaving the one you want to delete out. remove=true deletes the whole goal. Run list_custom_conversion_goals first to see the ids. confirm=false → dry-run.

update_gtm_entity

ChatGPT
EDITS an existing GTM tag / trigger / variable / workspace by its full path (from list_gtm_tags/triggers/variables/workspaces). kind = tag|trigger|variable|workspace. Provide only what changes: name (rename), parameters, firing_trigger_ids (tags only, comma-separated — empty = keep). parameters = JSON object {key: value} or a GTM parameter list; ONLY the keys you give are changed and every other parameter of the tag stays as it is (so {"eventName": "generate_lead"} changes just the event name and keeps the measurement ID). A list value becomes a GTM table/list, e.g. {"eventSettingsTable": [{"parameter": "form_id", "parameterValue": "contact"}]}. replace_parameters=true replaces the whole parameter set instead (needed only to REMOVE a parameter). GTM PUT is a full replace, so the current object is fetched, your fields applied, then written back (fingerprint preserved). SAFETY: confirm=false → preview; confirm=true → applies.

update_linkedin_campaign

ChatGPT
Updates a LinkedIn campaign (only the fields provided). start_date / end_date ('YYYY-MM-DD') change the run schedule (e.g. end an event campaign on the event day); the side you leave empty is kept. status: ACTIVE | PAUSED | DRAFT | ARCHIVED | COMPLETED | CANCELED. BIDDING: optimization_target sets the bid strategy — 'MAX_CLICK'/'MAX_IMPRESSION'/'MAX_CONVERSION'/'MAX_LEAD'/'MAX_VIDEO_VIEW'/'MAX_REACH' = automated 'Maximum Delivery' (pick the MAX_* matching the campaign objective; unit_cost ignored); 'NONE' = manual bidding (unit_cost REQUIRED, else it won't deliver); 'TARGET_COST_PER_CLICK/…' = target cost (unit_cost=target); 'CAP_COST_AND_MAXIMIZE_CLICKS/…' = cost cap (unit_cost=cap). cost_type: CPM | CPC | CPV (auto/cap modes use CPM). MAX_CONVERSION needs a conversion associated before it can go ACTIVE. ad_format changes the ad set's format (STANDARD_UPDATE | SPONSORED_UPDATE_NATIVE_DOCUMENT | CAROUSEL | SINGLE_VIDEO | SPOTLIGHT | …) so a campaign that was created as a single-image set can be corrected here instead of in the UI, where the edit wipes the targeting. LinkedIn may refuse a format change once the campaign has creatives attached; the error is passed through as-is. Budget increases are subject to the safety limit. confirm=false = preview.

update_linkedin_campaign_group

ChatGPT
Updates a LinkedIn campaign group (only the provided fields change). status: ACTIVE | PAUSED | DRAFT | ARCHIVED. Budget changes are subject to the safety cap (increases above +50% are blocked). start_date / end_date ('YYYY-MM-DD') change the run schedule; the side you leave empty is kept. objective_type tries to change the group's objective. Read this before using it: an objective set at GROUP level constrains every campaign inside the group, so a group created with ENGAGEMENT cannot host a Lead Generation ad set. LinkedIn normally refuses to change it once the group exists — the attempt is passed through and its real answer is returned, so you find out instead of guessing. If it is refused, the remedy is a new group with the objective left EMPTY (that is the default, and it keeps the group open to every objective). confirm=false = preview.

update_linkedin_campaign_targeting

ChatGPT
Updates a LinkedIn campaign's targeting (job title, function, seniority, industry, skill, company size, geography). Each parameter is a comma-separated list of URNs — found via linkedin_targeting_search (facet=titles/industries/skills/seniorities/employers...). If geo_urns is empty, country ('TR','US'...) is used. At least one location is required. LOCATION MODE: location_mode='permanent' targets only members whose PROFILE (permanent) location matches (facet profileLocations); location_mode='recent_and_permanent' (LinkedIn default) also includes recent/IP locations (facet locations). Leave EMPTY to keep the campaign's current location mode. Switching mode keeps the existing locations if geo_urns is empty. AUDIENCE LANGUAGE: audience_language sets the members' LinkedIn interface language (facet interfaceLocales) on the campaign — short code ('de','en','fr','es','tr'…) or full 'xx_XX'; empty=unchanged. This DOES change existing campaigns (the old language arg only applied when building targeting from scratch). EXCLUSIONS: every facet can be excluded — exclude_geo_urns (locations), exclude_industry_urns (industries — keep e.g. Legal Services / Education out), exclude_employer_urns (specific companies, urn:li:organization:...), exclude_title_urns, exclude_function_urns, exclude_seniority_urns, exclude_skill_urns, exclude_staff_count_urns. This MERGES: only the facets you pass are changed; everything else on the campaign (other excluded employers/industries/audiences, matched audiences, ad sizes, locales) is PRESERVED. confirm=false returns a preview that includes current_targeting, new_targeting, will_change and will_preserve — use it to READ the campaign's current targeting safely. Size URNs contain commas (urn:li:staffCountRange:(201,500)) and are parsed correctly. MATCHED AUDIENCES (retargeting / ABM): include_audience_urns adds matched audiences as an inclusion, exclude_audience_urns excludes them (e.g. suppress existing customers) — comma-separated 'urn:li:adSegment:...' values from list_linkedin_audiences. Only audiences with usable_for_targeting=true work: an ARCHIVED or EXPIRED audience resolves to 0 members and the ad set stops delivering. Check the resulting size with linkedin_audience_forecast before going ACTIVE. REMOVING A FACET: an empty parameter means 'leave unchanged', so to DELETE a facet pass the word none — e.g. exclude_audience_urns='none' drops the audience exclusion, industry_urns='none' drops the industry targeting. Works for every include and exclude_* parameter except geo_urns (a location is always required; replace it instead).

update_linkedin_creative_status

ChatGPT
Changes a creative's (ad's) intended status: ACTIVE | PAUSED | DRAFT | ARCHIVED. Note: a creative that is UNDER_REVIEW cannot be paused. confirm=false = preview.

update_meta_ad

ChatGPT
Pauses, resumes or archives ONE Meta (Facebook/Instagram) ad, or renames it. Works on a single ad only: the other ads next to it keep delivering. Use it to switch off one ad that is wasting budget. ad_id comes from list_meta_ads. status: ACTIVE (resume / turn on) | PAUSED (pause / stop) | ARCHIVED. confirm=false = preview.

update_meta_ad_creative

ChatGPT
Replaces an existing ad's CREATIVE (image, text, destination link, and call-to-action button) WITHOUT deleting the ad — a new creative is built and swapped in, so the ad keeps its ID, learning phase and history (no delete-and-recreate). Use this to FIX a wrong CTA (e.g. an ad stuck on 'Learn More' that should be 'Shop Now'), swap the image, rewrite the copy, or change the link. page_id REQUIRED (list_meta_pages). Image: ONE of image_hash / image_base64 / image_url. cta_type: LEARN_MORE | SHOP_NOW | SIGN_UP | BUY_NOW | GET_OFFER | CONTACT_US | APPLY_NOW | ORDER_NOW | SEND_MESSAGE | WHATSAPP_MESSAGE | INSTAGRAM_MESSAGE | CALL_NOW | ... — leave EMPTY to auto-pick from the ad set's messaging destination, else the campaign objective. USE THIS TO REPAIR AN INSTAGRAM DM AD stuck on a link button: on a destination_type=INSTAGRAM_DIRECT ad set pass cta_type='INSTAGRAM_MESSAGE' and the ad starts delivering (LEARN_MORE there is error 1487891). For Click-to-WhatsApp/Messenger/Instagram-DM ads (cta_type WHATSAPP_MESSAGE / SEND_MESSAGE / INSTAGRAM_MESSAGE) you can set/change the pre-filled chat: welcome_message = the ready-written text that auto-populates the user's compose box, greeting = optional in-thread greeting, ice_breakers = up to 4 tappable quick replies (≤80 chars each; if given they replace the auto-fill). This is how to add a WhatsApp welcome/ice-breaker message to an existing ad. BACKGROUND MUSIC: a single-photo ad cannot carry audio on its own (Meta rule); music is added as an Advantage+ creative enhancement that turns the photo into a short video with a soundtrack. Set music=true to enroll it — it applies on Instagram Reels/Stories/Feed placements and is quietly ignored on Facebook Feed; the result reports back which enhancements actually took (never claims music when Meta declined). For a specific song, put the music in a short video (upload_meta_video) and pass its video_id here — the existing ad is converted to that video creative while keeping its ad ID and learning. enhancements = other Advantage+ features (e.g. image_enhancement) by name. TURNING THEM OFF: Meta switches several Advantage+ enhancements ON BY DEFAULT (video touch-ups, music, text variations), which silently changes the advertiser's creative. Pass enhancements_off="all" to opt every one of them OUT, or a comma separated list of names to opt out selectively. Use get_meta_ad_creative to see which ones are currently on. INSTAGRAM IDENTITY: the ad's current Instagram account is carried over to the new creative automatically; pass instagram_user_id only to switch it to a different Instagram account. The result reports which Instagram account the new creative actually uses. confirm=false = preview.

update_meta_adset

ChatGPT
Updates a Meta ad set (only the fields provided). status: ACTIVE | PAUSED | ARCHIVED. Budget increases are subject to the safety cap. DELIVERY SETTINGS (existing targeting is preserved; only these fields change): • publisher_platforms — narrows placement; comma-separated: facebook,instagram,audience_network,messenger,whatsapp,threads (e.g. "instagram" = Instagram only). • facebook_positions / instagram_positions / whatsapp_positions / messenger_positions / audience_network_positions / threads_positions — the slot INSIDE a platform, comma-separated (facebook/instagram: story, feed, reels…; whatsapp: status). "Story + WhatsApp Status only" = publisher_platforms="facebook,instagram,whatsapp" + facebook_positions="story" + instagram_positions="story" + whatsapp_positions="status". Pass "-" to drop the restriction for that platform (all of its slots come back). See create_meta_adset for the full slot list. • audience_expansion — "off" (turns off Advantage custom audience / audience expansion; prevents a retargeting set from drifting from the list to a cold audience) or "on". TARGETING (omitted fields keep their current value; Meta replaces targeting atomically, so the tool reads the live spec and merges): • age_min / age_max — 18..65. • genders — "1" (men), "2" (women) or "1,2"; "-" CLEARS the restriction (all genders). "" means leave unchanged. Anything else is REJECTED — an invalid value used to silently clear the restriction and widen the ad set to everyone. • countries / regions / cities — comma-separated; region & city KEYS are the numeric key from search_meta_interests(kind="adgeolocation") (NOT the place name). Passing any of them replaces the location set. • places / custom_locations — RADIUS targeting (JSON). places = [{"key":"2034720566774469","radius":5.7,"distance_unit":"mile"}] (a pinned place + drop-radius; key from search_meta_interests(kind="adgeolocation")); custom_locations = [{"latitude":41.01,"longitude":28.97,"radius":10,"distance_unit":"kilometer"}]. distance_unit "mile" (1-50) or "kilometer" (1-80), clamped for you. Passing either replaces the location set. • geo_locations — the raw Meta geo_locations object (from another ad set's targeting.geo_locations) passed through verbatim to reproduce a proven radius/place target; it overrides countries/regions/cities/places/custom_locations, drops the deprecated location_types and clamps radius. • excluded_countries / excluded_regions / excluded_cities — EXCLUDE locations (Meta targeting.excluded_geo_locations), e.g. "Manisa +40 km but not İzmir" = keep geo_locations and pass excluded_regions="<İzmir region key>". Same keys as regions/cities (search_meta_interests(kind="adgeolocation") key), countries as ISO codes. Each field replaces only that exclusion dimension and keeps the included locations and the other exclusions; "-" removes that dimension's exclusions. excluded_geo_locations — the raw Meta excluded_geo_locations object (JSON) replacing ALL exclusions; "-" removes every exclusion. • interest_ids — comma-separated interest IDs; "" clears interest targeting (demographic entries are KEPT). • relationship_statuses / education_statuses — comma-separated numeric codes (3 = MARRIED; see create_meta_adset for the full code list). AND-ed with interests, so you can add "married" to an existing interest-targeted set without touching it. • family_statuses / life_events / industries / behaviors — comma-separated IDs from search_meta_interests(kind="adTargetingCategory", category_class=...) — parents & child age brackets, newly-weds/recently-moved, industry, behaviours. Each field you pass replaces only that dimension; the ones you omit are preserved. OPTIMIZATION: • optimization_goal (e.g. OFFSITE_CONVERSIONS) • pixel_id + custom_event_type (e.g. PURCHASE) — fixes an ad set optimizing for the wrong conversion event without rebuilding the campaign. • custom_conversion_id (from list_meta_custom_conversions) — points the ad set at a specific Meta CUSTOM CONVERSION instead of a standard pixel event; keep optimization_goal OFFSITE_CONVERSIONS. custom_conversion_id is…

update_meta_campaign

ChatGPT
Updates a Meta campaign (only the provided fields). status: ACTIVE | PAUSED | ARCHIVED (use PAUSED to pause). daily_budget in the main unit; increases are subject to the safety cap (above +50% is blocked). confirm=false = preview.

update_meta_custom_conversion

ChatGPT
Renames a custom conversion, or sets its description / default conversion value. conversion_id from list_meta_custom_conversions. USE THIS WHEN A CUSTOM CONVERSION WAS SET UP WRONG: Meta does not let this app delete or archive one (delete returns a permissions error, and writing is_archived is silently ignored), so the honest fix is to rename it — e.g. prefix it with 'SILINECEK' / 'DELETE ME'; final removal is done in Meta Events Manager → Custom conversions, where it takes two clicks. Never claim you deleted or archived it. default_conversion_value: leave at -1 to keep the current value. confirm=false = preview.

update_meta_instagram_post_ad

ChatGPT
Adds or changes the pre-filled WhatsApp/Messenger message (and optionally the button) on an EXISTING Instagram-post ad WITHOUT unlinking it from the organic post. Use this instead of update_meta_ad_creative whenever the ad was built from an Instagram post: update_meta_ad_creative rebuilds the creative from scratch and would drop source_instagram_media_id, so the ad would stop being the organic post and every like/comment collected so far would disappear from it. Here the Instagram media and the Instagram account are read off the live creative and carried over unchanged; the ad id and its learning are preserved and Meta re-reviews the new creative. welcome_message = the auto-filled compose text, greeting = optional in-thread greeting, ice_breakers = up to 4 tappable quick replies (≤80 chars). cta_type (WHATSAPP_MESSAGE / SEND_MESSAGE) is optional and REQUIRES link; leave both empty to keep the button coming from the ad set's destination_type. confirm=false = preview.

update_microsoft_ad_group

ChatGPT
UPDATES a Microsoft (Bing) ad group's settings. IMPORTANT: in Bing the campaign's END DATE lives on the AD GROUP — so "when does my campaign stop?" / "extend it by a month" is answered here, not on the campaign. Also sets the default CPC bid, start date, status and network. end_date / start_date: 'YYYY-MM-DD'. status: Active | Paused. network: OwnedAndOperatedAndSyndicatedSearch (Bing + partners) | OwnedAndOperatedOnly (Bing only) | SyndicatedSearchOnly. Leave a field empty to keep it unchanged. confirm=false previews.

update_microsoft_ad_group_status

ChatGPT
Changes an ad group's status. status: Active | Paused. (campaign_id required.) confirm=false = preview, confirm=true applies.

update_microsoft_ad_status

ChatGPT
Changes ad status. status: Active | Paused. ad_type is the ad type (default ResponsiveSearch). (ad_group_id required.) confirm=false = preview, confirm=true applies.

update_microsoft_campaign_budget

ChatGPT
Updates a campaign's daily budget. Subject to the same budget guardrails as Google/Meta/LinkedIn/TikTok: +50% per change, +100% cumulative per 24h, and no cut deeper than -80%. confirm=false = preview, confirm=true applies.

update_microsoft_campaign_status

ChatGPT
Changes campaign status. status: Active | Paused. confirm=false = preview, confirm=true applies.

update_microsoft_keyword

ChatGPT
Updates a keyword's bid (bid>0) and/or status (Active|Paused). confirm=false = preview, confirm=true applies.

update_microsoft_responsive_search_ad

ChatGPT
EDITS the copy of an existing Microsoft (Bing) Responsive Search Ad — swap a weak headline or description WITHOUT deleting and rebuilding the ad (a rebuild resets its performance history). PARTIAL update: only the fields you pass change; the rest are read from the live ad and written back. Microsoft limits: 3-15 headlines (≤30 chars), 2-4 descriptions (≤90 chars) — validated before the call. confirm=false = preview, confirm=true applies.

update_pmax_asset_group

ChatGPT
Updates — or REMOVES — a Performance Max ASSET GROUP itself (not its individual assets): landing page (final_url), name, status and the display path segments path1/path2. Only the arguments you pass are changed; the rest stay as they are. status: 'ENABLED' | 'PAUSED' | 'REMOVED'. 'REMOVED' deletes the whole asset group together with every headline, description, image and video linked to it — this is the ONLY way to get rid of the mandatory TEXT assets Google refuses to let you unlink one by one (an asset group must always keep 3 headlines, 1 long headline and 2 descriptions, so removing assets alone can never empty it). Removal CANNOT be undone: the group and its links are gone for good, so confirm it with the account owner first. Use get_pmax_assets to see the current values. confirm=false → dry-run.

update_pmax_assets

ChatGPT
Adds AND removes text assets in an existing Performance Max asset group. headlines/long_headlines/descriptions add new text. remove_resource_names: comma-separated resource_name values from get_pmax_assets — those assets are unlinked from the group. Doing both in one call REPLACES an asset atomically (needed when a field is already at its maximum, e.g. 15 headlines). Google minimums are enforced before anything is sent: 3 headlines, 1 long headline, 2 descriptions, 1 business name. confirm=false → dry-run.

update_rsa

ChatGPT
EDITS the copy of an existing Google Ads RESPONSIVE SEARCH AD (RSA) in place — swap, drop or add a headline or description while keeping the SAME ad and its performance history. This is the tool for "remove that one headline", "replace this description", "add two more headlines"; rebuilding the ad instead would reset its learning, burn one of the 3 enabled-RSA slots in the ad group, and leave a paused near-duplicate behind. It also edits the copy of a DEMAND GEN IMAGE (multi-asset) ad (type DEMAND_GEN_MULTI_ASSET_AD) in place, with the same partial or full-replace fields: Google allows 1-5 headlines ≤30 chars and 1-5 descriptions ≤90 chars there, with no pins and no path1/path2. The ad type is read from the live ad, so pass the ad id exactly as for an RSA. TWO WAYS to give the change, and the partial one is usually what you want: • PARTIAL (recommended): remove_headlines / add_headlines / remove_descriptions / add_descriptions. The live ad is read first, only your delta is applied, and everything you did not mention — including each surviving asset's PIN — is written back unchanged. remove_* matches on the exact text; a text that is not in the ad is reported instead of silently ignored. • FULL REPLACE: headlines / descriptions replace ALL existing ones, so you must re-send every line you want to keep. Do not mix the two for the same field. Google limits either way (RSA): 3-15 headlines ≤30 chars, 2-4 descriptions ≤90 chars — checked before the call, so an edit that would leave too few is refused rather than rejected by Google. final_url/path1/path2 optional. To change ONLY the destination URL, pass just final_url (leave the copy fields empty) — this updates final_urls on standard ads (RSA and other final-URL ad types). NOTE: call-only ads (CALL_AD) do NOT allow their URL to be edited via the API (Google returns VALUE_MUST_BE_UNSET); a call ad must be recreated with the new URL. To DELETE an ad entirely use update_ad_status(status='REMOVED'). PINNING: headline_pins / description_pins are optional lists in the SAME ORDER as the headlines/descriptions given; each entry pins that text to a fixed position — headline 1/2/3, description 1/2 (0/empty = unpinned). E.g. headline_pins=[1,0,0] pins the first headline to position 1. Google's own field names are accepted too, so headline_pins=['HEADLINE_1','',''] means the same thing. A position-to-text dictionary is NOT accepted — the list must follow the same order as the headlines you send. Text or pin changes start a new Google review. Pins are part of the replaced content, so send them together with the headlines/descriptions. confirm=false → dry-run.

update_tiktok_ad

ChatGPT
UPDATES a TikTok ad's CREATIVE in place — the ad text, landing page, call-to-action or display name. Until now only the status could be changed, so fixing a typo or swapping a landing page meant deleting and recreating the ad, which DESTROYS its social proof (likes, comments, shares) and performance history. Leave a field empty to keep it unchanged — the ad's identity, name, format and creative source are read from the ad itself and resent unchanged (TikTok validates the whole creative on every update, not just the changed fields). On a SPARK ad (one built from an organic TikTok post) the text and display name come from the post and cannot be changed here — only the CTA, landing page and ad name can. Note: TikTok's API does not allow editing ads inside an Upgraded Smart+ campaign at all; those must be edited in TikTok Ads Manager. call_to_action e.g. LEARN_MORE | SHOP_NOW | SIGN_UP | CONTACT_US. confirm=false previews.

update_tiktok_ad_status

ChatGPT
Enable/pause/delete TikTok ad(s). ad_ids comma-separated. operation: ENABLE | DISABLE | DELETE. confirm=false previews, confirm=true applies.

update_tiktok_adgroup

ChatGPT
UPDATES a TikTok ad group's TARGETING, BID and SCHEDULE in place. Until now only status and budget could be changed, so any targeting tweak meant deleting and rebuilding the ad group — which RESETS the learning phase and throws away performance history. Use this to widen/narrow locations, ages, genders, interests or audiences, change the bid, or extend the run dates without losing what the algorithm already learned. Leave a field empty to keep it unchanged. location_ids / age_groups / genders / interest_category_ids / audience_ids / excluded_audience_ids are comma-separated. schedule_start / schedule_end: 'YYYY-MM-DD HH:MM:SS'. Passing bid_price switches the ad group to manual bidding automatically (bid_type=BID_TYPE_CUSTOM) — TikTok silently ignores a bid sent without it. Set bid_type=BID_TYPE_NO_BID (with no bid_price) to go back to automatic bidding. pacing: PACING_MODE_SMOOTH (standard) | PACING_MODE_FAST (accelerated; needs a manual bid). confirm=false previews.

update_tiktok_adgroup_budget

ChatGPT
Updates a TikTok ad group budget (in the main unit of the account currency). Budget guardrails apply: +50% per change, +100% cumulative per 24h, no cut deeper than -80%. confirm=false previews, confirm=true applies.

update_tiktok_adgroup_status

ChatGPT
Enable/pause/delete one or more TikTok ad groups. adgroup_ids is comma-separated. operation: ENABLE | DISABLE | DELETE. confirm=false previews, confirm=true applies.

update_tiktok_campaign

ChatGPT
Renames a TikTok campaign. confirm=false is a preview, confirm=true applies.

update_tiktok_campaign_budget

ChatGPT
Updates a TikTok campaign budget (in the main unit of the account currency, e.g. 75 = 75.00). Budget guardrails: +50% per change, +100% cumulative per 24h, no cut deeper than -80%. confirm=false = preview, confirm=true applies.

update_tiktok_campaign_status

ChatGPT
Enable/pause/delete TikTok campaign(s). campaign_ids: comma-separated IDs. operation: ENABLE | DISABLE | DELETE. confirm=false = preview, confirm=true applies.

upload_conversion_adjustments

ChatGPT
ADJUSTS conversions you already reported — the refund/cancellation half of the closed loop. RETRACT cancels a conversion that fell through (refund, cancelled order, unqualified lead); RESTATE corrects its value (partial refund, upsell). Without this, Smart Bidding keeps optimizing toward revenue that never actually landed. conversion_action = name or ID. adjustments accepts a JSON array OR one row per line as 'key=value;key=value': • type=RETRACT;order_id=A-991;datetime=2026-08-05 09:00:00+03:00 • type=RESTATE;order_id=A-991;datetime=2026-08-05 09:00:00+03:00;value=1200 Identify the original conversion by order_id (recommended) OR by gclid + conversion_datetime. Fields: type (RETRACT|RESTATE), datetime (REQUIRED = when the adjustment happened), order_id, gclid, conversion_datetime (original conversion time, needed with gclid), value (RESTATE only). confirm=false → preview; confirm=true → applies.

upload_image

ChatGPT
Turns an image into a public image URL you can pass to any tool that needs one. If the photos are already LOCAL FILES in your own environment (the user attached them to the chat) and you can run shell commands, call upload_image() with no arguments to get an upload_url, POST the files there yourself (curl -sS -F file=@photo1.jpg -F file=@photo2.jpg <upload_url>, up to 10 files), then call upload_image(ticket=...) for the public URLs — the user does not have to drag anything. Otherwise: one public image URL you can pass to any tool that needs one (gbp_create_post image_url, gbp_upload_photo, upload_image_asset, upload_meta_image...). FOUR modes, in the order you should prefer them: (1) source_url=... when the photo is ALREADY somewhere on the web — the customer's own website, a WordPress media library, a Drive or Dropbox share link: we fetch it server-side and re-publish it, so NOTHING has to pass through the chat and the user does not have to do anything. Use this first whenever a URL exists. (2) call with NO arguments to get an upload link — give that link to the user, they drop their files, then call this tool again with the returned ticket. ONE link takes up to 10 photos: the user can drag all of them in at once and the link stays open for 45 minutes, so never ask for several links for several photos. (3) ticket=... to read the drop: returns images (one entry per uploaded file, each with its own public URL) plus count, and tells you whether the user can still add more. (4) image_base64=... only for a SMALL image already in the conversation — a real photo is tens of thousands of characters of base64 and will not fit in one model message, so for anything camera-sized use mode 1 or 2 instead. JPG or PNG, at least 250px on the short edge, up to 5 MB each. URLs stay reachable for 7 days, which is what image hosts like Google need because they fetch them asynchronously.

upload_image_asset

ChatGPT
Uploads an image to the Google Ads asset library → returns the asset resource_name. The returned value is used in PMax images, responsive display ads, and image extensions. JPEG/PNG/GIF, ≤5MB. aspect crops/pads the image to the ratio Google requires before upload ("1:1" for logos and square images, "1.91:1" for landscape/image extensions; also "square"/"landscape"/"4:5"); leave empty to upload as-is. fit="cover" crops the overflowing edges from the centre (right for photos), fit="pad" keeps the whole image and fills the rest with pad_color (right for wide logos — use "transparent" or "#FFFFFF"). confirm=false → dry-run.

upload_meta_image

ChatGPT
Uploads an image creative to Meta and returns its image_hash — reusable across ads. Give ONE of: object_story_id (an existing Page post — its image is reused, id from list_meta_page_posts), image_url (public link), or image_base64 (the file itself, e.g. one the user dropped into the chat; a data: prefix is accepted). JPEG/PNG/GIF/WebP, max 8MB. Prefer object_story_id for a post's own image: Facebook's CDN links are signed and short-lived, so a link copied earlier in the chat can come back 403 — with the post id the fresh link is resolved server-side. Pass the returned image_hash to create_meta_link_ad / create_meta_video_ad / create_meta_carousel_ad.

upload_meta_video

ChatGPT
Gets a video into the user's Meta ad account and returns its video_id, WITHOUT leaving the chat. Three ways, pick by what the user has: (1) THE FILE IS ON THEIR COMPUTER and they have no link: call this with no arguments. You get a one time drop link to give them; they open it, drag the file in, and that is all they do. Then call this again with ticket=<the token you were given> to read the result and carry on building the ad. The upload completes here; it does not require Ads Manager and does not need base64. (2) THEY HAVE A LINK (their own site, Drive, Dropbox, WeTransfer): pass video_url and the file is fetched server side and pushed to Meta, so share links that Meta itself cannot read still work. (3) A video already in the account: reuse its video_id, no upload needed. The video_id is reusable across ads. mp4, mov, m4v or webm, up to 300 MB.

upload_microsoft_offline_conversions

ChatGPT
Uploads offline conversions to a Microsoft Advertising account so that sales or leads recorded outside the website are attributed to the ad clicks that produced them. conversion_name = an offline conversion goal in the account (list_microsoft_conversion_goals; create one with create_microsoft_conversion_goal if missing). conversions = a JSON array or one row per line as 'msclkid=...;datetime=2026-08-01T14:30:00Z;value=2500'; msclkid and datetime (UTC) are required, value is optional. confirm=false previews, confirm=true uploads.

upload_offline_conversions

ChatGPT
UPLOADS OFFLINE CONVERSIONS to Google Ads — the closed loop between your CRM and your ads. Use it when a lead that came from an ad actually became a sale later (phone/showroom/CRM): Google then learns which clicks produce real revenue and Smart Bidding optimizes for money, not just form fills. conversion_action = the conversion action name or ID (list_conversion_goals / get_conversion_tags). For click-based upload it must be an "Import from clicks" action; for email/phone upload it must have enhanced conversions for leads enabled. conversions accepts EITHER a JSON array OR one row per line as 'key=value;key=value': • gclid=Cj0KC...;datetime=2026-08-01 14:30:00+03:00;value=2500;order_id=A-991 • email=ali@ornek.com;phone=05321234567;datetime=2026-08-01 14:30:00+03:00;value=2500 Fields: gclid | gbraid | wbraid | email | phone (any identifier), datetime (REQUIRED, 'yyyy-mm-dd hh:mm:ss+03:00' — the time the CONVERSION happened), value (optional), order_id (optional, prevents duplicates). Email/phone are SHA-256 hashed locally before sending — raw personal data never leaves the server. confirm=false → preview (nothing uploaded); confirm=true → uploads.

upload_tiktok_image

ChatGPT
Uploads a TikTok image creative from a public URL → image_id. confirm=false = preview, confirm=true uploads.

upload_tiktok_video

ChatGPT
Uploads a TikTok video creative from a public URL → returns video_id (used in create_tiktok_ad). The URL must point at the VIDEO FILE itself (.mp4/.mov); a tiktok.com post link is a web page, not a file — to advertise an existing post use create_tiktok_ad with tiktok_item_id (list_tiktok_posts). confirm=false = preview, confirm=true uploads.

wordpress_bulk_replace

ChatGPT
Applies the SAME find/replace to MANY WordPress posts/pages in one call, with a mandatory dry-run. wordpress_update_post's replace works well but takes a single id, so fixing the same defect on 11 pages meant 11 calls; this does it once, and its confirm=false PREVIEW shows every page that would change and HOW MANY times before anything is written, so an unintended broad match is visible first. Choose the target pages either with ids=[...] (get them from wordpress_search_content) or with a search filter (search=..., search_in/regex/type/status like wordpress_search_content). replace=[{"find":"old","with":"new"}] — same schema and safety as wordpress_update_post: a missing "with" is refused, never treated as a delete. match="normalized" (top-level or per entry) treats invisible characters (non-breaking space, smart quotes, long dashes) as their plain equivalents. DESTRUCTIVE: confirm=false previews per-page counts and writes NOTHING; confirm=true applies. Elementor/Divi pages whose visible text is not in post_content are reported as skipped, not silently missed. The search filter fetches every post one by one (~1 s each), so on a large site one call stops scanning after about 100 seconds and returns what it found so far with partial=true and next_offset; call again with offset=next_offset (same other arguments) to continue. For big sites it is faster to run wordpress_search_content first and pass its ids here.

wordpress_contact_forms

ChatGPT
Lists the Contact Form 7 forms on a connected WordPress site, or, with form_id, shows one form's fields, which of them are required, and the e-mail addresses written in its settings (the recipient/sender of the notification mail). Use it to diagnose a contact/quote form whose conversions stay at zero: check which fields are required and where the notification mail goes, then run wordpress_test_contact_form to see whether a submission actually sends the mail. Read-only; nothing is submitted. Needs the Contact Form 7 plugin on the site.

wordpress_create_post

ChatGPT
Create a post or page on a connected WordPress site. Defaults to a draft you can review before publishing. On a MULTILINGUAL site (Polylang or WPML) pass language ('en', 'tr', 'de'…) to place the page in that language — otherwise it lands in the site's default language (e.g. an English page would get the Turkish menu and enter the Turkish sitemap). The reply's post.language echoes the language the page ended up in, so you can confirm it. On a single-language site language is ignored. PAGE TEMPLATE + META (for page builders like WPBakery): template sets the page's theme template (the _wp_page_template value, e.g. "templates/vc-page-template.php") so a translated page opens with the same layout instead of the default sidebar one. meta writes arbitrary post meta as a dict — this is how WPBakery's own per-page CSS (_wpb_shortcodes_custom_css) or a theme's page options get onto an API-created page, which they otherwise never do. copy_meta_from=<source page id> is the shortcut: it copies the template + WPBakery custom CSS (and any keys you add with copy_meta_keys via meta) from a working page to this new one — the usual fix for translation pages that come out unstyled. Needs Connector 0.9.2 or newer.

wordpress_critical_css

ChatGPT
Check or regenerate LiteSpeed CRITICAL CSS (CCSS) for specific pages of a connected WordPress site. With LiteSpeed "Load CSS Asynchronously" on, a page without critical CSS renders blank until the full stylesheet loads (slow FCP/LCP), and a page whose design changed keeps its OLD critical CSS (layout shift, CLS). Purging the cache does not rebuild critical CSS; this tool does, page by page. action: • status — for each URL: whether critical CSS exists, whether it is queued, whether the live page actually inlines it, plus the site-level reasons LiteSpeed would skip it (CSS async off, QUIC.cloud not linked, UCSS inline on, no QUIC.cloud quota, per-URL CCSS off). Read-only. • regenerate — invalidates the stored critical CSS of those URLs, purges them, re-queues them and, when the queue is short, sends them to QUIC.cloud right away (confirm=true). Up to 10 URLs. urls: full page addresses or paths ("/service/x/"). Needs Connector 0.9.3 or newer.

wordpress_edit_menu

ChatGPT
Edit a WordPress navigation menu: add, rename, relink, reorder or remove navbar and footer links, create a menu, or assign one to a theme location. action is one of: add_item, update_item, delete_item, reorder, create_menu, assign_location, restore. Link to an existing page with page_id, or to any address with url. Use wordpress_menus first. Classic menus: pass menu_id, and item_id for an existing link. parent_id makes the link a dropdown child; order is the list of item ids in the sequence you want. Block themes: pass nav_id instead, and identify a link by its path (for example "2.1") rather than item_id; parent is the path of the link it should sit under; order is a list of paths. A previous version is kept, so action="restore" rolls the navigation back.

wordpress_edit_page_design

ChatGPT
Edit the design of an Elementor page: change headings, paragraphs, button labels, links, colours and other element settings. Use wordpress_page_design first to get element ids and field paths. Then either: edits=[{"id": "<element id>", "path": "title", "value": "New heading"}] to change existing values (paths may be nested, e.g. "link.url" or "icon_list.1.text"), or element="<id>" with settings={"title_color": "#d97757"} to set design settings, creating them if absent. The previous version is kept automatically; restore=true rolls the page back to it.

wordpress_get_post

ChatGPT
Read one WordPress post or page in full, including its raw content, categories, tags and featured image. raw=true additionally spells out invisible characters (non-breaking space, smart quotes) and counts them, which is what to check when a replace fails to match text you copied from here. Read the page with this BEFORE editing it. wordpress_list_posts deliberately leaves content out to stay small, so on a shortcode-based page (Avada/Fusion, WPBakery) this is the only way to see the existing layout — overwriting content you have not read wipes the page design.

wordpress_install_plugin

ChatGPT
Install a plugin on a connected WordPress site from a wordpress.org slug (e.g. 'wordpress-seo') or a zip URL, and optionally activate it. Installing and activating are reported separately: if the files land but activation fails you get the real activation error, not a blanket failure. When a plugin that was NOT already on the site fails to activate, its files are removed again (rolled_back=true) so nothing dead is left behind; pass keep_on_failure=true to keep them for debugging. Page cache plugins in particular need wp-config.php and wp-content to be writable, and some hosts refuse third-party cache plugins outright through a must-use plugin of their own. Both are host limits no retry will fix — check wordpress_performance first, and if it says install_recommended=false, do not install a cache plugin at all.

wordpress_languages

ChatGPT
Lists the languages a multilingual WordPress site (Polylang / WPML) is set up for, and ADDS a new one. action: 'list' (default) | 'add'. Adding the language is the FIRST step of publishing a site in another language, and skipping it fails silently: wordpress_create_post(language='de') answers ok while Polylang quietly ignores an unregistered language, so the page ends up with no language at all and falls back to the default one on its next save. list shows each language's code, locale, writing direction, post count and which one is the default. For 'add', code is the language code you will pass to wordpress_create_post ('de', 'ru', 'ar', 'fr'); locale is optional and derived from the code when omitted; the writing direction is set automatically for right-to-left languages (Arabic, Hebrew, Persian, Urdu) unless you pass rtl yourself. Adding a language is a structural change to a multilingual site, so confirm=false PREVIEWS it — the reply says whether it can actually be added before anything is written. Adding a language twice is safe — the existing one is reported back. WPML has no API for adding languages, so there the reply says to use the WPML screen. After adding a language, create its pages and then connect them with wordpress_link_translations, otherwise the language switcher and the hreflang tags stay empty.

wordpress_list_plugins

ChatGPT
List installed plugins on a connected WordPress site, showing which are active and which have updates available.

wordpress_list_posts

ChatGPT
List posts or pages on a connected WordPress site. Filter by type (post/page/custom), status, or a search term.

wordpress_manage_plugin

ChatGPT
Activate, deactivate, update or delete a plugin that is already installed on a connected WordPress site. Use it to switch off a plugin the site does not need — an unused analytics or tracking plugin keeps loading scripts on every page. Deactivating leaves the plugin and its settings in place, so it can be switched back on; delete removes its files, and an ACTIVE plugin is refused unless you deactivate it first. plugin is the file path from wordpress_list_plugins, for example "statcounter/statcounter.php". If activating fails, the reply now carries the real reason and a writability check: page cache plugins need wp-config.php and wp-content to be writable, and on hosts where they are not, no cache plugin can be enabled at all.

wordpress_media

ChatGPT
Browse the media library of a connected WordPress site: images already uploaded, with their id, URL, alt text and dimensions. Search by name to find an existing image instead of uploading a duplicate. The returned id is what you pass as featured_media to wordpress_create_post / wordpress_update_post. media_id returns ONE image in full: file size in bytes plus every intermediate size WordPress generated (medium, medium_large, large, theme sizes) with its real width, height and source_url, and the srcset WordPress itself would emit. That is what makes a hero/LCP image fixable: without the real list you would have to GUESS the "-768x347.jpg" style filenames, and a wrong guess puts a 404 on a live page. Use the returned srcset rather than building one by hand. return_image=true (with a media_id) actually SHOWS you the picture: the server fetches the image bytes itself and returns a downscaled (max 512px) preview as an image you can see in chat, instead of just a URL. Use it to match an existing image's style before creating new ones, or to confirm a file is the one you meant. If the server cannot fetch the bytes (site behind a WAF, etc.) it returns the URL and the reason instead. check_url asks the SITE to HEAD one of its own URLs and returns status, content type and length — for verifying an image variant exists, or checking a link or redirect. Only URLs on that same site are allowed. Needs Opus Growth Connector 0.6.1 or newer.

wordpress_menus

ChatGPT
List the navigation menus of a WordPress site: the navbar, footer menus, which theme location each one fills, and the links inside them. Call with no arguments first: it returns the classic menus and, on a block theme, the navigation blocks the header actually uses. Then pass menu_id to list a classic menu's links, or nav_id to list a navigation block's links. Either way you get the ids or paths you need before editing.

wordpress_option

ChatGPT
Read, and when asked, write a single WordPress setting by name — including the theme option that holds a theme's own tracking code. Read first with write=false. Themes and plugins keep settings like tracking snippets in these options, which is where a stray tag usually lives when wordpress_tracking reports it under "Seçenek:". Writing returns the previous value so the change can be undone. ARRAY OPTIONS: many plugins store a whole settings tab as one array option. Reading now tells you which case you are in — exists says whether the option is in the database at all, and type says array or string, so "not set yet, plugin default applies" is no longer indistinguishable from "array we could not read". To WRITE an array option pass value_json (a real object/list), not value: writing plain text over an array corrupts it, and the connector now refuses that instead of silently breaking the site. force_type=true overrides the refusal if you really mean it. Needs Connector 0.6.1 or newer. ONE KEY INSIDE AN ARRAY OPTION: pass key=... to change a single setting inside an array option and leave every other key untouched. This is the normal case for plugins that keep a whole settings tab in one option: Yoast's wpseo_titles holds more than 150 keys, so turning author archives into noindex means flipping noindex-author-wpseo alone. Without key you would have to send the entire array back and risk the site-wide title templates. The reply gives previous_value, new_value, how many keys the option still has, and applied read back from the database, so a change that did not stick is not reported as success. A key that does not exist is refused with the closest matching names instead of being created. Needs Connector 0.8.1 or newer. Inside an array option most settings are true/false or numbers, not text: pass them as value_json=false / value_json=3 so the type is kept. value="false" would store the text "false", which PHP reads as ON. PROTECTED SETTINGS: a handful of options can lock the site out or change who may sign in — siteurl, home, users_can_register, default_role and friends. Writing one of those is refused unless you also pass force=true. That is the deliberate override for the real cases (moving a site to a new domain, switching to https): read the current value first, tell the owner exactly what will change, and only then repeat the call with force=true. A wrong siteurl makes the site unreachable from the browser, so never set it blind. NAMES WITH A DOT: plugins that keep every setting under one prefix name them like litespeed.conf.cache-exc. Reading those needs Connector 0.7.7 or newer; older versions answer plugin_outdated for the dotted name only, while plain names on the same site work. If you see that, the site simply needs the connector update. Writing a dotted name has always worked, but never write one blind — read it first.

wordpress_page_design

ChatGPT
Inspect how a WordPress page is actually built (Elementor, WPBakery, Gutenberg, classic) and read its editable design fields. Call this BEFORE editing a page. On an Elementor page the visible design lives in page-builder data, not in the post content, so a plain content update changes nothing. This returns each element's id and the text, links and settings you can change. Pass element (an id from the list) to get that one element's full settings, including colours, typography and spacing.

wordpress_performance

ChatGPT
See a WordPress site's speed settings: which caching plugin runs it, which optimisations are on, and which of them are risky on this particular site. Run this before changing anything. It reports the page cache, minify, lazy load, combine and defer switches, whether the server actually supports the cache plugin, and which fragile page builders are installed — combining or deferring scripts is a known way to break an Elementor or Slider Revolution site. When no cache plugin is active, read install_recommended before suggesting one. It is false when the host manages caching itself through a must-use plugin (Natro, SiteGround, WP Engine, Kinsta and similar: cache_managed_by_host=true), when a previous activation was refused by host policy (plugin_activation_blocked=true), when wp-config.php or wp-content is not writable. In each of those cases installing a cache plugin cannot work: say so instead of trying. stale_cache_config=true is separate and does NOT block anything — it only means an earlier cache plugin was removed and left WP_CACHE behind in wp-config.php; mention the history, do not treat it as an error. WHY ONE PAGE IS NOT CACHED (Connector 0.7.7+, LiteSpeed): cache_exclusions lists what is kept out of the page cache — do_not_cache_uri, query_string, cookie, user_agent, role, category and tag, plus private_uri and the forced-cache lists. When page caching is on but a single page still misses every time, the answer is almost always an entry here. If the front page itself is excluded, cache_exclusion_warning says so outright. Read this before blaming TTL. cache_runtime adds crawler_enabled (the cache warmer) and public_ttl: on a site with many rarely-visited pages a cold cache, not a short TTL, is what visitors hit. These lists are edited with wordpress_option on the matching litespeed.conf.* name — read the current value first, then write the full list back.

wordpress_purge_cache

ChatGPT
Purge a connected WordPress site's page cache — the "Purge All" that a widget, option, SEO or theme-file change needs to actually go live. wordpress_set_performance already purges LiteSpeed when it flips a speed setting, but editing a widget, an option, an SEO field or a theme file does NOT trigger a purge, so a cached page keeps serving the old version (a LiteSpeed page can stay cached for days). Call this after such a change. It clears whichever page-cache plugin the site runs — LiteSpeed, WP Rocket, W3 Total Cache, WP Super Cache, Cache Enabler, WP Fastest Cache — plus the object cache, and reports which ones it hit. elementor_regenerate=true ALSO rebuilds Elementor's compiled CSS ("Regenerate CSS & Data"). Use it when a site moved from http to https and Elementor's cached CSS still has http:// font/asset URLs baked in (the usual cause of mixed-content warnings): regenerating rewrites them to https. Needs Connector 0.9.2 or newer.

wordpress_search_content

ChatGPT
Finds the WordPress posts/pages that contain an EXACT string (or regex) in their content or title — the precise search wordpress_list_posts(search=...) cannot do, because WordPress' own search is fuzzy and word-tokenised (it matches a shortcode like [vc_column_text] on every WPBakery page, and misses bracketed or punctuated tokens). Use it to locate a stale phone number, an outdated claim, or a broken shortcode across a whole site before fixing it, then feed the returned ids to wordpress_bulk_replace. search_in = "content" (default) | "title" | "both". regex=true treats query as a Python regular expression; otherwise it is a literal, case-sensitive substring. type filters post types ("page", "post" or "page,post"; empty scans both). status defaults to "publish". It scans up to max_scan posts (hard cap 400), newest-first, and tells you if the site has more than that. Every post is fetched one by one (~1 s each on a typical host), so one call stops after about 110 seconds and returns what it found so far with partial=true and next_offset; call again with offset=next_offset (same other arguments) to continue where it stopped. Each hit returns id, slug, title, type, match_count and a short snippet around the first match; the hit list is capped so the result stays small. Read-only.

wordpress_seo

ChatGPT
Read the SEO settings of a connected WordPress site: which SEO plugin runs it, the search engine verification codes in place, and, for one page, its meta title, meta description, canonical and focus keyword. Call with no id for the site level. Pass a page or post id to read that page's SEO fields, which is what seo_audit reports on when it says a meta description is missing.

wordpress_set_performance

ChatGPT
Turn a WordPress site's speed optimisations on or off, and undo them in one step if the site breaks. Start with the safe ones: page_cache, browser_cache, minify_css, minify_js, lazy_load. These rarely break a site and give most of the speed. combine_css, combine_js, defer_js, css_async and guest_mode are the ones that break Elementor and Slider Revolution pages, so they are refused unless you pass confirm_risky=true. Turn them on one at a time and look at the site afterwards. If something breaks, revert=true undoes the MOST RECENT change only, so change a little at a time. exclude=["revslider","elementor"] keeps named scripts out of the deferring or combining, which is the usual fix when a slider stops rendering. The exclusion list is editable both ways. exclude_from picks the list — "defer", "combine" or "lazy" — and exclude_mode says what to do with the names you pass: "add" (default), "remove" to take entries back out, or "replace" to make the list exactly what you passed (pass an empty list with replace to clear it). Every call returns the FULL list so you can see what is in it, and wordpress_get_performance also reports all three lists. This matters when you are tuning: excluding a hero background image fixes layout shift but can undo the loading-speed win, so you need to take single entries back out and measure again. clean_dropin=true removes a leftover wp-content/advanced-cache.php whose owning plugin is no longer active. That file is the usual reason every cache plugin fails to activate with a server error: a new one refuses to overwrite a drop-in belonging to a different plugin. It is backed up first, and refused outright if the owning plugin is still active. LITESPEED IMAGE AND LAYOUT-SHIFT SETTINGS are not switches here; set them with wordpress_option on the litespeed.conf.* name. LiteSpeed stores them as text, "1" for on and "" for off, so read first and write value="1" (not value_json=true): litespeed.conf.media-add_missing_sizes adds width/height to images that lack them, the usual fix for a high CLS with "images without explicit width and height". It only works while lazy_load is on. litespeed.conf.media-placeholder_resp reserves space for lazy images and litespeed.conf.media-lqip shows a blurred preview in that space. litespeed.conf.img_optm-auto makes LiteSpeed send unoptimised media library images to QUIC.cloud for compression on its own cron; progress is in litespeed.img_optm._summary (img_taken, reduced in bytes, last_pulled). Call wordpress_purge_cache afterwards, since a direct option write does not purge.

wordpress_set_seo

ChatGPT
Set SEO fields on a connected WordPress site: the meta description, meta title, canonical or focus keyword of a PAGE or of a CATEGORY/TAG archive, or the site's Google and Bing verification codes. Pass id for a post or page. Pass term_id (with taxonomy, default 'category') for a category or tag ARCHIVE — those archives rank for their own queries and ship with an empty title and no description unless someone sets them, and the SEO plugin keeps term fields somewhere completely different from page fields, so id and term_id are not interchangeable. Use wordpress_terms to find term ids and to write the archive's own body text, which is the term description rather than an SEO field. Pass id with description to fix a page whose meta description seo_audit flagged as missing. Pass google_verification with the code from Search Console to verify the site, with no id: you can paste the whole meta tag, the code is extracted from it. Verification is written into the site's SEO plugin when it has one, otherwise the connector outputs the tag itself. An empty string clears a page field; run wordpress_seo first to see the current values. A verification code is never replaced silently: if one is already set you get the existing value back and nothing is written, so you can check with the user before passing overwrite=true. Replacing a code that belongs to another Search Console property drops that property's verification.

wordpress_set_tracking

ChatGPT
Set a WordPress site's Google Tag Manager or GA4 measurement id, add code to the site header, or switch off an old tracking snippet. gtm_id goes into the GTM4WP plugin when that plugin is active, otherwise the connector emits the tag itself, so this works with or without a tag plugin. remove_id takes a measurement id like "GTM-5H2L5V63" or "AW-851563016" and removes it wherever it lives — a plugin setting, a header/footer box, or a WPCode snippet (that one is switched off, not deleted). This is how you stop a site double-counting after a container change. If the id is hard-coded in a theme PHP file it is reported back under skipped with the file and line; that copy can now be removed with wordpress_theme_file(mode="remove_block", contains="<id>"), which backs the file up and reverts automatically if the edit breaks PHP. head_code="" clears the header code the connector emits. To change a few words inside the connector's existing header/footer code (an address in a JSON-LD block, a phone number) without resending the whole block, pass replace=[{"find":"old text","with":"new text"}] and replace_in="head" (default) or "body". The current code is read from the site, each find must match exactly (case-sensitive), and confirm=false PREVIEWS how many times each one matches and writes NOTHING; confirm=true applies and re-reads the code to verify. A find whose replacement is already in place is reported as already_applied and skipped, so repeating the call is safe.

wordpress_site_info

ChatGPT
Overview of a connected WordPress site: name, active theme, plugin count, WordPress/PHP versions, WooCommerce status.

wordpress_test_contact_form

ChatGPT
Submits a TEST entry to a Contact Form 7 form through the plugin's own REST endpoint and reports what happened: mail_sent (the form works and WordPress sent the notification mail), mail_failed (the entry passed validation but WordPress could NOT send the mail, usually an SMTP/mail setup problem; this is also why a 'wpcf7mailsent' conversion never fires), validation_failed (with the fields that were rejected), or spam (a spam guard such as reCAPTCHA/Akismet blocked the test; real visitors may still get through). fields = {"field-name": "value"}; required fields you leave out are filled with obvious test values (e.g. test@example.com). A real submission sends a REAL e-mail to the form's recipient, so confirm=false (default) only previews the values that would be sent; confirm=true submits once.

wordpress_tracking

ChatGPT
See every place a WordPress site fires a Google Tag Manager or GA4 tag: the GTM4WP plugin, WPCode snippets, header/footer code, and the connector's own output. Run this before changing a measurement tag. It lists each source with the ids it emits and warns when two different GTM containers are live at once, which double-counts every visit.

wordpress_update_connector

ChatGPT
Updates the Opus Growth Connector plugin on a connected WordPress site to the latest version, or with check_only=true only reports the installed and latest versions without changing anything. site_url selects the site when more than one is connected.

wordpress_update_post

ChatGPT
Update an existing WordPress post or page by its ID. Only the fields you pass are changed; edits keep WordPress revisions. THE RESPONSE DOES NOT CARRY THE PAGE BACK. A write answers with ok, the replaced counts and any charset_warning — the three things that tell you whether the edit landed. The stored content is left out and marked content_omitted: true with content_chars, because a WPBakery home page is 45 KB and returning it made the tool result itself too large to read: the write had succeeded, but the caller saw a size error and could not tell (issue #293). Pass return_content=true if you really need the stored content back and the page is small. COMMENTS / PINGBACKS: comment_status and ping_status take 'open' or 'closed' and act on THIS post only. WordPress' own Settings > Discussion screen changes the default for NEW posts and leaves existing ones untouched, so closing comments on a published page is only possible here. Needs Opus Growth Connector 0.6.0 or newer on the site. On a page built with Elementor the content field is not what visitors see, so it is skipped and reported back to you: use wordpress_page_design and wordpress_edit_page_design for those pages. featured_media sets the featured image by media id (get one from wordpress_media); 0 removes it. categories and tags take names and are created if missing, which is how you move a published post out of "Uncategorized". author takes a user id, username or display name and changes the byline; use wordpress_users to find the right one. template, meta and copy_meta_from work as in wordpress_create_post — set the page's theme template, write WPBakery/theme post meta, or copy a working page's layout meta (template + WPBakery CSS) onto this one (Connector 0.9.2+). On a page built from shortcodes (Avada/Fusion, WPBakery) do NOT rewrite content: pass replace=[{"find": "old text", "with": "new text"}] instead. That changes only the text you name and leaves the shortcode layout untouched. If nothing matches, nothing is written and you are told so. Use wordpress_get_post first to see the exact text to match. Text copied out of a page often will NOT match literally, because editors leave invisible characters in it — non-breaking spaces, smart quotes, long dashes — that look like ordinary ones. When that happens the reply says so and tells you how many times it WOULD match; send the same call again with match="normalized" and those characters are treated as their plain equivalents. match is also accepted per entry ({"find": ..., "with": ..., "match": "normalized"}) when only one of several replacements needs it; the top-level value applies to every entry that does not carry its own. wordpress_get_post(raw=true) shows the invisible characters spelled out if you want to see them.

wordpress_upload_media

ChatGPT
Upload an image into a WordPress site's media library from a public URL, optionally setting it as a post's featured image in the same call. If the user has the picture on their own computer, call upload_image first to turn it into a public URL, then pass that URL here. Set post_id together with featured=true to make it the featured image of that post or page, which is what Elementor loop grids and most themes display on cards. Always give alt text: it is what screen readers and search engines read.

wordpress_users

ChatGPT
List the users of a connected WordPress site, or change the name a user is shown under on their posts. Call with no arguments to list users with their id, login and display name. Pass id together with display_name to fix a byline that shows the wrong name. Roles, passwords and email addresses are not touched.

wordpress_woo_create_product

ChatGPT
Create a WooCommerce simple product on a connected WordPress site. Defaults to a draft. Requires WooCommerce to be active.

wordpress_woo_orders

ChatGPT
List recent WooCommerce orders on a connected WordPress site (status, total, currency, customer, date). Requires WooCommerce to be active.

wordpress_woo_products

ChatGPT
List WooCommerce products on a connected WordPress site (name, SKU, price, stock, status). Requires WooCommerce to be active.

youtube_channels

ChatGPT
Lists the channels of the connected YouTube account (title, subscriber count, total views, video count).

youtube_overview

ChatGPT
YouTube channel OVERVIEW (last N days): views, watch time (min), avg. view duration (sec), avg. view percentage, subscribers gained/lost, likes, comments, shares. channel = a value from youtube_channels (leave empty if there's only one channel).

youtube_top_videos

ChatGPT
MOST-WATCHED videos (last N days): title + views + watch time (min) + avg. view duration (sec). channel = from youtube_channels (leave empty if there is only one channel).

youtube_traffic_sources

ChatGPT
How viewers FIND the videos (last N days): views + watch time by traffic source type — YouTube search, suggested, external sites, playlists, subscriptions, Shorts, etc. channel = from youtube_channels (leave empty if there's only one channel).

Capabilities

Writes

Example Prompts

Click any prompt to copy it.

Similar Apps