Policy states
What happens to a proposal after you POST it — and why "queued" is a success.
This is the page to read before automating writes. The write model is the one part of this API that doesn't behave like a normal REST resource, and it's deliberate: every change here moves real money against an API with no dry-run and no sandbox.
A write creates a decision, not a change
POST /accounts/{projectId}/actions/budget-change does not set a budget. It
records an action and asks the
safe-apply policy what to do with it:
{
"actionId": "9f2c…",
"kind": "budget_change",
"mode": "auto_applied",
"riskTier": "safe",
"policyReasons": [
"Budget raise of 20% is within the 30% safe bound.",
"30d CPA $3.10 is inside the target band."
],
"summary": "Set \"Brand US\" daily budget → $150.00 (+20%)"
}Two outcomes, both 2xx:
auto_applied— the policy cleared it and an apply job is enqueued.queued— it's waiting in/approvalsfor a human decision.
Call GET /accounts/{projectId}/policy to see the live rules and predict which
you'll get before you send.
mode is a snapshot, not an outcome
The apply worker re-runs the policy immediately before touching Apple, and
can demote an auto-approved action back to proposed if conditions changed. It
also re-runs local validation and hard-caps any budget change at 3× the
warehouse value.
So mode: "auto_applied" means "cleared at propose time", not "done". For
terminal state, poll the ledger:
curl ".../api/v1/accounts/current/actions?limit=10" -H "Authorization: Bearer tsk_…"The Location header on a 201 points at the action.
Queued is not an error
These all force review rather than failing:
- The org kill switch (
asaAutoApplyoff) — the global stop mechanism, checked at both propose and apply time. - The account isn't
active. - The 24-hour cap — 20 auto-applies per account, database-derived and therefore genuinely global (unlike the per-instance HTTP rate limits).
- The action's own risk tier — some kinds are always review, notably switching a campaign's bid strategy, because it resets Apple's automated bidding learning.
policyReasons always says which of these applied. Treat a queued result as
work successfully filed, not as a rejection.
What auto-applies
Bounded, low-risk, reversible changes only:
- Negative keywords — adds and removes
- Up to 10 EXACT keywords with bids ≤1.5× the account's 30-day average CPT
- Pausing a keyword or an ad group
- Budget raises ≤30% when the campaign's 30-day CPA is inside the operator's band
- Budget cuts ≤30%
- One ≤10% step on an existing
targetCpaor ad-groupcpaGoal - Reverting a change that auto-applied
Everything else queues.
Partial application is real
Apple's bulk endpoints can return 200 with per-row errors. An action that
added 40 keywords where 3 failed is applied with warnings — at least one
row succeeded, so it isn't a failure, but it isn't complete either. Always
check warnings on applied actions in the ledger.
Composite kinds (create_campaign, clone_campaign, create_ad_group,
clone_ad_group, change_match_type) sequence several Apple calls and are
not atomic. A mid-sequence failure rolls back on a best-effort basis and
reports the step that broke, so a failed composite can still have left
partial state behind. Re-read the account before retrying one.
Undo
Applied actions are revertible where prior state was snapshotted — which is
most of them, because Apple has no change history and every payload therefore
carries its own prior* fields:
curl -X POST ".../api/v1/accounts/current/actions/9f2c…/revert" \
-H "Authorization: Bearer tsk_…"This proposes and immediately applies the compensating change. Kinds with no
computable inverse return 422 not_revertible.
Safe automation checklist
GET /accounts/{projectId}/syncs— is the data fresh?GET /accounts/{projectId}/policy— is the kill switch on? How many auto-applies are left today?GET /accounts/{projectId}/plan— what's worth doing?POST …/actions/<kind>with anIdempotency-Key.- Read
modeandpolicyReasons. Poll the ledger for terminal state. - Check
warningson anything that reachedapplied.