The short version
A change reaches a live ad account only if it was expressible in a closed vocabulary, survived compilation against fresh account state, was approved by someone with the right role against a hash of that exact plan, still matches the account at publish time, and passes four independent kill switches. Failing any one of those stops the whole publish rather than the offending item.
1. The eleven tenets
These are enforced in code and covered by tests, not written on a wall. Tenets five through eleven exist specifically because Outfitter writes to live money.
- Deterministic core. Compilation and validation are pure functions. No number in a plan was authored by a model.
- Honest tenancy. One scoping dependency. A cross-tenant request is a 404, not a 403, because a 403 confirms the thing exists.
- Unverified fields ship off. A platform field we have not confirmed against live behavior ships flagged and disabled rather than quietly enabled.
- Both-engine tests. The suite runs against SQLite and PostgreSQL on every run, so a database-specific bug cannot hide.
- Every write through the gate. No adapter send path is reachable outside an approved, hash-verified plan.
- Closed operation vocabulary. An allowlist. Fields that are not listed do not exist in the schema.
- Blast radius and money caps. Hard ceilings on batch size and on per-item and aggregate money movement.
- Kill switches. A global switch and a workspace switch. Reads and validation keep working when publishing is off.
- Invalid never publishes. Spec and cap violations exclude at compile with a reason. They are never warned through.
- Verify hardest at the write path. Every gate gets an adversarial pass that tries to refute its own findings before the code is trusted.
- Sandbox by default. Adapters point at test endpoints until a workspace-level production flag is deliberately flipped.
2. Changeset lifecycle
A changeset is a batch of proposed changes against exactly one connected ad account. It moves through these states and cannot skip one.
| State | What is true |
|---|---|
| draft | Items are being added. Nothing has been checked. |
| compiled | A plan exists. Every item is a candidate or an exclusion with a reason, and the plan has a hash. |
| approved | The role requirement is satisfied against that specific plan hash. |
| publishing | Items are being sent. Each one has a journal row already committed. |
| published / partial / failed | Terminal. Per-item results are recorded, including which items never left. |
3. Compile and exclusion
Compilation takes the draft's items plus a snapshot of account state and returns a plan. It touches no database, reads no clock and makes no network call, which is what lets it be re-run identically at publish time.
Each item comes out of compile in one of three conditions:
- Candidate. Legal to publish. Money candidates carry the old value, the new value and the percentage move.
- Excluded. Will not publish, with a machine-readable reason on the row. Exclusions cannot be overridden from the interface.
- Warned. Will publish, with something worth reading first, such as a target that moved inside the trailing 14 days.
Compilation emits two fingerprints: one over the rules that produced the plan, and one over the plan itself including its exclusions. Both travel with the approval. Changing a rule invalidates approvals made under the old rule, by construction.
Exclusions are part of the plan. The plan hash covers what will not publish as well as what will. Approving a plan is approving its refusals too, which means an item cannot be quietly promoted from excluded to publishable between approval and publish.
4. Approval requirements
| Batch contains | Minimum role | Typed confirmation | Two people |
|---|---|---|---|
| No money operations | admin | No | Yes, when two or more eligible approvers exist |
| Any money operation | owner | Yes, the aggregate delta typed by hand | Yes, when two or more eligible approvers exist |
Two-person approval means two distinct user identities approving the same plan hash. When a workspace genuinely has only one eligible approver, one approval is sufficient and the run records the reason it was allowed, so the audit trail shows why the second signature is missing.
The requirement is re-evaluated as a live query at publish time rather than read from a stored status. Promoting a second owner after a single-owner approval turns a satisfied plan back into an unsatisfied one, and the publish refuses until the new requirement is met.
5. Kill switches
Four independent layers, checked in this order. The first failure refuses the entire publish. A connection still in sandbox mode short-circuits all four, because none of them gate anything except going live and going live is not being attempted.
| Order | Layer | Scope |
|---|---|---|
| 1 | Global publishing flag, an environment variable that defaults to off | Whole deployment |
| 2 | Workspace publishing toggle | One workspace |
| 3 | Production authorization timestamp on the connection | One ad account connection |
| 4 | Per-platform live allowlist | One platform |
Reads, inventory sync, compilation and spec validation keep working when publishing is off. Turning the gate off does not turn the product off.
Layer three, on the connection itself. A sandbox connection records the exact payload it would have sent and sends nothing, so a sandbox changeset that reaches published has moved no money. Changing the mode also changes the configuration digest, which invalidates every approval already given on an unpublished changeset.
6. Caps and limits
| Limit | Default | Ceiling |
|---|---|---|
| Items per changeset | 25, raisable by an owner | 200, not configurable |
| Per-item money move | 20% | 50%, not configurable |
| Aggregate daily-budget delta per changeset | Workspace cap | Set per workspace |
| Inventory freshness for money operations | 1 hour | Older is blocked, not warned |
The 20% per-item default mirrors bid-strategy doctrine rather than being an arbitrary round number. Moving a target more than a fifth in one step throws away the learning the platform has accumulated, so the tool makes the safe move the easy one.
7. Execution and resume
- Journal first. A pending row is written and committed before the network call is made. An item with no row provably never reached the platform.
- Deterministic idempotency keys. A retried item carries the same key, so a repeat cannot become a duplicate change.
- Transient-only retry. Timeouts and rate limits retry with backoff. A rejection does not.
- Circuit breaker. A platform that keeps failing gets cut off rather than hammered, and the trip is an audited event.
- Resume re-authorizes. Resuming an interrupted publish runs the entire authorization chain again from the beginning. It does not continue an old one on trust.
8. Rollback
Every mutating operation snapshots prior state into the item before it runs. Reverting drafts a new changeset that inverts the items that provably landed, and that changeset compiles, gets approved and publishes like any other. There is no fast path.
Four outcomes are tracked separately, because they need different responses:
- Landed. Invertible, and included in the rollback draft.
- Never sent. Nothing to undo.
- Failed. Nothing to undo.
- Sent, outcome unknown. Possibly live. Listed for a person to check, never guessed at.
Three operations have no inverse. Creating a responsive search ad, a Meta ad creative or a Meta ad produces something the vocabulary has no operation to remove. The rollback preview lists these in their own section rather than silently dropping them.
9. Audit trail
51 typed action constants cover the lifecycle, including compilation, approval, publish start, publish completion, drift refusal, rollback drafting, connector mode changes and circuit-breaker trips. Each publish records the request fingerprint, the platform response and the resulting platform identifiers.
The audit log has a reader API and a screen in the product. On plans that include it, the read-only public API exposes the same records for export into your own systems.
10. Credentials and data
- Encrypted at rest. Platform credentials are encrypted with a key held outside the database.
- Sandbox first. A new connection points at test endpoints. Moving it to production is a separate, recorded action.
- Paste credentials in this release. In-app OAuth is planned. Today you supply credentials directly, which means you can scope them yourself.
- Publishing is unreachable by API key. The public API is read-only by design.
- Hardened deployment. The container runs with a read-only root filesystem, dropped capabilities, no privilege escalation and a process limit. Dependency and static analysis run on every build.
11. Known limits
These are true today. They are listed here rather than discovered later, because a security document that only lists strengths is a marketing document.
- No cross-changeset rolling money cap. The aggregate delta within a single batch is capped. The total across many batches in a day is not. Approvals are the control there.
- Creates are not reversible. See section 8.
- Meta writes are switched off. The adapter is built and registered. All nine operations refuse until the write path clears its adversarial review.
- Microsoft and OpenAI Ads cannot publish. No write adapter exists for either. Their reads are beta.
- Four Google operations are unverified. Portfolio bid strategy targets, shared negative set attach and detach, and keyword CPC bids ship disabled because the verification account had no target to exercise them against.
Questions this page did not answer
Send them to info@sasquatchcreativetx.com. If the answer is a limitation, we will tell you it is a limitation.