Generate a full campaign draft (keywords + ad groups + ad copy)
generate_campaign_draftRun the end-to-end Upfixe campaign generator: keyword research from the landing page and user-provided seeds, budget/CPA filter (drops keywords whose top-of-page CPC > targetCpa × 0.5), automatic ad-group splitting by theme/volume/ROI, and AI-generated ad copy (up to 15 headlines + 4 descriptions per ad group). Persists the result as a row in campaign_drafts and returns the draftId. NOTHING is pushed to Google Ads — that's a separate, explicit step (publish_campaign_to_google_ads).
INTENDED SEQUENCE:
User provides campaign brief (landing page, country, language, budget, target CPA, any positive/negative seed keywords).
Call propose_campaign_goal — show the proposed goal, get user approval.
Call generate_campaign_draft (this tool) with the approved goal as
campaignGoal.Show the returned draft to the user — keywords per ad group, sample headlines, total campaign size — and ask if they want changes.
If they're happy and EXPLICITLY say they want to publish, call publish_campaign_to_google_ads({ draftId, confirm: true }).
Takes 1-2 minutes typically — keyword research and ad copy both involve external API calls. If it fails, the error message will say why (most often: Google Ads OAuth expired, landing page unreachable, or the targetCpa is so low that all keywords get filtered out).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Campaign name as it should appear in Google Ads. Keep under 80 chars. | |
| isCold | No | true = cold audience (brand name added to negatives so we don't bid on people already searching for the brand). false / omitted = branded campaign that may bid on the brand name. | |
| brandId | Yes | The brand the campaign is for. Must have a value-prop doc. | |
| country | Yes | Target geography — ANY granularity Google Ads accepts. The field name is 'country' for legacy reasons but you can pass a city, state, region, country, or postal code, and Google's geoTargetConstants:suggest API resolves it. Always disambiguate non-unique city names with the country: 'London, United Kingdom' not just 'London' (London, Ontario exists); 'Paris, France' not 'Paris' (Paris, Texas exists); 'Florida, USA' not just 'Florida'. For LOCAL-SERVICE brands (plumbers, dentists, restaurants, trades) ALWAYS push the user for city-level — running a London-only company at country-wide UK targeting wastes 95%+ of impressions on people who can't buy. | |
| language | Yes | ISO-639-1 language code, e.g. 'en', 'es', 'fr'. | |
| targetCpa | Yes | Target cost-per-acquisition in the account's billing currency. The keyword filter drops keywords whose top-of-page CPC exceeds targetCpa × 0.5, so set this honestly — too low and all keywords get filtered out, too high and the campaign overspends per conversion. | |
| dailyBudget | Yes | Daily budget in the account's billing currency. | |
| campaignGoal | No | Free-text goal summary the user approved (from propose_campaign_goal, possibly with their edits). If omitted, the generator builds one from the landing page automatically. If you've already called propose_campaign_goal and the user said yes, ALWAYS pass that goal here so the user's approval flows through. | |
| landingPageUrl | Yes | The specific page ads will drive traffic to. | |
| negativeKeywords | No | Negative keywords — terms that should NOT trigger the ad. | |
| positiveKeywords | No | 3-5 specific seed keywords the user explicitly wants targeted (max 5). NEVER pass broad or generic terms (e.g. 'small business', 'make a business') or a raw dump from Keyword Planner. The generator expands from these 3-5 seeds. |