Read-only by default. On purpose.
Meta bans accounts for automation patterns we don’t want a fresh LLM session to stumble into. So the ‘safe’ default is the only default: the 14 write tools refuse to call Meta and return the steps to reproduce the action by hand.
What writes return
Every create / update / duplicate / upload tool returns a structured payload — parameterized with the literal name, budget, objective, targeting, and creative the agent was about to send. Your agent can read it, surface it to you, and you paste it into Ads Manager.
{
"performed": false,
"reason": "writes_disabled",
"action": "create_campaign",
"intended_params": {
"account_id": "act_1307459321363937",
"name": "Spring sale - traffic test",
"objective": "OUTCOME_TRAFFIC",
"daily_budget_minor_units": 2000,
"special_ad_categories": []
},
"manual_steps": [
"1. Open Ads Manager for ad account 1307459321363937.",
"2. Click + Create. Pick objective OUTCOME_TRAFFIC (Traffic).",
"3. Name the campaign 'Spring sale - traffic test'.",
"4. Set daily budget to 20.00 USD.",
"5. Leave Special Ad Categories empty.",
"6. Save as draft (do NOT publish until reviewed)."
]
}Flipping writes on (self-hosted)
Self-hosting and want writes? Set the env var:
META_WRITES_ENABLED=trueOn the hosted api.brandmov.com the flag is off and will stay off until we’re comfortable with the audit trail. Even when on, the safety rails below stay engaged.
Safety rails (always on)
- Every create/update ships PAUSED.
status=ACTIVEis scrubbed toPAUSEDat the edge — you activate in Ads Manager after reviewing. - CBO conflicts (campaign-budget + ad-set-budget) detected before the POST — no cryptic Meta error at runtime.
- Legacy objectives (
CONVERSIONS,LINK_CLICKS, …) rejected with a mapping to the correctOUTCOME_*. COST_CAPwithout abid_caprefused locally, not four seconds later by Meta.- Outbound Graph calls are throttled per-process — default
META_API_MAX_CONCURRENT=4,META_API_MAX_PER_SECOND=5— so a chatty agent can’t trip Meta’s BUC rate limits. - Insights
compact=Truestrips theomni_,onsite_web_,offsite_conversion.fb_pixel_prefixes that agents otherwise drown in.
Why not just YOLO it
Two reasons. (1) Meta’s policies treat agent-driven writes as “programmatic activity” and an account ban is irreversible — a single hallucinated special_ad_categories on a housing ad gets your account taken down. (2) The user we’re actually optimizing for is a marketer who wants to see what the agent would have done before it happens. The refusal payload is the audit log.