Should AI agents have write access to ad accounts?
In short
Yes, but narrow write access only. Issue read-only tokens by default and a write token only for named actions. Let automated writes reduce spend, by pausing ads or creating ads that start paused, never activate ads or raise budgets. Require a minimum spend before any automatic pause, and record every change, including failed ones, in your own audit log.
Everything below describes the write paths of the Loyalz MCP server and panel in the production source of 4 November 2026 (backend commit 2c78adc). Each rule is a line of code, and the guardrails table below names the backend file it sits in. The list of Loyalz MCP tools shows what an agent can call; this page covers what stops it when the call would change spend.
Read and write access: issue them as separate tokens
An agent that reports on ads needs a read token, so a new token should be read-only unless someone asks for more. Ours are: the token endpoint sets scope to read when the request does not name one, and refuses any value other than read or write.
The read side is an allow-list, not a deny-list. A read token can call 12 named tools (query_metrics, meta_insights, store_orders and nine more) plus anything starting with one of six prefixes, such as get_ or list_. A tool added next month is closed to read tokens until a developer declares that it only reads. A deny-list fails the other way round: the new tool is open until somebody remembers to close it.
The scope is read from the database on every tool call, not once when the session opens. Delete a token and the agent's next call is refused, even mid-conversation.
| Token | What the agent can do | What it cannot do |
|---|---|---|
| Read (the default) | Query metrics, read Meta and Google Ads objects, list creatives, read publish results | Approve, publish, edit dashboards, change any setting |
| Write | Everything above, plus Studio approvals, publishing paused ads, dashboard edits | Activate ads, create campaigns or ad sets, raise budgets |
An optional expiry, from one day to ten years, can be set when the token is created, and the form in the panel suggests 90 days. Without one the token lives until it is deleted.
How to let an AI agent pause campaigns safely
Let automation move spend in one direction only: towards less of it, and only after an ad has had a fair chance to work. In Loyalz that is a pause rule, and it has four constraints.
- It can only pause. The function that touches Meta sends one field,
status: PAUSED. No code path in the rule engine activates an ad, changes a budget or deletes anything. - It needs a spend floor and a guard. A rule fires per ad, and only when the ad has spent at least the floor in the window and its ROAS is below, or its cost per lead above, the limit the merchant set. A rule with neither guard is refused at save time. Without the floor, a new ad with one click and no purchase would be paused within the hour.
- Saving a rule pauses nothing. Creating a rule runs it as a dry run and returns how many ads it would pause now. The comment in the code gives the reason: a typo must not take an account down before the person can re-read what they typed. A separate scheduled job does the pausing.
- It waits for attribution. Meta keeps attributing conversions for hours after a click, so an ad that looks hopeless at noon can look fine by evening. Each rule carries its own cadence, and the default is once a day.
| Cadence | Gap between evaluations |
|---|---|
| Every hour | 1 hour |
| Twice a day | 12 hours |
| Once a day (default) | 24 hours |
| Once a week | 168 hours |
The panel warns when someone picks hourly. Running a rule on demand, outside its schedule, is limited to agency staff.
Creation follows the same principle. When an agent publishes Studio creatives to Meta through MCP, every ad it creates is paused, it can only add ads to an ad set that already exists, and it has no tool to activate them, create a campaign or set a budget. A person turns the ads on. Meta's own Ads MCP server behaved the same way in our pilot on 26 July 2026: a request to archive or delete a campaign came back as paused, and the campaigns it created started paused.
How to audit what an AI agent changed
Write the audit row in your own database at the moment of the change, because the platform's history records that something changed but not why. Google has confirmed at least one case where changes made in an advertiser's account did not appear in its change history.
Every ad a rule pauses leaves one row in AdPauseAction:
| Column | What it answers |
|---|---|
rule | Which standing rule acted |
ad_id, ad_name | Which ad, by the ID Meta uses and the name people use |
spend, roas, cpl | The numbers the rule saw when it decided |
reason | The guard that tripped, as text, for example ROAS 0.00 < 1.00 |
succeeded, error | Whether Meta accepted the pause, and its message if not |
created_at | When |
The failure rows matter as much as the successes. A pause Meta rejected is written down with its error, so "the rule said it paused my ad, but the ad kept spending" has an answer in the panel rather than in a support thread.
The rule row keeps the person who created it, when it last ran, how many ads that run paused, and the last error. Read together, the two tables answer the question an account manager actually gets asked: who paused this ad, on what evidence, and could it have been anyone else.
Studio publishing is audited differently. Each publish is a batch with a state per item, and every mutation through MCP carries an idempotency key, so an agent that retries after a timeout gets the original batch back instead of a second set of ads.
Guardrails for AI in ad accounts, as enforced in code
The useful guardrails are the ones a model cannot talk its way past, because they live in the server and not in the prompt. These are the ones Loyalz enforces:
| Guardrail | What it prevents | Where it lives |
|---|---|---|
| Read-only token by default | A reporting agent holding write access it never needed | app/api/mcp_tokens.py |
| Allow-list of read tools | A new tool silently becoming callable by read tokens | loyalz_mcp/security.py |
| Scope checked on every call | A revoked token finishing the conversation it started | loyalz_mcp/security.py |
| Rules can only pause | Automation spending more than a person approved | insights/ad_pause.py |
| Spend floor plus at least one guard | Pausing ads before they have had a chance | insights/views_pause_rules.py |
| Dry run when a rule is saved | A mistyped threshold acting before anyone reads it | insights/views_pause_rules.py |
| Daily default cadence | Decisions made on conversions Meta has not attributed yet | insights/models/ad_pause_rule.py |
| New ads always paused, existing ad sets only | An agent launching spend or new campaigns | studio/meta_publish.py |
| Whole batch validated before any write | Half a batch published when one creative lacks approved copy | studio/views_publish.py |
| One audit row per paused ad, failures included | An account change nobody can explain | insights/ad_pause.py |
Prompt instructions such as "never raise a budget" are not on this list. They are worth writing, and they are not a control: the model reads them, and a model can be wrong.
Where these guardrails still fail
Five gaps remain, and each one is a judgement call rather than a bug.
- Approval is not a human step if the agent holds a write token. Studio refuses to publish a creative without approved copy, but
approve_assetsandapprove_ad_textsare MCP tools. An agent with write access can approve its own work and then publish it. The human gate is the token scope, not the approval flag, so give publishing agents a token only for the session in which a person is watching. - No purchases counts as a ROAS of zero. An ad that passed the spend floor and recorded no purchase fails any ROAS guard. That is intended for a sales campaign and wrong for a prospecting ad or an account with a broken pixel. Use a cost-per-lead guard, or a higher floor, for upper-funnel campaigns.
- The rule judges on Meta's attributed ROAS, not on store revenue. A pause rule is only as good as the number it reads; the difference between platform ROAS and profit applies to automation as much as to reporting.
- A Studio dry run still uploads media to Meta. It skips creating ads, but it is not a preview without side effects.
- Tokens created before scopes existed keep full access. The model's default is write, so that old integrations did not break. The Agents page in the panel labels each token "Read only" or "Read and write"; reissue anything older than the feature.
What to check before an agent gets write access
Ask these questions of any tool, ours included, before an agent gets a token that can change an ad account:
- Is read-only the default, and is it enforced by an allow-list?
- Which actions can the write token take, and which direction do they move spend?
- Does anything the agent creates start paused?
- Is there a minimum spend before automation may judge an ad?
- Does saving an automation preview it, or act on it?
- Where is the audit row written, and does it record failed changes?
- Can a token be revoked mid-session, and does the next call fail?
If a vendor answers the last two with "the platform's change history", the audit trail is someone else's and incomplete. For how the Loyalz server connects to ad and store data in the first place, see connect Meta Ads and Google Ads to Claude, and the AI agents and MCP guides for the rest of the cluster.
Common questions
Can an AI agent pause my Meta ads through Loyalz?
Not directly. Pausing by rule runs on a schedule set by a person in the panel; the MCP tools read Meta data and publish paused Studio ads, and none of them pauses or activates an existing ad.
Can an AI agent turn ads on or raise a budget through Loyalz?
No. Ads published through MCP are created paused in an existing ad set, and no tool activates ads, creates campaigns or changes budgets. A person turns ads on in the panel or in Ads Manager.
How do I give an agent read-only access?
Create an MCP token in the merchant panel without asking for write scope. Read-only is the default, and read tokens can call only tools on an allow-list.
Where do I see what an automated rule changed?
In the panel under the pause rule: one row per paused ad with the spend, ROAS or cost per lead the rule saw, the reason, and whether Meta accepted the change.
See it on your data
On a twenty-minute call we connect one ad account and one month of your orders, and work out the gap between what the platform claims and what your shop took. Profit and POAS stay blank until you enter your margin and costs per order.
Book a demo