V2 Logic Syntax Reference

Download the V2 Syntax Reference (.md)

Version 1.0 — Last Updated: April 2026

Overview

Merch Jar V2 Logic is a Domain Specific Language (DSL) for creating Segments of advertising entities by defining filter logic and calculating Custom Properties. Segments can be used for analysis, bulk actions, or automated Workflows.

Core Concepts

  • Segment: Output table of items matching the formula expression
  • V2 Logic: The Domain Specific Language described here. Segment queries are written using V2 Logic
  • Custom Property: Output column generated by a let variable definition (starting with $)
  • Variable: Named value defined using let (must start with $ sigil)
  • Dataset: Selected source data table (Campaigns, Ad Groups, Keywords & Targets, etc.)
  • Workflow: Trigger and Action pair
  • Template: Predefined Logic + optional Workflow. Each Template runs using V2 Logic and can be applied to datasets such as Keywords & Targets, Campaigns, or Search Terms.

Data Types

Number

Numeric values (integers, decimals). Percentages (%) auto-convert to decimal (25% becomes 0.25). Literal negative numbers supported.

clicks(7d) > 10
bid <= 1.50
acos(30d) < 25%
let $threshold = -0.5;
Percentages are automatically converted — do not manually divide by 100.
To negate a variable value, use multiplication: -1 * $bid_adjustment. Direct negation (-$bid_adjustment) is not supported.

String

Text values in double quotes. String concatenation and manipulation are not supported. Each string must be a complete literal value.

campaign name contains "Brand"

Array

User-defined collection of strings using square brackets. Used with contains all and contains any.

let $keywords = ["retro", "vintage"];
campaign name contains any ["summer", "spring"]

List

Predefined string values (enums) for properties like state or match type. Values in double quotes.

state = "enabled"
state contains any ["paused", "archived"]

Timestamp

Point in time, stored as seconds since Unix epoch. Used with properties like last bid change and now() results.

last bid change < now() - interval(7d)
campaign start date after "60 days ago"

Time Period

Duration specification used only in time-based property parentheses or variable assignments.

clicks(30d)
let $period = 7d..14d;

Date

Literal date values in YYYY-MM-DD format returned by certain properties. Cannot be used directly in timestamp arithmetic.

campaign start date = 2024-01-15

Boolean

True/false result of comparisons and logical operators. The final formula must evaluate to Boolean.

clicks(7d) > 0 and orders(7d) > 0

Time Period Formats

Xd — Relative

X days back from now, including the partial current day.

clicks(7d)

YYYY-MM-DD..YYYY-MM-DD — Literal Range

Exact calendar dates, inclusive.

spend(2024-01-01..2024-01-31)

Xd..Yd — Offset Relative

From X days ago to Y days ago, inclusive (Y must be greater than or equal to X).

sales(7d..14d) // from 7 days ago through 14 days ago
sales(3d..3d) // 3 days ago only

..Xd — Offset To Present

From now back through X days ago.

clicks(..60d) // from now through 60 days ago

Xd.. — Offset From Past

From X days ago through earliest available data.

spend(30d..) // from 30 days ago through earliest available data

lifetime or .. — Lifetime

Entire period of available data.

orders(lifetime) = 0

Common Pitfalls

Nested case Depth Limit

Nesting case statements more than 2–3 levels deep causes significant query performance degradation. Each case arm generates a conditional check evaluated against every row; deep nesting multiplies this across potentially millions of records. Limit nesting to 2 levels maximum.

Compound Conditions in case for let Assignments

Combining multiple conditions with and in a single case clause can cause load errors. Use nested case statements instead.

Metric Function Calls in Final Filter

Calling metric functions directly in the final filter can cause load errors. Pre-calculate into let variables as 0/1 flags.

Variable Usage in Offset Relative Time Periods

For Xd..Yd format, assign the complete range to a single variable. You cannot construct the range from separate variables.

Timestamp Arithmetic with Variables

Variables holding Time Periods must use the interval() function for arithmetic.

Custom Property Output

Variables assigned Time Period values appear as columns but rows show blank values.

Boolean Literals

Direct true/false literals are not supported. Use expression results instead.

// This will cause parsing errors:
let $period = case(condition => 7d, else 14d);


// Use this approach instead:
let $grace_cutoff = case(
condition => now() - interval(7d),
else now() - interval(14d)
);

Datasets

The selected dataset determines which properties are available and which entities will be analyzed.

campaigns

Campaign-level analysis and actions. Available properties: all performance metrics, budget, campaign identifiers, last budget change. Actions: Change Daily Budget, Set State.

ad-groups

Ad group-level analysis and actions. Available properties: all performance metrics, default bid, campaign/ad group identifiers. Actions: Change Default Bid, Set State.

keywords-targets

Keywords & Targets combined — recommended for most use cases. Available properties: all performance metrics, bid, match type, last bid change, campaign/ad group identifiers. Actions: Change Bid, Set State.

keywords

Manual keywords only (broad, phrase, exact match types). Available properties: all performance metrics, bid, match type, last bid change, campaign/ad group identifiers. Actions: Change Bid, Set State.

targets

Auto targeting and product targeting only (close match, loose match, product exact, similar, etc.). Available properties: all performance metrics, bid, match type, last bid change, campaign/ad group identifiers. Actions: Change Bid, Set State.

product-ads

Individual product advertisements (ASINs) within ad groups. Available properties: all performance metrics, campaign/ad group identifiers. Actions: Set State.

search-terms

Search queries that triggered ads. Available properties: all performance metrics plus the negated property. The state property is not available. Actions: Create Negative Exact Match.

Primary Properties

acos (Number, Time-Based)

Advertising Cost of Sale (spend / sales)

acos(30d) > 40%

ad group name (String)

Current name of the Ad Group

ad group name contains "Exact Match"

aov (Number, Time-Based)

Average Order Value (sales / orders)

aov(60d) > 15.00

bid (Number) — keywords-targets only

Current bid for Keyword or Target

bid > 0.75

budget (Number) — campaigns only

Current daily budget for Campaign. Alias: daily budget

budget > 10.00

campaign name (String)

Current name of the Campaign

campaign name contains "Brand - SP"

campaign start date (Date)

Configured start date of Campaign in literal format. Returns YYYY-MM-DD — cannot be used with now() arithmetic.

campaign start date = '2024-01-01'

clicks (Number, Time-Based)

Total number of ad clicks

clicks(7d) >= 5

cpc (Number, Time-Based)

Cost Per Click (spend / clicks)

cpc(14d) < 0.80

impressions (Number, Time-Based)

Total number of ad displays

impressions(14d) > 1000

last bid change (Timestamp) — keywords-targets only

Most recent bid update timestamp

last bid change < now() - interval(7d)

max bid (Number) — All datasets except search terms

Maximum bid limit set on the entity. Aliases: max_bid, maxbid

max bid > 2.00

min bid (Number) — All datasets except search terms

Minimum bid limit set on the entity. Aliases: min_bid, minbid

min bid > 0.10

last budget change (Timestamp) — campaigns only

Most recent budget update timestamp

last budget change < now() - interval(30d)

match type (List) — keywords-targets only

Keyword match type or auto-targeting type. Values: "broad", "phrase", "exact", "product exact", "similar", "close match", "loose match", "complements", "substitutes"

match type = "exact"

orders (Number, Time-Based)

Total orders directly attributed to ads

orders(30d) > 1

roas (Number, Time-Based)

Return on Ad Spend (sales / spend)

roas(30d) > 3.0

sales (Number, Time-Based)

Total revenue from attributed orders

sales(14d) > 50.00

spend (Number, Time-Based)

Total ad spend

spend(30d) > 100

state (List) — All datasets except search terms

Current status of entity. Values: "effectively enabled", "enabled", "paused", "archived"

state = "effectively enabled"

target acos (Number)

Desired ACOS percentage goal

target acos < 30%

negated (List) — search-terms only

Whether an exact match negative keyword already exists for this search term. Values: true, false

negated = false

search term (String) — search-terms only

The actual search query text that triggered the ad

search term contains "running shoes"
search term contains any ["brand name", "product category"]
search term does not contain any ["competitor", "irrelevant term"]

KDP-Specific Properties

blended acos (Number, Time-Based)

Blended ACOS (spend / blended profit)

blended acos(60d) < 35%

blended profit (Number, Time-Based)

Total estimated profit combining sales and KENP royalties

blended profit(30d) > 50.00

blended roas (Number, Time-Based)

Blended Return on Ad Spend (blended profit / spend)

blended roas(30d) > 2.0

pages read (Number, Time-Based)

KENP pages read attributed to ads

pages read(60d) > 2000

estimated royalties (Number, Time-Based)

Estimated royalties from KENP pages read

estimated royalties(30d) > 10.00

Secondary Properties

  • campaign end date (Timestamp) — Configured end date of Campaign
  • default bid (Number) — Default bid set at Ad Group level
  • roi (Number, Time-Based) — Return on Investment ((sales - spend) / spend)
  • cac (Number, Time-Based) — Customer Acquisition Cost (spend / orders)
  • adjusted orders (Number, Time-Based, KDP) — Orders adjusted by Order Impact Multiplier
  • adjusted sales (Number, Time-Based, KDP) — Sales adjusted by Order Impact Multiplier
  • adjusted page reads (Number, Time-Based, KDP) — KENP pages adjusted by KENP Impact Multiplier
  • adjusted estimated royalties (Number, Time-Based, KDP) — Royalties adjusted by KENP Impact Multiplier
  • blended aov (Number, Time-Based, KDP) — Blended Average Order Value
  • blended cac (Number, Time-Based, KDP) — Blended Customer Acquisition Cost
  • blended cvr (Number, Time-Based, KDP) — Blended Conversion Rate
  • blended rpc (Number, Time-Based, KDP) — Blended Revenue Per Click

Functions

let $variable_name = expression;

Defines a named variable with $ sigil. Creates a Custom Property column. Must end with semicolon. Variable names must be one word after $ (use underscores for spaces) and cannot conflict with keywords, properties, functions, or operators.

let $target_rpc = 0.75;
let $current_rpc = sales(30d) / clicks(30d);
let $period = 7d..14d;
let $unprofitable_acos_ratio = $chosen_acos / target_acos;

now()

Returns the current timestamp.

last bid change < now() - interval(14d)

interval(time_period)

Converts a Time Period to an internal duration for timestamp arithmetic. Cannot accept calculated values.

last bid change < now() - interval(7d)

// Convert a single duration to seconds
let $week_seconds = interval(7d);

// Subtract two durations to find span between
let $baseline_seconds = interval(30d) - interval(7d);
let $baseline_days = $baseline_seconds / 86400;

// Example use in logic
let $baseline_avg = impressions(7d..30d) / $baseline_days;

case(condition1 => value1, condition2 => value2, ..., else defaultValue)

Implements if/then/else logic. All return values must be the same data type. else is required. Cannot accept Time Period values.

let $performance = case(acos(14d) < 25% => "Good", acos(14d) < 40% => "Okay", else "Poor");
Limit nesting to 2 levels deep maximum. Deeper nesting causes significant query performance issues at scale. Maximum of 10 match arms per case statement recommended.

is_null(property)

Returns true if a property value is null, false otherwise. Commonly used with timestamp properties.

is_null(last bid change) // True if never changed
is_null(last bid change) = true // Explicit version
is_null(last bid change) = false // Has been changed
is_null(last budget change) = true // Never changed budget

let function_name(param) = expression;

Defines a reusable custom function that accepts a time period parameter. Useful for applying the same calculation across multiple time periods without repeating logic.

  • Custom function declarations must appear at the very top of the segment, before all let $variable statements
  • Functions can reference $variables declared anywhere in the segment (forward references are supported)
  • Arguments must be literal time periods (7d, 30d, etc.) — cannot pass a $variable as an argument
  • The same function can be called multiple times with different periods in one expression
// Declare before any $variable declarations
let rpc(x) = sales(x) / clicks(x);

// Call with different periods for trend comparison
rpc(7d) > rpc(30d)

// Use in case logic
let $trend = case(rpc(7d) > rpc(30d) => "improving", else "declining");

// Functions can reference $variables declared later in the segment
let efficiency(x) = clicks(x) / $target_clicks;
let $target_clicks = 20;
efficiency(30d) > 1

Boolean Variables

let $variable = comparison_expression;

Boolean comparison results can be assigned directly to variables and used throughout segment logic.

let $x = spend(30d) > 10;
let $y = clicks(30d) > 5;

// Use in final filter
acos(30d) > 20% and $x

// Combine boolean variables
let $both = $x and $y;
$both

// Use in case statements
let $result = case($x => "qualified", else "not qualified");

// Check explicit boolean state
let $not_x = $x = false;

Operators

Math: + - * / %

% is percentage conversion (25% = 0.25), not modulo.

bid + 0.10
now() - interval(7d)
acos(30d) < 25%

Logical: and or

and is evaluated before or.

clicks(30d) > 5 and state = "enabled"

Comparison: = != > >= < <=

state = "enabled"
clicks(7d) != 10

String: contains, does not contain, starts with, ends with

Case-insensitive comparisons.

campaign name contains "Brand"
ad group name ends with "-Exact"

Array: contains all, contains any, does not contain any

For String properties vs Arrays, or List properties vs Arrays.

campaign name contains any ["test", "archive"]
state contains any ["paused", "archived"]

Date: after, before

For Timestamp comparisons.

campaign start date after "14 days ago"
last bid change before now() - interval(2d)

Syntax Notes

  • Property Naming: Use exact names with spaces as defined (e.g., target acos, ad group name)
  • Case Sensitivity: Properties, functions, and operators are generally case-insensitive
  • String Literals: Use double quotes
  • Date Literals: Write without quotes (YYYY-MM-DD). Quoted values are treated as strings and will cause type errors.
  • Variable Names: Must start with $ and use underscores for spaces (e.g., $my_long_var displays as "My Long Var")
  • Comments: // for single line, /* */ for blocks
  • Final Expression: Must evaluate to Boolean after all let statements
  • Time Period Comparison: Use non-overlapping ranges (0d..13d and 14d..27d rather than 14d and 14d..28d)
  • Operator Precedence: Math (*, / before +, -), Logical (and before or). Use parentheses for clarity.
  • Array Creation: User-defined arrays use square brackets with comma-separated strings
  • Currency Symbol: $ symbols are ignored in numeric values
  • Percent Symbol: % after a number divides by 100 (25% = 0.25) — not a modulo operator
  • Variable Negation: Direct negation of variables (-$variable) is not supported. Use -1 * $variable instead.
  • Range Formats: Xd..Yd can only be used in metric function parentheses, not with interval() or timestamp arithmetic.

Error Handling

  • Division by Zero: Operations like X/0 return null. Null values will not match any comparisons and will be excluded from filters.
  • NaN Comparison: All comparisons with NaN evaluate false except != (NaN != value is true, NaN != NaN is true)
  • Infinity Comparison: Works as expected mathematically (Infinity > 100 is true)
  • Null Value Detection: Use is_null() to explicitly check for null values
  • Parsing Error — Undeclared variable: Using an undefined variable name or misspelled property
  • Parsing Error — Expected time period: Time-based property used without a (TimePeriod) specification
  • Parsing Error — Unbalanced parenthesis: Mismatched parenthesis count
  • Parsing Error — Incompatible types: Operation between incompatible data types
  • Parsing Error — Unexpected end of input: Missing closing bracket or semicolon after let statement
  • Parsing Error — Expected ';' after let: Missing semicolon after let statement

Workflow Actions

Change Bid — keywords-targets, keywords, targets

  • Increase ($): Increases current bid by fixed dollar amount
  • Increase (%): Increases by percentage (input as decimal: 0.07 for 7%)
  • Set ($): Sets bid to specific dollar amount
  • Decrease ($): Decreases by fixed dollar amount
  • Decrease (%): Decreases by percentage (input as decimal: 0.07 for 7%)
  • Set from variable: Uses Custom Property value for any above operations

Change Default Bid — ad-groups

Same operations as Change Bid, applies to Ad Group default bid setting.

Change Daily Budget — campaigns

Same operations as Change Bid, applies to Campaign daily budget.

Set State — campaigns, ad-groups, keywords-targets, product-ads

Sets entity state to: enabled, paused, archived

Create Negative Exact Match — search-terms

Creates a negative exact match keyword in the same ad group where the search term was found.

Action Rounding: Bid/budget calculations with sub-cent values are rounded using the account rounding strategy (default: Weighted Rounding).

Known Issues

  • KI-003: case() cannot return Timestamp values. Workaround: Use let statements with boolean logic.
PREVIOUS ARTICLE
NEXT ARTICLE