Case Study 08 · Marketing Operations

AI Ad Creative Studio

An internal web app that writes Meta ad copy from four live sources instead of memory: the business's own ad performance data, what customers actually say in chat and call transcripts, current web trends, and what competitors are running. Designed, built, deployed and in daily use in one working day.

Industry

EdTech / Marketing Operations

Build Time

Designed, built and deployed in one working day

Platform Cost

A few dollars a month in model usage, hosting effectively free

Stack

Cloudflare Worker + KV · Claude API · Meta Marketing API · Call and chat transcripts

4 Live data sources briefing every batch
15 Distinct problems solved during the build
<1 min To generate an on-brand ad batch
1 day Designed, built, deployed, in daily use

Every ad started from a blank prompt.

The business had run thousands of Meta ads across several education programmes, split between two creative formats: click-to-WhatsApp ads that route a prospect into a conversation with a sales closer, and lead-generation ads built around a keyword tile. Ad copy was written by hand in a general-purpose chat model, one prompt at a time, from memory of what had worked before.

That process worked, but three things were consistently missing. The model never saw the account's real performance data, so it had no idea which ads were actually converting or which angles had already been exhausted. It never saw what customers were actually worried about, since that context was locked inside thousands of chat threads and hundreds of call recordings nobody had time to read. And every batch started from scratch, with no memory of the last one.

Before

  • Ads written from memory in a general chat tool, one prompt at a time
  • No visibility into which of thousands of past ads actually converted
  • Customer pain points locked inside chat and call transcripts, unused
  • No shared record of what had already been tried and burnt
  • One person's workflow, not something the wider team could run
  • No structural checks against house copy rules before an ad shipped

After

  • One shared app, a batch of on-brand ads generated in under a minute
  • Every brief includes a self-refreshing library of proven winners and losers
  • A customer pain-point database mined from chat and calls, growing daily
  • Every ad checked against house rules before it can be copied out
  • Shared across the team, with every batch anyone makes visible to everyone
  • Cheap to run, a few dollars a month at normal usage

Ground truth before a single line of app code.

Before writing any application logic, every ad the account had ever run was exported from the Meta Marketing API with full lifetime metrics, then aggregated per creative. This became the ground truth for the winners library and the style examples the model would learn from, rather than starting from generic marketing advice.

Data pulled Used for
Every ad with lifetime performance metrics Winners and losers library, what is working right now
Every unique creative, deduplicated Copy examples the model learns structure and specificity from
Highest-spend creatives, carrying the large majority of total spend Proven, high-confidence patterns to brief every batch on
Existing keyword tile creatives Style reference and duplicate detection for new tiles
Programme codes with topic banks Structured tile generation per programme

On top of this sits a keyword and pain-point master document, the full ad copy history, and a brand rulebook that applies to every line of copy the app produces: no em dashes, regional English conventions, a banned word list, and correct programme naming throughout.

One page, one password, one deploy command.

The app is a single web page served by a Cloudflare Worker, with Claude doing the writing and Cloudflare KV storing everything shared across the team. Three tabs cover the full workflow.

01

WhatsApp ad generator

Pick a lead type, tick the hooks and pain points to cover, choose how many. Produces primary text, headline, call to action, and the correct routing template for each ad. Every generated ad is required to ask the customer's key qualifying detail and to reinforce a core trust signal, enforced automatically rather than left to memory.

02

Keyword tile generator

Pick a programme and a pain family. Produces the keyword tile, capitalised, a fixed number of parts, under a strict character limit, ending in a consistent call to action, plus the matching primary text. Every tile is checked against every tile already running, to prevent an accidental duplicate going live.

03

Research tab, five sources

A web trend scan, a competitor scan of the public ad library, the customer pain-point database, the last month of the account's own results, and the winners ranking. Results and winners are split by ad format so the two are never compared against each other. Anything selected here is written directly into the brief for the next batch.

Every batch carries controls beyond the tab itself: creative type (keyword tile, an AI image prompt, or both), a pain and gap intensity dial from 1 to 5, a freedom dial from strict template to deliberate outlier, toggles for which research feeds should shape the brief, batch size, and a "feeling lucky" mode that picks a random lead type, a current trend, and a real customer theme with no brief required at all.

What happens to every ad before it leaves the tool.

Every generated ad passes through a rule checker before it can be copied out: em dash detection, banned words, missing required qualifiers, character limits, emoji where none belong, duplicate tiles, and missing calls to action. Each card carries a revise box with quick preset chips, an undo, and a "more like this" action that produces six fresh variants of any card.

A background job runs every morning without anyone pressing anything: it pulls the last month of performance data, rebuilds the winners library from the recent window, and reads new chat and call transcripts into the pain-point database. Whoever generates a batch that day is briefed on the previous week's results automatically.

The hardest problem: a database that maintains itself.

Customer pain points and questions are the best source of ad angles available, but they were locked inside thousands of chat threads and hundreds of call recordings. A manual export would work once and go stale immediately. Building an automatic pipeline hit three real constraints at once: an outbound request cap per invocation on the hosting platform's free tier, a call platform that does not retain transcripts indefinitely, and rate limits on every API involved.

The solution was a two-phase pipeline that runs in short, resumable steps. Each step pulls a bounded batch of conversations into a queue, saves its position, then hands the queue to the model for extraction. Only the customer's side of each conversation is kept. Every line is tagged by kind (pain, desire, question, objection), theme, and programme, deduplicated, and stored. Raw call transcripts are saved the moment they are fetched, so they survive even after the call platform later drops them. A cursor is saved per conversation so nothing is ever read twice, and the process resumes cleanly if interrupted.

The backfill extracted well over a thousand customer-voice lines within its first working session, climbing daily. Once caught up, the database tops up every morning, and every ad brief now carries the most common customer worries for the relevant programme, plus a handful of rare ones flagged explicitly as angles nobody else is running.

Fifteen real problems, each documented and fixed.

The build surfaced fifteen distinct problems, from API access and hosting through to copy rules and team workflow. A representative selection, each with what went wrong and what closed it for good.

●

Writing from memory instead of results

The model had no idea which past ads had worked or which angles were already burnt. Fixed by building a winners library (high result count at the lowest cost per result) and a losers library (high spend for very few results), both written into every brief with the actual copy attached, rebuilding itself every morning.

●

Worked as a prototype, broke as a standalone app

The first version ran inside a chat interface and generated fine. Moving it standalone hit a concurrency error, since a consumer subscription and API access are separate services with separate billing. Fixed with a proper API key held server-side, retry with backoff on rate limits, and batches split into smaller passes so one failure never loses the whole set.

●

Nothing seemed to happen when you clicked

Long-running actions were working but reporting progress to a status line hidden on the wrong tab. Replaced with a visible toast in the corner on every tab: a spinner and live message while something runs, buttons disabled until it finishes, and results held on screen afterwards.

●

A retired API shortcut silently broke the winners library

A platform version upgrade deprecated the batch lookup the library relied on to fetch ad copy, and a scheduled refresh quietly returned zero winners with no obvious cause. Fixed by rewriting the fetch through the supported paginated route and pinning the API version explicitly, so the next deprecation cannot cause a silent failure again.

●

Build a diagnostic before guessing at the fix

A new integration returned a generic, unhelpful error on its first run. Rather than guess, a step-by-step test tool was built that checks each stage of the connection in sequence. It isolated the exact call causing the failure in one click, after an hour of guessing had found nothing.

●

The ads all looked the same

Team feedback after the first batches: descriptions varied but everything sat at the same intensity, and the model followed the rules so closely nothing surprising came out. Fixed with a pain-intensity dial and a freedom dial, each on a defined 1 to 5 scale, plus a one-click "make this winner odder" action, turning variation into a control rather than a lottery.

Deployed, in daily use, learning every morning.

Live and password protected

Deployed behind a login covering the entire app, not just the API, in daily use by the team that used to write every ad by hand.

Self-refreshing every morning

Performance data, the winners library, and the customer pain-point database all update on an unattended daily schedule, no manual trigger required.

Shared, not personal

Every completed batch is kept automatically and visible to the whole team, stamped with who made it, so nothing generated is invisible to colleagues.

Seven lessons worth carrying to the next build.

Get the data out first

The winners library and the existing tile set gave the model a floor to stand on that no amount of prompt engineering could replace.

Rules belong in code, not memory

A copy-rule fix that would have meant re-briefing a human took minutes once the rule lived in one place and was checked automatically on every batch.

Long jobs need a visible heartbeat

Two of the fifteen problems were the tool working correctly while looking completely dead. A visible progress signal would have prevented both.

Save progress every step

A pipeline that can be interrupted and resumed without losing its place is a pipeline people will actually run against a real backlog.

Read platform documentation carefully

Heavier verification tiers exist for reading other people's data. Reading your own business's own channel typically needs a much lighter access tier.

Build the diagnostic before the fix

A dedicated step-by-step connection test found a real fault in one click, after significant time spent guessing had found nothing.

Variation has to be a dial, not a hope. Left alone, a model briefed heavily on winners will converge back onto the winners. Making intensity and freedom explicit controls, on a defined scale, is what kept the output from flattening into a single template.

One Worker, four live data sources, one deploy command.

Component Technology Purpose Cost
Application and hosting Cloudflare Worker with static assets Serves the page, proxies the model API, talks to every external source, runs the daily job Free
Shared storage Cloudflare KV Batches, saved research, winners library, pain-point database, all shared across the team Free
Copywriting Claude API Every batch and revision, briefed on the winners library, research feeds and rule set Per use
Ad performance data Meta Marketing API Lifetime and recent performance, the winners and losers library, creative thumbnails Existing
Customer voice extraction Chat and call transcript APIs Pain point, desire, question and objection mining, resumable and deduplicated Existing
Scheduling Cron trigger, daily Unattended morning refresh of performance data, winners library and pain-point database Free

From a blank prompt to a briefed, checked, shared batch.

What used to be one person's manual workflow, opening a chat tool, pasting a brief, iterating from memory, is now a shared app any team member can run. Every batch is briefed on real performance data, real customer language, current trends, and competitor activity, checked automatically against house copy rules before it can leave the tool, and visible to the whole team the moment it is generated.

The build surfaced fifteen distinct real problems along the way, from API access and hosting through to copy rules and team workflow, each documented with what went wrong, what was done about it, and where it stands, so anyone on the team can pick it up, understand every decision, and extend it.

The pattern generalises well beyond ad copy: any creative workflow shaped like "brief the model on our own results, our own customers' language, and current context, then check the output against our own rules before a human ever sees it" can be assembled from the same parts.