Skip to content

Luxury home builderThailand — Remote

Campaign OS — Media-Buying Automation Plus a Reporting Dashboard

A system that reads both ad platforms every night, qualifies each website inquiry, and proposes the next move for a person to approve

Media-Buying AutomationMeta Marketing APIGoogle Ads ScriptsLead QualificationDashboard
Luxury home builder
2

Ad platformsMeta and Google Ads, read every night

Approved

Every changenothing touches an ad account without a click, and every change can be undone

240+

Automated testsacross the web app and the Python tooling

Context

A builder of custom luxury homes in Thailand, from about $300k a house, selling to a small pool of high-value local buyers. Their ads run on Meta and Google, and inquiries come in through the website's forms.

At roughly two genuine inquiries a week, the ad platforms never get enough signal to optimize on their own, and nobody could say which campaign a given buyer had come from. I built them one system that does the reading, the qualifying and the proposing, plus a dashboard that shows what is going on every morning.

The Challenge

  • Too little signal. Ad platforms learn from conversions, and two a week is far below what their bidding needs. Left alone, the algorithms guess.
  • Leads without a source. Most inquiries could not be traced back to a campaign, so budget decisions were made blind.
  • Not every inquiry is a buyer. A form fill from someone with no budget or land looks exactly like a real buyer to the ad platform.
  • Control matters. The business wanted every change reversible and nothing changed without sign-off.

What I Built

The nightly loop. Every night the system reads both ad platforms (Meta through the Graph API, Google through an in-account Ads Script), pulls the website inquiries, qualifies each one (does the buyer have the budget or the land?), and ties an inquiry to its campaign whenever the form carries a campaign tag that matches one. A rules engine then diagnoses the accounts and proposes specific changes: pause a campaign that spends without inquiries, move budget to the best performer, block wasteful search terms.

The nightly loop: read both ad platforms, collect inquiries, qualify them, tie tagged inquiries to their campaigns, diagnose and propose, wait for approval, apply, and check the result at day 3 and day 7

Guard rails. Nothing is applied until a person approves it. Before any change the system saves a restore point; spending ceilings and a nightly cap on the number of changes bound what it can do; a kill switch stops everything at once; and every change has an undo. Three and seven days after a change it checks the numbers and proposes a revert if they got worse. Each kind of action also has an automatic mode, off by default, to be switched on one action type at a time once the proposals have earned it.

Six guard rails: approval before anything applies, a restore point first, spending ceilings, a nightly change cap, a kill switch, and one-click undo

A two-tier conversion signal. Built, and switched off until the client opts in. Google can be told about every contactable inquiry, so its bidding has more to learn from, and separately about qualified buyers. Only qualified buyers count toward the system's own budget decisions. The platform gets volume; the budget follows quality.

The dashboard. One screen for the morning: last night's spend, real inquiries and cost per inquiry, the changes made, and the changes waiting for approval. Behind it, a full change history with undo, campaigns with wasted spend highlighted, every ad with its cost per lead, the limits and the kill switch, and a map of the night's pipeline.

The dashboard's screens: last night, changes, campaigns, ads, limits, playbook, scorecard and the system map

Open to AI tools. Besides the web app, the same core is reachable through a REST API and an MCP server with scoped keys. An AI assistant can read the accounts and draft proposals under exactly the same approval rules as a person.

The stack: Next.js 16, TypeScript, Drizzle ORM on Supabase Postgres, scheduled runs on Vercel, the Meta Graph API, Google Ads Scripts, and Python tooling for backups and diffs.

Timeline

  • September 3, 2026. Kickoff: backup and diff tooling for both ad platforms, and competitor research across the local market and national builders.
  • September 4. Scope set: optimize the campaigns, not rebuild the lead system. Campaign OS designed.
  • September 7–9. The dashboard screens, the guard rails and the 3- and 7-day outcome checks; Google sync live on September 8, with nightly reads running on the real accounts.
  • September 11. Inquiry pull, qualification and campaign matching.
  • Late September. A review of every write path, with fixes, and the nightly inquiry pull.

What I'd repeat

Propose, don't act. A system that touches someone else's ad spend earns trust one approved change at a time: show the evidence, let a person click, and keep the undo one click away. Autonomy comes later, one action type at a time.

Fix the signal before the spend. At two inquiries a week, no amount of budget tuning teaches an algorithm anything. Tying leads to their campaigns and separating "contactable" from "qualified" had to come before any optimization could mean something.

Need a similar engagement?

Book a call →