Claude Code is Anthropic's AI agent that works in your terminal: it reads files, runs commands and edits code. For marketing, point it at a folder that holds your funnel and it can build pages and set up tests the way a developer would.
The ElasticFunnels CLI makes that folder: ef init binds it to your brand, and every page becomes a file. Below, Claude Code builds an advertorial variant, puts it in a same-URL split test and reads the result. Every output is from a real run on our own brand.
The real test of an AI agent in marketing is whether you'd let it touch a live page. My answer is yes, on one condition: you read the diff before anything goes live. Everything below was run on our own brand, command by command, including the slightly embarrassing part where six test visits prove nothing at all.
What marketers use Claude Code for
Claude Code works on files and runs commands, so it fits any marketing job that lives in files or behind a command line. The common ones:
- Copy. Drafts and rewrites of headlines, emails and advertorials from a brief and your past winners. What makes an advertorial work is a good brief to start from.
- Landing pages. Building and editing pages as files, then publishing them. With ElasticFunnels every page is a file (the ef CLI); the same works with Codex.
- SEO. Audits of titles, descriptions, headings and internal links across a folder of pages, and fixes you review before they go live.
- Reporting. Reading numbers from a CLI or an export and explaining them. AI A/B testing shows an agent reading split-test and funnel stats.
- Ad research. Summarising competitor pages, offers and angles you save to the folder. It doesn't run or change your ads.
If I could hand an agent only one of these, it would be landing pages. Copy and SEO audits are useful, but a page in a split test ends in a number you can check, so you find out quickly whether the agent's work was any good.
The rest of this post is one worked example from that list: a landing page split test, run by Claude Code from brief to result.
What you need
- An ElasticFunnels brand. The CLI and split tests are included on every plan, trial included.
- Node.js 18 or newer and the CLI:
npm i -g @elasticfunnels/cli. - Claude Code, installed and signed in to your Claude account (see Anthropic's docs).
- A page to test. Here it is an advertorial at
demo/sleep-advertorial. Any page works.
Bind a folder to the brand once. ef init signs you in through the browser, pulls every page and writes the guidance files Claude Code reads.
mkdir my-brand && cd my-brand ef init
What ef init sets up for the agent: a CLAUDE.md with the commands and rules, two skills in .claude/skills (one for page events and split tests, one for stats), and a session hook that pulls fresh copies before each Claude Code session. You can re-run that part any time with ef claude.
Want to watch it step by step? Connecting Claude Code to your pages through MCP has its own tutorial, with a clip for every step: add an MCP server to Claude Code.
The loop Claude Code follows
Every change goes through the same steps. Pull the current state, change the files, wire the test, review the diff, push, then read the numbers. The review is the step that matters most: nothing goes live until you have seen what changes. I'd skip any other step before I skipped that one.

ef diff --server, push, read the result. Then the next test starts from what the last one taught you.Step 1: brief the agent
Start claude in the folder and describe the test like you would to a colleague: which page, what changes, how to split, and what to report back.
Pull demo/sleep-advertorial. Make a second version as a new page, demo/sleep-advertorial-b, with the headline "I tried 4 sleep fixes for 30 nights. Only one worked." Keep everything else the same. Split test the two 50/50 under the original URL, validate the graph, push everything and tell me the split test id.
Claude Code asks before it runs each command unless you have allowed it. Read what it proposes; that is your review step.
Step 2: create and publish the variant
First the agent pulls a fresh copy of the original, so it works from what is live:
ef pull page demo/sleep-advertorial
Then it creates the new page on the server, which also pulls it to disk as pages/demo/sleep-advertorial-b.ef. The tag keeps the test's pages findable in the app later.
ef pages create demo/sleep-advertorial-b --tag agent-demo
✓ Created page #16926 "demo/sleep-advertorial-b" (Demo Sleep Advertorial B).
✓ page demo/sleep-advertorial-b: tagged agent-demo.
It copies the original's body into the new file: it keeps line 1 of the new file (the new page's identity), replaces everything below it with the original's lines 2 to the end, and changes the headline. From a shell, the copy is:
{ head -n 1 pages/demo/sleep-advertorial-b.ef; tail -n +2 pages/demo/sleep-advertorial.ef; } > b.tmp \
&& mv b.tmp pages/demo/sleep-advertorial-b.ef
Then it checks the file and shows you exactly what will change on the server before it pushes. This is your review step: read the diff, then say yes.
ef lint pages/demo/sleep-advertorial-b.ef ✓ Linted 1 file(s) — no issues. ef diff --server pages/demo/sleep-advertorial-b.ef ef push pages/demo/sleep-advertorial-b.ef updated page pages/demo/sleep-advertorial-b.ef ✓ Pushed 1 file.
What the agent did: it wrote a page file and published it. It did not touch the first line of the file, which holds the page's identity; the CLI refuses a push if that line changes.
Step 3: wire the split test under one URL
A split test lives in the page's events graph, not in the page itself. The agent pulls the graph for the original page. This page had none, so the CLI wrote an empty starter:
ef pages events pull demo/sleep-advertorial
✓ Page has no events yet — wrote a starter graph to pages/demo/sleep-advertorial.events.json.
It then writes the graph: an entry node, a Split Test node with two outputs, a 50% Traffic Distribution node on each output, and a Load Another Page node under the second one. A weight with nothing under it serves the original page, so that arm is the control.

The part of sleep-advertorial.events.json that matters (connections omitted):
"2": { "data": { "type": "split_test", "value": 2,
"name": "Sleep advertorial: 3 a.m. vs 30 nights" } },
"3": { "data": { "type": "split_test_weight", "value": 50,
"name": "A: 3 a.m. (control)" } },
"4": { "data": { "type": "split_test_weight", "value": 50,
"name": "B: 30 nights" } },
"5": { "data": { "type": "page_variant", "value": "16926",
"slug": "demo/sleep-advertorial-b" } }
The server checks the graph before anything goes live, then the agent pushes it:
ef pages events validate demo/sleep-advertorial ✓ Events graph is valid. ef pages events push demo/sleep-advertorial Assigned node_code to 5 node(s) — the server does not, and split-test reporting needs it. ✓ Pushed events for "demo/sleep-advertorial" (page #16925).
The first output line is the CLI explaining itself: it gave each node a stable ID (a node code), because the reports use those IDs to tell the variants apart and the server doesn't add them on its own. Without them, every visit would land in one unnamed bucket. That's why the ef CLI adds them for you; a split test that can't tell its arms apart is worse than no test, because it still looks like one.
What the agent did: it used Load Another Page, not a redirect, so the variant renders at the original address. Saving the graph created split test #526.
Step 4: check that it rotates
Load the original URL a few times without cookies, before any real traffic reaches the page. We did it with curl on our demo page (since deleted; use your own page's URL); two private browser windows work too.
for i in 1 2 3 4; do
curl -s https://elasticfunnels.io/demo/sleep-advertorial | grep -o '<title>[^<]*'
done
<title>Why you wake up at 3 a.m. (and the simple fix a sleep researcher uses)
<title>I tried 4 sleep fixes for 30 nights. Only one worked.
<title>Why you wake up at 3 a.m. (and the simple fix a sleep researcher uses)
<title>I tried 4 sleep fixes for 30 nights. Only one worked.
Same URL, alternating headlines. New visitors are assigned in a fixed rotation that follows the weights, not by a coin flip, so the live split tracks 50/50 closely. A returning visitor keeps the variant they saw first for 7 days in the same browser (a cookie); a new browser or device counts as a new visitor.
Your checks count as visitors. Every request the split test assigns is a session in its report, including curl and private windows: the sessions in step 5 are our own checks. ElasticFunnels has no IP exclusion list, and a test's results can't be reset. The page's preview link doesn't help either: preview doesn't rotate variants. So check the rotation before real traffic starts and keep it to a handful of visits. For a clean report, read the results from the day real traffic started (ef stats split 526 --from YYYY-MM-DD, the first day of real traffic), or pause the test and start a fresh one. It's a small annoyance, and I'd rather you hear about it here than find a handful of mystery sessions in your first report.
Step 5: read the result
Ask the agent how the test is doing and it runs:
ef stats split 526
Sleep advertorial: 3 a.m. vs 30 nights (#526, 2026-09-19 → 2026-09-25, Europe/Bucharest)
VARIANT SESSIONS CONVERSION_RATE REVENUE SALES
A: 3 a.m. (control) 3 0.00% $0.00 0
B: 30 nights 3 0.00% $0.00 0
p-value — power —
No winner yet — not enough data to score this test.
Six test visits are nowhere near enough, and the output says so. The statistics come from the app: a two-proportion z-test on conversion rate, flagged until each arm reaches its minimum sample, with a correction when you run 3 or more variants. The CLI skill tells Claude Code to report that result, not to run its own test and not to pick a winner. That is how an agent avoids calling a test early. How many visitors is enough depends on your conversion rate and the lift you want to detect: the A/B test sample size calculator gives the number to plan for.
With real traffic, the same command shows the lift and the p-value, and names a leading variant once it clears the significance check with every arm at its minimum sample. Ending the test on it is your call: Mark as Winner in the app, or ef splits winner 526 <variant>. Letting the agent read the stats and propose the next test covers that part.
Write down why you ran it
The server stores the numbers, not the reason. Ask the agent to add an entry to split-tests.md in the project: the test id, the hypothesis, which arm is the control and what counts as a win. The CLI never syncs this file to the server, so it stays with your project and your git history.
## #526 — Sleep advertorial: 3 a.m. vs 30 nights (page demo/sleep-advertorial, #16925) - Variants: A "3 a.m." 50% (original, control) · B "30 nights" 50% (#16926) - Hypothesis: a first-person trial story beats the problem-first headline on cold traffic. - Winning means: B beats A on conversion rate, significant at the planned sample size. - Check with: ef stats split 526
What Claude Code can't do here
- Run your ads. Nothing in the CLI creates, edits or pauses campaigns or budgets. The ad keeps pointing at the same URL; that is the point of the same-URL test.
- Connect accounts. Payment gateways, affiliate networks and ad accounts are connected in the app.
- Set DNS. It can add a domain and print the records, but you create them at your registrar.
The same test in the app
No terminal? The in-app AI page builder writes advertorials and presells from a description, and the Page Events canvas builds the same Split Test graph by drag and drop. The result lands in the same report. More on building the page itself in the advertorial builder and what makes an advertorial work.
Every capability an agent has through ElasticFunnels is listed on the AI marketing agents page.
- I wouldn't give an agent blanket approval on a brand that takes real orders. Approving each push is slower, and it's the only moment you see the change before buyers do.
- Never test the rotation with more than a handful of visits. Every one of them stays in the report.
- Don't let the agent end a test because the numbers "look clear". That call is yours.
- A test without a written reason is one you'll misread in three months.
An agent won't make a weak offer sell. What it does well is take the tedious part of testing (the copy of the page, the graph, the push, the report) off your plate, so the number of tests you run stops depending on how busy your developer is this week. Keep your hands on the brief and the decision, and let it do the typing.



