Reference
MCP tools
SpendSignoff exposes two kinds of tools to your AI client: read tools that observe your accounts, and a single draft surface, propose_change, that stages reviewable changes. Both run under tightly scoped permissions.
Two scopes — and the one that does not exist
mcp.read and mcp.draft. There is no mcp.approve scope anywhere in the API. The AI literally cannot approve or apply a change — that authority lives only in the SpendSignoff app, behind a human.Read tools
Scope: mcp.read — observe, never mutate.
list_accountsmcp.readList linked ad accounts with token health and monthly spend.
{}list_campaignsmcp.readList campaigns for an account with status, budget, and pacing.
{ "account_id": "acc_…" }get_performancemcp.readFetch spend, conversions, and ROAS over a window.
{ "account_id": "acc_…", "range": "last_7d" }get_audit_logmcp.readRead applied changes and their before-states for rollback context.
{ "account_id": "acc_…", "limit": 50 }Draft surface — propose_change
Scope: mcp.draft — stages a draft, never applies it.
propose_change is the only write-shaped tool, and it does not write. It stages a draft — a before → after diff plus projected impact — and returns a draft_id in pending status. The draft lands in your approval queue; nothing reaches the live account until you confirm it.
example call
propose_change({
account_id: "acc_8f21",
rationale: "Display·Retarget is at 0.4x ROAS; Brand·Search is budget-capped.",
diffs: [
{ field: "Daily budget — Brand·Search", before: "$120", after: "$320" },
{ field: "Daily budget — Display·Retarget", before: "$200", after: "$0" }
],
impact: { kind: "revenue", amountUsd: 1240, label: "+$1,240 / mo projected" }
})
// → returns a DRAFT (draft_id, status: "pending").
// It is NOT applied. Approval happens in the SpendSignoff app.Drafts can go stale
stale and must be re-armed against a fresh snapshot before it can be approved. This prevents approving a change against numbers that no longer hold.Next
REST API
The /v1 namespaces: internal, app, and webhooks.