SpendSignoffSpendSignoff
Deep Dive9 min read··SpendSignoff

Build an AI paid-media agent: architecture and trade-offs

Every few weeks a thread appears on Hacker News or in a Slack group: "I am building an AI agent for Google Ads — any advice?" The architecture questions are usually the same, and the mistakes are usually the same. This is the honest answer, based on what SpendSignoff learned building and operating the system for several months.

The core loop: read, classify, propose, gate

A paid-media agent does four things: reads the account state (campaigns, spend, performance, pacing), classifies what is unusual or actionable, proposes a change, and waits at a gate before executing. The gate is where most designs go wrong.

Many early designs skip the gate or make it optional. This is a mistake. An ad account with write-capable AI and no approval gate is a liability — a single bad inference can double a daily budget or pause every campaign. The gate is not a UX nicety; it is the product's core safety property.

The gate must be server-side

Client-side approval screens can be bypassed by prompt injection. The only reliable gate is a server-side scope check: the model does not hold mcp.approve, so the write cannot happen regardless of what the prompt says.

Token vault design: what to encrypt and where

You will store OAuth refresh tokens for Google Ads and Meta. These are the highest-value credentials in your system — they grant continuous access to spending accounts. Storing them encrypted with a KMS key rather than a static secret means a database breach does not expose live tokens.

The practical implementation: envelope encryption in Postgres. The KMS wraps a data-encryption key; the DEK encrypts the token. Decryption requires both the database row and the KMS key, which a stolen DB dump does not include.

Platform API rate limits and how to hit them

The Google Ads API has per-developer-token quotas. Meta's Marketing API has per-account rate limits that reset on a rolling 24-hour window. If you build a polling loop that runs every minute across dozens of accounts, you will hit these limits within days.

The right model: event-driven sync, not polling. Trigger a data pull on user request or on a scheduled heartbeat that spaces calls by account, not by wall-clock time. For V1, a 15-minute polling interval per account is defensible. Sub-minute polling is not.

The autonomy spectrum: propose-only vs autopilot

Every team building an ad agent debates this: how much should the agent do without confirmation? The answer in production is almost always "less than the original design assumed."

Propose-only V1 is the right default. Users trust a system that shows its reasoning before acting. A system that acts and then explains has a much higher bar to clear before users feel comfortable. Start with propose-only, instrument approval rates, and graduate to autopilot for the changes that get approved 95% of the time with zero edits.

  • Propose-only: every change is a draft, every draft needs approval. Highest user confidence, slowest throughput.
  • Envelope autopilot: changes within pre-approved parameters (e.g. budget adjustments under 20%) execute automatically. Moderate confidence, faster throughput.
  • Full autopilot: agent executes without review. Fastest, lowest confidence, not recommended for V1.

What you can skip in V1

  • Real-time bid management — the API latency makes it impractical, and Google's own smart bidding does it better.
  • Creative generation at scale — useful feature, but it multiplies approval queue volume. Add it after the core workflow is proven.
  • Cross-platform attribution beyond ROAS — a correct attribution model takes months to validate. Ship a simple normalized view first.
  • Custom ML models — platform signals are good enough for budget and bid proposals. Build your own signals only when you have enough account data to trust them.

FAQ

How do you handle accounts that have two-factor authentication on the platform side?
OAuth handles this. You connect through the platform's OAuth flow, which includes whatever MFA the user has configured. Once the refresh token is issued, subsequent calls are token-based and do not require MFA again unless the token expires or is revoked.
Is a separate database table per ad platform a good design?
Usually not. A normalized schema with a platform column and typed action enums scales better. Per-platform tables fragment your queries and make cross-platform reporting much harder to build later.

Connect an account read-only and watch the operator work.

Reads are free on every plan. Nothing spends without your two-step approval.

Book a demo

Related reading

    Build an AI paid-media agent: architecture and trade-offs — SpendSignoff · SpendSignoff