Overview
Magnus is an analytics platform for mobile and web app publishers. It aggregates marketing spend from ad networks, subscription and in-app revenue, ad-network monetization, payment-provider outcomes and first-party product-analytics events into one place, so a publisher can ask how a campaign, an app or an experiment is actually performing. Over MCP the app exposes that data read-only. Three tools answer questions: marketing_timeline_report for acquisition and monetization performance (installs, spend, revenue, ROMI, forecasts), finances_risk_report and finances_risk_approval_rates for payment risk (approval rate, refunds, fraud alerts, disputes, chargebacks). A second group answers product-analytics questions from the publisher's own SDK events: event counts, funnels, retention, user-property segments and A/B experiment configuration. Everything else is a lookup helper that exists to feed those tools with real ids, filter values and metric definitions, because app ids, event names and metric semantics are account-specific and cannot be guessed. Access is scoped to the companies the signed-in user is an active member of, and that membership is re-checked on every single call, so the model can never reach another tenant's data. The OAuth scope the app requests, mcp:analytics.read, grants read access only: no tool in the server can create, update or delete anything.
Tools
apps_app_project_list
ChatGPTapps_app_project_note_list
ChatGPTbase_segment_options
ChatGPTbase_tag_create
ChatGPTbase_tag_list
ChatGPTcompanies_company_list
ChatGPTmanaged_connections says, per ad network, whether you hold a managed ad-account connection of your own in that company: connected lists the networks that work, expired the ones whose token has to be renewed in Magnus. The expenses_optimus_facebook_*_create tools and their lookups read exactly one company and only one where Facebook is connected, so this field is where their company_ids comes from — take the id from here rather than trying companies one by one.companies_company_notification_ads_management_create
ChatGPTcompanies_company_notification_ads_management_update
ChatGPTcompanies_company_notification_creative_tags_create
ChatGPTcompanies_company_notification_creative_tags_update
ChatGPTcompanies_company_notification_fetch
ChatGPTdata.arguments is exactly the argument set of the rule's kind — the same keys, the same shapes — plus status. To change a rule, copy data.arguments, change what should differ, add notification_id (this rule's id) and send the whole of it to the kind's _update tool: companies_company_notification_<kind>_update, where kind is data.kind. To copy a rule, send data.arguments without status to the kind's _create tool. Beside the arguments: the app the rule reads (app_id is the argument, app.name is for the person), the Slack channels with their names, who created it and when it last ran. A rule can carry what no create tool can send — filters nobody uses any more, a forecast horizon, a metric outside the catalog, a status handler that would activate rather than pause. Those sit under data.not_authorable, named, and meta.caveats says what an update does with them: the filters and the horizon are kept as they are, an activating status becomes a pause, and a metric outside the catalog has to be replaced before the rule can be updated at all. When NOT to use: to find a rule by name or kind, list them with companies_company_notification_list; to see what a rule did, read companies_company_notification_log.companies_company_notification_list
ChatGPTkind is the tab the rule sits on in the UI and decides which tools change it: marketing (a Slack alert on marketing metrics), ads_management (changes live budgets, bids or statuses in an ad account when the condition holds), creative_tags (tags creatives by performance), retention_chart (an alert on a saved evtruck retention chart). A paused rule is stored and inert; an enabled one runs at its schedule. Nothing is cut: the answer holds every rule of the covered companies that passes the filters, and the filters narrow rather than search — kinds, statuses and app_ids are exact sets. A rule that is not here does not exist in those companies, or your role cannot see them. When NOT to use: this does not say why a rule fired or stayed silent — that is companies_company_notification_log — and it does not read the messages Magnus itself sends about expired tokens; those are not exposed here.companies_company_notification_log
ChatGPTcompanies_company_notification_marketing_create
ChatGPTcompanies_company_notification_marketing_update
ChatGPTcompanies_company_notification_retention_chart_create
ChatGPTcompanies_company_notification_retention_chart_update
ChatGPTevtruck_event_device
ChatGPTevtruck_event_names
ChatGPTevtruck_event_params
ChatGPTevtruck_event_statistics
ChatGPTevtruck_funnel
ChatGPTevtruck_install_device
ChatGPTevtruck_retention
ChatGPTevtruck_retention_chart_list
ChatGPTevtruck_user_property_device
ChatGPTevtruck_user_property_history_device
ChatGPTexpenses_creative_tag_replace
ChatGPTexpenses_facebook_business_credit_list
ChatGPTerror and no lines, which is the answer to "why is X empty", not a Magnus fault. A row holds only the lines the business owns: a line another business shared with it - Facebook lists it under the receiver too, as a node of its own with the granting line's legal entity - is answered under the business that owns it and left out here, and so is the stub a revoked share leaves, every sum zero. A legal entity therefore appears at most once, under the business whose line it is; a line shared from a business outside the covered set is not in the answer at all. Nothing comes from Magnus tables. Units. Every amount is a decimal in the currency of its own line, stated in currency next to it; lines differ, so never assume USD and never add lines in different currencies. credit_available is what is left to spend, balance what is used, max_balance the limit, online_max_balance the limit for self-serve spend. Rows with error. A business Facebook refused - most often because that user lacks finance access in it - or does not list among their businesses at all keeps its row with error and an empty extended_credits, and the answer is partial with those businesses in meta.caveats; the fix is on the Facebook side (a business admin grants the role or finance access), a reconnect of the connection named in meta.connection, or another company in company_ids. An empty extended_credits with no error means no credit line pays for the business - it pays by card. When NOT to use: this is not spend - marketing_timeline_report answers that; not the ad-account list - expenses_optimus_facebook_ad_account_list does. It changes nothing.expenses_google_billing_list
ChatGPToutstanding is assembled: invoices_not_yet_due - the invoices issued in the last 3 months whose due date has not passed, credit memos subtracted - plus accrued_this_month - the cost Google has served this month and not invoiced yet. An invoice past its due date is taken as paid: an overdue one looks the same and is not in the number. Every amount is a decimal in currency, the payer's; payers differ, so never assume USD and never add rows in different currencies. Which payers. The payers behind the accounts of that company that spent in the last 30 days (at most 20 accounts, dearest first; more makes the answer partial and says so), through their current billing setup. An account paying by card has no payer here and no invoices: it is charged automatically and is left out. No rows means none of the spending accounts is on monthly invoicing. meta.accounts_considered is how many accounts were looked at. What it reads. One connection of that company, named in meta.connection: its first linked collector connection by id, the same rule as the Facebook credit-lines tool. A refusal is about that Google user's access - to billing, or to an account - and the fix is on the Google side, a reconnect of that connection, or another company in company_ids. A payer whose invoices or costs could not be read keeps its row with error and null amounts, never a partial sum; an account that could not be attributed to a payer is named in meta.caveats; either makes the answer partial. When NOT to use: this is not spend - marketing_timeline_report answers that; not the per-account budget or the invoice detail. It changes nothing.expenses_optimus_entity_fetch
ChatGPTeditable is the set of fields this server can write, not_editable says why each of the others cannot, bounds gives the allowed range as absolute numbers — half to double the current value, capped going up by this server's ceiling for the field — and limits says what is left: calls changing this object and calls per person this hour, and for the calendar day (UTC) how many objects may still be created, how many deleted, and how much money, in USD, may still be set in motion. For a Facebook campaign it also says whether the budget is held by the campaign or by its ad sets, which is what decides whether an ad set under it may carry one; for a Facebook ad set and ad it returns the settings themselves — targeting, goal, the creative — in the words the create tools accept, the targeting being Facebook's own object verbatim and whole, and for a Google Ads campaign, ad group, ad or asset group the settings in the words the expenses_optimus_google__create tools accept. When NOT to use — not to find an id: expenses_optimus_entity_list finds an object by its name, spend, installs and revenue per campaign come from marketing_timeline_report, and this tool answers about one object whose id you already have. Not to read performance: it returns settings and no metric of any kind. Not for the other networks Magnus collects (Apple Search Ads, Snapchat, Unity Ads, ironSource, AppLovin, Mintegral, Pinterest) — their campaigns appear in the same reports, but here they answer with a refusal, because Magnus has no write integration for them. Ids — `entity_id` is the network's own id, passed as a string. Do not send it as a number: Facebook ids run past what a JSON number holds exactly and the low digits are silently lost. The same id can exist twice when one ad account is linked by two companies; then the answer is a refusal listing each candidate with an `entity_ref`, and you call again with the same `entity_id` plus the `entity_ref` of the one you meant. `entity_ref` is opaque: copy it verbatim, never build one. Reaching a Google Ads object without Magnus's record — `customer_id` together with `entity_type` reads the object directly on Google under your connection in the company that reaches the account: a campaign created moments ago without an ad, which Magnus has not recorded yet, is read that way, with its children as Google lists them now and no `entity_ref`. The Google update cells take the same pair. Left out, the object is found through Magnus's record, as before. Units — `budget` and `bid` are decimal numbers in the ad account's own currency, in its main unit: a $50.00 daily budget is 50, not 5000. `ad_account.currency` says which currency, and two accounts on two networks can both read 120 and mean different money. On a Facebook campaign or ad set the same money is also stated live under Facebook's own names — `daily_budget` or `lifetime_budget`, and `bid_amount` — whichever the object carries; those are the names expenses_optimus_facebook__create and _update take, and editable and bounds use them too, while current.budget and current.bid are the mirror's cross-network names. status is active or paused, this server's own two words; the network's own wording (ENABLE, ADSET_PAUSED, PENDING_REVIEW) is reported separately as current.status_vendor and a campaign set to active can still read PENDING_REVIEW there. What comes back depends on `entity_type`, so read that first. A campaign carries current with its objective, budget and bid strategy, budget_owner saying whether the budget is held here or by its ad sets, a live top-level bid_strategy in the …expenses_optimus_entity_list
ChatGPTentity_id, with the name, the status, the parent, the app and the company of each. Start here whenever a person names an object and you have no id for it. It reads what Magnus recorded — the rows the collectors wrote on their last run and every card read refreshed since — not the ad network, so a campaign with no spend is listed the same as one with spend, which marketing_analytic_dimension_values cannot do: that helper lists only what spent or installed in a period. A freshly created object appears once Magnus has recorded it: a Facebook or TikTok object at once, a Google Ads campaign with its first ad group or asset group, since Magnus attributes a Google campaign to an app by the landing page of its ads. Narrowing. entity_type picks the level, campaign by default; network one of the three, all three by default; name_contains any part of the name, case-insensitive; status Magnus's word for the object's state — active, paused or deleted; parent_id the network id of the campaign above an ad group or the ad group above an ad; app_ids the Magnus apps. Rows come newest-synced first, at most limit; when more match, meta.completeness is partial and meta.caveats says so — narrow rather than raise the limit. Response Guidelines: 1. entity_id is the id the card and every write tool take; parent_id is the same kind of id one level up, and on a campaign it is the ad account. 2. status is Magnus's word and status_vendor the network's own last recorded one; both are as of synced_at, so confirm the current state with the card before acting on it. 3. Two rows with one entity_id under two companies are the same object linked twice — say which company you mean when you go on. 4. An object missing here is not proof it does not exist on the network: it may not be recorded yet, or it may be outside the covered companies — say that, rather than that it is gone. When NOT to use: it says nothing about spend or results (marketing_timeline_report does), it does not read the network (the card does), and it lists nothing this server cannot write to — Facebook, Google Ads and TikTok only.expenses_optimus_facebook_ad_account_list
ChatGPTaccount_id is a bare numeric string with no act_ prefix, which is the form every create tool takes. Ids are strings everywhere on this server because Facebook's are longer than a float can hold exactly. Response Guidelines: an account appears here only if your managed Facebook connection in the named company can see it, so a missing account means the connection does not reach it, the account is inactive, or it is reached from another of your companies — say that rather than that the account does not exist. has_funding_source false means the account cannot spend even once something is turned on. The list is not paginated and is not truncated: it is every active account that connection reaches. When NOT to use: this does not report spend or performance — marketing_timeline_report does that. It also does not list campaigns; expenses_optimus_entity_fetch reads one object.expenses_optimus_facebook_ad_create
ChatGPTcreative_id, and duplicating an ad is exactly that: the same creative_id under a new name, because a creative is shared by design and re-authoring identical content only invites Facebook to hand the same id back. There are no creative content fields here. No titles, no bodies, no link, no images: an ad points at a creative and carries none of its content. If the content needs to change, author a new creative with expenses_optimus_facebook_creative_create and pass its id here. There is no status parameter. The ad is created paused; activation is expenses_optimus_facebook_ad_update. If a creative you just made is not found yet, one retry is fair. A freshly created object can take a moment to become visible. One repeat is reasonable; an access error is not a timing problem and must not be retried, and never try other ids to see which works. Say what you are about to do before you do it — the ad set, the creative and the name — and let the person stop you. What a refusal means. Every refusal here happened before Facebook was called and nothing was created. One of them is the daily count: each person may create at most 300 objects a calendar day (UTC) through this server — ads included — and the refusal says when it resets. A failure on the creation itself is different: network_rejected is Facebook's own refusal with its message, and nothing was created; network_unreachable is Facebook giving no verdict, so the request may still have gone through — Magnus learns of the ad only at the next collection, and the sentence says to look for the name in Ads Manager before creating again. When NOT to use: this cannot change an ad set or a creative, and it cannot turn anything on.expenses_optimus_facebook_ad_update
ChatGPTcompany_ids comes from — this tool needs the write_company_id it answered, exactly one id — and it is what refreshes the values this tool measures a change against: the current budget, bid, status and name as Magnus holds them, and the absolute range a new value has to land in. Nothing is read from the network before the write, so a card read long ago means a corridor computed from a stale base; synced_at on the card says how old it is. Several fields per call. Send any of status, name, creative_id together; they go to Facebook as one request, applied together or refused together. Send only the arguments you change: an argument you send replaces its value whole — a list is the complete list after the change, an object the whole object — and an argument you leave out is not touched, so do not send back arguments copied from the card unchanged. A field that is refused stops the whole call before the network is reached, with every problem named, and nothing is changed. The answer reports each field's own outcome under changes. Each call counts once against the allowance: at most 3 calls per object and 300 calls per person per hour, and only calls that went through are counted — a refusal costs nothing but the round trip. Status, name and the creative are what an ad carries here: money lives one level up. If you meant to change a daily_budget or a bid_amount, the object you want is the ad set, and expenses_optimus_entity_fetch on this ad names it as parent. Creative — creative_id replaces the creative the ad shows with another creative of the same ad account. Facebook creatives are immutable, so this is how an ad's image, text or link changes: make the new creative with expenses_optimus_facebook_creative_create, then put its id on the ad here; the old creative stays in the account. The current one is creative_id on the card, and nothing is checked before Facebook — an id of another account or of a deleted creative comes back as Facebook's own refusal. Creative reports key on the ad's name, so a swap under the same name stays one creative in them; the ad goes through review again. Name — name renames the ad, 1 to 255 characters, and the current name is title on the card. Two things follow that the person has to hear first. Magnus keys creative-level cost rows by the ad's name as of each collection, so after a rename the ad appears as two creatives in creative reports: spend before the change under the old name, spend after it under the new one. And creative tags are parsed from the name's template — FB_<name>_<locale>_<format> — so a name outside it loses them. Say both before renaming an ad. Facebook allows two ads of one name, and so does this tool. Units — status is active or paused, this server's own two words. The network's own wording (ACTIVE, ADSET_PAUSED, PENDING_REVIEW) is what the card reports separately as current.status_vendor, and it is never accepted here. Say what you are about to do before you do it. One ad account is routinely shared by several companies, so the change is visible in the reports of every company that shares it, not only the one you passed. Ceilings and daily limits — going up, a budget or bid may not be set above this server's ceiling: 5000 USD a day for a campaign budget, 1000 USD for an ad set or ad group budget, 200 USD for a bid, converted into the ad account's currency; the card's bounds.max already includes it. Decreasing and pausing are always allowed. Each person may also set at most 25000 USD a calendar day (UTC) in motion through this server — budget increases, the budgets of objects switched on and created budgets, summed — and the answer's limits.per_day says what is left and when it resets.expenses_optimus_facebook_adgroup_update
ChatGPTcompany_ids comes from — this tool needs the write_company_id it answered, exactly one id — and it is what refreshes the values this tool measures a change against: the current budget, bid, status and name as Magnus holds them, and the absolute range a new value has to land in. Nothing is read from the network before the write, so a card read long ago means a corridor computed from a stale base; synced_at on the card says how old it is. Several fields per call. Send any of status, daily_budget, bid_amount, value_rule_set_id, name, targeting, adset_schedule together; they go to Facebook as one request, applied together or refused together. Send only the arguments you change: an argument you send replaces its value whole — a list is the complete list after the change, an object the whole object — and an argument you leave out is not touched, so do not send back arguments copied from the card unchanged. A field that is refused stops the whole call before the network is reached, with every problem named, and nothing is changed. The answer reports each field's own outcome under changes. Each call counts once against the allowance: at most 3 calls per object and 300 calls per person per hour, and only calls that went through are counted — a refusal costs nothing but the round trip. Budget and bid both live here on Facebook, which is why this cell carries them beside status — but a campaign using Advantage campaign budget holds the money itself, and then this ad set reports no budget of its own and the refusal says so and names the campaign. Change it there. Value rules — value_rule_set_id attaches one of the account's value rule sets, replacing the one attached, and an empty string detaches it. Take the id from expenses_optimus_facebook_value_rule_list; the card shows the attached set under value_rule_set. Nothing is checked before Facebook: a set the account does not hold, or one that does not fit the bid strategy — Facebook applies value rules only under LOWEST_COST_WITHOUT_CAP or COST_CAP, on this ad set or on its campaign when the campaign owns the budget — comes back as Facebook's own refusal. Attaching or detaching changes how the ad set bids from the next auction on. Targeting — targeting is Facebook's own targeting object, and it travels whole: take targeting from the card, change what should differ — a country added, an exclusion removed — and send all of it back. Facebook replaces the object with what it is sent, so a fragment drops every key it does not name, audiences and exclusions included; the card is read just before the call for that reason, since a change made in Ads Manager in between would be overwritten. Keys Facebook answers on a read and refuses on a write, age_range among them, are stripped here. Nothing is checked before Facebook beyond the object having geo_locations: an audience the account cannot use or a key it will not take comes back as Facebook's own refusal. Changing targeting resets the ad set's learning phase, and targeting that reaches the EU needs a beneficiary on the ad set, which this tool does not set — say both before the call. Ad scheduling — adset_schedule replaces the ad set's schedule with the list sent, whole, as targeting travels: take it from the card's authoring block, change the windows and send all of them back; an empty list turns scheduling off. Every window names whose clock it follows, timezone_type, never assumed: ADVERTISER is the ad account's own time zone (time_zone on expenses_optimus_facebook_ad_account_list), USER each viewer's own — ask when the person has not said which, and say it back. pacing_type travels with it — day_parting beside …expenses_optimus_facebook_adset_create
ChatGPTbudget_owner, and that single value decides whether this ad set may carry a budget at all. To duplicate an existing ad set, read its own card the same way: it answers these same fields in this same vocabulary — change what should differ and create, and whatever its authoring_unsupported names will not carry over. The parent campaign decides where the budget lives. Under a campaign that owns the budget, an ad set may not carry daily_budget, lifetime_budget, bid_strategy, bid_amount or bid_constraints — Facebook refuses it with "Must Use Campaign Bid Strategy". Under a campaign that does not, this ad set must carry a budget or nothing will ever be spent — daily_budget, or lifetime_budget with the end_time Facebook requires beside it, never both. If neither level has one, that is a decision nobody has made yet: ask, do not guess. The objective decides which goals are available. For a sales campaign that is usually OFFSITE_CONVERSIONS, or VALUE when the advertiser is optimising return on spend. This is guidance rather than a whitelist — some goals are gated per account, and Facebook is the final word: it answers "Performance goal isn't available with this objective" when the combination is not allowed for you. Targeting is Facebook's own object, whole. The keys are the Marketing API's and none is renamed here, so an ad set's targeting from expenses_optimus_entity_fetch travels into this argument verbatim, audiences and exclusions included. geo_locations is required: countries are two-letter codes and whole regions are country_groups — "Europe" is europe, not a list of countries you compose. Never invent ids for flexible_spec or custom_audiences: real ones come from the advertiser or from a card, never from a placeholder like 0 or 123. targeting_automation.advantage_audience defaults to 1, and with it on your age range is a suggestion Facebook may widen — only an explicit 0 makes it binding. Keys Facebook answers on a read and refuses on a write, age_range among them, are stripped before sending. Units. daily_budget, lifetime_budget and bid_amount are Facebook's own names and decimal amounts in the ad account's own currency: 50 means fifty dollars, not fifty cents. Meta's own MCP server and the raw Marketing API take integer minor units under the same names; never carry a number between the two. bid_constraints.roas_average_floor is not money — it is Facebook's hundredfold scale, where 200 means a return of two. Value rules. value_rule_set_id attaches one of the account's value rule sets — the advertiser's own statement of which ages, genders, places, devices or placements are worth a higher or lower bid. Take the id from expenses_optimus_facebook_value_rule_list and never invent one. Facebook applies a set only under the highest-volume strategy (LOWEST_COST_WITHOUT_CAP) or COST_CAP, so a set beside a bid cap or a return floor is refused here — and under a campaign that owns the budget it is the campaign's strategy that decides. value_rules_applied is not an argument: it is sent as true beside the id and disclosed under effective_arguments. Facebook warns that the overall cost per result may rise. Ad scheduling. adset_schedule runs the ads only in the hours and days it lists, as Facebook's own list of windows; the card of an ad set that has one shows it under the same name, and a copy carries it. Meta documents scheduling for a lifetime budget — this ad set's lifetime_budget with its end_time, or the campaign's when the campaign owns the budget — and its newer Ads Manager offers it on a daily budget too; this server sends the schedule as given, and the card read back states what Facebook kept under adset_schedule, …expenses_optimus_facebook_campaign_create
ChatGPTaccount_id and the company_id this tool needs, and the currency any budget here is stated in. To duplicate an existing campaign, read its card with expenses_optimus_entity_fetch — its objective, daily_budget or lifetime_budget and budget_owner are all there — and create with what should differ; the ad sets and ads are copied separately, each from its own card. Where the budget lives is decided here, and it cannot be changed later without moving the ad sets. Setting daily_budget or lifetime_budget makes this a campaign-budget campaign: Facebook then spreads that budget across its ad sets and refuses any ad set that carries a budget of its own, with "Must Use Campaign Bid Strategy". Leaving both out makes it an ad-set-budget campaign, and then every ad set needs its own. Neither is a default to fall into — ask which one is wanted if the person has not said. The two names are Facebook's own and a campaign carries one of them: a lifetime_budget needs stop_time, the end Facebook requires, and is spread over the days until it. Either kind carries a bid_strategy: Facebook requires one on an ad-set-budget campaign and picks its own on a budgeted one, so when you leave it out this server sends LOWEST_COST_WITHOUT_CAP itself and says so in meta.effective_arguments. Units. daily_budget and lifetime_budget are decimal amounts in the ad account's own currency: 50 means fifty dollars in a USD account, not fifty cents. Meta's own MCP server and the raw Marketing API take integer minor units under the same names; never carry a number between the two. Read the currency from the account list rather than assuming USD. Ceilings and daily limits. A campaign daily budget above this server's ceiling — 5000 USD a day, stated in the account's currency by the account list under ceilings — is refused before Facebook is called, with no override: larger budgets are set in the ad manager. A lifetime budget is judged by the day too: its ceiling is that figure times the days until stop_time. A refusal that names a hundredth of what you sent is telling you that you sent cents. Each person may also create at most 300 objects a calendar day (UTC) through this server, campaigns, ad sets, creatives and ads together, and set at most 25000 USD a day in motion — created budgets, a lifetime one whole, budgets switched on and budget increases, summed. The card and every write answer report what is left under limits.per_day. There is no status parameter. Everything this server creates is paused. Turning it on is expenses_optimus_facebook_campaign_update, which has the guards for it. Say what you are about to do before you do it. Name the account, the objective and the budget arrangement, and let the person stop you. What a refusal means. Every refusal here happened before Facebook was called and nothing was created. Fix what it names and call again; do not retry the same call unchanged. A failure on the creation itself is different: network_rejected is Facebook's own refusal with its message, and nothing was created; network_unreachable is Facebook giving no verdict, so the request may still have gone through — Magnus learns of the campaign only at the next collection, and the sentence says to look for the name in Ads Manager before creating again. When NOT to use: this does not create ad sets, ads or creatives — those are separate tools and a campaign on its own never delivers. It also cannot activate anything.expenses_optimus_facebook_campaign_update
ChatGPTcompany_ids comes from — this tool needs the write_company_id it answered, exactly one id — and it is what refreshes the values this tool measures a change against: the current budget, bid, status and name as Magnus holds them, and the absolute range a new value has to land in. Nothing is read from the network before the write, so a card read long ago means a corridor computed from a stale base; synced_at on the card says how old it is. Several fields per call. Send any of status, daily_budget, name together; they go to Facebook as one request, applied together or refused together. Send only the arguments you change: an argument you send replaces its value whole — a list is the complete list after the change, an object the whole object — and an argument you leave out is not touched, so do not send back arguments copied from the card unchanged. A field that is refused stops the whole call before the network is reached, with every problem named, and nothing is changed. The answer reports each field's own outcome under changes. Each call counts once against the allowance: at most 3 calls per object and 300 calls per person per hour, and only calls that went through are counted — a refusal costs nothing but the round trip. Units — daily_budget is Facebook's own name for the field and a decimal number in the ad account's own currency, in its main unit: 50 means $50.00. The Meta Ads MCP server and the raw Marketing API express the same number as an integer in the minor unit, so never carry a value between the two. The card states the current one live as daily_budget beside the mirror's current.budget. status is active or paused, this server's own two words; the network's own wording is never accepted here. Name — name is the one non-money field here: no corridor and no ceiling, 1 to 255 characters, and the current name is title on the card. Facebook allows two campaigns of one name, and so does this tool. What a refusal means — a change outside half-to-double the current value is refused with the range named, and there is no override argument: the honest answer is a smaller change or a visit to the ad manager. A campaign whose budget sits on its ad sets has no campaign budget to change, and the refusal says so and sends you to the ad sets, which is where it is. Every refusal happened before Facebook was called; a failure Facebook answered carries its own reason. Say what you are about to do before you do it. One ad account is routinely shared by several companies, so a budget change is visible in the reports of every company that shares it, not only the one you passed. It also pauses Magnus's own budget automation for this campaign for a while, which is intended — a person's decision outranks the schedule — but the person should know it. When NOT to use — not for a Facebook ad set or ad (they have their own tools), not for Google Ads or TikTok, not to read anything, and not to change what no tool here writes: objective, schedule and bid strategy are the ad manager's. Ceilings and daily limits — going up, a budget or bid may not be set above this server's ceiling: 5000 USD a day for a campaign budget, 1000 USD for an ad set or ad group budget, 200 USD for a bid, converted into the ad account's currency; the card's bounds.max already includes it. Decreasing and pausing are always allowed. Each person may also set at most 25000 USD a calendar day (UTC) in motion through this server — budget increases, the budgets of objects switched on and created budgets, summed — and the answer's limits.per_day says what is left and when it resets.expenses_optimus_facebook_change_history
ChatGPTdating before date_from. include_effective_status adds Facebook's own status flips (review, learning, a campaign pause cascading onto every ad); it is off by default because one pause writes one flip per ad. Nothing before 2025-12-10 exists, and ad-level rows before 2026-04-01 are not read. coverage says how many objects of the tree have a recorded state at all; a gap there is named in caveats. Dating — exact: the day of Facebook\'s own edit stamp, in the ad account\'s day, which is the day marketing_timeline_report uses; observed: the day Magnus saw the new state; interval: the edit happened between date_from and date_to inclusive, the day itself is unknown; created: the object was created that day and new_value holds its initial settings. Records are chronological by date_from. Ids and units — entity_id is the network\'s own id as a string; a JSON number loses digits. adgroup is the Facebook ad set, as everywhere on this server. Money is in the ad account's currency in major units: a $50.00 daily budget is 50. status is active or paused, otherwise Facebook's own word lowercased; effective_status is always Facebook's word lowercased. A targeting record carries only the changed top-level keys, on both sides, and lists them in changed_keys. A budget_owner record says the daily budget moved between the campaign and its ad sets.expenses_optimus_facebook_creative_create
ChatGPTimages or videos, never both and never neither, at most 4 of them. A video's thumbnail_hash is the cover shown before it plays; leave it out and Facebook picks a frame itself. With more than one medium you should give each its own placements, or they all compete for the same surfaces. The texts are variants, not a sequence. Every headline in titles can appear with every body in bodies; Facebook picks. titles is what Ads Manager calls the headline and bodies is its primary text — those are the Marketing API names and this tool keeps them. Links may carry Facebook macros verbatim. {{campaign.id}}, {{placement}} and the rest pass through untouched and are not broken URLs — most creatives in this account use them. caption is the domain shown under the ad, so it has to look like a domain; arbitrary text such as "Shop now" is rejected by Facebook. Say what you are about to do before you do it — the page, the media by filename, the link and the texts — and let the person stop you. What a refusal means. A refusal that names an argument happened before Facebook was called and nothing was created; so did the daily count — each person may create at most 300 objects a calendar day (UTC) through this server, creatives included. Media ids are checked against the uploader's records and then against the ad account itself, because Facebook does not verify them at creation: a wrong one produces a creative that renders as nothing rather than an error. Ids copied from an existing creative's card pass the account check. A refusal that starts with "Facebook refused to create it" is Facebook's own answer; its "object you are trying to access is not visible to you" sentence almost always means this connection holds no role on the page_id sent — read that page's card with expenses_optimus_facebook_page_list first: a page it cannot answer for is a page this tool cannot publish under, and media belonging to another page's creatives follows the same rule. When NOT to use: this does not create an ad and nothing about it delivers on its own. It also cannot change an existing creative.expenses_optimus_facebook_custom_audience_create
ChatGPTtargeting — custom_audiences or excluded_custom_audiences, each {id} — through expenses_optimus_facebook_adset_create or expenses_optimus_facebook_adgroup_update, and expenses_optimus_facebook_custom_audience_delete is what removes it. Two kinds, one per call. A rule-based audience is rule: people a pixel saw, an app reported or a page engaged, kept for retention_seconds and filtered by event and URL — Facebook works out the subtype (WEBSITE, APP, ENGAGEMENT) and the retention from the rule itself, so neither is an argument here; prefill asks Facebook to include the people it already saw before the audience existed. A lookalike is origin_audience_id with lookalike_spec: people like the seed audience, in a country, as close as type or ratio says; Facebook builds it even from a seed too small to use and then reports delivery_status 300, so read the seed's size first. Not made here: a customer list (uploading people), sharing an audience with another account, and the value-based kind — those are Ads Manager's. Call expenses_optimus_facebook_custom_audience_list first, every time. It answers the account's existing audiences, the exact shape of a rule and a lookalike_spec as Facebook holds them, the pixel_id an existing WEBSITE audience reads — the one to build from, since a pixel the pixel list shows may still be refused as a source — and the seed a lookalike starts from. The pixel comes from expenses_optimus_facebook_pixel_list, the page from expenses_optimus_facebook_page_list, the app id off the card of a campaign promoting that app. This server sends rule and lookalike_spec to Facebook exactly as given and translates nothing, so a shape Facebook does not take comes back in Facebook's own words — and Facebook checks the shape, not the contents: a misspelt event or field makes an audience that matches nobody. Ids. account_id is the bare numeric string from expenses_optimus_facebook_ad_account_list, no act_ prefix, and company_ids is the company_id that list answered beside it — exactly one. Limits — at most 600 objects may be created per person per calendar day (UTC) through this server, audiences and ad objects together. Only creations that went through are counted; a refusal costs nothing but the round trip. Say what you are about to do before you do it, and wait for a yes. Name the account, the audience's name and who is in it in plain words — which pixel, app or page, which events, how many days back; for a lookalike, the seed and how close. What a refusal means — arguments_invalid, no_linked_account, token_expired and daily_limit_reached happened before Facebook was called and nothing was created; fix what the sentence names, then call again, never the same call unchanged. network_rejected is Facebook's own verdict, in its words, and nothing was created: the two to know are the account not having accepted the Custom Audience terms, which a person does once in the browser at the link the sentence gives, and a pixel or app the account may not build from, which the sentence says how to replace. network_unreachable is Facebook giving no verdict: the audience may exist, so read the list before creating again. When the answer is partial, the audience was created and reading it back did not confirm it: read caveats, say it was created, and do not repeat the call. When NOT to use — not to put an audience on an ad set, which is targeting on the ad set tools; not to change one, which is expenses_optimus_facebook_custom_audience_update; not to upload a customer list or share an audience, which this server does not do; not for Google Ads or TikTok, whose audiences are not made here.expenses_optimus_facebook_custom_audience_delete
ChatGPTcustom_audience_id comes from and where name is read: name has to be the audience's current name exactly, as the proof that the id was read and not guessed. A name that does not match is refused without saying what the real one is. The list also shows lookalike_audience_ids: a seed with lookalikes cannot be deleted, on Facebook's own rule, until they are deleted first, one call each. Ads using it are read live. Before deleting, every ad targeting the audience — including or excluding it — is read from Facebook, and any ad that is not deleted or archived refuses the deletion, naming how many there are, how many of them are running, and the first few with their ad sets. An audience in an ad set's exclusions is in use as much as one in its inclusions: take it out of targeting with expenses_optimus_facebook_adgroup_update first, or delete those ads. Ids. account_id is the bare numeric string from expenses_optimus_facebook_ad_account_list, no act_ prefix, and company_ids is the company_id that list answered beside it — exactly one. Limits — deletions share the hourly allowance of 300 calls per person with the update tools, and at most 100 objects may be deleted per person per calendar day (UTC), audiences and ad objects together. Only deletions that went through are counted. Say what you are about to do before you do it, and wait for a yes. Name the audience, its kind and its size, and say that the deletion cannot be undone. What a refusal means — arguments_invalid, entity_not_found, name_mismatch, lookalikes_exist, in_use, rate_limited and daily_limit_reached happened before the deletion was sent and nothing was deleted. entity_not_found is Facebook's one answer for an audience already deleted and for one this connection may not see — Facebook does not tell the two apart, so read the list: an id missing from it is gone. network_rejected is Facebook's own verdict, in its words, and nothing was deleted. network_unreachable is Facebook giving no verdict: the audience may be gone, so read the list before sending it again. When the answer is partial, the deletion went out and reading the audience back did not confirm it gone: read caveats, say the audience was deleted, and never call this tool again for the same id. When NOT to use — not to take an audience off an ad set, which is targeting on expenses_optimus_facebook_adgroup_update and leaves the audience in place; not for campaigns, ad sets or ads, which expenses_optimus_facebook_entity_delete deletes; not to remove people from a customer list, which this server does not do.expenses_optimus_facebook_custom_audience_list
ChatGPTcustom_audience_id comes from for the ad set's targeting (custom_audiences and excluded_custom_audiences, each {id}) and for expenses_optimus_facebook_custom_audience_update and _delete, and where the shape of a rule and a lookalike_spec is read before expenses_optimus_facebook_custom_audience_create makes one. Call this before naming an audience. Every kind Facebook holds is here under subtype: WEBSITE (people a pixel saw), APP (people an app reported), ENGAGEMENT (people who engaged with a page or its content), LOOKALIKE (people like a seed audience, lookalike_spec saying how close and where), CUSTOM (a customer list uploaded by hand) and the rarer ones. rule is the audience's own definition — event sources, retention, filters — decoded from the JSON text Facebook answers; lookalike_spec is Facebook's object with origin (the seed), type, ratio and the country or countries; lookalike_audience_ids names the lookalikes built from this audience, which is what stops its deletion; pixel_id is the pixel a WEBSITE audience reads, "0" when none. Search. name_contains matches a part of the name, case as Facebook matches it — the only match Facebook offers on a name, so an exact name is found by containing it and picking the row whose name equals it. subtype narrows to one kind. There is no paging: at most limit rows come back, and when the account holds more, meta.completeness is partial and meta.caveats says so — narrow rather than raise the limit. Ids. account_id is the bare numeric string from expenses_optimus_facebook_ad_account_list, with no act_ prefix, and company_ids is the company_id that list answered beside it. custom_audience_id is a string too. Response Guidelines: approximate_count_lower_bound and approximate_count_upper_bound are Facebook's rough size, -1 when it has none; a freshly made audience reads 1000 / 1000, which is the floor Facebook shows before it has counted, so never judge a new audience by its size. delivery_status and operation_status are Facebook's {code, description}: 200 is ready, 300 too small or still updating, 433 a lookalike Facebook could not build, 441 still finding people, 450 out of date — quote the description rather than the code. retention_days is 0 on a lookalike and on a customer list. permission_for_actions.can_edit false means the audience is shared into this account and cannot be changed or deleted from it. A field Facebook did not answer is null, not missing. When NOT to use: this creates and changes nothing; it does not say how an audience performed, which marketing_timeline_report answers; and it does not list Facebook's saved audiences, which are not custom audiences and have no id here.expenses_optimus_facebook_entity_delete
ChatGPTcompany_ids comes from — this tool needs the write_company_id it answered, exactly one id — where entity_id is confirmed to be the object the person means, and where title is read: name has to be that title exactly, as the proof that the id was read and not guessed. A name that does not match is refused without saying what the real one is. Pause first. A running object is refused: switch it off with the level's update tool, which is reversible, and delete it afterwards. Deleting is for an object nobody will turn on again; to stop spending, pausing is the answer and it can be undone. One level, one call. The tool works out from the id whether it is a campaign, an ad set or an ad. Deleting a campaign deletes its whole tree on Facebook in one go; the answer names how many ad sets and ads Magnus knew under it as cascade, from the Magnus mirror, so a child created moments ago may not be counted. An ad in an ad set with a creative sequence cannot be deleted at all, and Facebook says so. Limits — deletions share the hourly allowance of 300 calls per person with the update tools, and at most 100 objects may be deleted per person per calendar day (UTC): the object itself, whatever it takes down with it. Only deletions that went through are counted; a refusal costs nothing but the round trip. Say what you are about to do before you do it, and wait for a yes. Name the object, its level and, for a campaign or an ad set, how many children go with it. One ad account is routinely shared by several companies, so the deletion is visible in the reports of every company that shares it, not only the one you passed. Automation rules in Magnus that name the object should be switched off; they will otherwise fail on their next run. What a refusal means — every refusal happened before Facebook was called and nothing was deleted. An object already deleted or archived is reported as such and is not an error; an object that is running is sent back to the update tool. When the answer is partial, the deletion went out and something afterwards could not be confirmed: read caveats, say the object was deleted, and never call this tool again for the same id. When NOT to use — not to stop spending, which is a pause; not to archive, which this server does not do; not for Google Ads or TikTok objects, which are deleted in their own ad manager; and not to delete a creative, which has no delete here.expenses_optimus_facebook_media_file_list
ChatGPTimage_hash and video_id that expenses_optimus_facebook_creative_create takes come from. Call this first, every time, and search it by name. These files were uploaded from Google Drive and kept their real filenames, so a person asking for "the summer bundle creative" can be answered by passing that as file_name. Never invent a hash or a video id: Facebook does not check that an image hash exists when a creative is created, so a made-up one produces a creative that renders as nothing rather than an error. Ids. image_hash is a 32-character hex string and video_id is numeric — a creative takes one kind or the other, never both in the same call. company_ids is the one company whose files to read — the same id the creative will be created under. Response Guidelines: this reads Magnus's own uploader records, not Facebook's media library, and every file in it belongs to the company whose connection uploaded it. If the answer is empty for a company that plainly runs ads, the uploader has simply never been used there — say that, rather than that the account has no creatives. Files whose upload failed are left out because they carry no usable id. Rows are ordered newest first and paged: read meta.pagination.next_offset and pass it back as offset when there is more. When NOT to use: this is not a performance report and says nothing about how a creative did — marketing_timeline_report grouped on creative answers that.expenses_optimus_facebook_page_list
ChatGPTpage_id every creative is published under — and, given a page_id, the Instagram account attached to that page. Call this before creating a creative, every time. A creative without a page is refused by the Marketing API with "Facebook Page is Missing", and a page you merely know the name of is not the same as one your connection holds a role on. This is what tells the two apart. Two modes. Without page_id you get the pages your connection in the named company has a role on, id and name. With one you get that page's card: its name, its instagram_user_id, and leadgen_tos_accepted, which says whether the page may run lead forms. Instagram is required. expenses_optimus_facebook_creative_create needs an instagram_user_id, so a page whose card answers null for it cannot be used for a creative yet — say so plainly and offer another page rather than creating without one, which is not possible here. Response Guidelines: a page missing from the list is one your connection has no role on, which is not the same as a page that does not exist — do not tell a person their page is gone. A card asked for by id refuses when the connection holds no role on that page, and that refusal is about your access rather than about the page. When NOT to use: this reports nothing about the page's audience, its posts or its performance. It exists to hand a page_id and an instagram_user_id to a creative.expenses_optimus_facebook_pixel_list
ChatGPTpixel_id you get the account's pixels, id and name. With one you get the events that pixel reported, each already shaped as a promoted_object — a custom_event_type on its own, or OTHER alongside the custom_event_str naming the event. Pass a whole entry through to expenses_optimus_facebook_adset_create unchanged. Ids. account_id is the bare numeric string from expenses_optimus_facebook_ad_account_list, with no act_ prefix, and company_ids is the company_id that list answered beside it. Pixel ids are strings too. Response Guidelines: the event list covers a short recent window — roughly yesterday until now — so it is what the pixel has been seeing lately and not everything it has ever seen. An event a person expects and does not find may simply not have fired recently; say that rather than that the pixel cannot report it. An empty event list is not a reason to invent a custom_event_type. When NOT to use: this counts nothing. It does not say how many conversions there were or what they were worth — marketing_timeline_report answers that.expenses_optimus_facebook_value_rule_create
ChatGPTvalue_rule_set_id, and expenses_optimus_facebook_value_rule_delete is what removes it. Call expenses_optimus_facebook_value_rule_list first, every time. It answers the account's existing sets — Facebook allows at most six on an account — and, in rules, the exact shape a rule takes: a name, adjust_sign INCREASE or DECREASE, adjust_value as the percentage (INCREASE 1 to 1000, DECREASE 1 to 90) and criterias on age, gender, operating system, device, location, placement, audience label or conversion location. When several rules match a person Facebook applies the first, so the order of the list is part of the set. Copy that shape: this server sends rules to Facebook exactly as given and translates nothing, so a rule Facebook does not take comes back in Facebook's own words. Ids. account_id is the bare numeric string from expenses_optimus_facebook_ad_account_list, no act_ prefix, and company_ids is the company_id that list answered beside it — exactly one. product_type is Facebook's own word: AUDIENCE for the sets ad sets attach, LEADGEN_ADS and OMNI_CHANNEL for those products; read it off an existing set when in doubt. Limits — at most 600 objects may be created per person per calendar day (UTC) through this server, sets and ad objects together. Only creations that went through are counted; a refusal costs nothing but the round trip. Say what you are about to do before you do it, and wait for a yes. Name the account, the set's name and each rule in plain words — who is worth more or less, and by how much. Facebook warns that value rules can raise the overall cost per result. What a refusal means — arguments_invalid, no_linked_account, token_expired and daily_limit_reached happened before Facebook was called and nothing was created; fix what the sentence names, then call again, never the same call unchanged. network_rejected is Facebook's own verdict on the rules, in its words, and nothing was created. network_unreachable is Facebook giving no verdict: the set may exist, so read the list before creating again. When the answer is partial, the set was created and reading it back did not confirm it: read caveats, say it was created, and do not repeat the call. When NOT to use — not to attach a set to an ad set, which is value_rule_set_id on the ad set tools; not to change a set, which is expenses_optimus_facebook_value_rule_update; not for Google Ads or TikTok, which have no value rules here.expenses_optimus_facebook_value_rule_delete
ChatGPTvalue_rule_set_id comes from and where name is read: name has to be the set's current name exactly, as the proof that the id was read and not guessed. A name that does not match is refused without saying what the real one is. Ids. account_id is the bare numeric string from expenses_optimus_facebook_ad_account_list, no act_ prefix, and company_ids is the company_id that list answered beside it — exactly one. Limits — deletions share the hourly allowance of 300 calls per person with the update tools, and at most 100 objects may be deleted per person per calendar day (UTC), sets and ad objects together. Only deletions that went through are counted. Say what you are about to do before you do it, and wait for a yes. Name the set and, if the list shows a last_attach_time, say that ad sets may be using it. What a refusal means — arguments_invalid, entity_not_found, already_deleted, name_mismatch, rate_limited and daily_limit_reached happened before the deletion was sent and nothing was deleted; a set already deleted is reported as such and is not an error. network_rejected is Facebook's own verdict, in its words, and nothing was deleted. network_unreachable is Facebook giving no verdict: the set may be gone, so read the list before sending it again. When the answer is partial, the deletion went out and reading the set back did not confirm it: read caveats, say the set was deleted, and never call this tool again for the same id. When NOT to use — not to detach a set from an ad set, which is an empty value_rule_set_id on expenses_optimus_facebook_adgroup_update and leaves the set in place; not for campaigns, ad sets or ads, which expenses_optimus_facebook_entity_delete deletes.expenses_optimus_facebook_value_rule_list
ChatGPTvalue_rule_set_id for expenses_optimus_facebook_adset_create comes from — each with the rules it holds, so you can tell the advertiser what attaching it would do. Call this before attaching a set. A value rule set is the advertiser's own statement of which audiences, placements or conversion locations are worth a higher or lower bid: each rule raises or lowers the bid by a percentage (INCREASE 1 to 1000, DECREASE 1 to 90) for the people matching its criteria — age, gender, operating system, device, location, placement, audience label or conversion location — and when several rules match, Facebook applies the first one. Facebook warns that the overall cost per result may rise. Read the set's name and rules here rather than guessing an id: an id that is not on this list is refused by the create tool. Ids. account_id is the bare numeric string from expenses_optimus_facebook_ad_account_list, with no act_ prefix, and company_ids is the company_id that list answered beside it. value_rule_set_id is a string too. Response Guidelines: the list is not paginated and is not truncated — Facebook allows at most six sets on an account. rules is passed through exactly as Facebook answers it; describe a set from what it contains and do not summarise rules the answer does not hold. status is Facebook's own word (ACTIVE, DRAFT, DELETED); a DELETED set cannot be attached and the create tool refuses it. A field Facebook did not answer is null, not missing. When NOT to use: this creates or edits nothing — sets are made in Ads Manager. It also does not report how a set performed; marketing_timeline_report answers spend and results.expenses_optimus_google_ad_account_list
ChatGPTcustomer_id every expenses_optimus_google__create tool starts from, with the currency it bills in, its time zone, the manager account it is reached through and the largest budget and bid this server will set in it. Call this first, every time. Do not carry a customer id over from a report or from memory: this answer is read live from Google under your own connection and is what says whether the account is reachable under it at all. A create call against an account that is not on this list is refused before anything is built. It reads one company per call — the one in `company_ids`, which is the company where you hold a managed Google Ads connection; companies_company_list says which those are under managed_connections. Units. Every amount on this server is a decimal in the account's own `currency_code`, its main unit — 50 means fifty dollars in a USD account. The Google Ads API and Google's own MCP server state money in micros, a millionth of the unit; never carry a number between the two. Read `currency_code` before quoting any amount to a person and never assume USD. An account billing in a currency without hundredths — JPY, KRW, ISK and a few others — is listed but cannot be created in through this server: the create tools refuse it with `currency_not_supported`. Ceilings. `ceilings` states, in the account's currency, the most this server will set: `campaign_budget` per day, `adgroup_budget` per day — Google has no ad group budget, it is here so the answer has the same shape across networks — and `bid`, which bounds a target CPA, a CPC bid ceiling and an ad group CPC bid alike. They are USD ceilings converted at today's rate; `converted` false means no rate could be read and the USD number is applied as is, which is stricter for every currency weaker than the dollar; `ceilings` is null when Google did not state the account's currency. A create above them is refused with no override — plan within them or leave the larger amount to the Google Ads interface. Ids.* customer_id is a string of digits with no dashes, which is the form every create tool takes; Google's interface shows the same id as 123-456-7890. manager_id is the manager (MCC) account your connection reaches this account through, null when it reaches it directly; the create tools take it from this same answer themselves, you never pass it. Response Guidelines: an account appears here only if your managed Google Ads connection in the named company can see it, so a missing account means the connection does not reach it, the account is cancelled or closed, or it is reached from another of your companies — say that rather than that the account does not exist. Manager accounts themselves are not listed; their client accounts are, and a client that is cancelled or closed is left out. An account the connection holds directly is listed whatever its status, except one Google refuses to read as not enabled — cancelled, deactivated or a sign-up never finished: it is left out and named in meta.caveats, so read the caveats before saying an account is missing. The list is not paginated and is not truncated: it is every account that connection reaches. When NOT to use: this does not report spend or performance — marketing_timeline_report does that. It does not list campaigns either: expenses_optimus_entity_list finds one by its name and answers the id the create tools and expenses_optimus_entity_fetch take.expenses_optimus_google_ad_create
ChatGPTname. Have the ad group's id in hand first, every time — from expenses_optimus_google_adgroup_create or from an existing group's card in expenses_optimus_entity_fetch — and the customer_id and company_id from expenses_optimus_google_ad_account_list. The group is read live before anything is built: a group under a Performance Max campaign is refused with the name of the tool that fits, and so is a format that does not match the family — responsive_search_ad goes under a Search group of type SEARCH_STANDARD, demand_gen_multi_asset_ad and demand_gen_video_responsive_ad under a Demand Gen group. Send exactly one of the three. To copy an existing ad, read it with expenses_optimus_entity_fetch: its card carries the format block and the URL fields in the words this tool takes, the images and videos by asset id. Responsive search ad: the texts are variants, not a sequence. Google combines any headline with any description, so each text has to stand on its own; responsive_search_ad.headlines takes 3 to 15 texts of at most 30 characters, descriptions 2 to 4 of at most 90, all distinct — this server compares them without regard to case — and every limit counted as Google counts, a full-width character taking two. pinned_field fixes a text to a position (HEADLINE_1 to HEADLINE_3, DESCRIPTION_1 or DESCRIPTION_2) and is best left out: pinning lowers the combinations Google can test. path1 and path2 are the display path shown after the domain, at most 15 characters each, and change nothing about where the click goes. Demand Gen multi-asset ad: images from the account, texts inline. headlines and descriptions take 1 to 5 texts each, of at most 40 and 90 characters, distinct; business_name at most 25 characters and logo_images (1 to 5, square, from 144×144) are required; at least one marketing_images (1.91:1, from 600×314) or square_marketing_images (1:1, from 300×300) is required — a portrait alone does not carry the ad — and portrait_marketing_images (4:5, from 480×600), tall_portrait_marketing_images (9:16, from 600×1067) and classic_display_images (fixed banner sizes: 300×250, 336×280, 728×90, 970×90, 160×600, 300×600 or 320×50) are optional, up to 20 of each kind. call_to_action_text is the button, from the texts Google accepts; left out, Google picks one. An image already in the account comes by asset_id from expenses_optimus_google_asset_list(kind: IMAGE); a new one as an https image_url this server downloads and uploads, at most 5120 KB, jpeg, png or gif. The counts and lengths are checked here; the proportions and sizes are Google's call, and its refusal comes back with the error code and the field it points at. Demand Gen video responsive ad: YouTube videos from the account or by id. videos takes 1 to 5 entries, each a public YouTube video by youtube_video_id — this server makes the asset — or an existing YOUTUBE_VIDEO asset_id from expenses_optimus_google_asset_list(kind: YOUTUBE_VIDEO); headlines (at most 40 characters), long_headlines and descriptions (at most 90) take 1 to 5 texts each, distinct; business_name and logo_images as in the multi-asset ad. The counts here follow Google's help centre and the live ads of this kind; the first live creation confirms them. Where the click goes. final_urls takes full http(s) URLs — send exactly one: Magnus attributes the campaign only when an ad …expenses_optimus_google_ad_update
ChatGPTcompany_ids comes from — this tool needs the write_company_id it answered, exactly one id — and it states the object in the very words this tool takes, so a change starts from what the object holds. This tool then reads the object from Google Ads again just before the write: that read is what tells a change from a value already held, is what a list needs — Google changes a list element by element — and is what every value_old in the answer comes from. It is not a second card: read the card, decide, then call this. Several fields per call. Send any of status, responsive_search_ad, demand_gen_multi_asset_ad, demand_gen_video_responsive_ad, final_urls, final_mobile_urls, final_url_suffix, tracking_url_template, url_custom_parameters together; they go to Google Ads as one atomic request, applied together or not at all. Send only the arguments you change: a list is the complete list after the change; a format object — responsive_search_ad, demand_gen_multi_asset_ad or demand_gen_video_responsive_ad — is the whole format after the change, which Google replaces whole; a scalar replaces the value held; and an argument you leave out is not touched, so do not send back arguments copied from the card unchanged. A field that is refused stops the whole call before the network is reached, with every problem named, and nothing is changed. The answer reports each field's own outcome under changes. Each call counts once against the allowance: at most 3 calls per object and 300 calls per person per hour, and only calls that went through are counted — a refusal costs nothing but the round trip. The format is fixed. An ad is a responsive search ad, a Demand Gen multi-asset ad or a Demand Gen video responsive ad from its creation, and the card states which as type. Send the object of that format — responsive_search_ad, demand_gen_multi_asset_ad or demand_gen_video_responsive_ad — and only that one; the object of another format is refused, since Google never changes a format: that is a new ad through expenses_optimus_google_ad_create. An ad of a format this server does not create takes status from here and nothing else; the name is Google's to keep as well. The format object is sent whole. Google replaces an ad's headlines, descriptions, images and videos as complete lists, so the object is the format after the change: take it from the card, change what should differ, send all of it — the counts, lengths and distinctness the create demands hold here too. Images by image_url and videos by youtube_video_id become assets of the account in the same atomic request; images and videos already in the account come by asset_id, and the card names under authoring_unsupported an asset Google would refuse to link again. final_urls, final_mobile_urls and url_custom_parameters are complete lists as well. Where the click goes. A new final URL is where Magnus attributes the ad: with exactly one final URL whose host a Magnus tracker knows, the ad counts under that app from the next collection on; with any other, the ad is unattributed from then on. Say so before changing it. Units — status is active or paused, this server's own two words; the network's own wording is what the card reports as status_vendor and is never accepted. An ad removed in Google Ads cannot be resumed from anywhere, including here. There is no money on an ad: the budget sits on the campaign, the bids on the ad group. What a change costs on Google's side. Switching the bidding strategy, moving a target far, or replacing the texts and images of an ad restarts Goo…expenses_optimus_google_adgroup_create
ChatGPTadgroup on every network. A real object in a real account, paused and free until it is turned on; undoing it is expenses_optimus_google_entity_delete, once no ad is left under it. Have the campaign's id in hand first, every time — from expenses_optimus_google_campaign_create or from an existing campaign's card in expenses_optimus_entity_fetch — and the customer_id and company_id from expenses_optimus_google_ad_account_list. The campaign is read live before anything is built, and its family decides which fields this call takes: a Performance Max campaign is refused with the name of the tool that fits it, expenses_optimus_google_asset_group_create, and a field of the other family is refused by name. To copy an existing group, read it with expenses_optimus_entity_fetch: its card states the group in this tool's own argument names — the keywords as texts and the group's own assets by id under Search; the countries, languages, channels and audience ids under Demand Gen — and authoring_unsupported names what it could not express. Under a Search campaign, keywords are what makes the group show. keywords are what people search for, each with a match type — BROAD, PHRASE or EXACT — and negative_keywords are what the group must not show for; up to 1000 of each per call. A group created without keywords is made, never serves, and the answer says so in a caveat; expenses_optimus_google_adgroup_update changes both lists afterwards, whole or keyword by keyword through its partial lists, so keywords can be added, paused and removed later — still, send the ones you know here, since the group serves from the moment it is turned on. Google may still refuse a keyword itself — a policy word, a duplicate — and then nothing is created, the group and the keywords being one atomic request. A keyword may carry status paused, which keeps it in the group without serving; left out, it is created active. The card states the status of each keyword, and names under authoring_unsupported, counted by field, what keywords keep of their own — a bid, landing pages, tracking — which a copy does not carry. A negative keyword has no status: Google keeps every negative on while it is listed and refuses a status on one. cpc_bid and ad_rotation_mode are Search fields too. The group's own assets, under Search. ad_group_assets names the assets of the account the group shows in place of its campaign's — sitelinks, callouts, structured snippets, calls, prices, promotions, app links and images — each by asset id under the field it serves as; a lead form, the business name and the logo sit on the campaign, as campaign_assets of expenses_optimus_google_campaign_create. The card of a Search group states them under this name, with ad_group_asset_names naming each id beside the block and each link with its status, so a copy carries them as they are; expenses_optimus_google_asset_list lists what the account holds by kind, its images under kind IMAGE. The links travel in the same atomic request as the group. The group's own device adjustments, under Search. device_bid_modifiers are the group's adjustments over the campaign's — a device and what its bid is multiplied by, 0 switching the device off for this group. What Google does with a value other than 0 depends on the campaign's strategy, as on expenses_optimus_google_campaign_create: under Maximize conversions without a target and under Maximize conversion value it honours 0 alone. A device the campaign switches off with 0 stays off for its groups: Google refuses a group's adjustment other than 0 for it. The card of a Search group states the group's own adjustments; the campaign's,…expenses_optimus_google_adgroup_update
ChatGPTadgroup on every network. This is a real change to live advertising: it takes effect immediately, it spends real money, and it cannot be undone from here. Call expenses_optimus_entity_fetch first, every time. It is where company_ids comes from — this tool needs the write_company_id it answered, exactly one id — and it states the object in the very words this tool takes, so a change starts from what the object holds. This tool then reads the object from Google Ads again just before the write: that read is what tells a change from a value already held, is what a list needs — Google changes a list element by element — and is what every value_old in the answer comes from. It is not a second card: read the card, decide, then call this. Several fields per call. Send any of status, name, keywords, keywords_patch, keywords_add, negative_keywords, negative_keywords_add, negative_keywords_remove, cpc_bid, target_cpa, ad_rotation_mode, ad_group_assets, device_bid_modifiers, countries, excluded_countries, languages, channel_controls, audience_id, user_interest_ids, user_list_ids, excluded_user_list_ids, custom_audience_ids together; they go to Google Ads as one atomic request, applied together or not at all. Send only the arguments you change: a list is the complete list after the change, a partial list only the elements it names; channel_controls, the one object here, is sent whole — selected_channels is the exact set of channels after the change, a channel it does not name set to false, and channel_strategy replaces the set — so send it as the card states it, with your change made; a scalar replaces the value held; and an argument you leave out is not touched, so do not send back arguments copied from the card unchanged. A field that is refused stops the whole call before the network is reached, with every problem named, and nothing is changed. The answer reports each field's own outcome under changes. Each call counts once against the allowance: at most 3 calls per object and 300 calls per person per hour, and only calls that went through are counted — a refusal costs nothing but the round trip. The campaign decides the family. A group takes exactly the arguments its campaign's family takes on expenses_optimus_google_adgroup_create — keywords and negative_keywords with their partial lists, cpc_bid, ad_rotation_mode, ad_group_assets and device_bid_modifiers under Search; countries, excluded_countries, languages, channel_controls and the audience ids under Demand Gen — and an argument of the other family is refused by name; the card states the family as campaign_advertising_channel_type. A group under a campaign of a family this server does not create groups in — Display, App, Video, Shopping, Performance Max — takes status, name, cpc_bid, target_cpa and nothing else. Under a Demand Gen campaign of the old kind, without upgraded targeting, Google takes no geography and no language on the group, and those are refused; the audiences are taken. Bids. cpc_bid is the group's bid under a campaign that bids by hand, and is stored unused under Google's own strategies; target_cpa is the group's own target cost per acquisition, overriding the campaign's for this group, taken under a campaign on Maximize conversions or Target CPA and refused under any other, where Google would store it and never bid to it. Each has to land between half and double what the group holds: for target_cpa the target in effect — its own, or the campaign's while it has none, which the card states as effective_target_cpa — and never above the bid ceiling; where n…expenses_optimus_google_asset_group_create
ChatGPTcustomer_id and company_id from expenses_optimus_google_ad_account_list. The campaign is read live before anything is built: a Search campaign is refused with the name of the tool that fits it, expenses_optimus_google_adgroup_create. Images already in the account come by asset_id from expenses_optimus_google_asset_list(kind: IMAGE), videos from expenses_optimus_google_asset_list(kind: YOUTUBE_VIDEO); a new image comes as an https image_url this server downloads and uploads, at most 5120 KB, jpeg, png or gif. To repeat an existing group's creatives, read it with expenses_optimus_entity_fetch: its card lists the texts and the asset ids of its images and videos in the words this tool takes, and the campaign's own card its logos. Google demands a minimum set in one request. headlines 3 to 15 of at most 30 characters, long_headlines 1 to 5 of at most 90, descriptions 2 to 5 of at most 90, one of them 60 or fewer, all texts distinct (compared without regard to case) and every limit counted as Google counts, a full-width character taking two; at least one marketing_images (1.91:1, from 600×314) and one square_marketing_images (1:1, from 300×300), up to 20 of each kind; portrait_marketing_images (4:5, from 480×600), tall_portrait_marketing_images (9:16, from 600×1067), youtube_videos (up to 5, at least ten seconds, by youtube_video_id or by an existing asset_id) and call_to_action are optional. The counts and lengths are checked here; the proportions and sizes of an image are Google's call, and its refusal comes back with the error code and the field it points at. The group, the assets it makes and every link between them travel in one atomic request, so a refusal creates nothing — not even the uploaded images. What steers the group is not what it is made of. search_themes — up to 50, in a person's own words — and one audience_signal_id from expenses_optimus_google_audience_list tell Google whom to look for; they are hints, not targeting, and exclude nobody. Google takes exactly one audience per group and refuses a second. A group without signals still runs and still learns, only slower, so when copying a group carry its signals over — the card lists them — and when authoring one ask what the customers of this product search for rather than inventing themes. An audience the card names under authoring_unsupported is Google's own persona of that group and cannot be carried: the copy goes without it, or with a saved audience from the list. The business name and logos are not here. With brand guidelines on, which is how this server creates Performance Max campaigns, they live on the campaign — expenses_optimus_google_campaign_create took them — and Google refuses them on an asset group. Where the click goes. final_urls takes full http(s) URLs — send exactly one: Magnus attributes the campaign only when the asset group has exactly one final URL and its host is one a Magnus tracker knows, and a second URL leaves it unattributed. When the URL fits, Magnus records the campaign and this group in this same call and the card says mirror: true; when it does not, nothing under the campaign is recorded in Magnus until an asset group with such a URL exists, and expenses_optimus_entity_fetch and the update tools reach the chain only by customer_id meanwhile. What the server fills in, and one naming rule. Th…expenses_optimus_google_asset_group_update
ChatGPTentity_type: ad for it and names this tool as its write_tool. This is a real change to live advertising: it takes effect immediately, an edited group goes back to Google's review, and it cannot be undone from here. Call expenses_optimus_entity_fetch first, every time. It is where company_ids comes from — this tool needs the write_company_id it answered, exactly one id — and it states the object in the very words this tool takes, so a change starts from what the object holds. This tool then reads the object from Google Ads again just before the write: that read is what tells a change from a value already held, is what a list needs — Google changes a list element by element — and is what every value_old in the answer comes from. It is not a second card: read the card, decide, then call this. Several fields per call. Send any of status, name, final_urls, final_mobile_urls, path1, path2, headlines, long_headlines, descriptions, marketing_images, square_marketing_images, portrait_marketing_images, tall_portrait_marketing_images, youtube_videos, call_to_action, search_themes, audience_signal_id together; they go to Google Ads as one atomic request, applied together or not at all. Send only the arguments you change: a list is the complete list after the change; a scalar replaces the value held; and an argument you leave out is not touched, so do not send back arguments copied from the card unchanged. A field that is refused stops the whole call before the network is reached, with every problem named, and nothing is changed. The answer reports each field's own outcome under changes. Each call counts once against the allowance: at most 3 calls per object and 300 calls per person per hour, and only calls that went through are counted — a refusal costs nothing but the round trip. Lists are sent whole. final_urls, final_mobile_urls, headlines, long_headlines, descriptions, marketing_images, square_marketing_images, portrait_marketing_images, tall_portrait_marketing_images, youtube_videos, search_themes are each the complete list after the change: take the list from the card, change it, send all of it. This server reads what the group holds and applies the difference — a text, image, video or theme no longer listed is unlinked, a new one is made and linked, the rest is untouched with its performance history. The create's counts, lengths and distinctness hold for a list that is sent, and Google refuses a group left below its minimums — at least one landscape and one square image, three headlines, one long headline, two descriptions with a short one among them. Images by image_url and videos by youtube_video_id become assets of the account in the same atomic request; an asset the card names under authoring_unsupported — one Google's own automation made, one of a shape it no longer takes — is outside these words and is left as it is. call_to_action is one button: another replaces it, and an empty string leaves the choice to Google. audience_signal_id is the one audience the group is steered by: another id replaces it, an empty string removes it; Google's own persona of the group, which the card names under authoring_unsupported, stays as it is. Where the click goes. A new final URL is where Magnus attributes the group: with exactly one final URL whose host a Magnus tracker knows, the group counts under that app from the next collection on; with any other, it is unattributed from then on. Say so before changing it. Units — status is active or paused, this server's own two words; the network's own w…expenses_optimus_google_asset_list
ChatGPTasset_id the Google create and update tools take comes from — the links of a campaign or an ad group, the images and videos of a brand, an asset group and a Demand Gen ad. kind is Google's own name for the kind: SITELINK, CALLOUT, STRUCTURED_SNIPPET, CALL, PRICE, PROMOTION, LEAD_FORM, MOBILE_APP, TEXT for a business name, IMAGE for a logo or any other image, YOUTUBE_VIDEO for a video. Call this before linking or reusing an asset, and never invent an asset id: the create and update tools read every id back from the account and refuse one it does not hold, one of another kind than the field takes, an image of the wrong shape for its field, and one Google's own automation made (source AUTOMATICALLY_CREATED). name_prefix matches the start of the name, not a fragment: an image uploaded through Magnus keeps its Google Drive filename, so "the Summer bundle image" is kind: IMAGE with name_prefix: "Summer", and "bundle" finds nothing. To copy what an object already links, take the ids from its card in expenses_optimus_entity_fetch instead. This server makes no sitelink, callout or snippet itself — a new one is made in the Google Ads interface, then linked by id; an image the account does not hold yet goes to the create tools as image_url, and they upload it. Ids. customer_id is the account, as a string of digits from expenses_optimus_google_ad_account_list; company_ids is the one company whose connection reads it — the same id the creation will be made under. Response Guidelines: one row per asset, in Google's order — for an image or a video Magnus's uploads first, newest first, then the rest of the library — at most limit rows; when the account holds more, meta.completeness is partial and meta.caveats says so. There is no paging, so narrow with name_prefix where the assets carry names, raise the limit up to its cap, or read the campaign's card for the assets it already links. An empty answer means the account holds no asset of that kind under this connection — say that, rather than that the campaign shows none: an asset linked at the account level is listed here too, and applies to every campaign on its own. When NOT to use: this is not a performance report and says nothing about how an asset or a creative did — marketing_timeline_report does that; it creates nothing; and it does not say which assets a given campaign links — expenses_optimus_entity_fetch does, on the campaign's card.expenses_optimus_google_auction_insights_report
ChatGPTexpenses_optimus_google_audience_list
ChatGPTuser_lists, excluded_user_list_ids, custom_audiences and user_interests — read live from the account under your own connection, never from Magnus. kind audience is the account's saved Audiences, combinations of segments and demographics, and feeds audience_id: most Demand Gen groups target with exactly one of them; a Search campaign takes no audience of this kind at its own level. Google's own per-asset-group personas — the audiences it makes for one Performance Max asset group, named AssetGroupPersona_… — are left out, since no other group can name them. kind user_list is the account's user lists — remarketing, customer match, lookalike — and feeds user_list_ids to reach and excluded_user_list_ids to leave out on a Demand Gen group, user_lists and excluded_user_list_ids on a Search campaign; each row says with eligible_for_display and eligible_for_search where Google lets the list serve — a Demand Gen group needs the first, a Search campaign the second — and status says whether a list is still collecting members (OPEN) or closed, a closed list staying usable. kind custom_audience is the account's custom segments, built from search terms, URLs and apps, and feeds custom_audience_ids or custom_audiences. kind user_interest is Google's own taxonomy of affinity and in-market segments — constants of Google, not objects of the account, the same in every account — and feeds user_interest_ids or user_interests; it is large, so name_prefix is required for it and taxonomy_type narrows it further, and the row's description is the name of the segment's parent. Search by the start of the name. name_prefix matches the beginning of the name as Google matches it — "Sports" finds "Sports Fans" and not "Fans of Sports". Never invent an id: Google refuses an audience the account does not hold after the whole group has been built, and a person's words for a segment are turned into an id here and nowhere else. Ids. customer_id is the account, as a string of digits from expenses_optimus_google_ad_account_list; company_ids is the one company whose connection reads it — the same id the creation will be made under. Response Guidelines: one row per object, in Google's order, at most limit rows — when the account holds more, meta.completeness is partial and meta.caveats says so; there is no paging, so narrow with name_prefix or taxonomy_type rather than raising the limit. An empty answer for kind audience or user_list means the account has none of that kind under this connection; say so, rather than that nothing can be targeted — a Demand Gen group can be created without any audience and reach everyone in its countries. When NOT to use: this is not a performance report and says nothing about how an audience did; it creates nothing, and it does not list keywords, placements or topics, which Demand Gen does not take.expenses_optimus_google_campaign_copy
ChatGPTcustomer_id and the company_id, as for every creation. source_campaign_id is the campaign to copy — the entity_id on its expenses_optimus_entity_fetch card or a row of expenses_optimus_entity_list — and name the new one's. The source is read live as its card is read, and each object of the copy is that card's block in the arguments of its level's create tool: what a card states travels, what it cannot state does not. What is copied. Every level as its create tool takes it: the campaign with a budget of its own of the same amount, its strategy, goals, targeting, negatives, assets and audiences, on Performance Max its brand and automation; each ad group with its keywords, each with its status, and the rest of its settings — on Demand Gen its countries, languages, channels and audiences — and each ad under it; on Performance Max each asset group. What Google removed is not copied. What is not. What the cards name under authoring_unsupported and authoring_unread — a shared negative list, a keyword's own bid, a portfolio strategy, what this server does not read — is listed by not_carried once per level and path, with how many objects hold it; an object of a type no create tool makes — an expanded text ad, a dynamic search ad group — is left out whole, under the path type. The account's assets, audiences and goals are reused by id, never duplicated; an asset group's texts and a Performance Max business name are made anew, as Google keeps each as an asset of its own. Statuses and dates. Every object is created paused; each keyword and asset link keeps its source status, and plan states each object's source_status for turning it on afterwards with the update tool of its level. A start or an end already behind becomes today, the end with a caveat — overrides.end_date_time sets another. What the call changes. overrides takes any argument of expenses_optimus_google_campaign_create in its own names and shapes, each in place of the source's, whole — a strategy object replaces the source's strategy, conversion_goals and custom_conversion_goal_id replace each other — and add_negative_keywords adds to the source's negatives; the family cannot change. ad_groups names the groups to copy, in order — on Performance Max its asset groups — by source_adgroup_id, each with an optional new name and, on Search, a keyword_filter; left out, every group is copied under its own name, and [] copies the campaign alone. ads: skip copies the ad groups without their ads. Limits. One atomic request, and Google takes at most 10000 operations in one — the budget, the campaign and each keyword, link, criterion, group and ad count one each — so a larger copy is refused, never split: narrow ad_groups or keyword_filter. No per-call list cap applies: a group of 1200 keywords travels whole. The money ceilings bind every level, so a source above them cannot be copied as it is; the day's allowance counts every object made — at most 300 objects a person a day — and the day's money counts the budget. Dry run. dry_run: true — worth it before every copy — reads the source, builds the whole request and has Google validate it, without making anything: the answer is the plan with not_carried, money and meta.effective_arguments, and nothing is created, recorded or counted against the day's allowance. echo sets how much a copy answers: summary, the default, is the plan as counts, the ids of what was made under created, readback — every object read back and compared with what was sent — and mirror; full adds each object's arg…expenses_optimus_google_campaign_create
ChatGPTcustomer_id and the company_id this tool needs, and the currency and time zone every amount and date here is read in; the manager account the connection reaches the customer through is taken from that same answer, so you never pass it. The chain is campaign_create, then expenses_optimus_google_adgroup_create and expenses_optimus_google_ad_create for a Search or Demand Gen campaign, or expenses_optimus_google_asset_group_create for a Performance Max one. To copy an existing campaign with everything under it, expenses_optimus_google_campaign_copy does it in one call from the source's id. To author one from another's settings instead, read that campaign with expenses_optimus_entity_fetch: its card states the campaign in this tool's own argument names — the family, the budget and the strategy, the conversion goals it bids on, the countries, the finer places it targets and the ones it leaves out, languages and negative keywords, the dates and the URL settings, on Search the audiences it reaches or leaves out and the sitelinks, callouts and other assets linked to the campaign as campaign_assets, on Performance Max the business name, the logos and the automation — so the fields travel back verbatim, and authoring_unsupported names what the card could not express: a portfolio strategy or one this tool has no word for, such as target impression share, shared negative lists, an audience or an extension on a Performance Max or Demand Gen campaign — and any logo or campaign asset Google would refuse a new link to, named with its asset id, which is why a copy can carry fewer logos or sitelinks than the original shows. Say what you are not copying before you create. A campaign a person names is found by its name with expenses_optimus_entity_list, which answers the id the card takes once Magnus has recorded the campaign — with its first ad group or asset group. The family is decided here and cannot change later. advertising_channel_type SEARCH is a keyword campaign: it needs ad groups with keywords and responsive search ads, takes target_spend, network_settings, campaign_assets and the audience lists, and refuses the Performance Max fields. PERFORMANCE_MAX runs on every Google surface from asset groups: it needs business_name and at least one logo in logos, and refuses target_spend and network_settings. This server creates every Performance Max campaign with brand guidelines on, which cannot be changed afterwards, so the name and the logos travel on the campaign, never on the asset group. DEMAND_GEN runs on YouTube, Discover, Gmail, the Display network and Maps from ad groups that carry the targeting: the campaign takes the budget, the strategy, the dates and the URL settings and nothing else — countries, languages and negative_keywords are refused here, the countries and languages go on expenses_optimus_google_adgroup_create, and the networks are Google's own — and it takes target_spend beside the other two strategies. This server creates every Demand Gen campaign with upgraded targeting and grouped audiences, both immutable, as Google's own interface does, and discloses them; Google has a daily budget minimum for this family that it names only in its refusal, which this server passes on in the account's currency. Ask which family is wanted if the person has not said. Bidding. At most one of maximize_conversions, maximize_conversion_value, target_spend and…expenses_optimus_google_campaign_update
ChatGPTcompany_ids comes from — this tool needs the write_company_id it answered, exactly one id — and it states the object in the very words this tool takes, so a change starts from what the object holds. This tool then reads the object from Google Ads again just before the write: that read is what tells a change from a value already held, is what a list needs — Google changes a list element by element — and is what every value_old in the answer comes from. It is not a second card: read the card, decide, then call this. Several fields per call. Send any of status, name, budget, maximize_conversions, maximize_conversion_value, target_spend, manual_cpc, conversion_goals, custom_conversion_goal_id, countries, locations, excluded_locations, languages, negative_keywords, negative_keywords_add, negative_keywords_remove, network_settings, campaign_assets, user_lists, excluded_user_list_ids, custom_audiences, user_interests, device_bid_modifiers, ad_schedules, geo_target_type_setting, ai_max_setting, targeting_setting, view_through_conversion_optimization_enabled, start_date_time, end_date_time, tracking_url_template, final_url_suffix, url_custom_parameters, business_name, logos, landscape_logos, asset_automation_settings together; they go to Google Ads as one atomic request, applied together or not at all. Send only the arguments you change: a list is the complete list after the change, a partial list only the elements it names; an object — network_settings, geo_target_type_setting, ai_max_setting, a strategy object — is changed leaf by leaf, so a leaf you leave out keeps its value, while targeting_setting is replaced whole; a scalar replaces the value held; and an argument you leave out is not touched, so do not send back arguments copied from the card unchanged. A field that is refused stops the whole call before the network is reached, with every problem named, and nothing is changed. The answer reports each field's own outcome under changes. Each call counts once against the allowance: at most 3 calls per object and 300 calls per person per hour, and only calls that went through are counted — a refusal costs nothing but the round trip. The family is fixed. advertising_channel_type was decided at the creation and is not taken here; the card states it. A Search, Performance Max or Demand Gen campaign takes exactly the arguments its family takes on expenses_optimus_google_campaign_create, and an argument of another family is refused by name. A campaign of a family this server does not create — Display, App, Video, Shopping — takes status, name, budget, start_date_time, end_date_time, tracking_url_template, final_url_suffix, url_custom_parameters and the target of the strategy it runs, and nothing else. Bidding. At most one of maximize_conversions, maximize_conversion_value, target_spend and manual_cpc per call. The card states the running strategy as one of the four objects — a Target CPA campaign as maximize_conversions with its target_cpa, a Target ROAS one as maximize_conversion_value with its target_roas — and sending that same object with another target changes the target alone. Sending any other object switches the strategy, which G…expenses_optimus_google_conversion_goal_list
ChatGPTkind custom_goal is the account's custom conversion goals — each a named set of its conversion actions — with the id custom_conversion_goal_id takes on expenses_optimus_google_campaign_create and expenses_optimus_google_campaign_update, and the actions inside each goal named. kind conversion_action is the account's conversion actions themselves — what a custom goal is built from — with their category, origin, status, type and default value. kind account_goal is the account's standard goals, the category and origin pairs conversion_goals takes, with whether the account bids on each by default. Call this before naming a goal. A custom goal is named by id and never guessed: a campaign create or update naming a goal the account does not hold is refused. To copy a campaign that bids on a custom goal, take the id from its card instead — expenses_optimus_entity_fetch states it as custom_conversion_goal_id. Search by the start of the name. name_prefix matches the beginning of a custom goal's or a conversion action's name as Google matches it — "Purchase" finds "Purchase — web" and not "Web purchase". The standard goals have no names and take no prefix. Ids. customer_id is the account, as a string of digits from expenses_optimus_google_ad_account_list; company_ids is the one company whose connection reads it — the same id the creation will be made under. Response Guidelines: one row per object, in Google's order, at most limit rows — when the account holds more, meta.completeness is partial and meta.caveats says so; there is no paging, so narrow with name_prefix rather than raising the limit. An empty answer for kind custom_goal means the account has no custom goals under this connection: a campaign then bids on conversion_goals or on the account's own goals — say so, rather than that nothing can be optimised for. Removed goals and removed actions are left out, except an action a custom goal still names, which is listed inside that goal with its status. When NOT to use: this is not a conversion report and says nothing about how many conversions came in — marketing_timeline_report does that; it creates nothing — a new custom goal is expenses_optimus_google_custom_conversion_goal_create — and it does not say which goals a given campaign bids on — expenses_optimus_entity_fetch does, on the campaign's card.expenses_optimus_google_custom_conversion_goal_create
ChatGPTecho: full. This is a real object in a real ad account, not a draft. A goal costs nothing on its own: it changes what a campaign optimises for only once expenses_optimus_google_campaign_create or expenses_optimus_google_campaign_update names it as custom_conversion_goal_id, and expenses_optimus_google_custom_conversion_goal_update with status REMOVED is what removes it. Call expenses_optimus_google_conversion_goal_list first, every time. With kind conversion_action it answers the account's conversion actions with their ids, categories, origins and values — the only things a goal can be made of — and with kind custom_goal the goals the account already has, so a goal that exists is reused rather than made twice. conversion_action_ids is the whole set: a campaign on the goal optimises for these conversions and no other, so a goal meant for purchases names the purchase actions alone. Ids. customer_id is the string of digits from expenses_optimus_google_ad_account_list, and company_ids is the company_id that list answered beside it — exactly one. The answer's custom_conversion_goal_id is what the campaign cells take. Limits — at most 600 objects may be created per person per calendar day (UTC) through this server, goals and ad objects together. Only creations that went through are counted; a refusal costs nothing but the round trip. Dry run. dry_run: true runs every check of this call and has Google validate the very request the creation would send, without applying it: the answer names the goal that would be made under would_create, a refusal comes back as the real call's would, and nothing is made, recorded or counted against the allowance. echo sets how much the answer carries: summary, the default, is custom_conversion_goal_id, readback — whether the goal read back holds what was sent — created_at and limits; full adds the goal as Google holds it — name, status and conversion_actions; none keeps the id and readback.verified alone. Say what you are about to do before you do it, and wait for a yes. Name the account, the goal's name and each conversion action in plain words — what a campaign on this goal will and will not optimise for. What a refusal means — arguments_invalid, no_linked_account, unknown_account, token_invalid and daily_limit_reached happened before the goal was sent and nothing was created; fix what the sentence names, then call again, never the same call unchanged. network_rejected is Google's own verdict, in its words, and nothing was created. network_unreachable is Google giving no verdict: the goal may exist, so before calling again look it up by its name in expenses_optimus_google_conversion_goal_list(kind: custom_goal), with name_prefix — a second call could make it twice. When the answer is partial, the goal was created and reading it back did not confirm it: read caveats, say it was created, and do not repeat the call. When NOT to use — not to put a campaign on a goal, which is custom_conversion_goal_id on the campaign cells; not to change or remove a goal, which is expenses_optimus_google_custom_conversion_goal_update; not to make a conversion action, which is done in the Google Ads interface; not for Facebook or TikTok, which have no custom conversion goals here.expenses_optimus_google_describe
ChatGPTapplies is its rule: the families that take it (SEARCH, PERFORMANCE_MAX, DEMAND_GEN), the conditions it needs (requires) and those that make it required (required_when), each explained under conditions, and what is offered instead where a family refuses it. With campaign_id the campaign is read live under your connection, as the create tools read the campaign they go under, and applies is true, false, or null where the verdict needs what a campaign alone does not say — an ad group's type — each with its reason; required counts the campaign's conditions too. For entity_type campaign, campaign_id stands for a campaign of the same type and strategy. Example. {"entity_type": "adgroup", "campaign_id": "111", "customer_id": "1234567890", "company_ids": [1]} under a Demand Gen campaign answers countries required and applicable, and keywords not applicable with what to send instead. A field. unit: currency — the account's main unit, 50 is fifty dollars — fraction, iso_country, language_code, id, date, text, enum, list, boolean or object. values: what to choose from — a list, or for an object or a list of objects each key's by its path, as [].match_type. default: what the server fills in — for a list, what an entry sent without a key is made with, as [].status; today is the account's current day. lookup: {tool, kind}, where the ids come from. note: the first sentence of the create tool's own description of the field; the rest is there. Ids. company_ids and customer_id only with campaign_id: the account from expenses_optimus_google_ad_account_list and its one company. notes say what this reference does not know — Google's own rules outside the vocabulary are found by the create tool's dry_run — and what a campaign's verdicts leave to say. When NOT to use: not for an object that exists — its card states its fields; not for Facebook or TikTok.expenses_optimus_google_entity_delete
ChatGPTcustomer_id and its Google id, exactly as the create tools read their parent and as expenses_optimus_entity_fetch and the update tools do when given customer_id. What to pass. company_ids is exactly one id, the company whose Google Ads connection reaches the account. customer_id is the account, as expenses_optimus_google_ad_account_list answers it and as every create answer carries it. entity_type is the level in the card's words — campaign, adgroup or ad, an asset group being an ad — and entity_id the Google id the create answer or the card or expenses_optimus_entity_list gave you. name is the object's current name exactly, the proof that the id was read and not guessed; a mismatch is refused without saying what the real name is. It is asked only where Google holds a name: an ad without one — every responsive search ad, since Google supports a name on Demand Gen ads and on formats this server does not make — is deleted on its id alone, the pause being the guard; leave name out for it, and what is sent for such an ad is not read. Pause first. A running object is refused: switch it off with the level's update tool, which is reversible, and delete afterwards. Deleting is for an object nobody will turn on again; to stop spending, pausing is the answer and it can be undone. Limits — deletions share the hourly allowance of 300 calls per person with the update tools, and at most 100 objects may be deleted per person per calendar day (UTC), on Facebook and Google Ads together. Only deletions that went through are counted. Say what you are about to do before you do it, and wait for a yes. Name the object and its level. One account is routinely reached by several companies, so the removal is visible in the reports of every company that reads it. What a refusal means — every refusal happened before Google was called and nothing was deleted. An object already removed is reported as such and is not an error; a running one is sent back to the update tool; one with live children is sent back to remove them first. Google's own "retry the request" answers are retried here twice before they are reported. When the answer is partial, the removal went through and something afterwards could not be confirmed: read caveats, say the object was deleted, and never call this tool again for the same id. When NOT to use — not to stop spending, which is a pause; not for Facebook or TikTok objects, which have their own tools or ad managers; not for assets, audiences or keywords, which this server does not delete.expenses_optimus_google_geo_target_list
ChatGPTlocations and excluded_locations — a region, a state, a city, a metropolitan area, an airport, a postal code, anything Google can aim a campaign at. The places are Google's own constants, the same in every account, read live from Google under your own connection and never from Magnus, whose own copy of them is years old. One call asks about at most 25 names and answers a row per match, in Google's order, each row saying under search_term which of the names it answers. Countries do not come from here. countries of expenses_optimus_google_campaign_create takes ISO-3166-1 alpha-2 codes (US, DE, BR) directly, and that is how a whole country is targeted. This lookup is for everything below that line, and for the exclusions — a country targeted with one region left out is countries plus excluded_locations. Write the names in the language of `locale`. locale tells Google which language the names are in — English unless you set it — so a place goes out under its name in that language: the English "Munich" rather than "München" under the default, whatever language the person wrote in, or with locale set to the language they wrote in. A name Google matched nothing for is named in meta.caveats with the locale it was read in, rather than silently missing. Say which country, and choose by `type` and `canonical_name`. Without country Google searches the whole world, and a name like Springfield or Cambridge then answers rows on three continents. A city's name answers more than the city: its county, the region around it and its airport come back beside it — San Francisco in the US answers the City, San Francisco County, the San Francisco Bay Area and the airport. What a person means by a city's name is the row whose type is City; mention the wider rows when they may have meant the whole area. canonical_name is the fully qualified name as Google writes it — "Munich,Bavaria,Germany" — and it, not name, is what tells two matches apart; say which row you took. A postal code is searched by its digits ("10115") with country set. Ids. customer_id is the account, as a string of digits from expenses_optimus_google_ad_account_list; company_ids is the one company whose connection reads it — the same id the creation will be made under. The places themselves are Google's and belong to no account: the account decides whose connection the read runs under, not which places exist. Response Guidelines: one row per match, at most limit rows — when Google suggested more, meta.completeness is partial and meta.caveats says so; there is no paging, so narrow with country or ask about fewer names rather than raising the limit. type is Google's own word for what the place is (Country, State, Region, County, City, Neighborhood, Postal Code, Airport and so on), parent_id the place it sits inside, and reach Google's estimate of how many people it covers, which is null where Google states none. A row whose status is REMOVAL_PLANNED is one Google is retiring: it is named in meta.caveats and must not be sent to a create, which refuses it. When NOT to use: not for countries — those are ISO codes on the create tool itself. This is not a targeting report and does not say what a campaign is aimed at today — expenses_optimus_entity_fetch states that — nor a performance report about a place. It creates and changes nothing, and it knows nothing about radiuses, location groups or a place's distance from an address, none of which this server can set.expenses_optimus_google_keyword_report
ChatGPTexpenses_optimus_google_search_term_report
ChatGPTexpenses_optimus_tiktok_ad_update
ChatGPTcompany_ids comes from — this tool needs the write_company_id it answered, exactly one id — and it is what refreshes the values this tool measures a change against: the current budget, bid, status and name as Magnus holds them, and the absolute range a new value has to land in. Nothing is read from the network before the write, so a card read long ago means a corridor computed from a stale base; synced_at on the card says how old it is. Several fields per call. Send any of status together; TikTok takes settings and status through two endpoints, so they go out one after another, settings first; when the second request fails after the first went through, the answer says exactly which field is applied and which is not. Send only the arguments you change: an argument you send replaces its value whole — a list is the complete list after the change, an object the whole object — and an argument you leave out is not touched, so do not send back arguments copied from the card unchanged. A field that is refused stops the whole call before the network is reached, with every problem named, and nothing is changed. The answer reports each field's own outcome under changes. Each call counts once against the allowance: at most 3 calls per object and 300 calls per person per hour, and only calls that went through are counted — a refusal costs nothing but the round trip. Status is the only writable field: money lives on the ad group. status is active or paused, this server's own two words; TikTok's own wording (ENABLE, DISABLE) is never accepted here. Say what you are about to do before you do it. One ad account is routinely shared by several companies, so the change is visible in the reports of every company that shares it, not only the one you passed. Ceilings and daily limits — going up, a budget or bid may not be set above this server's ceiling: 5000 USD a day for a campaign budget, 1000 USD for an ad set or ad group budget, 200 USD for a bid, converted into the ad account's currency; the card's bounds.max already includes it. Decreasing and pausing are always allowed. Each person may also set at most 25000 USD a calendar day (UTC) in motion through this server — budget increases, the budgets of objects switched on and created budgets, summed — and the answer's limits.per_day says what is left and when it resets.expenses_optimus_tiktok_adgroup_update
ChatGPTcompany_ids comes from — this tool needs the write_company_id it answered, exactly one id — and it is what refreshes the values this tool measures a change against: the current budget, bid, status and name as Magnus holds them, and the absolute range a new value has to land in. Nothing is read from the network before the write, so a card read long ago means a corridor computed from a stale base; synced_at on the card says how old it is. Several fields per call. Send any of status, budget, bid together; TikTok takes settings and status through two endpoints, so they go out one after another, settings first; when the second request fails after the first went through, the answer says exactly which field is applied and which is not. Send only the arguments you change: an argument you send replaces its value whole — a list is the complete list after the change, an object the whole object — and an argument you leave out is not touched, so do not send back arguments copied from the card unchanged. A field that is refused stops the whole call before the network is reached, with every problem named, and nothing is changed. The answer reports each field's own outcome under changes. Each call counts once against the allowance: at most 3 calls per object and 300 calls per person per hour, and only calls that went through are counted — a refusal costs nothing but the round trip. All three fields live here on TikTok, and the bid is the conversion bid price. One warning about budgets: an ad group switched to an unlimited budget in the ad manager is NOT detected here — Magnus stops updating the stored number and keeps the last one it saw, so a budget change may be accepted against a value that is no longer real. Read the budget in the ad manager before changing it on an ad group you did not set up yourself. Units — money is a decimal number in the ad account's own currency, in its main unit: 50 means $50.00. status is active or paused, this server's own two words; TikTok's own wording (ENABLE, DISABLE) is never accepted here. A change has to land between half and double the current value, and there is no override. Say what you are about to do before you do it. One ad account is routinely shared by several companies, so the change is visible in the reports of every company that shares it, not only the one you passed. Ceilings and daily limits — going up, a budget or bid may not be set above this server's ceiling: 5000 USD a day for a campaign budget, 1000 USD for an ad set or ad group budget, 200 USD for a bid, converted into the ad account's currency; the card's bounds.max already includes it. Decreasing and pausing are always allowed. Each person may also set at most 25000 USD a calendar day (UTC) in motion through this server — budget increases, the budgets of objects switched on and created budgets, summed — and the answer's limits.per_day says what is left and when it resets.expenses_optimus_tiktok_campaign_update
ChatGPTcompany_ids comes from — this tool needs the write_company_id it answered, exactly one id — and it is what refreshes the values this tool measures a change against: the current budget, bid, status and name as Magnus holds them, and the absolute range a new value has to land in. Nothing is read from the network before the write, so a card read long ago means a corridor computed from a stale base; synced_at on the card says how old it is. Several fields per call. Send any of status, budget together; TikTok takes settings and status through two endpoints, so they go out one after another, settings first; when the second request fails after the first went through, the answer says exactly which field is applied and which is not. Send only the arguments you change: an argument you send replaces its value whole — a list is the complete list after the change, an object the whole object — and an argument you leave out is not touched, so do not send back arguments copied from the card unchanged. A field that is refused stops the whole call before the network is reached, with every problem named, and nothing is changed. The answer reports each field's own outcome under changes. Each call counts once against the allowance: at most 3 calls per object and 300 calls per person per hour, and only calls that went through are counted — a refusal costs nothing but the round trip. There is no bid at campaign level on TikTok: bidding lives on the ad group. A campaign on an unlimited budget reports no budget at all, and that is refused with the reason rather than attempted — there is nothing to halve or double. Units — money is a decimal number in the ad account's own currency, in its main unit: 50 means $50.00. status is active or paused, this server's own two words; TikTok's own wording (ENABLE, DISABLE) is never accepted here. A change has to land between half and double the current value, and there is no override. Say what you are about to do before you do it. One ad account is routinely shared by several companies, so the change is visible in the reports of every company that shares it, not only the one you passed. Ceilings and daily limits — going up, a budget or bid may not be set above this server's ceiling: 5000 USD a day for a campaign budget, 1000 USD for an ad set or ad group budget, 200 USD for a bid, converted into the ad account's currency; the card's bounds.max already includes it. Decreasing and pausing are always allowed. Each person may also set at most 25000 USD a calendar day (UTC) in motion through this server — budget increases, the budgets of objects switched on and created budgets, summed — and the answer's limits.per_day says what is left and when it resets.finances_customer_info
ChatGPTfinances_risk_approval_rates
ChatGPTfinances_risk_approval_rates_metrics
ChatGPTfinances_risk_dimension_values
ChatGPTfinances_risk_report
ChatGPTfinances_risk_report_metrics
ChatGPTmarketing_analytic_dimension_values
ChatGPTmarketing_hourly_report
ChatGPTmarketing_timeline_report
ChatGPTmarketing_timeline_report_metrics
ChatGPTmcp_feedback_create
ChatGPTgoal: the result the person needed, in plain words. obstacle: what stopped it or what was inconsistent — the refusal that could not be fixed, the answer that looked wrong, the capability that is not there. expected: the behaviour or result that was missing. workaround: how it was done anyway, when it was. Name the tool in tool when the record is about one. Write facts you saw, not guesses about the cause; the cause is worked out from the record and the calls of this session together. Whatever you send is recorded as sent, extra fields included; the one call refused is one with no text at all. What not to write. No names, e-mail addresses, phone numbers, ids of people or secrets: the record is stored as you send it. Describe the person's request, not the person. What happens. The record is stored beside the log of your calls and read later, in bulk, by someone reviewing this server. Nothing is fixed by it now and nobody replies. Once it is recorded, continue the person's task and tell them plainly what could not be done. When NOT to use — not to retry a call, which this does not do; not to ask a question, which nobody answers; not for a 429 or a reference-code error, which are reported to the person as they are; not for a refusal you can fix by changing what its sentence names.mutator_experiment_create
ChatGPTmutator_experiment_fetch
ChatGPTmutator_experiment_list
ChatGPTmutator_experiment_statistics
ChatGPTmutator_remote_config_draft_create
ChatGPTmutator_remote_config_draft_fetch
ChatGPTmutator_remote_config_draft_list
ChatGPTmutator_remote_config_fetch
ChatGPTpredictions_product_revenue_metrics
ChatGPTpredictions_product_revenue_report
ChatGPTslack_channel_list
ChatGPTCapabilities
Links
App Stats
97
Tools
ChatGPT
Platforms
Category
Ad Performance AnalysisWorks with
Data refreshed daily






