Performance advertising · AI systems · Attribution

I build the systems the spend runs on.

I sit between marketing and engineering. I have bought the media and I write the code that automates it: running the spend, plumbing the data, putting a model where it earns its place, and checking the result against closed revenue.

return on ad spend on YouTube for Koinly
Meta CAPIbuilt, with pixel deduplication
15 minbetween attribution repair runs
8+years in marketing before the engineering

The shape of the work

Spend to revenue, with the plumbing shown

Ad spend flows to leads, through a filter and CRM repair, to deals and closed revenue, which is attributed back to the channel that started it Ad platformsstaged by API Leadsform fills FilterBACKTESTEDnon-buyers removed Dealssales works them Revenueclosed won Source repair, every 15 minutestracking data overwrites the hand-typed field revenue joined backto first touch unattributable deals get their own bucket, never spread across channels
The repair job in the middle is why the attribution at the end means anything. Most attribution problems are data problems.

What I do

Automate the ad stack

Campaigns built and staged through platform APIs rather than by hand in a UI, with performance pulled back into one tracker on a schedule. Includes running ads inside ChatGPT through the OpenAI Ads API, which is new enough that most of the work is reading the errors.

Server-side tracking

Built Meta's Conversions API on my own funnel: every server event carries an event ID matched to the browser pixel, so the pair deduplicates instead of counting twice. Offline conversions push from the CRM back to the platforms.

Fix the data underneath

Most attribution problems are data problems. Source fields typed by hand, deals created by reps with no channel attached, stale numbers reaching a dashboard. I repair those on a schedule and show when the repair job stops.

Put models where they pay

Models draft the weekly summary, sort inbound messages by intent, and generate variants. Each one has a fallback so a failed call degrades the output rather than stopping the run.

Measure against revenue

Clicks and form fills are the easy numbers and they lie. I follow leads to whether they were real, and closed revenue back to the channel that started it.

On shipping LLM systems

If it decides something, it gets tested first.

The clearest example I have is a classifier that filters non-buyers out of paid search leads. It was not shipped because the output looked good. It was shipped because it ran against six months of past leads where the outcome was already known, disqualified about one in five, and blocked zero of the buyers who had gone on to real deals.

That is the pattern I apply to anything a model touches:

  • Build a labelled set from history before writing the logic.
  • Care more about the false positives than the accuracy figure. Blocking a real buyer costs more than letting a bad lead through.
  • Prefer plain rules where a wrong answer is expensive. The lead filter is three readable conditions, not a prompt.
  • Log every decision, so the thing can be re-scored later against what actually happened.
  • Show the health of the job somewhere a person looks weekly.

A prompt that works in a playground is a demo. The eval, the fallback, and the log are what make it a system.

Evidence

Built and running

Server-side conversion tracking

Meta Conversions API with pixel deduplication on my own funnel, and closed deals pushed from HubSpot back to Google Ads.

Read the case study

Attribution repair

Hand-typed source fields overwritten by tracking data every fifteen minutes, then closed revenue joined back to first touch.

Read the case study

Lead qualification gate

A classifier backtested against six months of known outcomes before it was allowed near a live form.

Read the case study

Paid search testing

Campaigns split by intent, one variable per test cell, and landing page work driven by reading every lead.

Read the case study

Weekly reporting system

Eight channels pulled automatically into one deck, with a model-written summary and a fallback when it fails.

Read the case study

Growth cockpit

A Rust dashboard over five systems that hands its context straight to an AI coding session.

Read the case study

Outbound signal engine

Client build. Buying signals read from job ads and community posts, with every message human-approved.

Read the case study

RefreshAgent

My own product: marketing data monitoring with an API that my other scripts run on.

Read the case study

How I work

Anything running unattended reports its own health.

I am comfortable owning a system defined by an objective rather than a spec. What I insist on is that anything running unattended reports its own health, because the failure mode of automated marketing is not a crash. It is a job that quietly stopped in March and a number that has been wrong ever since.

Every scheduled job I build writes a log, and the ones that matter show a status light on a dashboard someone opens each week.

Confidentiality

What I withhold.

The case studies describe methods and structures. Employer figures are rounded or given as change rather than absolute values. Account identifiers, campaign names, keywords, and anything about a named contact or deal are left out.

I will walk through any of it in detail in a conversation, at the level of method.