Custom Parameter targeting reference
Every Custom Parameter operator with a worked example, how the Value list and the request value are split, has all of, the absent-parameter matrix, the Expression kind, the Cookie Value dimension with and / or between cookies, and the REST API.
The tables behind Target on custom parameters passed in the ad tag. Every comparison is case-sensitive on the value and case-insensitive on the key name, and values are compared as strings unless the operator is numeric.
The editor
| Control | Shown for | What it does |
|---|---|---|
| Type | always | The rule kind: Expression, Value, One of or Substring. Changing it clears the other fields. |
| Parameter | Value, One of, Substring | The key to read off the request, with any cp. / cp_ prefix already stripped. Picked from the registered parameters (name · label) or typed after Other parameter…; a plain text box when nothing is registered. It has an ⓘ only for a list parameter on a Value or One of rule: Arrives as a list split on ”,” on One of, Arrives as a list: use One of to match one item on Value. |
| Operator | Value, One of, Substring | The comparison; the choices depend on the kind, and the label has no ⓘ. has all of appears only for a list parameter and is cleared when the parameter changes to a plain one. |
| Value | Value, One of, Substring | One value for Value; a comma-separated list for One of and Substring. The ⓘ beside it says what the picked operator matches, how to separate values and, on a list parameter, previews a match in that parameter’s own separator. The placeholder shows the shape (sport, news, travel). When the parameter has a registered list of values, One of picks from it and a Value equals / not equals rule picks one. |
| Expression | Expression | A single boolean expression; the ⓘ recaps the syntax. |
| Include / Exclude | always | Saves the drafted rule as a whitelist or blacklist entry; disabled until the editor is complete. |
What each kind matches
| Kind | Operator | Matches when | Worked example |
|---|---|---|---|
| Value | equals | The value is exactly equal | section equals sport matches cp.section=sport, not Sport |
| Value | not equals | The value is anything else, including absent | section not equals sport matches cp.section=news |
| Value | > ≥ < ≤ | Both sides parse as numbers (dot decimals, 1e3 accepted) and the comparison holds | age ≥ 18 matches cp.age=18 and cp.age=18.5, not cp.age=17 or a missing age |
| One of | is one of | The value exactly equals one entry in the list | section is one of sport, news, weather matches cp.section=news |
| One of | is not one of | The value equals no entry, including absent | the same list rejects news, accepts travel |
| One of | has all of | Every entry in the list is an item of the value; other items may be there too. List parameters only | segments has all of 1, 3 matches cp.segments=1,2,3,4,5 and 1,2,3, not 1,4,5 |
| Substring | contains | The value contains at least one entry as a substring | path contains /sport/, /live/ matches cp.path=/2026/sport/final |
| Substring | not contains | The value contains no entry, including absent | the same list rejects that path, accepts /news/uk |
| Expression | — | The expression evaluates to true | The Expression kind |
The Value kind’s rule value is trimmed when loaded and has no length limit. The Value field also
expands $name: $otherparam compares one request parameter against another.
How the Value list is split
One of and Substring take a list in one Value field. This is the rule’s own list; whether the request value is split too is a setting of the parameter — see When the request sends several values.
- The separator is a comma only. A semicolon becomes part of the value. A line break is not a
separator either: the box is a single line, so a pasted column arrives with its lines run
together —
345,456on two lines do not become two values. Put commas between them first. - The whole list is URL-decoded before it is split, then each entry is trimmed and empties are
dropped. Duplicates collapse — the entries are held as a set. So a typed
+reads as a space and%2Bas+; write a literal%as%25. A typed list holding a lone%(50%, a+b) fails to decode as a whole, and its+then stays a plus. - One of: a backslash makes the next character literal —
\,is a comma inside the value,\\a backslash, and a backslash before any other character is dropped. - Substring: splits on commas not preceded by a backslash and keeps the backslash, so a
part written
a\,bmatches the literal texta\,b. A Substring part can therefore never hold a comma;%2Cdoes not help, because the list is decoded before it is split. - Values picked from a parameter’s registered list are written in this form for you —
18+,50%anda\bare stored as18%2B,50%25anda\\b— so each pick counts whole, commas and spaces included, and the rule’s summary shows them as picked. - Substring URL-decodes the incoming request value too, so
/sport/matches a request carrying%2Fsport%2Fand vice versa. Value and One of compare the raw string.
When the request sends several values
A tag can pack several values into one parameter — cp.genre=rock,pop, or a CTV stitcher’s
contentGenre=Comedy,Drama. Out of the box a One of rule compares that value whole, so
genre is one of pop does not match rock,pop. Register the parameter under
Admin → Custom parameters and switch on The request can carry
several values, choosing its separator — comma, pipe, semicolon or space. From then on every
One of rule on that parameter reads the request value item by item:
| Rule | Request cp.genre=rock, pop,,Jazz (items rock, pop, Jazz) |
|---|---|
Include · is one of pop | matches — any item in the list is enough |
Include · is one of jazz | no match — values stay case-sensitive, Jazz ≠ jazz |
Include · is not one of pop | no match — no item may be in the list |
Include · has all of rock, jazz | no match — rock is an item, jazz is not (Jazz ≠ jazz) |
Include · has all of rock, pop | matches — every value is an item; Jazz being there too does not matter |
Exclude · is one of pop | fires — the request is turned away when any item matches |
| any rule, parameter absent or blank | no items: is one of and has all of never match, is not one of always does |
- Items are trimmed and empty ones ignored:
rock,,popandrock , popboth read asrock,pop. - has all of asks for every value at once:
segmentshas all of1, 3matches1,2,3,4,5and1,2,3, not1,4,5(3is missing). Order does not matter, a repeated item counts once, other items may be there. On a parameter split on|the request reads1|2|3|4|5and the editor’s ⓘ shows the example in that separator. It is offered only on a list parameter — read whole it could only mean “equals” — and the server refuses it elsewhere. There is no negated form: add the same rule with Exclude. - A value without the separator is a one-item list, so it matches exactly as before the parameter was registered. Only values that contain the separator change.
- The setting belongs to the parameter, not to the rule: every One of rule on it, on any entity, at any level, splits the same way. The ⓘ beside Parameter says so, the ⓘ beside Value previews a match, and the saved chip ends in · list.
- Only One of splits. Value, Substring and Expression rules read the whole value — the editor
points a Value rule on a list parameter at One of — and so does the
$$CUSTOM_PARAM()$$macro. In reports, the Advanced report’s custom-parameter filter matches an item the same way, while grouping by Custom parameter shows the whole value. - Ad serving keeps the registered parameters in memory and reloads them every minute.
- A One of value that itself holds the separator (
rock\,pop) can never equal one item, so that rule never matches; the editor warns as you type it and the parameter’s admin page lists such rules under Used by. - The request value is URL-decoded once, by the server, before it is split:
rock%2Cpoparrives asrock,popand splits; a double-encodedrock%252Cpoparrives asrock%2Cpopand does not.
Several rules: any of or all of
Within the dimension, Include rules are combined with or — one matching rule is enough —
while the first Exclude rule that matches rejects the request. Rules on one parameter are
always alternatives, because a request carries one value for it: tier equals gold and
tier equals silver mean “tier is gold or silver”. Rules on different parameters, every rule on a
list parameter (including a single has all of), and every Expression are groups of their own.
The connector between groups is the list’s mode: or ▾ by default, and ▾ when every group
must match — ( tier=gold or tier=silver ) and genre is one of [pop]. A list with one group shows
no connector. On a list parameter two One of rules joined by and need both items in the
request; one has all of rule does the same inside one rule. Click the connector on the
Targeting card, or call POST …/targeting/match-mode with type=CUSTOM
(And / or between groups). A rule added later joins
its list’s mode; a copied campaign or creative and a target template keep it. The classic UI shows
the mode in the group header and has no control.
When the request does not carry the parameter
A missing parameter resolves to the empty string and the comparison runs normally, so the negative operators match. (Epom can configure a server-side default value for a parameter — none is set out of the box; where one is, an absent or blank parameter takes it.)
| Kind and operator | Result when absent | As an Include rule | As an Exclude rule |
|---|---|---|---|
Value equals | no match | request is rejected | rule does not fire; request passes |
Value > ≥ < ≤ | no match (empty is not numeric) | request is rejected | rule does not fire; request passes |
One of is one of / has all of | no match | request is rejected | rule does not fire; request passes |
Substring contains | no match | request is rejected | rule does not fire; request passes |
Value not equals | match | request passes | request is rejected |
One of is not one of | match | request passes | request is rejected |
Substring not contains | match | request passes | request is rejected |
An Include rule with a positive operator added before the tag sends the parameter takes the
entity off air on that placement; an Exclude rule with a negative operator fires on every
request that omits it. “Exclude sport” is Exclude + equals, not Include + not equals.
The Expression kind
Expression is the only kind that can combine several parameters or use || and &&. It is a
JavaScript expression, evaluated server-side, in which each parameter is written $key.
The substituted value is quoted for you. $section is replaced by 'sport' — quotes
included, JavaScript-escaped, '' when missing — so compare against a quoted literal or a bare
number and never wrap the $key yourself:
$section == 'sport' && $age >= 18
For cp.section=sport&cp.age=21 that becomes 'sport' == 'sport' && '21' >= 18, which is true
— JavaScript coerces the quoted number for >=. '$section' == 'sport' produces
''sport'' == 'sport', a syntax error.
String and array methods and regular expressions work — though for “any of these values” a One of rule on a list parameter does the same without running a script on every request:
$cat.split(",").indexOf("a") >= 0
($a + $b) > 0
($p1 == 1 || $p1 == 'a') && ($p2 >= 12 && $p2 <= 22)
/^en/i.test($lang)
- A
$keyname may contain letters, digits, underscores and$. Longer names are substituted first, so$aais not corrupted by a rule that also mentions$a. Key names ignore case. $CLIENT_USER_AGENTis always available: the request’s full User-Agent string, injected by the ad server. It is the supported way to test something the dimension list has no picker for:/Android (.*?);/i.exec($CLIENT_USER_AGENT)[1] >= '2.3'.===and!==are strict: against a quoted literal they behave like==, but$age === 18compares a string with a number and never matches. The placeholder reads$parameter1 === 'value'; keep===for quoted literals and use==for numbers.- An invalid expression is rejected when you add the rule, with “Please enter valid expression (Sample: ($p1==1 || $p1==‘a’) && ($p2>=12 && $p2<=22))”. The check is server-side: Include enables as soon as the box is non-empty.
- An unquoted bare word is an identifier, not a literal:
$section == sportthrows, which is read as “could not evaluate” and rejects the request on an Include rule. An expression that returns a non-boolean ($sectionalone) passes the save-time check and never matches. - Each evaluation has a statement budget, generous for a boolean expression; a script that spends it (a loop that does not end) is aborted and read as “could not evaluate”.
- Results are cached for an hour, keyed on the fully substituted text. Harmless for a pure
function of the parameters; stale for anything else, such as
new Date(). Use the Time Range or Day and Time dimensions for wall-clock logic.
Target the show a stitcher describes
A CTV stitcher that calls a VAST tag set up with an
upstream macro preset
fills four content parameters on the request. They are targetable like any parameter, with no
cp. prefix:
| Parameter | Carries | Example rule |
|---|---|---|
contentGenre | the show’s genre, as the stitcher names it | One of — is one of — Sports, News, as Include |
contentRating | its age rating, such as TV-PG | One of — is one of — TV-MA, TV-14, as Exclude |
contentLivestream | 1 for a live stream, 0 on demand | Value — equals — 1, as Include |
contentLanguage | the show’s language | One of — is one of — en, es, as Include |
They are read the way buyers receive them in content.*: a value the stitcher left as an
unexpanded macro ({{CONTENT_GENRE}}, [player_params.contentGenre]) counts as not sent, values
are trimmed, and a live flag sent as true / false reads as 1 / 0. Genre and rating are
free text, so match the spellings your stitcher sends — a
troubleshooting task on the placement shows the full request URL. A
request without the value, or with the macro unfilled, behaves exactly like one that omits the
parameter (absent-parameter matrix).
The persisted variant — Cookie Value
Cookie Value is a separate dimension with two fields, Name and Value, both required and trimmed. It reads a value that persists on the visitor’s browser rather than one on the current request.
It does not read any first-party cookie your own script writes. It reads only cookies Epom
itself set, whose names carry Epom’s third-party cookie prefix — eas_3p_ by default. The
Name you type is the part after that prefix, so a cookie named eas_3p_segment is matched
by a rule on segment. Names compare exactly; values compare whole, ignoring case
(Cart-Abandoner matches cart-abandoner).
Those cookies are written by Epom’s cookie-setting endpoints — /multi-cookie.gif,
/multi-cookie.js and /multi-cookie.html — which turn each query parameter into a cookie:
<img src="https://<ad-serving-domain>/multi-cookie.gif?segment=cart-abandoner|30d"
width="1" height="1" alt="">
The value takes the form value|lifetime|format:
| Part | Meaning |
|---|---|
value | What the rule’s Value field is matched against. |
lifetime | 30d days, 12h hours, 45m minutes, 30s seconds, 1y years. A bare number means days. Omitted, the cookie lasts 365 days. |
format | PLAIN (the default) or BASE64. |
| Custom Parameter | Cookie Value | |
|---|---|---|
| Where the value lives | the current ad request | a cookie Epom set on the browser |
| Who writes it | the publisher’s tag or the placement defaults | Epom’s /multi-cookie.* endpoints |
| Lifetime | that one request | the cookie’s, 365 days by default |
| Rule kinds | four | one — name plus exact value |
| Value comparison | case-sensitive | case-insensitive |
Everything else matches Custom Parameter: same card, same Include/Exclude, applied immediately. A Cookie Value rule whose Name is empty is skipped at serving time — the editor will not create one, but a rule imported through the API can be in that state and then silently does nothing.
Several cookies: any of or all of
The Include rules of one entity form one list and its Exclude rules another. Inside a list, the
values given for one cookie name are always alternatives: a browser keeps one value per name,
so tier = gold and tier = silver mean “tier is gold or silver”. The connector between
different cookie names is the list’s mode — or ▾ by default, and ▾ when every cookie
is needed (And / or between groups).
| List | or (default) | and |
|---|---|---|
| Include | the visitor needs one of the listed values of any one cookie | the visitor needs every listed cookie, each with one of its listed values |
| Exclude | one listed value of any cookie keeps the visitor out | the visitor is kept out only when every listed cookie has one of its listed values |
An Include list of tier = gold, tier = silver and region = eu reads
( tier=gold or tier=silver ) or ▾ region=eu. Click the connector and it reads
( tier=gold or tier=silver ) and ▾ region=eu: a visitor with tier=silver and region=eu
passes, a visitor with only tier=gold does not, and bronze + eu fails. A list that names one
cookie only (test = 1, test = 2) has a single group and no connector — it means “test is 1
or 2” either way. A cookie added later joins its list the way the list combines now, so a fourth
cookie added to an and list is required too. A copied campaign or creative and a target
template keep each list’s mode. The classic UI has no control; it reads the list out in the group
header, e.g. · (tier = gold or silver) and region = eu. A longer list shows its first three
values and counts the rest (· segment A or segment B or segment C +47 more); hover the header for
the whole expression.
A visitor who saw pricing and booked a demo and started sign-up, in any order, but did not buy:
-
Fire one pixel per step, each under its own cookie name:
<img src="https://<ad-serving-domain>/multi-cookie.gif?visited_pricing=1|30d" width="1" height="1" alt=""> <img src="https://<ad-serving-domain>/multi-cookie.gif?visited_demo=1|30d" width="1" height="1" alt=""> <img src="https://<ad-serving-domain>/multi-cookie.gif?visited_signup=1|30d" width="1" height="1" alt=""> <img src="https://<ad-serving-domain>/multi-cookie.gif?purchased=1|90d" width="1" height="1" alt=""> -
Add three Cookie Value rules with Include —
visited_pricing=1,visited_demo=1,visited_signup=1— and click the or ▾ between them so it reads and. -
Add
purchased=1with Exclude.
The list asks which cookies the visitor has, not the order they were set in.
- To require several steps, give each step its own cookie name.
step=pricingfollowed bystep=demooverwrites the first, andstep=pricingplusstep=demoin one list means “step is pricing or demo” — never both. - Every cookie of an and list must still be alive when the visitor reaches the last step. A
|1dcookie on the first step of a week-long funnel quietly drops the visitor. - and does not widen reach. A Cookie Value rule matches only when the browser sends the ad server’s cookies with the ad request — not on Safari or any iPhone browser, not on Firefox across sites, not in private windows, apps or connected TV — and those visitors fail an and list exactly as they fail a single cookie. With detailed targeting logging on for the ad servers, a troubleshooting task records which cookie a rejected request was missing, or that it carried no targeting cookies at all.
- A value both included and excluded is flagged as a contradiction while the Exclude list keeps out a visitor with that value alone — when it reads or, or holds one group only. An and Exclude list of two groups or more keeps out only a visitor matching all of them, so the shared value is no contradiction there; the card flags it only when the Include list asks for every one of those groups, and only for values the Exclude list also names, because then nobody gets through. The same holds across levels: a campaign’s and Exclude list does not flag a creative that includes one of its cookies. Switching an Exclude list back to or checks again, and the switch (on the card or over the API) answers with the warning when a pair turns into a contradiction.
API
{entity} is advertiser, campaign, banner, site, zone or placement.
Create a Custom Parameter rule — POST /rest-api/{entity}/{id}/targeting/custom/create.do
with rule (INCLUDE / EXCLUDE), customValue, and for the non-expression kinds customType,
operation and parameter. Enum values are the names: customType is EXPRESSION, VALUE,
ONE_OF or SUBSTRING; operation is EQUALS, NOT_EQUALS, GREATER, GREATER_OR_EQUALS,
LESS, LESS_OR_EQUALS, IN, NOT_IN, ALL (has all of), CONTAINS or NOT_CONTAINS
(note the plural EQUALS). A name outside that list is refused with a 400 that names the parameter
and lists the accepted values; a missing operation or parameter, an operation the kind does not
support (CONTAINS on VALUE), and ALL on a parameter that is not registered as a list are
400s too. An invalid expression is a 400 whose message starts Please enter valid customValue
(Sample: …).
Switch a list between or and and — POST /rest-api/{entity}/{id}/targeting/match-mode with
type (CUSTOM, COOKIE_VALUE, DMP or API_JSON_FIELD), rule (INCLUDE / EXCLUDE) and
matchMode (ANY / ALL). The response carries the type, the rule, the saved matchMode, how
many rules changed and, when a value is now both included and excluded, a warning. A missing or
unknown type, rule or matchMode, a type without a mode, or an empty list is a 400; 403 and 404 as
usual. A rule added later joins its list’s mode, and every rule of these types reads back with its
matchMode and matchGroup (the parameter or cookie name).
Cookie Value — POST /rest-api/{entity}/{id}/targeting/cookie-value/create adds a cookie
rule, which joins its list’s mode.
Permissions
| What | Demand side (advertiser, campaign, creative) | Supply side (site, zone, placement) |
|---|---|---|
| See and use Custom Parameter | Custom under the role’s Targeting permissions + Manage Demand Targeting | Custom under Pre-targeting permissions + Manage Supply Pre-targeting |
| See and use Cookie Value | Cookie Value under Targeting permissions | Cookie Value under Pre-targeting permissions |
| Switch or / and | the same, plus the right to manage the type; a reader without it sees plain and / or | the same |
| Shared entity | Can View shows the rules; adding, removing or switching needs Can Edit or Is Owner | — |