API & MCP
Adwave MCP server
Connect Cursor, Claude Code, or another compatible agent to Adwave for signup, wallet funding, campaigns, and performance reporting.
-
01Your integration
Use a script, service, or AI assistant.
-
02Authenticate
Use a workspace API key or supported OAuth connection.
-
03Adwave
Work with that workspace’s businesses, campaigns, and wallet.
Launching, creating paid ads, and changing budgets can spend wallet funds. Check costs before approving those actions.
On this page
What connectors should know
Transport: Streamable HTTP, POST https://waverunner.adwave.com/api/mcp. Clients must send Accept: application/json, text/event-stream. Discovery documents: /.well-known/oauth-authorization-server and /.well-known/oauth-protected-resource on the same app origin.
-
Without a key:
signup_startandsignup_pollonly. Email one-time code. The poll returns an API key once. - With a key or Cursor OAuth: businesses, campaigns, wallet checkout, ad review, tracking, Catalog Video, and Scale agency tools. Same org scope, validation, and rate limits as the REST API.
-
Cursor plugin: the Adwave Cursor plugin teaches signup_start, named channels (
platforms/inventorySurfaces), wallet checkout, and tracker install. Cursor can also connect with OAuth (clientcursor-mcp). -
Money: integer cents on a prepaid wallet. New business accounts start with $60 starter credit. Launch charges the first media day when ads are ready;
awaitingCreatives: truemeans that charge is still deferred. A402includesnextTool: "create_wallet_checkout". Never send card numbers through MCP.
Quick start
-
Sign up from the agent with
signup_start/signup_poll(email one-time code). The poll returns an API key once. Cursor can also connect with OAuth (static public clientcursor-mcp, PKCE S256). - Or create an API key in the app: Settings → API keys. The key is shown once. Keys are scoped to the organization that was active when you created them; switch workspaces in the sidebar first if you need a key for another organization. There is no required key prefix.
-
Add the server to your client: point it at
https://waverunner.adwave.com/api/mcpwith the key as a bearer token (configs below), or complete Cursor OAuth. -
Ask your agent something: try "List my Adwave campaigns and their spend". The agent discovers tools from
tools/listand the server instructions.
How to connect each client
Cursor: add this to your project's .cursor/mcp.json or your global ~/.cursor/mcp.json. Manage the connection from Cursor's Customize page. See Cursor's MCP configuration reference.
{
"mcpServers": {
"adwave": {
"url": "https://waverunner.adwave.com/api/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}
Claude Code: add an HTTP server with a bearer header, as described in the Claude Code MCP reference:
claude mcp add --transport http adwave https://waverunner.adwave.com/api/mcp \
--header "Authorization: Bearer YOUR_API_KEY"
xAI API: add an MCP tool to your Responses API request using the app endpoint and an Authorization header. This is a tool configuration object, not a complete API request. See xAI's Remote MCP reference for the surrounding request:
{
"type": "mcp",
"server_label": "adwave",
"server_url": "https://waverunner.adwave.com/api/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
Local desktop configuration: if your client accepts local stdio servers, mcp-remote can bridge to Adwave over HTTP. The example below uses Claude Desktop's claude_desktop_config.json format and requires Node.js with npx. Other clients may use different configuration files.
{
"mcpServers": {
"adwave": {
"command": "npx",
"args": [
"-y", "mcp-remote", "https://waverunner.adwave.com/api/mcp",
"--transport", "http-only",
"--header", "Authorization:${ADWAVE_AUTH_HEADER}"
],
"env": { "ADWAVE_AUTH_HEADER": "Bearer YOUR_API_KEY" }
}
}
}
Custom agents: this example uses version 1 of the official @modelcontextprotocol/sdk TypeScript package. Use the equivalent HTTP transport and bearer header in other SDKs:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const client = new Client({ name: "my-agent", version: "1.0.0" });
await client.connect(
new StreamableHTTPClientTransport(new URL("https://waverunner.adwave.com/api/mcp"), {
requestInit: {
headers: { Authorization: `Bearer ${process.env.ADWAVE_API_KEY}` },
},
}),
);
const { tools } = await client.listTools();
const report = await client.callTool({
name: "get_campaign_report",
arguments: { campaignId: "018f..." },
});
await client.close();
The server is stateless: no sessions to create or clean up, and a dropped connection can reconnect. Treat the API key like a password. Anyone holding it can spend from your wallet.
This is a stateless POST endpoint. GET and DELETE return 405; there is no persistent SSE stream or session to manage.
Cursor OAuth
Cursor can connect without pasting a key. Adwave advertises a static public client cursor-mcp (PKCE S256, token endpoint auth method none). The connector reads /.well-known/oauth-authorization-server and /.well-known/oauth-protected-resource, then sends the human through /oauth/authorize. That page shows who is asking, which workspace it will work in, and what it can do; the code is issued only when the person approves, and the client exchanges it at /oauth/token. Other clients should use a bearer API key from signup or Settings.
Ad review webhook
Manual-approval orgs can poll list_ad_reviews or register a URL with set_ad_review_webhook. HTTPS is required except on localhost. The tool returns secret once; store it. get_ad_review_webhook never includes the secret. Each set call rotates it. clear_ad_review_webhook stops delivery.
We POST { "type": "ad_review.pending", "campaignId", "campaignName", "pending" } with x-webhook-id, x-webhook-timestamp, and x-webhook-signature (v1,<hex>). Verify HMAC-SHA256 of id.timestamp.rawBody (periods between the three values, exact JSON bytes) and reject timestamps more than 300 seconds off. A down endpoint does not undo the review email; poll list_ad_reviews if a delivery is missed. Same contract as API ad reviews.
Agency tools
An agency API key (Scale) can list, create, read, and update child clients, disable or restore a client, pull a child's campaign report, and download a cost CSV of media spend at cost. Prefer update_agency_client with operatingMode managed or self_serve (same setting as the console). Client-scoped keys stay inside that client. There is no leads inbox tool. Fund the wallet with create_wallet_checkout (hosted Stripe URL; never send card numbers). offboard_agency_client pauses live campaigns and that client's keys; it does not delete history. restore_agency_client does not resume campaigns.
Signup tools (no API key)
Unauthenticated tools/list returns only these. Open verificationUri (/mcp-signup/verify?user_code=…). The human confirms the displayed code matches userCode, then enters the email one-time code. After signup_poll returns apiKey, reconnect with that bearer token to see the authenticated catalog.
WRITE signup_start
Create an Adwave account from an agent. Sends an email one-time code and returns deviceCode (keep secret), userCode to show the human, verificationUri to open (/mcp-signup/verify?user_code=...), and poll interval. Never returns an API key. Next: the human confirms userCode on that page, enters the email code, then you call signup_poll.
WRITE signup_poll
Wait until the human finishes the email one-time code. Pass deviceCode from signup_start. status=pending: keep polling at interval seconds. status=consumed: apiKey is returned once (store it; reconnect with Authorization: Bearer). status=expired with reason device_expired or otp_expired: start over with signup_start. Includes starterCreditCents when the account is new.
Authenticated tools
Same org scope as REST API v1. Read tools send readOnlyHint; money and disable tools send destructiveHint where it applies. Grouped by resource:
- Businesses
- Creatives
- Campaigns
- Audiences
- Tracking
- Wallet
- Ad reviews
- Catalog Video
- Organization
- Agency
Businesses
READ list_businesses
List your businesses with their analysis status. Poll after add_business until ready.
WRITE add_business
Add a business by website URL (analysis) or by name, category, description, and offerings (manual, ready immediately). Agency parent keys cannot add a business (use a client workspace).
WRITE archive_business
Archive a business that has no live or launching campaigns. Hidden from list_businesses; restore any time with unarchive_business.
WRITE unarchive_business
Restore an archived business to active use.
READ get_business
Business detail: the extracted profile, restrictedContent (locks and attestation), googleListing (read-only Google listing status, matched locations, hours, photo/review presence, and Maps asset status; no tokens or email), and every buyer profile on the business profile with its id and Profile reach (targetingSeeds). Campaigns copy Reach after interest targeting is planned; buyer profile seeds stay as written on the Creative profile.
WRITE update_business_restricted_content
Confirm system locks, attest restricted categories (or none), and set political disclosure fields. Cannot clear system locks; refused for restrictive upgrades while campaigns are launching, live, or paused.
WRITE create_persona
Create a buyer profile for a business from free-text notes (enqueues enrichment; Adwave builds the full buyer profile). Free.
WRITE update_persona
Partial update of a buyer profile: name, description, demographics, motivations, pain points, selected flag, and/or demographic pins and exclusions (catalog segment keys).
READ get_business_ltv
Customer lifetime value for a business: average/quartile LTV and acquisition-month payback cohorts.
Creatives
READ list_creatives
List generated creatives for the business (newest first) with static variants, video ads, render status, and asset URLs. Paginated: default 20, max 100 per page; pass the returned nextCursor to fetch the next page.
READ list_ad_revisions
Media revision history for one static, video, or audio creative (prior deliverable URLs). Distinct from campaign serving version lineage.
WRITE restore_ad_revision
Restore a prior media revision to current (free). Live/paused campaign ads require a rationale; replaces DSP creatives when externalized.
WRITE generate_creatives
Charge and enqueue a library ad generation batch for a ready business (paid from the wallet; starter credit applies). Quote first with get_creation_quote.
READ get_creation_quote
Pre-flight price for an ad generation batch. Pass campaignId for statics × interest lines; omit for library Launch-pack pricing.
Campaigns
READ list_campaigns
List campaigns with objective, status, and daily budget.
WRITE create_campaign
Create a draft campaign with one of seven outcomes (awareness, traffic, leads, calls, visits/foot traffic, bookings, or sales), optionally with targeted states, named platforms (google_ads, meta, reddit, google_search; a present platforms array is authoritative and does not re-add open inventory), inventorySurfaces (mobile-web, mobile-apps, tv), playbook (follow-journey | open-mix), followSpend (omitted stamps on; pass false to opt out), prospectSpend (omitted stamps on), winback (omitted stamps on), metaStoryBeats (omitted stamps off; only true is on; not a spend path), driveTo (tactic + confirmed locationIds; omit on visits to use every confirmed store), a landing URL, a geo-holdout for lift measurement, and audience boost. After interest targeting is planned, Autopilot builds a recommended creative plan (reuse ready creatives, generate only gaps across targeted buyer profiles; generationDeferred). Pass campaignAds: false for reuse-only. Metered: quote with campaignId once lines exist. Channels use Preparing → Starting → Live after launch.
READ get_campaign
Full setup detail for one campaign: status, budget, target buying CPM (cpmTargetCents), optional limits (cpmBand), current edit revision (cpmRevision) and delivery confirmation (cpmSync), flight dates, landing URL, targeted states, platforms, inventory surfaces, autopilotMode (auto | manual; manual = Guided), followSpend, prospectSpend, winback, metaStoryBeats (false when absent), playbook, audioEnabled, mail (Neighborhood mail enabled + pieceCap), measureLift, audience boost, geo-holdout states, buyer profiles, budgetMix (Your split vs Autopilot), driveTo (tactic, locationIds, snapshot), and the offer/instructions brief.
WRITE update_campaign_budget
Change a campaign's daily budget. No proration; the next daily charge uses the new value.
WRITE update_campaign_buying_cpm
Set or clear target buying CPM and optional limits in USD cents. Include expectedCpmRevision from get_campaign (zero before the first edit). Estimates include audience fees and exclude platform and agency markup. Saved edits remain pending until delivery confirms them. Auto can later adjust inside the limits; these settings do not promise a fixed average CPM or delivery.
WRITE update_campaign_budget_mix
Set Your split (channel and buyer profile shares of daily media) or return the mix to Autopilot. Returning to Autopilot requires a short reason. Shares must add up to 100%.
WRITE rename_campaign
Rename a campaign in any status except mid-launch. Display-only; never affects serving or billing.
WRITE update_campaign_end_date
Set or clear the flight end date (YYYY-MM-DD or null). Live or paused pushes the new schedule to every channel; a draft updates the setup value. 422 invalid_end_date when the date is before today on a live flight, not after the start date, or more than 365 days out; nothing is written when a field is invalid.
WRITE update_campaign_landing_url
Change the landing page on a live or paused campaign; refreshes serving ads to the new URL.
WRITE update_campaign_autopilot
Switch a campaign between Autopilot (auto) and Guided (manual). Returning to Autopilot requires a short reason.
WRITE update_campaign_setup
Edit draft campaign setup: US states (empty = national), named platforms (a present array is authoritative; google_ads, meta, reddit, google_search), live inventory tiles (inventorySurfaces: mobile-web, mobile-apps, tv), targeted buyer profiles (icpIds from get_business), spend legs (followSpend, prospectSpend, winback), Meta story (metaStoryBeats; not a spend path; sole-field PATCH is valid), playbook (follow-journey | open-mix), audioEnabled, Neighborhood mail (mail: enabled + pieceCap 200..5000; draft/error only), geo-holdout (measureLift), driveTo (foot-traffic stores; live updates strategy only and does not push geo), and/or audience boost. Platforms, inventorySurfaces, spend-leg flags, metaStoryBeats, audioEnabled, and driveTo also apply live/paused (Audio is 422 audio_unavailable until that inventory is live). icpIds, playbook, and mail are refused once the campaign has launched. icpIds takes at most 12 buyer profiles (422 persona_cap); an id that is not a selected buyer profile is 422 invalid_icp_ids, never silently dropped. A Follow-only social mix with no pool source (Mobile web, Mobile apps, Google Search, or an imported customer list) is 422 follow_needs_pool. Switch Autopilot vs Guided with update_campaign_autopilot (campaign-wide). Send at least one field.
WRITE launch_campaign
Launch a draft. Charges day one when ads are ready, or commits with awaitingCreatives while ads are still rendering. 402 includes nextTool create_wallet_checkout. 409 license_inactive when the agency license is not active.
READ launch_campaign_dry_run
What launching would do, without doing it: canLaunch plus pass/fail for every pre-check, every launch requirement, and day-one wallet coverage. Reads only; charges nothing.
WRITE cancel_campaign_launch
Cancel a launch that is still waiting on creatives (no charge yet). Returns the campaign to draft.
WRITE retry_campaign_launch
Retry a failed campaign: resets it to draft and re-runs the launch checks (may defer charge while ads render).
WRITE pause_campaign
Pause a live campaign. Future daily charges stop; today's charge stands.
WRITE resume_campaign
Resume a paused campaign. Re-charges today idempotently before serving restarts.
WRITE end_campaign
Permanently end a campaign. Stops all serving and charges; cannot be undone.
WRITE bulk_campaign_lifecycle
Pause, resume, or end up to 100 campaigns of one business in one call. Same rules per campaign as the single tools; every id must belong to that business or nothing is requested. Returns per-campaign results and a batchId.
WRITE archive_campaign
Archive a campaign that is not launching or live. Hidden from list_campaigns; restore any time with unarchive_campaign.
WRITE unarchive_campaign
Restore an archived campaign to your lists.
WRITE delete_campaign
Permanently delete a never-run draft campaign. Generated ads stay in the Ad Library; campaigns that have run can only be archived.
READ get_campaign_report
Full performance rollup: spend, conversions, revenue, CPA, ROAS, sessions (ad visits from this campaign's ads), per-platform breakdowns with channel-specific fields (clickable vs TV views, assisted conversions as reporting-only union plus path breakdown, identity coverage, seed impact), and CPM breakout (all-in, audience list-rate fees, media remainder).
READ get_campaign_analytics
Daily funnel timeseries (impressions → clicks → ad visits/sessions → conversions), device/region mix (observed phones/computers/TVs, not sold channel cards), where ads appeared as publishers (TV channels, web/apps, Google networks, Meta platforms, Reddit communities), per-buyer-profile and per-segment performance, per-day spend story. Channel cards stay Mobile web / TV / Google / Meta; deviceMix is a breakdown.
READ get_campaign_lift
Lift scorecard: geo-holdout causal lift, cross-channel overlap, and Autopilot effectiveness (acted vs observe-only), each with a 95% CI and significance flag. Includes holdoutInconclusive when the holdout read is still directional.
READ list_campaign_landing_pages
Landing pages on a campaign: slug, publish status, and content kind (Waveform pending/source, or a historical block page). Does not return HTML.
READ get_landing_page
One landing page and its variants, including the latest Waveform attempt state. Does not return HTML or a Waveform generation id.
WRITE retry_landing_page
Enqueue a new Waveform generation for one variant. The server mints the attempt; do not send a Waveform generationId.
READ get_campaign_customers
Customers we introduced (first-touch-new identities with no prior graph presence before ad exposure) plus new vs returning conversions and cost per new / introduced customer.
READ get_campaign_ads
Every ad in the campaign Ads set (not the business creative library) with status, buyer profile, and lifetime performance.
WRITE set_ad_serving
Request pause or resume for one campaign ad across every platform. If pending: true, Google Search is still confirming the update; serving describes the requested state. On live or paused campaigns, pass reasonCode (Autopilot override). Use resume_optimizer_pause for Autopilot pauses and resume_user_pause for manual pauses. Refuses to pause the last serving ad on this interest segment line.
Audiences
WRITE set_audience_boost
Turn audience boost on or off (draft, live, or paused). Turning it on requires an imported customer list for that business.
READ list_audiences
Website audiences (observe-first): first-party visitor segments with member counts, platform audiences (campaign reach, synced lists, lookalikes), customer sources for Audience boost, plus Customize targeting guides (interest and demographic catalog picks Autopilot always includes on the next launch). Profile reach (targetingSeeds) is on each buyer profile via get_business.
WRITE create_segment
Create a custom first-party segment for a business with a rule definition, evaluated immediately.
WRITE set_segment_active
Activate or pause a segment. Paused segments stop evaluating membership and syncing.
WRITE delete_segment
Delete a custom (non-system, non-discovered) segment.
WRITE refresh_segment
Enqueue a re-evaluation of one segment's membership.
WRITE customize_audience
Add or remove an interest or demographic catalog pick that Autopilot always includes on the business's next launch.
WRITE resolve_audience_suggestion
Accept or dismiss a discovered-audience suggestion. Accept creates a segment with the suggestion's exact proposed rule.
WRITE update_audience_sync_consent
Turn organization-wide audience sync on or off (on by default). Off stops customer-list uploads and Meta/TikTok/Reddit exposure sharing.
WRITE import_customer_csv
Import a customer CSV for a business to seed Audience boost. Hashed at rest; raw emails are never returned. Rate limited.
Tracking
READ get_tracking_setup
A business's site tag snippet, server-side conversion webhook (POST JSON to conversionWebhook.url; the token is in the path, no bearer), conversion rules, and settings. Wire carts, CRMs, and your own systems into attribution.
WRITE create_conversion_rule
Create a conversion rule for a business (pageview, post_action, checkout_return, or call_click). Intent-page-looking patterns require acknowledgeIntentPage: true.
WRITE update_conversion_rule
Set or clear a conversion rule's per-conversion value estimate. Applies to new conversions only.
WRITE delete_conversion_rule
Delete a conversion rule.
WRITE update_tracking_settings
Update a business's tracking-tag settings (autocapture, lead detection, origin enforcement, view-through window). Disabling recommended settings requires acknowledgeImpact: true.
WRITE upsert_form_conversion_value
Set or clear the per-conversion value estimate for a named Form conversion (lead, signup, or subscribe). Creates a value-only carrier when needed; clearing with null removes the estimate. Applies to new conversions only.
WRITE resolve_conversion_suggestion
Accept or dismiss an AI-discovered conversion suggestion. Accept creates or links a conversion rule; dismiss remembers the verdict forever.
WRITE send_test_conversion
Record a test conversion event for a business to verify the tag/webhook pipeline end to end.
Wallet
READ get_wallet
Prepaid wallet balance and auto-refill state. Check before launching. Fund with create_wallet_checkout (hosted Stripe URL; never send card numbers).
READ get_wallet_ledger
Paginated wallet ledger (newest first): deposits, daily campaign charges, refunds, adjustments, and ad-creation charges. Default 25, max 100 per page.
WRITE create_wallet_checkout
Create a Stripe Checkout session to fund the prepaid wallet. Returns a hosted url plus requestedCents (what the wallet is credited) and grossCents (what the card is charged, processing fee included); never send card data through MCP. Amount is integer cents, same floor and ceiling as wallet top-up in the app. Launch 402 includes nextTool create_wallet_checkout.
WRITE create_billing_portal_session
Create a Stripe Customer Portal session so the human can manage saved payment methods. Returns a hosted url. 422 portal_not_configured if the billing portal is not enabled yet; use create_wallet_checkout instead.
Ad reviews
READ list_ad_reviews
List ads waiting on manual approval for this organization (kind, entityId, campaign). Empty when the org auto-approves. Approve with approve_ad before those ads can serve. Optional push: set_ad_review_webhook, or poll this tool.
WRITE approve_ad
Approve one pending_review ad so it can serve. Pass kind (static, video, or audio) and entityId from list_ad_reviews. Does not launch a campaign; it only clears the approval hold on that ad.
READ get_ad_review_webhook
Show whether this org has an ad-review webhook. Returns { configured, url }. Never returns the secret; set_ad_review_webhook mints a new one.
WRITE set_ad_review_webhook
Register an HTTPS URL (http on localhost only) to receive signed POSTs when ads need manual approval. Returns { url, secret } once. Store the secret; GET never shows it. Each call rotates the secret.
WRITE clear_ad_review_webhook
Stop sending ad-review webhooks. Clears the URL and secret.
Catalog Video
READ list_catalogs
List Catalog Video product catalogs (requires Catalog Video).
WRITE create_catalog
Create a Catalog Video product catalog from a shop URL, feed URL, or csvText (CSV). Requires Catalog Video.
READ get_catalog_items
List synced products for a catalog (SKU, title, price, image). Paginated (limit/cursor, default 100, max 500).
WRITE sync_catalog
Enqueue a catalog re-sync from its source.
READ list_catalog_video_runs
List Catalog Video production runs for a business. Paginated (limit/cursor, default 50, max 200).
WRITE create_catalog_video_from_url
Kick off the shop-URL → Catalog Video auto-pipeline for a business. Idempotent on (businessId, url). Requires Catalog Video.
WRITE create_catalog_video_run
Create a draft Catalog Video run linked to a catalog (optional template + SKUs + defaultTier). Requires Catalog Video.
READ get_catalog_video_run
Catalog Video run detail with progress counters and status.
READ quote_catalog_video_run
Pre-flight price for a Catalog Video run (sample or full tranche).
WRITE launch_catalog_video_run
Charge the first tranche and start Catalog Video production. Pass the quote's mode + quoteHash to guard against a stale quote. Metered.
WRITE update_catalog_video_run
Edit SKUs, aspect ratios, tier, sync cadence, refresh budget, new-item policy, or launch mode while draft/quoting/paused.
WRITE pause_catalog_video_run
Manually pause a producing/live Catalog Video run. Money-neutral.
WRITE resume_catalog_video_run
Resume a paused Catalog Video run and re-arm remaining production work.
WRITE archive_catalog_video_run
Archive a draft/failed/paused/live Catalog Video run. Terminal, non-refunding.
WRITE retry_catalog_video_run
Clear the error on a failed Catalog Video run and resume it.
READ get_catalog_video_manifest
Per-variant CDN / VAST URLs for a Catalog Video run. Paginated (limit/cursor, default 100, max 500).
READ get_catalog_video_feeds
Platform supplemental feed URLs once a run is live.
READ export_catalog_video_run
Google video_link activation kit (instructions + CSV) for operator handoff.
READ list_catalog_video_templates
List Catalog Video templates for the org (optional businessId).
READ get_catalog_video_template
Catalog Video template detail: status, hook count, pitched concept.
WRITE pitch_catalog_video_template
Pitch a Catalog Video template (optional hookCount for opening-hook variants) and start the human-review probe pipeline.
WRITE approve_catalog_video_template
Human-approve a Catalog Video template before launching a run.
READ list_campaign_catalog_video_runs
List bindable Catalog Video runs for a campaign and its current binding. Requires Catalog Video.
WRITE bind_campaign_catalog_video
Bind or clear a campaign's catalogVideoRunId for native Catalog Video serving (requires Catalog Video).
Organization
WRITE retry_failed_placements
Re-run distribution for a live campaign's failed placements only; serving placements are left untouched.
READ list_recommendations
Pending cross-campaign budget moves the portfolio optimizer computed but didn't auto-apply, with the marginal-CPA evidence.
WRITE apply_recommendation
Apply a pending recommendation. Budget moves use the validated transactional budget-edit path. Guided Search edits may return pending: true while Google reviews or confirms the change; accepted does not mean it is already serving.
WRITE dismiss_recommendation
Dismiss a pending budget recommendation without moving any money.
READ list_integrations
List connected customer-data sources (provider, status, last sync). Tokens are never returned. Pass businessId to filter to one website.
WRITE sync_integration
Enqueue a sync for an already-connected customer-data source. Connect/disconnect stay in the web app.
WRITE update_organization
Update organization product settings. Currently just crossCampaignOptimization: enabling it lets the portfolio optimizer apply cross-campaign budget moves automatically.
READ export_org_data
Full organization data export as one large JSON document (businesses, buyer profiles, creatives, campaigns, ledger entries, delivered spend, segments, per-ad performance, and more). Rate limited to 5 requests per hour per organization.
Agency
READ list_agency_clients
List child client organizations for an agency API key. Scale license required. Refused on a client-scoped key.
WRITE create_agency_client
Create a child client organization under the agency. Scale license required.
READ get_agency_client
Read one child client's stored experience/billing modes (Managed vs Self-serve pair), fee percent, monthly spend cap, digest routing, configured mail recipients per class, campaignPolicy (stored + inheritance-resolved defaults/locks), and effective rate split. Change the pair with update_agency_client operatingMode. defaultCreativeSource controls new campaigns: byo starts with creative generation frozen, autopilot keeps normal creation, and null restores the normal default. Existing campaigns are unchanged.
WRITE update_agency_client
Update one child client: operatingMode (managed or self_serve), the agency fee percent (null = inherit the agency default), the monthly spend cap in cents (null = no cap), digest mail routing (true = client + agency, false = agency only, null = follow experience mode), mailRecipients per class (ops/digest; null or [] returns that class to the existing default recipient), dspBilling on Self-serve (platform_seat or client_dsp), and campaignPolicy defaults/locks (null clears the child's stored policy). defaultCreativeSource controls new campaigns: byo starts with creative generation frozen, autopilot keeps normal creation, and null restores the normal default. Existing campaigns are unchanged. Only the fields you pass change.
WRITE offboard_agency_client
Disable a child client: pause live campaigns and turn off that client's API keys. Pass confirmName matching the client name. Does not delete history. Restore with restore_agency_client.
WRITE restore_agency_client
Restore a disabled child client so they can sign in again. Does not resume paused campaigns. Pass confirmName matching the client name.
READ list_agency_client_campaigns
List campaigns that belong to one child client org.
READ get_agency_client_campaign_report
Performance report for a campaign in a child client org. Spend is pass-through media at cost.
READ get_agency_client_campaign_analytics
Analytics for a campaign in a child client org.
READ get_agency_cost_report
CSV of pass-through media spend across child clients for agency finance.
READ list_agency_client_api_keys
List the API keys bound to one child client. 404 for a client outside this agency.
WRITE create_agency_client_api_key
Mint an API key that authenticates only as that child client (never the agency). Secret returned once.
WRITE revoke_agency_client_api_key
Disable one API key bound to a child client.
READ list_agency_campaigns
Campaigns across every child client with the owning client on each row. Optional status and client filters.
Agent onboarding path
Start with add_business, wait until analysis is ready, and review buyer profiles with get_business. Use create_campaign to save a draft, then get_creation_quote with its campaignId once targeting is planned. Check get_wallet and use create_wallet_checkout if needed. Confirm the budget and launch with the user before calling launch_campaign. A deferred creative quote is not a completed free quote.
The full workflow, step by step
This is the sequence agents are taught via the server's instructions; the tools used at each step are listed below:
-
add_businesswith your website URL. Analysis is free and takes a minute or two. Polllist_businessesuntil the status isready. -
get_business: review the extracted profile and the buyer profiles. Each buyer profile has an id and Reach (targetingSeeds); pass a subset asicpIdsto target specific buyer profiles. After interest targeting is planned, Reach is copied into that campaign's flight targeting. -
create_campaign: a draft; launching is separate. Named platforms includegoogle_ads,meta,reddit, andgoogle_search(Search under Google, on by default when the daily budget covers it). After interest targeting is planned, Autopilot builds a recommended creative plan (reuse ready creatives, generate only gaps across targeted buyer profiles;generationDeferred: true; metered from the wallet, with starter credit applying). PasscampaignAds: falsefor a reuse-only plan with no generation charge. Daily budget is at least $30. After create, useupdate_campaign_setupto change channels, inventory tiles, Follow spend, playbook (Follow vs Open mix), geo, oricpIds(which buyer profiles the draft targets). Switch Autopilot vs Guided withupdate_campaign_autopilot(campaign-wide, not per channel). -
get_creation_quote: price the batch. PasscampaignIdonce lines exist for the recommended creative plan gaps (statics only where needed across buyer profiles, plus missing videos); omitcampaignIdfor library Launch-pack pricing. Returns unit breakdown, billing mode, and remaining wallet funds. Watch generation withlist_creatives. -
get_wallet: confirm the balance covers at least the first day. If it does not,create_wallet_checkoutand open the returned URL. Thenlaunch_campaign. A 402 from launch includesnextTool: "create_wallet_checkout". - Manual-approval orgs:
list_ad_reviewsthenapprove_adbefore those ads can serve. Optional:set_ad_review_webhookfor a signed POST when new ads need review. -
get_campaign_report: spend, conversions, revenue, CPA, ROAS, per-platform breakdowns. Manage spend withupdate_campaign_budget,update_campaign_end_date,update_campaign_landing_url,update_campaign_autopilot,pause_campaign,resume_campaign,end_campaign, orbulk_campaign_lifecyclefor many campaigns of one business at once. Before a launch,launch_campaign_dry_runlists every check, requirement, and wallet coverage as pass/fail without charging anything. -
Optimize:
get_campaign_analyticsfor daily trends and buyer profile/segment/device breakdowns,get_campaign_adsfor per-ad numbers, thenset_ad_servingto pause the weak ads (on live or paused campaigns, pass areasonCode; when resuming, useresume_optimizer_pauseorresume_user_pauseto match how the ad was paused). Wire your own conversion data in withget_tracking_setupso the loop runs on real outcomes.
A tool call and its result look like this (all money values are integer cents):
// → tools/call
{ "name": "create_campaign",
"arguments": {
"businessId": "018f3a2b-...",
"name": "Summer Sale",
"objective": "sales",
"dailyBudgetCents": 5000,
"offer": "Free shipping over $40"
} }
// ← result
{ "campaign": { "id": "018f3c91-...", "status": "draft" },
"campaignAdsEnqueued": false, "generationDeferred": true }
Use cases & example prompts
Paste these into any connected agent and adjust the specifics. They exercise the whole surface:
Sign up from Cursor
No API key yet. The agent creates the account, the human enters the email code, then the agent reconnects with the returned key.
Connect to Adwave MCP. Start signup with my email, open the verification link, wait until I enter the one-time code, then save the API key and list my businesses.
Zero to live campaign
You have a website and a budget, and want an agent to handle everything: analysis, buyer profiles (personas), ad generation, and launch.
Add acme-coffee.com to Adwave, wait for the analysis, show me the buyer profiles it found, then create a $50/day sales campaign with the offer “free shipping over $40”. Show me the generated ads and the wallet balance, then ask me before launching.
Morning performance check
A recurring assistant task: pull yesterday's numbers and flag anything that needs a decision.
Pull the report for every live Adwave campaign. Summarize spend, conversions, CPA, and ROAS, compare platforms, and tell me if anything looks off or is worth pausing.
Budget management
Shift money toward what works without logging into the dashboard.
My “Summer Sale” campaign is converting at 4x ROAS and “Brand Awareness” is barely spending. Raise Summer Sale to $80/day and drop Brand Awareness to $30/day.
Spend guardrails
Stop spending fast when priorities change: pausing keeps the campaign resumable, ending is permanent.
We're out of inventory until Thursday. Pause every live Adwave campaign now, and remind me to resume them Thursday morning.
Audience review
Review Website audiences and platform sync state before planning the next campaign.
List my Adwave audiences. How many people are in the high-intent segment, and which platform audiences are synced and ready for Delivery?
Closed-loop creative optimization
The full optimization loop: pull daily and per-ad data, join it with your own numbers (margins, inventory, CRM), decide, and push the changes back.
Pull the last 30 days of analytics and the per-ad performance for my “Summer Sale” Adwave campaign. Cross-reference the conversion revenue with the product margins in my spreadsheet, then pause any ad whose margin-adjusted CPA is above $40 and tell me if the budget should move.
Approve ads from your own queue
The org requires manual approval. Register a webhook so new ads arrive as a signed POST instead of polling.
Set my Adwave ad-review webhook to https://example.com/hooks/ads. Save the secret, then whenever a signed POST arrives, list pending reviews and approve the ones I named.
Wire in your own conversion data
Attribution is only as good as the conversions it sees. Feed them from any backend so reports and per-ad numbers reflect reality.
Get my Adwave tracking setup and write a small script that posts a conversion to the webhook whenever a Stripe payment succeeds, including the order id and the customer email.
Errors & limits
Failed tool calls return the HTTP status and the same JSON error the REST API sends, so agents can react precisely:
| Error | Meaning | What to do |
|---|---|---|
| 401 unauthorized | Invalid or revoked API key or OAuth token. Missing Authorization on initialize, tools/list, ping, or signup tools is not an error (signup only). Missing Authorization on any other method is 401. | Send a valid Settings API key as Authorization: Bearer, or complete Cursor OAuth. Do not call signup_start while a Bearer is set. |
| 402 insufficient_funds | The wallet can't cover the first day's budget | Call create_wallet_checkout, open the Stripe URL, then launch again |
| 422 portal_not_configured | Stripe Customer Portal is not enabled yet | Use create_wallet_checkout until the billing portal is enabled |
| 404 *_not_found | The id doesn't exist in your organization | Re-list (businesses/campaigns) and use a returned id |
| 409 no_servable_ads | Nothing ready to serve: BYO, Catalog Video, or prepaid with no finished ad; Autopilot if ads cannot start or generation already failed | Upload ads, add a buyer profile, or wait for generation. Autopilot starts ads at launch if none were requested yet |
| 409 byo_missing_serve_families | Upload-your-ads campaign is missing a finished ad for a required surface family | Upload the missing mobile, TV, or follow ad, then launch again |
| 422 illegal_combo | Agency client update mixed Managed and Self-serve fields illegally | Send operatingMode managed or self_serve, or a legal experience/billing pair |
| 409 invalid_status | The campaign isn't in a state that allows the action | Check list_campaigns (e.g. only live campaigns pause) |
| 409 last_serving_ad | Pausing this ad would leave its buyer profile with nothing to serve | Resume or generate another ad for that buyer profile first |
| 400 override_required | Live or paused campaign: Autopilot manages this creative; reasonCode is required | Pass reasonCode (and optional note) on set_ad_serving, then retry |
| 400 resume_reason_mismatch | Resume used the wrong dedicated reason for how the ad was paused | Use resume_optimizer_pause for Autopilot pauses, resume_user_pause for manual pauses |
| 422 invalid_input | A parameter failed validation | Fix the field named in the issues array and retry |
| 429 rate_limited | Too many requests this minute | Back off and retry (120 reads / 30 writes per minute) |
Standard REST limits are 120 reads and 30 writes per minute, shared by keys belonging to the same user. MCP also limits authenticated requests to 120 per minute. Some operations have lower limits; unauthenticated signup requests are limited to 40 per minute per IP address. Daily creation limits are listed in Creating ads.
