MCP tool reference
Every one of the 128 tools the Marketing OS MCP endpoint exposes — what each one does, its risk tier and its arguments. Generated from the registry itself.
This page is generated from the tool registry itself, so it cannot fall behind what your client sees. CI fails if it does.
The MCP endpoint exposes 128 tools across every channel Marketing OS touches. They are not a separate API: there is one registry, and the app, the agent and your AI client all call the same tools through it.
Every tool except list_businesses takes a businessId. Call list_businesses first to
get one.
Risk tiers
A tool's risk tier decides whether a call happens or waits for a person. 29
of the tools below are high: calling one files it in the approvals inbox and returns
AWAITING APPROVAL. Nothing reaches Meta, Google, a social platform or a budget until
somebody approves it in the app.
| Tier | What a call does |
|---|---|
read | Runs immediately. Changes nothing. |
low | Runs immediately. Reversible, and written to the decision log. |
high | Staged for approval. Runs only once a human approves it. |
Money guardrails sit under all three: a spend cap, a ROAS floor or a geo rule refuses a call outright, approved or not.
Every tool, by channel
The business itself
| Tool | Tier | What it does |
|---|---|---|
add_knowledge | low | Add a knowledge item |
add_operating_note | low | Add an operating note |
agent_get_run | read | Read an agent run |
agent_list_runs | read | List agent runs |
agent_run | low | Run an agent |
budget_forecast | read | Month-end budget forecast |
budget_reallocation_plan | read | Cross-channel budget reallocation plan |
competitor_add | low | Watch a competitor |
competitor_list_ads | read | Competitor ads |
creative_list_experiments | read | Creative test cycles |
get_brand_profile | read | Brand profile |
get_business_context | read | Business context and economics |
get_guardrails | read | Money guardrails |
goal_approve_plan | low | Approve a goal’s plan |
goal_create | low | Create a goal |
goal_list | read | List goals |
goal_set_status | low | Pause or resume a goal |
list_businesses | read | Your businesses |
list_entity_changes | read | History of one campaign, ad set or ad |
list_knowledge | read | List knowledge items |
list_operating_notes | read | Operating notes |
operator_list_runs | read | Operator runs |
operator_run_now | low | Run the operator now |
revert_change | high | Undo one change |
rules_dry_run | read | Preview an automated rule |
rules_list | read | Automated rules |
search_knowledge | read | Search the knowledge base |
update_brand_profile | low | Update the brand profile |
add_knowledge
Add one item to the Business Brain’s knowledge base so every future agent run can cite it. Kinds: guideline, past_campaign, testimonial, faq, case_study, research, note. It is indexed for search in the background. In-app only.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
kind | guideline | past_campaign | testimonial | faq | case_study | research | note | yes | — |
title | string | yes | — |
body | string | yes | — |
{
"kind": "guideline",
"title": "<title>",
"body": "<body>"
}add_operating_note
Record a standing instruction for this business — something to always or never do, in plain language, e.g. "Never run comparison ads against Brand X" or "The owner wants every promotion to mention free shipping". Every future agent run for this business reads it as part of its context, and it does not expire until the owner retires it. Use it when the owner tells you a rule, not to store facts — facts belong in the knowledge base (add_knowledge).
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
text | string | yes | The standing instruction, in plain language — what to do or never do, and why if it helps. Written as an instruction to the agents, e.g. "Never run comparison ads against Brand X". |
{
"text": "<text>"
}agent_get_run
One agent run: status (queued, running, completed, failed), the error if it failed, and what it produced — assetId is the marketing asset a writer created or revised (null until it exists), and outputs lists the result events (asset_created, asset_revised, recommendations_ready, tasks_created) with their payloads.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
runId | string | yes | The run id, from agent_run or agent_list_runs. |
{
"runId": "<runId>"
}agent_list_runs
The 30 most recent built-in agent runs for this business (blog_writer, sales_copy, fb_ads), newest first, with status and brief. Terminal conversations and operator passes are not listed. Use agent_get_run for what a run produced.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
agent_run
Start one of the built-in agents on a brief: blog_writer drafts an SEO blog post, sales_copy drafts email / ad_copy / landing_page / social_post copy (assetType), fb_ads analyses the Meta account into prioritized recommendations and puts the urgent ones on the board. Pass existingAssetId to have the writer revise that asset instead of drafting a new one. Drafts pass Brand Voice review and land in review — nothing is published. The run is asynchronous: this returns it queued — call agent_get_run for its status and the asset it produced. It uses AI credits.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
brief | string | yes | What to write about, or for fb_ads what to focus on. The agent reads the Brain itself. |
assetType | blog_post | email | ad_copy | landing_page | social_post | no | — |
existingAssetId | string | no | — |
agentKey | blog_writer | sales_copy | fb_ads | yes | blog_writer drafts a blog post; sales_copy drafts email, ad, landing-page or social copy; fb_ads analyses the Meta account into recommendations and board tasks. |
{
"brief": "<brief>",
"agentKey": "blog_writer"
}budget_forecast
Project this month’s spend, conversions, revenue and ROAS per ad channel (Meta, Google Ads, TikTok Ads) from month-to-date delivery plus a weekday-aware mean of the last 14 days, each remaining day capped by the budgets currently committed. Includes the pacing ratio against the project’s monthly spend cap, in the organization’s reporting currency. When Klaviyo is connected, nonPaidRevenue projects email/SMS revenue beside the channels — it has no spend, so it is never in ROAS or pacing.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
budget_reallocation_plan
Compare the ad channels’ ROAS over the last 7 complete days and, when the best returns at least 1.5× the worst and both have 20+ conversions, propose moving daily budget from the worst channel’s least efficient entities to the best one’s most efficient — never to anything under the ROAS floor, never past the single-step limit, and never adding money overall. Returns a planId and one row per change, each with the exact *_set_budget tool and input to call (amounts in each entity’s own currency). Changes nothing by itself.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
maxShiftPct | number | no | The most of each donor entity’s daily budget the plan may move, in percent. Default 20, at most 50. |
floor | number | no | The ROAS a receiving channel and entity must clear. Defaults to this project’s own floor (its guardrail minimum ROAS, else its break-even). |
competitor_add
Start watching a rival brand's Meta ads. Takes their Facebook Page — a URL, an @handle or the bare handle. Their live ads are read immediately and then weekly, and new angles land in the weekly digest. There is a per-plan limit on how many competitors one project can watch; adding one past it is refused.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
pageHandle | string | yes | The competitor's Facebook Page — a full URL ("https://facebook.com/acmecoffee"), an @handle, or the bare handle. This is what their ads are looked up by. |
name | string | no | What to call them. Defaults to the handle when omitted. |
website | string | no | The competitor's website, if you know it. Optional and never fetched on add. |
{
"pageHandle": "<pageHandle>"
}competitor_list_ads
What rival brands this business watches are currently advertising on Meta, with a one-line description of the angle each ad makes. Read it before proposing a new creative angle: it shows which arguments the market is already saturated with and which nobody is making. Describes their ads — it never reproduces their copy.
Risk tier: read — runs immediately, changes nothing
Returns third-party text: the result arrives fenced as untrusted data, and a change proposed after reading it in the same agent run waits for approval (ADR 0038).
| Argument | Type | Required | Description |
|---|---|---|---|
limit | integer | no | How many ads to return, newest first. Default 40. |
creative_list_experiments
This business’s creative test cycles, newest first. Each one reports its target ad set, the window every arm gets, the floor arms are judged against, and every arm with its live metrics, how long is left on its window, what the rule would say about it right now, and — once judged — the decision, why, and the numbers it was made on.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
limit | integer | no | How many cycles to return, newest first. Default 10. |
get_brand_profile
The Business Brain’s brand profile: identity and voice, audience, offerings, competitors, the visual system (colors, fonts, icons), the SEO keyword profile, the social posting rules, company knowledge, and the AI instructions — always/never rules, banned words and required mentions. Read this before writing ANY copy for this business; the banned-word and always/never rules are not suggestions.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
get_business_context
This business’s goal, storefront URL, currency and the economics every ad decision is graded against: break-even ROAS, average order value in cents, target CPA and CPL in cents, monthly budget and daily budget cap — plus activeGoal, the measurable goal the owner set (metric, target, current value, pace against a straight line to the deadline, days left), or null when none is active. Read this before proposing any budget or judging whether a campaign is actually profitable — a ROAS is meaningless without the break-even it is measured against.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
get_guardrails
The hard ceilings this business enforces on every AI-initiated change: daily and monthly spend caps in cents, the minimum ROAS an entity needs before delivery may be increased, the largest single budget jump allowed, allowed and excluded countries, and blocked creative angles. Nothing can spend past these — not an approval, not an autonomy setting, not this API. Read them first and propose inside them.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
goal_approve_plan
Approve the Director’s plan for a goal in planning: every human step becomes a board task and every agent step starts an agent run (blog_writer, sales_copy or fb_ads) right away, and the goal becomes active. The runs are asynchronous and draft in-app content for review — nothing is published or spent. Check progress with agent_list_runs. Refused when the goal has no pending plan.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
goalId | string | yes | The goal id, from goal_list or goal_create. |
{
"goalId": "<goalId>"
}goal_create
Set a measurable goal: an objective, a metric (organic_clicks, sessions, site_conversions, ad_conversions, ad_revenue, ad_roas — each a trailing 28-day value), an absolute targetValue and an optional deadline (YYYY-MM-DD). The baseline is read from the metric source now and the target must exceed it, or the call is refused with the baseline in the message. The Director plans it in the same call: the goal comes back in planning with its plan (3–10 steps for the writers, the ads agent or a human). Nothing runs until goal_approve_plan.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
objective | string | yes | — |
metricKey | organic_clicks | sessions | site_conversions | ad_conversions | ad_revenue | ad_roas | yes | — |
targetValue | number | yes | — |
deadline | string | no | — |
followUpOfGoalId | string | no | — |
{
"objective": "<objective>",
"metricKey": "organic_clicks",
"targetValue": 0
}goal_list
Every goal for this business, newest first: objective, metric, status (planning, active, paused, completed, missed), deadline, progress from baseline toward target (measured from the metric source, never estimated) and the Director’s plan. A goal in planning has a plan waiting for goal_approve_plan.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
goal_set_status
Pause an active goal or resume a paused one. A goal still in planning, or one already completed or missed, is refused. Pausing stops its progress refresh; it does not cancel tasks or runs already started.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
goalId | string | yes | The goal id, from goal_list or goal_create. |
status | active | paused | yes | active resumes a paused goal; paused stops it. |
{
"goalId": "<goalId>",
"status": "active"
}list_businesses
List the businesses (projects) in this Marketing OS organization with their business context — the economics every other tool is graded against: goal, break-even ROAS, average order value, target CPA/CPL, daily and monthly budget, currency. ALWAYS call this first: every other tool takes a businessId from here.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
list_entity_changes
Every change made to one ad-account entity, newest first: who or what made it (you, the operator agent, an AI client or an accepted recommendation), exactly what changed, whether a guardrail refused it, and whether it can still be undone. Pass an id from the same entity’s revertible calls to revert_change to roll one back. Includes the approvals filed for it and the recommendations written about it.
Risk tier: read — runs immediately, changes nothing
Returns third-party text: the result arrives fenced as untrusted data, and a change proposed after reading it in the same agent run waits for approval (ADR 0038).
| Argument | Type | Required | Description |
|---|---|---|---|
channel | meta | google_ads | tiktok_ads | no | Which ad channel the entity belongs to: meta, google_ads or tiktok_ads |
entityId | string | yes | The entity’s id on its own channel — a Meta id from meta_list_campaigns, a Google Ads id from google_ads_list_campaigns, or a TikTok Ads id from tiktok_ads_list_campaigns |
{
"entityId": "<entityId>"
}list_knowledge
List the Business Brain’s knowledge base: guidelines, past campaigns, testimonials, FAQs, case studies, research and notes, each with its full body.
Risk tier: read — runs immediately, changes nothing
Returns third-party text: the result arrives fenced as untrusted data, and a change proposed after reading it in the same agent run waits for approval (ADR 0038).
Takes no arguments.
list_operating_notes
What this business has already decided: every change it rejected, every one it undid, the measured results of past content, and the standing instructions its owner typed. Read these before proposing anything — they are the reason a given idea may already have been ruled out, and re-proposing a rejected change is the fastest way to lose the owner’s trust.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
includeExpired | boolean | no | Include notes whose window has already lapsed. Default false. |
limit | integer | no | How many to return, newest first. |
operator_list_runs
The operator agent’s recent runs for this business, newest first: when each ran, what it wrote up, and every action it took — with each action marked as done, waiting for the owner’s approval, done on autopilot, or refused by a spending guardrail. Also reports whether the operator is scheduled and when it next runs.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
limit | integer | no | How many runs to return, newest first. Default 10. |
operator_run_now
Wake the operator agent up immediately for this business instead of waiting for its daily slot. It reviews performance, measurement and spend, then acts inside the business’s guardrails — filing anything that needs approval rather than doing it. Returns as soon as the run is queued; poll operator_list_runs for the write-up. Refused while a run is already in progress, and it costs AI credits.
Risk tier: low — runs immediately, reversible and audited
Takes no arguments.
revert_change
Put back exactly what one earlier tool call overwrote, from the snapshot that call captured — a budget, or a delivery status. Only calls list_entity_changes reports as revertible can be undone, and only once. An undo is a real change to a live ad account, so it always requires a human approval and is refused outright when putting the old value back would cross a spend cap or a ROAS floor.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
invocationId | string | yes | The tool call to undo — an id from list_entity_changes |
{
"invocationId": "<invocationId>"
}rules_dry_run
What one automated rule would match and do if it were evaluated right now: every matching campaign or ad set with the observed metric value, spend and conversions over the rule’s lookback, and what would happen to it (dry_run = would act, cooling_down = this rule already acted on it recently, skipped = nothing to do and why). Reads the synced mirror only. Changes nothing, files nothing, and does not use up the rule’s daily evaluation.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
ruleId | string | yes | Rule id from rules_list |
{
"ruleId": "<ruleId>"
}rules_list
The owner’s automated rules for this business: each one’s channel (meta, google_ads, tiktok_ads or all), level (campaign or ad_set), scope (a name pattern or explicit ids), condition (metric, comparator, threshold, lookback days, minimum spend/conversions — spend, cpa and cpc thresholds are in cents, ctr in percent, roas and frequency as ratios), action (pause, enable, set_budget_pct with pct, or notify), cooldown, and its last evaluation. Rules are evaluated once a day at 06:00 business time and act through the same approvals and guardrails as you do. Read this before changing a campaign’s status or budget, and do not act against an enabled rule — for example, do not re-enable something a rule paused, or raise a budget a rule would cut.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
enabledOnly | boolean | no | Only rules that are switched on (the ones that act). Default true. |
search_knowledge
Search this business’s knowledge base by meaning and by keyword at once, returning the best-matching passage of each hit rather than the top of the document. Use it to cite the business’s own facts — policies, pricing, testimonials, past results — instead of inventing them.
Risk tier: read — runs immediately, changes nothing
Returns third-party text: the result arrives fenced as untrusted data, and a change proposed after reading it in the same agent run waits for approval (ADR 0038).
| Argument | Type | Required | Description |
|---|---|---|---|
query | string | yes | What to look for, in plain language — matched on meaning and on keywords |
limit | integer | no | How many items to return (default 5) |
{
"query": "<query>"
}update_brand_profile
Replace the whole brand profile. Every section is sent every time, so read it with get_brand_profile first and send it back with your edits applied — anything you leave out is cleared. In-app only: this changes no live ad or post.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
brand | string | no | — |
industry | `` | apparel_accessories | beauty_personal_care | food_beverage | home_garden | health_fitness | electronics | jewellery | pets | baby_kids | sports_outdoors | b2b_software | professional_services | home_services | health_practice | legal | financial_services | real_estate | education | travel_hospitality | automotive | nonprofit | other | no | — |
voice | string | no | — |
audience | string | no | — |
offerings | string | no | — |
competitors | string | no | — |
notes | string | no | — |
colors | object[] | no | — |
branding | object | no | — |
seo | object | no | — |
social | object | no | — |
knowledgeBase | object | no | — |
aiInstructions | object | no | — |
marketingAssets | object | no | — |
Meta ads
| Tool | Tier | What it does |
|---|---|---|
creative_decide_arm | low | Decide one arm of a creative test cycle |
creative_start_experiment | high | Start a creative test cycle |
lead_list | read | Lead velocity |
lead_update_status | low | Update a lead’s status |
meta_activate_campaign_plan | high | Switch a deployed campaign on |
meta_activate_entity | high | Activate a Meta campaign, ad set or ad |
meta_approve_campaign_plan | low | Approve a campaign plan |
meta_create_ad | high | Create a Meta ad in an existing ad set |
meta_create_catalog_campaign | high | Create an Advantage+ catalog campaign |
meta_deploy_campaign_plan | high | Create the plan’s campaign (paused) |
meta_draft_campaign_plan | low | Draft a campaign plan |
meta_generate_recommendations | low | Generate a recommendation batch |
meta_get_recommendations | read | Latest AI recommendations |
meta_implement_recommendation | high | Apply a recommendation |
meta_list_ad_sets | read | List Meta ad sets |
meta_list_ads | read | List Meta ads |
meta_list_campaign_plans | read | List campaign plans |
meta_list_campaigns | read | List Meta campaigns |
meta_list_pages | read | List Facebook Pages |
meta_pause_entity | low | Pause a Meta campaign, ad set or ad |
meta_reject_recommendation | low | Dismiss a recommendation |
meta_set_bid_strategy | high | Change a Meta bid strategy |
meta_set_budget | high | Change a Meta daily budget |
meta_set_campaign_plan_budget | low | Change a campaign plan’s daily budget |
meta_set_campaign_plan_page | low | Pick the Facebook Page for a plan |
creative_decide_arm
Record a decision for one arm and carry it out: "killed" pauses the ad, "promoted" raises the target ad set’s daily budget and drafts derivatives of the winner, and "kept" only records the verdict. The change itself goes through the usual gate — a pause runs, a budget increase is filed for approval — so this does not let a decision skip an approval. Refused on an arm that is already decided.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
decision | killed | promoted | kept | yes | kill pauses the ad, promote raises the ad set’s budget, keep only records it |
reason | string | no | Why — recorded on the arm beside the numbers |
experimentId | string | yes | Cycle id from creative_list_experiments |
armId | string | yes | Arm id from creative_list_experiments |
{
"decision": "killed",
"experimentId": "<experimentId>",
"armId": "<armId>"
}creative_start_experiment
Launch 2–8 creative variants as competing ads in one ad set and judge them against one floor after one fixed window. Every arm is created PAUSED and delivers nothing until it is activated separately, so this does not start spend — but it does create real objects in the ad account, so it always requires a human approval. Arms come from the creative studio (variantId) or from ads that already run (adMetaId). Once the window elapses and an arm has cleared the minimum spend and impressions, anything under the floor is paused automatically and the best survivor is put forward for a budget increase.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
name | string | yes | What this cycle is called |
hypothesis | string | no | What the cycle is trying to learn, in the operator’s own words |
adSetMetaId | string | yes | The ad set every arm is launched into — from meta_list_ad_sets |
pageId | string | no | Facebook Page the generated ads post under; required when any arm is a variant |
windowHours | integer | no | How long every arm runs before it can be judged. Default 48. |
floorMetric | roas | cpa | ctr | no | roas and ctr are floors (higher passes); cpa is a ceiling (lower passes) |
floorValue | number | yes | The floor itself: 1.4 for ROAS, a percentage for CTR, cents for CPA |
minSpendCents | integer | no | No arm is decided below this spend, whatever the window says |
minImpressions | integer | no | No arm is decided below this many impressions |
promoteRule | budget_increase | no | — |
promoteBudgetPct | integer | no | How much more daily budget the winner’s ad set gets, as a percentage |
draftDerivatives | boolean | no | Draft new variants from the winner’s angle when one is promoted |
arms | object[] | yes | — |
{
"name": "<name>",
"adSetMetaId": "<adSetMetaId>",
"floorValue": 0,
"arms": []
}lead_list
Instant-form leads for this business: how many arrived since a day (default the last 30 days), broken down by status (new, contacted, qualified, won, lost) and by campaign, plus the 50 most recent leads by id and status. Pair it with campaign spend to read cost per lead and how many leads each campaign turns into customers. It never returns what a person typed into the form — names, emails and phone numbers stay in the inbox.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
status | new | contacted | qualified | won | lost | no | Only leads currently in this status. |
since | string | no | Only leads submitted on or after this day (YYYY-MM-DD). Default: the last 30 days. |
lead_update_status
Move one lead along the pipeline — new, contacted, qualified, won or lost — by its id from lead_list. Use it when the owner tells you what happened with a lead ("the plumber booked the Hendersons"), so cost-per-customer reads stay true. The business’s webhook endpoints receive a lead.status_changed event.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
leadId | string | yes | The lead id, from lead_list. |
status | new | contacted | qualified | won | lost | yes | The new status. |
{
"leadId": "<leadId>",
"status": "new"
}meta_activate_campaign_plan
Flip a deployed plan’s campaign and everything under it to ACTIVE (on Meta the ad set and ads; on Google the ad groups and ads, the campaign last). This is the step that starts spending money, so it always requires a human approval and is refused outright when it would cross the project’s daily or monthly spend cap or its geo rules.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
planId | string | yes | Campaign plan id from meta_list_campaign_plans |
{
"planId": "<planId>"
}meta_activate_entity
Start (or resume) delivery for one campaign, ad set or ad. Always requires a human approval: this is the step that starts spending money.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
level | campaign | adset | ad | yes | Which level the Meta id names |
metaId | string | yes | The entity’s Meta id, e.g. from meta_list_campaigns |
{
"level": "campaign",
"metaId": "<metaId>"
}meta_approve_campaign_plan
Mark a drafted plan as approved, which is what makes it deployable. Still nothing on Meta: approving a plan creates no campaign and spends nothing.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
planId | string | yes | Campaign plan id from meta_list_campaign_plans |
{
"planId": "<planId>"
}meta_create_ad
Put a new image, video or carousel ad into an existing ad set, from asset-library images/videos (assetId) or finished Creative Studio variants (variantId). It lands PAUSED by default and delivers nothing until meta_activate_entity is approved. Optional placements rewrite the AD SET’s publisher platforms and positions (Meta scopes placements there), so they apply to every ad in it. Always requires a human approval: it creates real objects in a real ad account.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
adSetMetaId | string | yes | The existing ad set the ad goes into, e.g. from meta_list_ad_sets |
pageId | string | yes | The Facebook Page the ad runs as |
name | string | no | Defaults to the headline |
creative | object | yes | kind (image|video|carousel), assets [{assetId|variantId, carousel card headline/description/linkUrl?}], headline, primaryText, description?, cta, linkUrl, utm? {source, medium, campaign, content?} |
placements | object | no | Rewrites the AD SET’s placements (Meta scopes them there), affecting every ad in it. Omit to leave them as they are. |
status | PAUSED | ACTIVE | no | PAUSED by default; switch on later with meta_activate_entity |
{
"adSetMetaId": "<adSetMetaId>",
"pageId": "<pageId>",
"creative": {}
}meta_create_catalog_campaign
Create a Meta Advantage+ catalog (dynamic product ads) campaign that advertises one product set from the store catalog: objective Sales, one ad set and one dynamic ad whose headline and text are filled per product from template tokens such as {{product.name}}. Audience: "viewed_not_purchased_14d" (retarget product viewers who did not buy), "prospecting" (broad) or a Meta custom audience id. The campaign lands PAUSED and delivers nothing until meta_activate_entity is approved on it. Always requires a human approval, and is refused when its daily budget would cross the project’s daily or monthly spend cap or it targets a blocked country.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
productSetId | string | yes | The product set to advertise — its id from commerce_get_meta_catalog |
audience | viewed_not_purchased_14d | prospecting | string | yes | "viewed_not_purchased_14d" retargets people who viewed a product in the set in the last 14 days and did not buy; "prospecting" lets Meta find new buyers (broad audience); or a Meta custom audience id, e.g. a synced Shopify segment from the Audiences page. |
dailyBudgetCents | integer | yes | The campaign’s daily budget, in cents of the ad account’s currency |
countries | string[] | yes | ISO 3166-1 alpha-2 country codes to deliver in |
pageId | string | yes | The Facebook Page the ads run as, from meta_list_pages |
template | object | yes | The dynamic creative. Tokens Meta fills per product: {{product.name}}, {{product.description}}, {{product.price}}, {{product.current_price}}, {{product.brand}} |
name | string | no | Defaults to the product set name |
{
"productSetId": "<productSetId>",
"audience": "viewed_not_purchased_14d",
"dailyBudgetCents": 0,
"countries": []
}meta_deploy_campaign_plan
Create the approved plan’s campaign on its channel: for a Meta plan the campaign, ad set and ads on Meta; for a Google plan the Search campaign, ad groups, keywords and responsive search ads in Google Ads, each through the Google Ads tools. Everything lands PAUSED and delivers nothing until meta_activate_campaign_plan is approved separately, so this does not start spend — but it does create real objects in the ad account, so it always requires a human approval, and that approval covers every create the deploy makes. Resumable: when an earlier deploy failed part-way (the plan is approved again with a deployState naming what already exists), calling it again creates only the missing steps, never a second campaign.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
planId | string | yes | Campaign plan id from meta_list_campaign_plans |
{
"planId": "<planId>"
}meta_draft_campaign_plan
Draft a complete campaign from a brief, written against this business’s own economics. channel "meta" (the default) drafts a Meta campaign: structure, objective, daily budget, audience, interests and ad creatives. channel "google_ads" drafts a Google Search campaign for the linked Google Ads account instead: 1–3 ad groups with keywords, negative keywords and responsive search ads, and a bidding strategy chosen from the account’s conversion actions; the Meta-only fields (pixel, geo radius, special ad categories, locales, assets) are ignored for it. Nothing is created on either platform — this returns a plan with status "generating" and the draft lands a few seconds later, so poll meta_list_campaign_plans for the result. All money is in cents; countries are ISO 3166-1 alpha-2 codes. Whether the Meta pixel is installed is read from the synced ad account, so pixelInstalled is ignored unless the account is a demo. For lead gen, dealValueCents and closeRatePercent are saved to the business. For a local business, geo narrows the countries to a radius around an address (Meta geocodes it) or to places by their Meta location keys; countries is still required and names the country the address is in. For a campaign around a promo code, pass promotionId from promotions_list (create one with promotions_create_draft first if it does not exist) and leave offer and landingUrl out: the plan leads with the promotion’s code and links to its discount. Never invent a code or a discount.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
channel | meta | google_ads | no | Where the campaign runs: "meta" (Facebook & Instagram, the default) or "google_ads" (a Google Search campaign in the linked Google Ads account) |
businessModel | ecommerce | lead_gen | yes | — |
monthlyBudgetCents | integer | yes | — |
aovCents | integer | no | — |
breakEvenRoas | number | no | — |
targetCplCents | integer | no | — |
dealValueCents | integer | no | Lead gen: the value of one closed deal, in cents |
closeRatePercent | number | no | Lead gen: the percent of leads that become customers (0–100) |
offer | string | no | The offer the ads should lead with: the hook, price, discount or guarantee |
landingUrl | string | no | The page every ad links to; omitted = the business storefront URL |
promotionId | string | no | A promotion id from promotions_list (or the one promotions_create_draft returned). Fills offer ("10% off with code X") and landingUrl (the discount link) when those are omitted, grounds the copy in the code, and tags the ads with the promotion’s utm_campaign. Call promotions_list first; never invent a code or a discount |
leadProductIds | string[] | no | Up to 3 synced commerce product ids (from the catalog) the ads should feature |
emailListSize | none | under_1k | 1k_to_10k | over_10k | yes | — |
socialFollowing | none | under_1k | 1k_to_10k | over_10k | yes | — |
creativeAssets | product_photos | videos | ugc | testimonials | brand_assets[] | yes | — |
creativeAssetIds | string[] | no | — |
countries | string[] | yes | — |
geo | object | no | Local targeting: { kind: "places", places: [{ key, name, type, countryCode, radiusKm? }] } from the geo search, or { kind: "radius", addressString, radiusKm (1–80) }. Omitted = the whole countries |
pixelInstalled | boolean | no | Ignored for a real ad account (derived from the synced pixel); only honoured on a demo account |
bidStrategy | object | no | — |
specialAdCategories | HOUSING | FINANCIAL_PRODUCTS_SERVICES | EMPLOYMENT | ISSUES_ELECTIONS_POLITICS[] | no | Special ad categories the campaign falls under (housing, financial products, employment, politics); empty = none. Any category forces ages 18–65+ and all genders |
ageMin | integer | no | The youngest age to target (18–65); omitted = the planner decides |
locales | integer[] | no | Meta adlocale ids for the languages to target (e.g. 1001 English (All)); empty = all |
{
"businessModel": "ecommerce",
"monthlyBudgetCents": 0,
"emailListSize": "none",
"socialFollowing": "none"
}meta_generate_recommendations
Ask the analyst agent for a fresh recommendation batch for one ad channel. Returns immediately with a batch in status "generating" — poll meta_get_recommendations for the result. There is a 24-hour cooldown per channel and it needs that channel’s data synced, so it fails with the reason when either gate is closed. Costs AI credits; changes nothing in the ad account.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
channel | meta | google_ads | tiktok_ads | no | Which ad channel to analyse: meta, google_ads or tiktok_ads. Each has its own prompt and its own 24-hour cooldown. |
meta_get_recommendations
The most recent AI recommendation batch for this business: prioritized optimization actions (scale_up / scale_down / pause / resume / shutdown / duplicate / refresh_creative / fix_fatigue / new_campaign / maintain) with their reasoning chain, the exact metrics cited, the precise change to make (budget moves carry exact cents), and each one’s status. Covers every connected ad channel — each recommendation says which one it is about. Also reports, per channel, why a new batch cannot be generated yet.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
meta_implement_recommendation
Apply one pending recommendation to the live ad account it is about — Meta, Google Ads or TikTok Ads, whichever the recommendation’s channel says. Only recommendations marked autoImplementable can be applied this way. It changes real delivery or real budgets, so it always requires a human approval and is refused when it would cross a spend cap or the entity’s ROAS floor.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
recommendationId | string | yes | Recommendation id from meta_get_recommendations |
note | string | no | Why this is worth doing — shown to the approver and kept in the audit log |
{
"recommendationId": "<recommendationId>"
}meta_list_ad_sets
List ad sets with performance metrics, optionally narrowed to one campaign.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
from | string | no | Start date (YYYY-MM-DD). Defaults to 29 days ago. |
to | string | no | End date (YYYY-MM-DD). Defaults to today. |
campaignMetaId | string | no | Narrow to one campaign |
meta_list_ads
List individual ads with performance metrics and creative summaries, optionally narrowed to one campaign or ad set.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
from | string | no | Start date (YYYY-MM-DD). Defaults to 29 days ago. |
to | string | no | End date (YYYY-MM-DD). Defaults to today. |
campaignMetaId | string | no | Narrow to one campaign |
adSetMetaId | string | no | Narrow to one ad set |
meta_list_campaign_plans
List this business’s drafted campaign plans, newest first, with each plan’s channel (meta or google_ads), status (generating / draft / approved / deploying / deployed / live / failed) and its full drafted structure — for a Meta plan (plan) the objective, daily budget in cents, targeting, interests and ad creatives; for a Google plan (googlePlan) the budget, bidding, ad groups, keywords and responsive search ads — and the platform ids once it has been deployed.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
meta_list_campaigns
List this business’s Meta ad campaigns with performance over a date range: spend, impressions, clicks, CTR, CPC, CPM, conversions, revenue, ROAS and CPA/CPL.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
from | string | no | Start date (YYYY-MM-DD). Defaults to 29 days ago. |
to | string | no | End date (YYYY-MM-DD). Defaults to today. |
meta_list_pages
List the Facebook Pages the connected Meta user manages. Ads post under a Page, so a plan needs one set with meta_set_campaign_plan_page before it can be deployed with image ads. Returns an empty list for a demo ad account or when Meta is not connected.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
meta_pause_entity
Stop delivery for one campaign, ad set or ad. Reversible with meta_activate_entity, which needs a human approval.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
level | campaign | adset | ad | yes | Which level the Meta id names |
metaId | string | yes | The entity’s Meta id, e.g. from meta_list_campaigns |
{
"level": "campaign",
"metaId": "<metaId>"
}meta_reject_recommendation
Dismiss one pending recommendation with a reason. Nothing on Meta changes; the rejection is recorded and feeds back into how future batches are graded.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
recommendationId | string | yes | Recommendation id from meta_get_recommendations |
reason | cant_implement | already_doing | not_relevant | budget_constraints | other | yes | Why it is being dismissed: cant_implement, already_doing, not_relevant, budget_constraints or other |
note | string | no | Free-text detail for the record |
{
"recommendationId": "<recommendationId>",
"reason": "cant_implement"
}meta_set_bid_strategy
Set how one campaign (with a campaign budget) or ad set bids: lowest cost, a cost cap (average cost per result), a bid cap (maximum per auction) or a minimum ROAS (value-optimised ad sets only). Changing the bid strategy of a live campaign resets Meta’s learning phase, so use it as a last resort — after budget and creative changes have not fixed a drifting CPA. Refused when the strategy does not suit what the ad set optimises for, and when a cap is under 25% of the trailing-7-day cost per result (it would stop delivery). Requires a human approval unless the project’s autonomy grant releases high-tier changes.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
entityLevel | campaign | ad_set | yes | campaign for a campaign with a campaign budget (CBO); ad_set for an ad set that carries its own budget |
metaId | string | yes | The campaign’s or ad set’s Meta id |
strategy | LOWEST_COST_WITHOUT_CAP | COST_CAP | LOWEST_COST_WITH_BID_CAP | LOWEST_COST_WITH_MIN_ROAS | yes | LOWEST_COST_WITHOUT_CAP (no cap), COST_CAP (average cost per result), LOWEST_COST_WITH_BID_CAP (maximum bid per auction) or LOWEST_COST_WITH_MIN_ROAS (minimum return on ad spend; value-optimised ad sets only) |
amountCents | integer | no | Required for COST_CAP and LOWEST_COST_WITH_BID_CAP: the cap, in cents of the ad account’s currency |
minRoas | number | no | Required for LOWEST_COST_WITH_MIN_ROAS: the ROAS floor as a ratio, e.g. 2.5 |
{
"entityLevel": "campaign",
"metaId": "<metaId>",
"strategy": "LOWEST_COST_WITHOUT_CAP"
}meta_set_budget
Set the daily budget of one CBO campaign or ad set, in cents. Always requires a human approval, and is refused outright when it would cross this project’s daily or monthly spend cap, or when the entity is under its ROAS floor.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
level | campaign | adset | yes | Budget lives on a CBO campaign or an ad set |
metaId | string | yes | The entity’s Meta id |
dailyBudgetCents | integer | yes | The new daily budget, in cents of the ad account’s currency |
confirmLargeChange | boolean | no | Set true to deliberately make a jump at or over the project’s single-step budget limit (a doubling by default). Ceilings on total spend still apply and cannot be confirmed away. |
planId | string | no | The planId from budget_reallocation_plan, when this change is one row of that plan. Omit for a one-off change. |
{
"level": "campaign",
"metaId": "<metaId>",
"dailyBudgetCents": 0
}meta_set_campaign_plan_budget
Override the drafted daily budget before the plan is deployed, in cents. Still just a row — no Meta object exists yet — so this does not spend anything. Refused once the plan has been deployed; change a live campaign with meta_set_budget instead.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
planId | string | yes | Campaign plan id from meta_list_campaign_plans |
dailyBudgetCents | integer | yes | The campaign’s daily budget, in cents of the ad account’s currency. Meta enforces a minimum of roughly 100 cents/day, and the business’s own daily cap trims anything above it. |
{
"planId": "<planId>",
"dailyBudgetCents": 0
}meta_set_campaign_plan_page
Set the Facebook Page the plan’s ads will post under. Required before deploying a plan that has ad creatives. Page ids come from meta_list_pages.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
planId | string | yes | Campaign plan id from meta_list_campaign_plans |
pageId | string | yes | Facebook Page id from meta_list_pages |
{
"planId": "<planId>",
"pageId": "<pageId>"
}Google Ads
| Tool | Tier | What it does |
|---|---|---|
google_ads_add_keywords | low | Add keywords to a Google Ads ad group |
google_ads_add_negative_keywords | low | Add negative keywords to a Google Ads campaign or ad group |
google_ads_create_ad_group | high | Create a Google Ads ad group (paused) |
google_ads_create_campaign | high | Create a Google Search campaign (paused) |
google_ads_create_conversion_action | high | Create the Google Ads conversion action for Shopify purchases |
google_ads_create_promotion_asset | high | Add a promotion asset to a Google Ads campaign |
google_ads_create_responsive_search_ad | high | Create a Google responsive search ad (paused) |
google_ads_enable_entity | high | Enable a Google Ads campaign, ad group or ad |
google_ads_list_ad_groups | read | List Google Ads ad groups |
google_ads_list_ads | read | List Google Ads ads |
google_ads_list_campaigns | read | List Google Ads campaigns |
google_ads_list_keywords | read | List Google Ads keywords |
google_ads_list_search_terms | read | List Google Ads search terms |
google_ads_pause_entity | low | Pause a Google Ads campaign, ad group or ad |
google_ads_propose_search_term_changes | read | Propose Google Ads search-term hygiene |
google_ads_set_campaign_budget | high | Change a Google Ads daily budget |
google_ads_set_keyword_status | low | Pause or enable a Google Ads keyword |
google_ads_add_keywords
Add positive keywords (text + EXACT, PHRASE or BROAD match) to an ad group. They land ENABLED, so they serve as soon as their ad group and campaign do. A keyword moves no budget, so this runs without an approval under the default grant — but a keyword bid above the ad group’s own max CPC is a bid raise and is refused here: omit cpcBidCents to inherit the ad group’s bid. A partly accepted batch is reported as a partial failure naming what landed.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
adGroupGoogleId | string | yes | The ad group the keywords go in |
keywords | object[] | yes | The keywords to add. Sent to Google in batches of up to 1,000. |
planId | string | no | The planId from google_ads_propose_search_term_changes, when this call is one row of that proposal. Copy it unchanged; omit for a one-off change. |
{
"adGroupGoogleId": "<adGroupGoogleId>",
"keywords": []
}google_ads_add_negative_keywords
Stop ads showing for search terms that waste money: add negative keywords to a whole campaign or to one ad group. A negative only narrows who sees the ads — it moves no budget — so this runs without an approval under the default grant, and it is undoable from the decision log (the undo removes exactly the negatives this call added). Rows of a google_ads_propose_search_term_changes proposal carry its planId and each term’s evidence, and never negative the business’s own brand or products.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
level | campaign | ad_group | yes | Block the terms for a whole campaign, or for one ad group only |
id | string | yes | The campaign or ad group Google Ads id, matching level |
keywords | object[] | yes | The negatives to add, sent to Google in one batch. |
planId | string | no | The planId from google_ads_propose_search_term_changes, when this call is one row of that proposal. Copy it unchanged; omit for a one-off change. |
{
"level": "campaign",
"id": "<id>",
"keywords": []
}google_ads_create_ad_group
Create an ad group under an existing campaign. It lands PAUSED and delivers nothing until it is enabled separately, but it creates a real object in a real ad account, so it always requires a human approval.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
campaignGoogleId | string | yes | The campaign the ad group belongs to |
name | string | yes | — |
cpcBidCents | integer | no | Max CPC bid in cents. Omit on a campaign whose bidding strategy sets bids itself. |
{
"campaignGoogleId": "<campaignGoogleId>",
"name": "<name>"
}google_ads_create_campaign
Create a Search campaign and its own daily budget in the linked Google Ads account. It lands PAUSED and delivers nothing until google_ads_enable_entity is approved separately, so this does not start spend — but it does create real objects in a real ad account, so it always requires a human approval.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
name | string | yes | The campaign name, as it will appear in Google Ads |
dailyBudgetCents | integer | yes | Daily budget in cents. Nothing is spent until the campaign is enabled separately. |
biddingStrategy | manual_cpc | maximize_clicks | maximize_conversions | no | manual_cpc is the safe default for a brand-new campaign: a smart strategy has no conversion history to learn from yet. |
startDate | string | no | YYYY-MM-DD. Omit to start as soon as the campaign is enabled. |
endDate | string | no | YYYY-MM-DD. Omit to run until paused. |
countries | string[] | no | ISO 3166-1 alpha-2 country codes the campaign serves in, set as its location targeting. Omit to leave Google’s default, which is every country. |
{
"name": "<name>",
"dailyBudgetCents": 0
}google_ads_create_conversion_action
Create the UPLOAD_CLICKS purchase conversion action that server-side Shopify orders are uploaded against, and point this project’s Google conversion sends at it. Spends nothing, but it adds a real object to a real ad account — and, when made primary, changes what Smart Bidding optimises on — so it always requires a human approval. Returns the existing action when one with the same name is already there.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
name | string | no | The conversion action name, as it will appear under Goals → Conversions |
primaryForGoal | boolean | no | Whether Smart Bidding optimises on it. Leave false when the store already counts purchases through the Google tag — a primary upload would count every sale twice. |
google_ads_create_promotion_asset
Create a Google Ads promotion asset — the "10% off · code TORY10" line shown under a Search ad — and attach it to a campaign. Takes the promotion’s code, discount, redemption dates and the link it sends people to. When an asset with the same code is already in the account it is attached instead of duplicated. Spends nothing, but it is public ad copy in a real account, so it always requires a human approval.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
campaignGoogleId | string | yes | The Search campaign the promotion shows under |
promotionTarget | string | yes | What the promotion is on, ≤ 20 characters, e.g. "All products" or "Hoodies" |
promotionCode | string | yes | The code shoppers enter, ≤ 15 characters — e.g. a promotion from promotions_list |
discount | object | yes | The discount, as the promotion defines it |
redemptionStartDate | string | no | First day the code can be redeemed |
redemptionEndDate | string | no | Last day the code can be redeemed |
finalUrl | string | yes | Where the promotion links — ideally the store’s discount link, which applies the code |
{
"campaignGoogleId": "<campaignGoogleId>",
"promotionTarget": "<promotionTarget>",
"promotionCode": "<promotionCode>",
"discount": {}
}google_ads_create_responsive_search_ad
Create a responsive search ad in an existing ad group: 3–15 headlines (≤30 characters), 2–4 descriptions (≤90), a final URL and optional display paths and pins. Copy is checked against Google’s editorial basics first (no exclamation marks in headlines, no all-caps words, no repeated punctuation, no duplicates) and refused locally when it fails. The ad lands PAUSED — google_ads_enable_entity at level "ad" switches it on — but it is public ad copy in a real account, so it always requires a human approval.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
adGroupGoogleId | string | yes | The ad group the ad goes in, e.g. from google_ads_create_ad_group or google_ads_list_ad_groups |
headlines | string[] | yes | 3–15 headlines, each at most 30 characters. No exclamation marks, no all-caps words, no repeated punctuation, no duplicates. Google mixes them, so each must read on its own. |
descriptions | string[] | yes | 2–4 descriptions, each at most 90 characters, at most one exclamation mark each. |
finalUrl | string | yes | The landing page a click goes to |
path1 | string | no | First display-URL path segment, e.g. "plumbing" |
path2 | string | no | Second display-URL path segment; needs path1 |
pinnedHeadlines | object[] | no | Fix a headline (by index into headlines) to position 1, 2 or 3. Pinning cuts the combinations Google can test, so pin only what must always show. |
pinnedDescriptions | object[] | no | Fix a description (by index into descriptions) to position 1 or 2 |
{
"adGroupGoogleId": "<adGroupGoogleId>",
"headlines": [],
"descriptions": [],
"finalUrl": "<finalUrl>"
}google_ads_enable_entity
Switch a campaign, ad group or ad to ENABLED. Always requires a human approval: this is the step that starts spending money, and it is refused outright when the entity is under this project’s ROAS floor.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
level | campaign | ad_group | ad | yes | Which level the Google Ads id names |
googleId | string | yes | The entity’s Google Ads id, e.g. from google_ads_list_campaigns |
{
"level": "campaign",
"googleId": "<googleId>"
}google_ads_list_ad_groups
List ad groups with performance metrics and CPC bids, optionally narrowed to one campaign. An ad group is Google’s equivalent of a Meta ad set.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
from | string | no | Start date (YYYY-MM-DD). Defaults to 29 days ago. |
to | string | no | End date (YYYY-MM-DD). Defaults to today. |
campaignGoogleId | string | no | Narrow to one campaign |
google_ads_list_ads
List individual ads with performance metrics, ad type, final URL and the first headline/description, optionally narrowed to one campaign or ad group.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
from | string | no | Start date (YYYY-MM-DD). Defaults to 29 days ago. |
to | string | no | End date (YYYY-MM-DD). Defaults to today. |
campaignGoogleId | string | no | Narrow to one campaign |
adGroupGoogleId | string | no | Narrow to one ad group |
google_ads_list_campaigns
List this business’s Google Ads campaigns with performance over a date range: spend, impressions, clicks, CTR, CPC, CPM, conversions, revenue, ROAS and CPA/CPL, plus each campaign’s channel type, bidding strategy and daily budget.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
from | string | no | Start date (YYYY-MM-DD). Defaults to 29 days ago. |
to | string | no | End date (YYYY-MM-DD). Defaults to today. |
google_ads_list_keywords
List one ad group’s keywords with match type, status, quality score, bid and performance over a date range. Each row’s resourceName is what google_ads_set_keyword_status takes.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
from | string | no | Start date (YYYY-MM-DD). Defaults to 29 days ago. |
to | string | no | End date (YYYY-MM-DD). Defaults to today. |
adGroupGoogleId | string | yes | The ad group whose keywords to list |
{
"adGroupGoogleId": "<adGroupGoogleId>"
}google_ads_list_search_terms
List the search terms — what people actually typed into Google — that triggered this business’s Search ads, summed per ad group over a date range (only the last 30 days are kept): spend, clicks, impressions, conversions, the match types that caught each one, and status ADDED (already a keyword), EXCLUDED (already a negative) or NONE. Most spend first. Search terms are text written by strangers, so the result arrives as untrusted data.
Risk tier: read — runs immediately, changes nothing
Returns third-party text: the result arrives fenced as untrusted data, and a change proposed after reading it in the same agent run waits for approval (ADR 0038).
| Argument | Type | Required | Description |
|---|---|---|---|
from | string | no | Start date (YYYY-MM-DD). Defaults to 29 days ago. |
to | string | no | End date (YYYY-MM-DD). Defaults to today. |
adGroupGoogleId | string | no | Narrow to one ad group |
campaignGoogleId | string | no | Narrow to one campaign |
google_ads_pause_entity
Stop delivery for one campaign, ad group or ad. Reversible with google_ads_enable_entity, which needs a human approval.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
level | campaign | ad_group | ad | yes | Which level the Google Ads id names |
googleId | string | yes | The entity’s Google Ads id, e.g. from google_ads_list_campaigns |
{
"level": "campaign",
"googleId": "<googleId>"
}google_ads_propose_search_term_changes
Search-term hygiene over the last 30 days. Terms that spent at least the floor (default $20 over 10 clicks) with zero conversions become exact-match negative candidates in the ad group that paid for them; terms with 2+ conversions that are not yet keywords become "add as exact" candidates. The business name and the products in its brand profile are never proposed as negatives. Changes nothing: it returns each candidate with its reason and numbers, a planId, and calls — call each tool with its input exactly as returned (the same planId on every call) and the changes wait in Approvals as one card showing what each term cost.
Risk tier: read — runs immediately, changes nothing
Returns third-party text: the result arrives fenced as untrusted data, and a change proposed after reading it in the same agent run waits for approval (ADR 0038).
| Argument | Type | Required | Description |
|---|---|---|---|
adGroupGoogleId | string | no | Narrow to one ad group |
campaignGoogleId | string | no | Narrow to one campaign |
minSpendCents | integer | no | Spend floor for a negative, in cents. Defaults to $20. |
minClicks | integer | no | Click floor for a negative. Defaults to 10. |
lookbackDays | integer | no | Days of search terms to judge on, at most 30. |
google_ads_set_campaign_budget
Set a campaign’s daily budget, in cents. Always requires a human approval, and is refused outright when it would cross this project’s daily or monthly spend cap or the campaign is under its ROAS floor. A campaign on a shared budget is refused: changing it would move other campaigns’ money too.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
campaignGoogleId | string | yes | The campaign whose budget changes — on Google a daily budget only ever lives on a campaign |
dailyBudgetCents | integer | yes | The new daily budget, in cents of the Google Ads account’s currency |
confirmLargeChange | boolean | no | Set true to deliberately make a jump at or over the project’s single-step budget limit (a doubling by default). Ceilings on total spend still apply and cannot be confirmed away. |
planId | string | no | The planId from budget_reallocation_plan, when this change is one row of that plan. Omit for a one-off change. |
{
"campaignGoogleId": "<campaignGoogleId>",
"dailyBudgetCents": 0
}google_ads_set_keyword_status
Set one keyword to ENABLED or PAUSED. A keyword carries no budget of its own — its campaign’s budget and the project’s caps still bound spend — so this runs without an approval under the default grant. Undoable from the decision log.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
criterionResourceName | string | yes | The keyword’s resourceName, as google_ads_list_keywords returns it |
status | ENABLED | PAUSED | yes | — |
{
"criterionResourceName": "<criterionResourceName>",
"status": "ENABLED"
}TikTok Ads
| Tool | Tier | What it does |
|---|---|---|
tiktok_ads_create_ad_group | high | Create a TikTok Ads ad group (paused) |
tiktok_ads_create_campaign | high | Create a TikTok Ads campaign (paused) |
tiktok_ads_enable_entity | high | Enable a TikTok Ads campaign, ad group or ad |
tiktok_ads_list_ad_groups | read | List TikTok Ads ad groups |
tiktok_ads_list_ads | read | List TikTok Ads ads |
tiktok_ads_list_campaigns | read | List TikTok Ads campaigns |
tiktok_ads_pause_entity | low | Pause a TikTok Ads campaign, ad group or ad |
tiktok_ads_set_budget | high | Change a TikTok Ads daily budget |
tiktok_ads_create_ad_group
Create an ad group under an existing TikTok campaign, with its own daily budget and country targeting. It lands DISABLED and delivers nothing until it is enabled separately, but it creates a real object in a real ad account, so it always requires a human approval.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
campaignTiktokId | string | yes | The campaign the ad group belongs to |
name | string | yes | — |
dailyBudgetCents | integer | yes | Daily budget in cents — TikTok requires one on every ad group it creates. |
countries | string[] | yes | ISO-3166-1 alpha-2 country codes to target (US, GB). Checked against the project’s geo guardrails, then translated to TikTok location ids; an unmapped country is refused. |
optimizationGoal | CLICK | CONVERT | REACH | VIDEO_VIEW | LEAD_GENERATION | no | The event TikTok optimizes delivery for. CLICK needs no pixel history. |
bidPriceCents | integer | no | A fixed bid in cents. Omit to let TikTok bid automatically (BID_TYPE_NO_BID). |
scheduleStartTime | string | no | YYYY-MM-DD HH:mm:ss in the advertiser’s timezone. Omit to start as soon as the ad group is enabled. |
{
"campaignTiktokId": "<campaignTiktokId>",
"name": "<name>",
"dailyBudgetCents": 0,
"countries": []
}tiktok_ads_create_campaign
Create a TikTok campaign with its own daily budget in the linked advertiser. It lands DISABLED and delivers nothing until tiktok_ads_enable_entity is approved separately, so this does not start spend — but it does create a real object in a real ad account, so it always requires a human approval.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
name | string | yes | The campaign name, as it will appear in TikTok Ads |
objectiveType | TRAFFIC | CONVERSIONS | LEAD_GENERATION | REACH | VIDEO_VIEWS | no | TRAFFIC is the safe default for a brand-new campaign: CONVERSIONS and LEAD_GENERATION need a working pixel or lead form and an optimization event with history. |
dailyBudgetCents | integer | yes | Daily budget in cents. Nothing is spent until the campaign is enabled separately. |
{
"name": "<name>",
"dailyBudgetCents": 0
}tiktok_ads_enable_entity
Switch a TikTok campaign, ad group or ad to ENABLE. Always requires a human approval: this is the step that starts spending money, and it is refused outright when the entity is under this project’s ROAS floor.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
level | campaign | ad_group | ad | yes | Which level the TikTok Ads id names |
tiktokId | string | yes | The entity’s TikTok Ads id, e.g. from tiktok_ads_list_campaigns |
{
"level": "campaign",
"tiktokId": "<tiktokId>"
}tiktok_ads_list_ad_groups
List ad groups with performance metrics, optimization goal, bid and budget, optionally narrowed to one campaign. TikTok calls this rung an ad group, where Meta has an ad set — but unlike Google, a TikTok ad group can carry its own daily budget.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
from | string | no | Start date (YYYY-MM-DD). Defaults to 29 days ago. |
to | string | no | End date (YYYY-MM-DD). Defaults to today. |
campaignTiktokId | string | no | Narrow to one campaign |
tiktok_ads_list_ads
List individual ads with performance metrics, ad format, landing page URL and the ad text / call to action, optionally narrowed to one campaign or ad group.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
from | string | no | Start date (YYYY-MM-DD). Defaults to 29 days ago. |
to | string | no | End date (YYYY-MM-DD). Defaults to today. |
campaignTiktokId | string | no | Narrow to one campaign |
adGroupTiktokId | string | no | Narrow to one ad group |
tiktok_ads_list_campaigns
List this business’s TikTok Ads campaigns with performance over a date range: spend, impressions, clicks, CTR, CPC, CPM, conversions, revenue, ROAS and CPA/CPL, plus each campaign’s objective, budget mode and daily budget.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
from | string | no | Start date (YYYY-MM-DD). Defaults to 29 days ago. |
to | string | no | End date (YYYY-MM-DD). Defaults to today. |
tiktok_ads_pause_entity
Stop delivery for one TikTok campaign, ad group or ad. Reversible with tiktok_ads_enable_entity, which needs a human approval.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
level | campaign | ad_group | ad | yes | Which level the TikTok Ads id names |
tiktokId | string | yes | The entity’s TikTok Ads id, e.g. from tiktok_ads_list_campaigns |
{
"level": "campaign",
"tiktokId": "<tiktokId>"
}tiktok_ads_set_budget
Set a TikTok campaign’s or ad group’s daily budget, in cents. Always requires a human approval, and is refused outright when it would cross this project’s daily or monthly spend cap or the entity is under its ROAS floor. An entity that is not on BUDGET_MODE_DAY is refused: a lifetime budget, an uncapped one, and an ad group whose campaign optimizes the budget all have no daily number to change.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
level | campaign | ad_group | yes | Which rung carries the budget. Unlike Google, a TikTok daily budget lives on the campaign OR the ad group — whichever one currently reports BUDGET_MODE_DAY. |
tiktokId | string | yes | The campaign or ad group whose daily budget changes |
dailyBudgetCents | integer | yes | The new daily budget, in cents of the TikTok advertiser’s currency |
confirmLargeChange | boolean | no | Set true to deliberately make a jump at or over the project’s single-step budget limit (a doubling by default). Ceilings on total spend still apply and cannot be confirmed away. |
planId | string | no | The planId from budget_reallocation_plan, when this change is one row of that plan. Omit for a one-off change. |
{
"level": "campaign",
"tiktokId": "<tiktokId>",
"dailyBudgetCents": 0
}Organic social
| Tool | Tier | What it does |
|---|---|---|
social_cancel_post | low | Cancel a scheduled social post |
social_create_post | high | Publish or schedule a social post |
social_get_post | read | Read one social post |
social_list_accounts | read | List connected social accounts |
social_list_comments | read | List comments on social posts |
social_list_posts | read | List social posts |
social_post_insights | read | Read how recent social posts performed |
social_reply_to_comment | high | Reply to a social comment |
social_schedule_post | high | Schedule a social post from the library or the creative studio |
social_cancel_post
Stop a draft or scheduled post before it publishes. Only destinations still pending are cancelled — anything already published stays published, because nothing can unsay it. Refused once a post has finished publishing.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
postId | string | yes | Social post id from social_list_posts |
{
"postId": "<postId>"
}social_create_post
Compose one caption plus optional media and publish it to the named connected accounts, now or at scheduledAt. Always requires a human approval. With promotionId, the caption carries that promotion’s code.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
caption | string | yes | — |
mediaAssetIds | string[] | no | — |
targets | object[] | yes | — |
scheduledAt | string | no | — |
marketingAssetId | string | no | — |
promotionId | string | no | A promotion id from promotions_list. Appends "Use code CODE for …" to the caption unless the caption already contains the code. Never type a code into the caption that promotions_list did not return |
{
"caption": "<caption>",
"targets": []
}social_get_post
Fetch one composed post in full: its caption, media, schedule and every destination’s own status, external post id and error. This is how you find out whether a post that was approved actually went out, and where it failed if it did not.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
postId | string | yes | Social post id from social_list_posts |
{
"postId": "<postId>"
}social_list_accounts
List this business’s connected publishing destinations (Facebook Pages, Instagram accounts, TikTok creators, YouTube channels, Google Business Profile locations) with their status. Call this first to get the socialAccountIds social_create_post needs.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
social_list_comments
List comments people left on this business’s posts (Facebook, Instagram, YouTube — including posts published outside Marketing OS) and Google reviews of its Business Profile locations (platform gbp, with a 1–5 rating), newest first — filter to unanswered or flagged, or one platform. Each has the commentId social_reply_to_comment takes. TikTok comments cannot be read by this app.
Risk tier: read — runs immediately, changes nothing
Returns third-party text: the result arrives fenced as untrusted data, and a change proposed after reading it in the same agent run waits for approval (ADR 0038).
| Argument | Type | Required | Description |
|---|---|---|---|
platform | facebook_page | instagram | tiktok | youtube | gbp | no | Narrow to one platform |
filter | all | unanswered | flagged | no | all, unanswered (no reply yet) or flagged |
limit | integer | no | At most this many, newest first |
social_list_posts
List composed social posts newest first, with each post’s per-destination target status, external post id and error.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
status | draft | scheduled | publishing | published | partially_failed | failed | cancelled | no | Narrow to one post status, e.g. scheduled |
social_post_insights
Per connected platform: the newest published posts with their +72h reach, views and engagement, the averages over them, days since the last post, and how many posts are already scheduled or waiting for approval. Also lists creative-studio outputs that passed the brand-voice review and have never been posted — their studioOutputId is what social_schedule_post takes. Cite these numbers when proposing a post.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
platform | facebook_page | instagram | tiktok | youtube | gbp | no | Narrow to one platform |
limit | integer | no | How many of the newest published posts to return per platform |
social_reply_to_comment
Post one public reply under a comment on the business’s own post, or the owner’s reply to a Google review, as the business. Waits for a human approval unless the business has granted autonomy for this risk level. Refused if the comment already has a reply, and on TikTok, which has no reply API.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
commentId | string | yes | Social comment id from social_list_comments |
message | string | yes | The public reply, exactly as it should appear under the comment |
{
"commentId": "<commentId>",
"message": "<message>"
}social_schedule_post
Schedule one post whose media is already in this business’s library: an uploaded image or video (assetId, from list_media_files) or a creative-studio output (studioOutputId, from social_post_insights). There is no way to pass an image URL. Requires an evidence line citing social_post_insights. Waits for a human approval unless the business has granted autonomy for this risk level.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
targets | object[] | yes | Connected accounts to post to, by socialAccountId from social_post_insights |
caption | string | yes | The caption, in the brand voice |
assetId | string | no | An image or video from list_media_files. Give this or studioOutputId, not both |
studioOutputId | string | no | A creative-studio output from social_post_insights.unpostedStudioOutputs |
scheduledAt | string | yes | ISO-8601 instant in the future. Leave room for the owner to approve it first |
evidence | string | yes | One sentence citing social_post_insights numbers that justify this post, e.g. "last 4 Reels averaged 2.1k views; this one reuses the top angle" |
promotionId | string | no | A promotion id from promotions_list. Appends "Use code CODE for …" to the caption unless the caption already contains the code. Never type a code into the caption that promotions_list did not return |
{
"targets": [],
"caption": "<caption>",
"scheduledAt": "<scheduledAt>",
"evidence": "<evidence>"
}Content and creative
| Tool | Tier | What it does |
|---|---|---|
create_marketing_asset_draft | low | Draft a marketing asset |
create_media_folder | low | Create an asset library folder |
get_marketing_asset | read | Read one marketing asset |
list_marketing_assets | read | List marketing assets |
list_media_files | read | List uploaded images and videos |
list_media_folders | read | List asset library folders |
save_media_to_library | low | Save a file to the asset library |
studio_create_batch | low | Generate Studio variants |
studio_get_batch | read | Read a Studio batch |
studio_list_batches | read | List Studio batches |
studio_quota | read | Studio allowance |
update_marketing_asset | low | Revise a marketing asset |
video_create_captions | low | Caption a video |
video_create_slideshow | low | Make a slideshow video |
video_get_job | read | Read a video edit |
video_list_jobs | read | List video edits |
video_resize | low | Resize a video |
create_marketing_asset_draft
Create a new draft asset (v1) for a human to review. The draft is inert: nothing reaches the outside world until it is approved and published.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
type | blog_post | email | ad_copy | landing_page | social_post | yes | What kind of asset this is |
body | object | yes | — |
note | string | no | Why this revision exists |
{
"type": "blog_post",
"body": {}
}create_media_folder
Create a folder in the asset library, at the top level or inside another folder. Only when the owner asked for a new folder — never to tidy up unprompted.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
name | string | yes | The folder’s name, e.g. "Spring campaign" |
parentFolderId | string | no | Folder id from list_media_folders to create it inside. Omit for the top level. |
{
"name": "<name>"
}get_marketing_asset
Fetch one marketing asset in full, including its revision history.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
assetId | string | yes | Marketing asset id from list_marketing_assets |
{
"assetId": "<assetId>"
}list_marketing_assets
List this business’s marketing assets (blog posts, emails, ad copy, landing pages, social captions) with their status and version, most recently updated first.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
list_media_files
List this business’s uploaded media — images and videos with their kind, category, dimensions, size, library folder (folderId, null = the top level) and a short-lived URL. These are the ids social_create_post takes as media and that campaign plans build image ads from. Uploading new files is done in the Marketing OS app, or by attaching one in the Terminal.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
list_media_folders
List the folders in this business’s asset library, each with its id, its full path (e.g. "Campaigns / Spring") and how many files it holds. Read this before filing a file, so the folders you offer are the ones that exist.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
save_media_to_library
Save an image or video to the asset library, in a folder or at the top level. Use it for a file the owner attached in this conversation once they have said they want it kept and where. It also moves a file that is already in the library to another folder.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
assetId | string | yes | Media id — of a file attached in this conversation, or from list_media_files |
folderId | string | no | Folder id from list_media_folders or create_media_folder. Omit for the top level of the library. |
{
"assetId": "<assetId>"
}studio_create_batch
Generate on-brand ad images from one product photo in the library: one variant per angle × placement (2 angles × 3 placements = 6 images), each with ad copy that passed Brand Voice review. Every image counts against the organization’s monthly Studio cap (studio_quota shows what is left); a batch bigger than what remains is refused. The job is asynchronous: this returns the batch with every variant queued — call studio_get_batch for progress and the finished asset ids. It never publishes or spends ad money.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
sourceAssetId | string | yes | An image asset in this business’s library (asset_list) — the product photo. |
angles | studio | lifestyle | in_use | flat_lay | bold_graphic | seasonal[] | yes | Creative angles; one variant per angle per placement. |
placements | feed | reels | square | landscape[] | yes | Where the ads run; each placement is generated at its own aspect ratio. |
productNotes | string | no | — |
offer | string | no | — |
{
"sourceAssetId": "<sourceAssetId>",
"angles": "studio",
"placements": "feed"
}studio_get_batch
One Creative Studio batch and its variants: each variant’s angle, placement and status (queued, running, succeeded, failed), and once it succeeds the generated image’s assetId and its ad copy. Use the assetId with the social, video or Meta ad tools.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
batchId | string | yes | The batch id, from studio_create_batch or studio_list_batches. |
{
"batchId": "<batchId>"
}studio_list_batches
The 50 most recent Creative Studio batches for this business, newest first: status (queued, running, completed, failed), angles, placements and how many variants succeeded or failed. Use studio_get_batch for the variants themselves.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
studio_quota
This month’s Creative Studio image allowance for the organization: the monthly cap, how many images are used or reserved by running batches, how many remain, and the estimated cost per image in cents. Check it before a large studio_create_batch.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
update_marketing_asset
Replace an asset’s body (creating a new version) and/or move it along the status ladder. published is refused here — publishing goes through the approvals inbox.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
assetId | string | yes | Marketing asset id from list_marketing_assets |
status | draft | in_review | approved | published | archived | no | — |
body | object | no | — |
note | string | no | — |
{
"assetId": "<assetId>"
}video_create_captions
Burn captions into a video from the library, as a new video asset. Captions come from the audio (source "transcribe") or from a script you pass ({ text }), styled bold-center or lower-third. The render is asynchronous: this returns the job queued — call video_get_job for its status and the new asset id. Renders count against the plan’s platform allowance and are refused once it is spent. The source is never overwritten.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
source | string | object | yes | "transcribe" to caption from the audio (needs a transcription model), or { text } to time a script. |
style | bold-center | lower-third | no | — |
assetId | string | yes | The video asset to edit, from asset_list. The source is never overwritten. |
{
"source": "<source>",
"assetId": "<assetId>"
}video_create_slideshow
Turn 3–8 images (Studio outputs or any library image, in play order) into a 10–20 second silent video with slow Ken Burns motion, optionally with one caption line, counted toward a Studio batch. The render is asynchronous: this returns the job queued — call video_get_job for its status and the new asset id. Renders count against the plan’s platform allowance and are refused once it is spent. The source is never overwritten.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
imageIds | string[] | yes | — |
durationSec | integer | no | — |
placement | reels | square | landscape | no | — |
captionText | string | no | — |
music | string | no | — |
batchId | string | yes | The Studio batch the slideshow belongs to, from studio_list_batches. |
{
"imageIds": [],
"batchId": "<batchId>"
}video_get_job
One video edit: its kind, status (queued, running, succeeded, failed), the step it is on, the error if it failed, and outputAssetId — the new video — once it succeeds.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
jobId | string | yes | The job id, from a video_* create tool or video_list_jobs. |
{
"jobId": "<jobId>"
}video_list_jobs
The 50 most recent video edits for this business (captions, resize, slideshow), newest first, optionally for one source video or one Studio batch: status, the step it is on, and the output asset id once it succeeds.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
sourceAssetId | string | no | Only edits of this video. |
batchId | string | no | Only edits counted toward this Studio batch. |
video_resize
Reshape a video from the library for a placement — reels (9:16), square (1:1) or landscape (16:9) — by cropping to fill or padding inside the placement’s safe zone (default pad), as a new video asset. The render is asynchronous: this returns the job queued — call video_get_job for its status and the new asset id. Renders count against the plan’s platform allowance and are refused once it is spent. The source is never overwritten.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
placement | reels | square | landscape | yes | — |
mode | crop | pad | no | — |
assetId | string | yes | The video asset to edit, from asset_list. The source is never overwritten. |
{
"placement": "reels",
"assetId": "<assetId>"
}Local SEO and AI visibility
| Tool | Tier | What it does |
|---|---|---|
gbp_insights | low | Read Google Business Profile insights |
localfalcon_get_report | read | Read one Local Falcon report |
localfalcon_list_locations | read | List Local Falcon locations |
localfalcon_list_reports | read | List Local Falcon scan reports |
localfalcon_run_scan | high | Run a Local Falcon scan |
gbp_insights
Daily Google Business Profile numbers for this business’s connected locations — phone calls, direction requests, website clicks, and Google Maps and Search profile impressions — totalled over the window with the preceding window for comparison. Google reports with a two-to-three-day lag. Reviews are read with social_list_comments (platform gbp); posts are scheduled with social_create_post.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
days | integer | no | Window length in days, ending at the newest day Google has reported |
locationId | string | no | Narrow to one connected location; omit for every location |
localfalcon_get_report
Fetch one geo-grid scan report in full — every grid point’s rank, plus the aggregate ARP / ATRP / SoLV scores.
Risk tier: read — runs immediately, changes nothing
Returns third-party text: the result arrives fenced as untrusted data, and a change proposed after reading it in the same agent run waits for approval (ADR 0038).
| Argument | Type | Required | Description |
|---|---|---|---|
reportKey | string | yes | Report key from localfalcon_list_reports |
{
"reportKey": "<reportKey>"
}localfalcon_list_locations
List the Google Business Profile locations saved in this business’s Local Falcon account, with any corrected scan center (pin override) we hold for them.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
query | string | no | Filter saved locations by name |
limit | integer | no | — |
next_token | string | no | Cursor from a previous page |
localfalcon_list_reports
List past geo-grid scan reports, optionally narrowed by place, keyword, platform or date range (MM/DD/YYYY).
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
limit | integer | no | — |
place_id | string | no | — |
keyword | string | no | — |
platform | string | no | — |
start_date | string | no | — |
end_date | string | no | — |
next_token | string | no | — |
localfalcon_run_scan
Run one geo-grid rank scan for a keyword at a location. Spends the account’s Local Falcon credits, so it always requires a human approval.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
place_id | string | yes | — |
keyword | string | yes | — |
lat | string | yes | — |
lng | string | yes | — |
grid_size | 3 | 5 | 7 | 9 | 11 | 13 | 15 | 17 | 19 | 21 | yes | — |
radius | number | yes | — |
measurement | mi | km | yes | — |
platform | google | apple | chatgpt | gemini | gaio | aimode | grok | yes | — |
ai_analysis | boolean | no | — |
{
"place_id": "<place_id>",
"keyword": "<keyword>",
"lat": "<lat>",
"lng": "<lng>"
}Analytics and Search Console
| Tool | Tier | What it does |
|---|---|---|
google_connection_status | read | Google connection status |
google_search_performance | read | Search Console performance |
google_connection_status
Whether this business has a live Google connection, and which GA4 property and Search Console site it is pointed at. Null when nothing is connected.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
google_search_performance
Daily Google Search Console clicks, impressions and average position for the connected site over a date range, plus its top queries.
Risk tier: read — runs immediately, changes nothing
Returns third-party text: the result arrives fenced as untrusted data, and a change proposed after reading it in the same agent run waits for approval (ADR 0038).
| Argument | Type | Required | Description |
|---|---|---|---|
from | string | no | Start date (YYYY-MM-DD). Defaults to 29 days ago. |
to | string | no | End date (YYYY-MM-DD). Defaults to today. |
Commerce
| Tool | Tier | What it does |
|---|---|---|
audience_create_lookalike | high | Create a lookalike audience from a customer segment |
audience_list | read | List customer segments and their audiences |
commerce_create_meta_catalog | low | Create the Meta product catalog |
commerce_create_product_set | low | Create a Meta product set |
commerce_get_economics | read | Get store unit economics |
commerce_get_meta_catalog | read | Get the Meta product catalog |
commerce_list_products | read | List store products |
commerce_pause_ads_for_out_of_stock | low | Pause ads for sold-out products |
audience_create_lookalike
Create a lookalike audience on Meta, Google Ads or TikTok Ads seeded from one of the store’s customer segments. The segment must already be synced to that channel (see audience_list). Spends nothing by itself, but it is a new targeting object in the ad account, so it always waits for a human approval. top_ltv_20 at 1 % is usually the strongest seed.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
segment | all_buyers | buyers_90d | top_ltv_20 | lapsed_180d | yes | The source segment: all_buyers, buyers_90d, top_ltv_20 (usually the best seed) or lapsed_180d |
channel | meta | google_ads | tiktok_ads | yes | Which ad platform to create the lookalike on (meta, google_ads or tiktok_ads). The segment must already be synced there. |
ratio | number | no | Lookalike size as a share of the country, 0.01–0.10. Meta uses it as is; Google and TikTok map it to NARROW (≤2 %), BALANCED (≤5 %) or BROAD. |
country | string | yes | ISO-3166-1 alpha-2 country the lookalike is built in, e.g. US |
{
"segment": "all_buyers",
"channel": "meta",
"country": "<country>"
}audience_list
The four built-in customer segments computed from the connected store (all buyers, buyers in the last 90 days, top 20 % by lifetime value, lapsed 180+ days) with their sizes, and for each channel (Meta, Google Ads, TikTok Ads and the store’s Klaviyo) whether the segment is synced there, the platform audience or list id, the size and match rate the platform reports, and any lookalikes already created from it. Use it before proposing a lookalike or suggesting retargeting or a win-back email.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
commerce_create_meta_catalog
Create this business’s Meta product catalog, owned by a Business Manager, with a daily scheduled feed that Meta fetches from us — built from the store mirror, one item per variant. Needed once, before any product set or catalog campaign; calling it again returns the existing catalog. Requires the catalog_management Meta permission, and says so by name when the connection lacks it.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
businessManagerId | string | no | The Meta Business Manager that will own the catalog. Omit when the connected Meta user has exactly one; with several the call is refused and lists them to pick from. |
name | string | no | Defaults to "<shop name> catalog" |
commerce_create_product_set
Create a named product set in the Meta catalog, filtered by a Shopify collection handle, any of a list of tags, and/or a minimum gross margin (10–90, in steps of 10). Conditions are ANDed. A catalog campaign advertises exactly one product set.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
name | string | yes | Shown in Ads Manager, e.g. "Summer sale" |
filter | object | yes | collection?, tags?, minMarginPct? — ANDed together |
{
"name": "<name>",
"filter": {}
}commerce_get_economics
The unit economics derived from the connected store: average order value over the last 90 days, catalog gross margin, and the break-even ROAS those imply — next to the values currently set on the business. Use it to ground any judgment about whether spend is profitable rather than asking the user for these numbers.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
commerce_get_meta_catalog
The Meta catalog fed from the connected store, if one exists: its catalog id, the public feed URL Meta fetches daily, and every product set with its filter, its Meta id and how many store products it matches today. Product set ids from here are what meta_create_catalog_campaign takes.
Risk tier: read — runs immediately, changes nothing
Takes no arguments.
commerce_list_products
List the products in this business’s connected store, highest-revenue first, with price range, gross margin, stock on hand and units sold over the last 90 days. Use it before proposing what to advertise, and to check whether something is still in stock.
Risk tier: read — runs immediately, changes nothing
Returns third-party text: the result arrives fenced as untrusted data, and a change proposed after reading it in the same agent run waits for approval (ADR 0038).
| Argument | Type | Required | Description |
|---|---|---|---|
limit | integer | no | How many products to return, richest-selling first |
outOfStockOnly | boolean | no | Only products with zero inventory across every variant |
commerce_pause_ads_for_out_of_stock
Find the ads that are selling a product the store has run out of, and pause them. Matching is inferred — a product page link in the ad’s creative, or the product title in the ad’s name or headline — so it DEFAULTS TO A DRY RUN: call it with dryRun true, show the operator the matched ads and why each matched, and only call it again with dryRun false once they agree. Pausing is reversible with meta_activate_entity.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
dryRun | boolean | no | Leave true to only LIST the ads that would be paused and why. Set false to actually pause them. Always dry-run first and show the list — the product/ad match is inferred, not reported by Meta. |
Promotions and promo codes
| Tool | Tier | What it does |
|---|---|---|
promotions_create_draft | low | Save a promotion |
promotions_list | read | List promotions |
promotions_performance | read | Read a promotion’s orders and revenue |
promotions_publish_to_store | high | Create a discount code on the store |
promotions_create_draft
Save a promo code the business runs, usually with a partner: code, partner, discount and product scope, optionally dates and a landing path. Use only the code and discount the user stated — never invent either. Nothing leaves the app: with a Shopify store connected the promotion is saved as a draft and the code does NOT exist on the store until promotions_publish_to_store is approved; with no store it is saved active, ready to quote in ads and posts. The code, discount and scope cannot be changed afterwards.
Risk tier: low — runs immediately, reversible and audited
| Argument | Type | Required | Description |
|---|---|---|---|
code | string | yes | The code exactly as the user gave it (2–32 letters, digits, "-" or "_"; upper-cased). Never make one up |
partnerName | string | yes | Who the promotion is with: the collaborator, creator or partner (e.g. "Tory Media") |
discountType | percent | fixed | yes | "percent" (discountValue 10 = 10% off) or "fixed" (10 = 10.00 off in the store currency) |
discountValue | number | yes | — |
currency | string | no | — |
scope | string | object | no | "all" for every product, or { productIds } with ids from commerce_list_products |
startsAt | string | no | ISO-8601 with offset. Omitted = starts when it goes live |
endsAt | string | no | ISO-8601 with offset, after startsAt. Omitted or null = no end date |
landingPath | string | no | A storefront path such as "/products/copper-kettle" (never a full URL). Omitted with a single product in scope = that product’s page |
{
"code": "<code>",
"partnerName": "<partnerName>",
"discountType": "percent",
"discountValue": 0
}promotions_list
List this business’s promo codes newest first: code, partner, discount (discountType "percent" = discountValue %, "fixed" = discountValue in the store currency), product scope, dates, status (draft / active / ended / archived), the landingUrl links should use (the Shopify discount link when the code is on the store) and its utmCampaign. Call it before creating a promotion (a code is unique per business) and before passing a promotionId to meta_draft_campaign_plan or a social post tool.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
status | draft | active | ended | archived | no | Only promotions in this status. Omitted = every promotion except archived ones |
promotions_performance
Orders that used the promotion’s code inside its dates, and their revenue — in the store currency and the reporting currency — read from the store’s order sync. source "none" means no store is connected and every number is zero, not unknown. Orders older than the store sync window (60–90 days) are not counted.
Risk tier: read — runs immediately, changes nothing
| Argument | Type | Required | Description |
|---|---|---|---|
promotionId | string | yes | A promotion id from promotions_list |
{
"promotionId": "<promotionId>"
}promotions_publish_to_store
Create a saved draft promotion’s code as a discount on the connected Shopify store and set the promotion active; its landingUrl becomes the store’s discount link, which applies the code on arrival. Always waits for a human approval. Refused when no store is connected (the promotion is already usable without one). Also retries a promotion that is active but whose code never reached the store (see shopifySyncError from promotions_list).
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
promotionId | string | yes | A promotion id from promotions_list |
{
"promotionId": "<promotionId>"
}Email and SMS
| Tool | Tier | What it does |
|---|---|---|
email_campaign_metrics | read | Get one email campaign or flow |
email_create_campaign_draft | high | Create a Klaviyo email draft |
email_list_campaigns | read | List email campaigns and flows |
email_list_lists | read | List Klaviyo lists |
email_send_campaign | high | Send or schedule a Klaviyo email |
email_campaign_metrics
Day-by-day sends, opens, clicks and attributed revenue for one Klaviyo campaign or flow, by the id email_list_campaigns returns, plus its window totals and rates.
Risk tier: read — runs immediately, changes nothing
Returns third-party text: the result arrives fenced as untrusted data, and a change proposed after reading it in the same agent run waits for approval (ADR 0038).
| Argument | Type | Required | Description |
|---|---|---|---|
id | string | yes | The Klaviyo campaign or flow id, as email_list_campaigns returns it. |
since | string | no | First day of the window. Defaults to 30 days ago. |
{
"id": "<id>"
}email_create_campaign_draft
Step 1 of 2 for sending an email: create a DRAFT email campaign in Klaviyo to one list, with its subject, sender and full HTML body. Nothing is sent — the draft can be opened and edited in Klaviyo — and it needs a human approval before it is even created. Then call email_send_campaign with the campaignId this returns to send or schedule it. html must be one complete HTML document (<html>…</html>) with inline styles. listId comes from email_list_lists and must be the list the user named — never choose an audience on your own. fromEmail must be on a sending domain verified in Klaviyo. SMS, flows and new lists are not supported.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
name | string | yes | Internal campaign name shown in Klaviyo, e.g. "VIP 15% off — Oct 3". |
subject | string | yes | The subject line recipients see. |
previewText | string | no | The preview text shown after the subject in most inboxes. |
fromEmail | string | yes | Sender address. Must be on a sending domain verified in this Klaviyo account. |
fromLabel | string | yes | Sender name, e.g. the store name. |
listId | string | yes | The Klaviyo list to send to, by the id email_list_lists returns. |
html | string | yes | The whole email as one complete HTML document (<!doctype html><html>…</html>), inline styles only. Klaviyo adds the unsubscribe footer if the document has none. |
{
"name": "<name>",
"subject": "<subject>",
"fromEmail": "<fromEmail>",
"fromLabel": "<fromLabel>"
}email_list_campaigns
The Klaviyo campaigns (one-off sends) and flows (automations such as welcome, abandoned cart or win-back) with activity in a window, highest attributed revenue first: sends, opens, clicks, open and click rates, and the order value Klaviyo attributed to each, in the Klaviyo account’s currency. SMS is included, marked by medium. This is revenue with no ad spend behind it — report it beside the paid channels, never add it to their revenue or ROAS.
Risk tier: read — runs immediately, changes nothing
Returns third-party text: the result arrives fenced as untrusted data, and a change proposed after reading it in the same agent run waits for approval (ADR 0038).
| Argument | Type | Required | Description |
|---|---|---|---|
since | string | no | First day of the window, YYYY-MM-DD. Defaults to 30 days ago. The mirror holds 90 days. |
kind | campaign | flow | no | Only campaigns (one-off sends) or only flows (automations). Both by default. |
limit | integer | no | How many to return, highest attributed revenue first. |
email_list_lists
Every list in the connected Klaviyo account, id and name, alphabetically. Use it to find the listId email_create_campaign_draft takes for the list the user named. Never pick a list the user did not name — ask instead.
Risk tier: read — runs immediately, changes nothing
Returns third-party text: the result arrives fenced as untrusted data, and a change proposed after reading it in the same agent run waits for approval (ADR 0038).
Takes no arguments.
email_send_campaign
Step 2 of 2: send a Klaviyo draft campaign created by email_create_campaign_draft, now or at sendAt (an ISO-8601 instant in the future). A sent email cannot be recalled, so this always waits for a human approval, and the card names the lists and how many profiles they hold. Only a campaign still in Draft can be sent. Never send a draft the user has not asked to send.
Risk tier: high — staged for approval before anything happens
| Argument | Type | Required | Description |
|---|---|---|---|
campaignId | string | yes | The draft campaign id email_create_campaign_draft returned. |
sendAt | string | no | ISO-8601 instant to schedule the send for, e.g. 2026-10-02T09:00:00-04:00. Omit to send as soon as it is approved. Leave the owner time to approve it first. |
{
"campaignId": "<campaignId>"
}Something wrong or missing on this page? Tell us and we will fix it.