Skip to content

Documentation

Recipes — builder or text

Eight real rules, five of them buildable from menus and three that only exist written out.

Eight rules sellers actually run — several of them ship as templates in the app — each shown as the text says it, and, where the builder can draw it, as the sentence you assemble from menus.

The principle underneath: everything the builder draws, the text can say; the reverse is not true. Two shapes exist only written out — bracketed nesting and let bindings — and a rule using either is edited as text from then on.

1. Pause bleeding keywords

Targets with real click volume and no orders over the last 30 days. The plain one — start here.

The clicks floor is doing two jobs: sample size, and making ad_orders = 0 mean anything at all, because a target that never ran is stored as a zero too.

In the builderwhen clicks(last30) >= 15 and ad_orders(last30) = 0then pause

on target
in all markets
when
  clicks(last30) >= 15 and ad_orders(last30) = 0
then pause
with min_clicks 15, cooldown 14, max_per_run 50, fresh_data

2. Bring the bid down where it costs too much

The opposite discipline: only targets that do sell, at an ACoS above what the royalty leaves.

A target that sells and costs too much wants a lower bid; one that does not sell wants pausing. Treating them the same is how a rule turns off the thing that was nearly working.

max_change 20% sits deliberately above the 15% move: it is the ceiling, not the move.

In the builderwhen clicks(last30) >= 15 and ad_orders(last30) > 0 and acos(last30) > 70then lower bid by 15%

on target
in all markets
when
  clicks(last30) >= 15 and ad_orders(last30) > 0 and acos(last30) > 70
then lower bid by 15%
with min_clicks 15, max_change 20%, cooldown 21, max_per_run 25, fresh_data

3. The spending that stopped

A campaign whose last week runs at under a quarter of its monthly pace — found by comparing a metric with its own past, so no slope column has to exist. The builder calls this compare with another metric.

In the builderwhen spend(last7) < spend(last30) × 0.25 and spend(last30) > 10then report only

on campaign
in all markets
when
  spend(last7) < spend(last30) * 0.25 and spend(last30) > 10
then report only
with cooldown 14, max_per_run 50

4. Stop the campaigns that only spend

A campaign that spent and sold nothing — where every target inside it also got clicks and sold nothing.

The quantifier is what makes this a campaign decision rather than a target one. A campaign holding a single target that is nearly working is refused, because every() is not true of it. And not true of an empty campaign either.

In the builderwhen spend(last30) > 20 and ad_sales(last30) = 0 and every(target: clicks ≥ 3 and orders = 0)then pause

on campaign
in all markets
when
  spend(last30) > 20 and ad_sales(last30) = 0
  and every(target: clicks(last30) >= 3 and ad_orders(last30) = 0)
then pause
with min_spend 20, cooldown 30, max_per_run 10, fresh_data

5. The hoodies Germany is missing

The gap grain selects rows that do not exist — design, marketplace and product-type combinations nothing was ever published as.

A gap has no history of its own, so its conditions count the design behind it: any(product: …) asks does this design already prove itself somewhere?

publish it is the matching verb. It parses, it demands a max_per_run cap, and it declines to queue while the catalogue write path earns its safety — so the honest action today is report only, and you publish by hand from the list it gives you.

In the buildergrain gap, when any(product: units(last90) ≥ 25) and product_type = "PULLOVER_HOODIE"then report only

on gap
in DE
when
  any(product: units(last90) >= 25)
  and product_type = "PULLOVER_HOODIE"
then report only
Product types are Amazon's own keys, not English words: PULLOVER_HOODIE, ZIP_HOODIE, STANDARD_TSHIRT, POPSOCKETS. A rule asking for "HOODIE" matches nothing and tells you it matched nothing, which looks identical to having no gaps.

6. Targets losing real money

Written out only.

ACoS answers is this expensive relative to sales. This answers did it take more money than its sales were worth to me — spend minus the royalty share of ad sales, arithmetic no column holds.

The let binding is the point, and it is what the builder has no control for. Set the 0.35 to your own royalty share.

A connected assistant can compose this one for you — it is the arithmetic question it is best at, and the one it will ask you for your royalty share before answering. Ask it for "targets that are costing me more than they bring in" rather than for an ACOS rule; ACOS cannot see the share you keep.

on target
let lost = spend(last30) - ad_sales(last30) * 0.35
in all markets
when
  clicks(last30) >= 10 and lost > 5
then pause
with min_clicks 10, cooldown 30, max_per_run 25, fresh_data

7. Bleeding, or gone quiet

Written out only.

Two failure modes in one pass: enough clicks to judge, and either arm of failure. Flattened into the builder's one-connector-per-level rule this becomes a cross-product of clauses; written out it is one sentence with brackets — which is exactly the nesting that makes it a text rule.

on target
in all markets
when
  clicks(last30) >= 15 and (acos(last30) > 120 or ad_orders(last30) = 0)
then pause
with min_clicks 15, cooldown 30, max_per_run 25, fresh_data

8. Pause the worst, ease the merely bad

Written out only.

One question — how bad is it — with a different answer for each degree. then case(...) is an ordered list of rungs: the first that matches wins, and exactly one answers any target.

on target
in all markets
when
  clicks(last30) >= 10
then case(
  when acos(last30) > 150 then pause,
  when acos(last30) > 80 then lower bid by 20%,
  else nothing)
with min_clicks 10, max_per_run 25, cooldown 30, max_change 25%, fresh_data

Written as two lookouts instead, the gentle one has to say acos(last30) <= 150 too, or it fires on the targets the stern one just paused. That is the first threshold written backwards, in a second place, and nothing keeps the two in step — edit 150 here and forget it there, and a target at exactly 150 is caught by neither, quietly, for as long as the pair exists.

else nothing says what happens to the targets no rung claimed: they are left alone, and reported as neither matched nor held back, because this lookout has nothing to say about them. Put a verb there instead and the ladder claims every matched row.

The cap is the lookout's, not the rung's: max_per_run 25 is twenty-five rows in total, and the sternest rung is served first because it is first in the ladder.

9. A rule somebody sent you

Written out only.

Text is the sharing format. Paste a rule from a friend, a forum or an assistant, and the editor checks it line by line — every refusal with a line, a column and the nearest thing that works. Nothing runs until you save it.

Two things to know about a pasted rule: it arrives unnamed, because the name lives beside the editor rather than in the text; and // comments are read but not kept, because Format re-prints the rule from what it means.

// weekend bleeders — decrease only, caps left strict on purpose
on target
in US, DE
when
  spend(last7) >= 5 and ad_orders(last7) = 0
then lower bid by 15%
with cooldown 14, max_per_run 25, fresh_data

The line, in one sentence

Use the builder to compose what to watch from what exists — including qualifiers up (campaign.spend), quantifiers down (every(target: …)) and metric-to-metric comparisons. Move to text when a rule needs brackets or a let, or when it arrives by paste.

And remember that every one of them, whichever surface wrote it, proposes into the same Publish queue.