What happens when ads live in your codebase?

Your Ads Are Code. Your Pipeline Is Limitless. CI/CD, image generation, staging accounts, drift detection — all the infrastructure you already have, now for ads.

YAML
# .github/workflows/ads.yml
on:
  pull_request:
    paths: ['campaigns/**']
  push:
    branches: [main]
    paths: ['campaigns/**']

jobs:
  plan:
    if: github.event_name == 'pull_request'
    steps:
      - run: ads plan --ci   # posts diff as PR comment

  apply:
    if: github.event_name == 'push'
    steps:
      - run: ads apply       # executes on merge to main

Your engineering team ships features through CI/CD — reviewed, tested, staged, then deployed.

Your ad campaigns are managed in a browser tab. Changes are manual, untracked, and invisible to the rest of your team.

There's no PR, no diff, no approval process. Anyone with access can change anything at any time.

When ads live in code, your entire pipeline — CI, staging, automation, monitoring — becomes available to your ad campaigns.

What becomes possible

Plan on PR, apply on merge

Add ads to your CI/CD pipeline. When a campaign file changes, `ads plan --ci` posts the diff as a PR comment. Merging to main runs `ads apply`. The same workflow you use to ship software, now for ads.

Terminal
## Acme Ads — Planned Changes

+ campaign/q2-launch                      create
+ campaign/q2-launch/brand-kw             create
~ campaign/retarget/budget                $25 → $40/day
- campaign/old-winter-promo               delete

  2 to create · 1 to update · 1 to delete
  These changes will apply when this PR is merged.

Image generation pipelines

Generate ad creatives programmatically and wire them directly into your campaigns. A script calls an image generation API, saves the assets, and updates the campaign files — all before `ads apply` runs.

TypeScript
import { fal } from '@fal-ai/client'

const images = await fal.subscribe('fal-ai/flux/dev', {
  input: { prompt: 'Acme SaaS UI, dark mode, product shot', num_images: 4 },
})

await updateCampaignCreatives('q2-launch', images.map(i => i.url))
await exec('ads plan && ads apply')

Staging accounts for safe testing

Run `ads apply --account staging` to apply changes to a test account first. Validate that everything looks right before touching production.

Terminal
$ ads apply --account staging

  Creating campaign/q2-launch...               ✓
  Creating campaign/q2-launch/brand-kw...      ✓
  Creating campaign/q2-launch/brand-kw/rsa...  ✓

  Applied 3 changes. Run `ads apply --account production` when ready.

Capabilities

CI/CD integration

Plan on pull request, apply on merge. The same git workflow you use for code, applied to ad campaigns.

Image generation pipelines

Generate creatives programmatically — fal.ai, DALL-E, Midjourney — and wire them directly into campaign files before apply.

Staging account support

Apply to a test account first. Validate changes before they touch your production campaigns.

Cron-based drift detection

Schedule `ads pull` in CI to catch manual UI changes before they cause problems.

Budget automation

Write scripts that adjust budgets based on performance data, season, or business events — then commit and apply.

Monitoring and alerts

Hook `ads plan` output into Slack, PagerDuty, or any webhook to get notified when campaigns drift.