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=true

On 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=ACTIVE is scrubbed to PAUSED at 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 correct OUTCOME_*.
  • COST_CAP without a bid_cap refused 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=True strips the omni_, 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 asprogrammatic 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.

Next: Tool reference — all tools grouped by category, with the 14 write tools clearly flagged.