How the Targeting Engine Works
The targeting engine lets you define segments — named groups of customers — by writing rules against customer, account, journey, and place data. Segments can then be assigned to features (campaigns, recommendations, items, etc.) to control visibility and personalisation.
Core Concepts
Segment
A segment is a named, reusable group definition. It holds metadata (name, description, active) and a list of rules that determine who belongs to it. A segment with no rules matches everyone.
Rule
A rule is a single condition on a customer attribute. Every rule has:
attribute— the data point to inspect (e.g.account.points)operator— how to compare (e.g.gte)value— the comparison value, stored as JSONruleGroup— an integer (0, 1, 2 …) that controls AND/OR groupingnegate— whentrue, inverts the rule result
Assignment
An assignment links a segment to a target entity (e.g. a campaign or product item). Each assignment has a mode:
include— the target is visible only to customers who match this segmentexclude— the target is hidden from customers who match this segment
Evaluation Logic
AND / OR Rule Grouping
Rules inside a segment are evaluated in groups. The ruleGroup field controls this:
- Rules in the same group are combined with OR — the group passes if any rule matches
- Rules in different groups are combined with AND — the segment passes only if every group passes
account.tier eq Gold OR account.tier eq Platinum), and Group 1 has one rule (account.orders gte 5). A customer must be in Gold OR Platinum and have at least 5 orders.Include / Exclude Precedence
The engine always evaluates exclusions first:
- If the customer matches any exclude assignment → result is
false(hidden), regardless of includes - If there are no include assignments → result is
true(visible to all) - If the customer matches any include assignment → result is
true - Otherwise →
false
The negate Flag
Setting negate: true on a rule inverts its outcome. A rule that would normally pass will fail, and vice versa. This avoids needing to create an extra segment just to express "not in this tier".
Operators
| Operator | Description | Value format |
|---|---|---|
eq | Equal to | Scalar: "Gold", 100 |
ne | Not equal to | Scalar |
gt | Greater than | Number or date timestamp |
gte | Greater than or equal to | Number or date timestamp |
lt | Less than | Number or date timestamp |
lte | Less than or equal to | Number or date timestamp |
in | Actual value is in the provided list | Array: ["Gold","Platinum"] |
contains | String contains substring | String: "gmail" |
exists | Value is not null / undefined | No value needed |
between | Value is between two bounds (inclusive) | Object: {"from": 10, "to": 50} |
Available Attributes
Account namespace
Data scoped to the customer's loyalty account at a specific place.
| Attribute | Type | Operators | Description |
|---|---|---|---|
account.classification | string | eq ne in exists | Account classification label |
account.cltv | number | eq ne gt gte lt lte between | Customer lifetime value for this account |
account.daysSinceLastOrder | number | eq ne gt gte lt lte between | Whole days elapsed since the account's last order |
account.lastOrderDate | date | eq ne gt gte lt lte between | Date of the account's most recent order (YYYY-MM-DD) |
account.orders | number | eq ne gt gte lt lte between | Total number of orders placed on this account |
account.points | number | eq ne gt gte lt lte between | Current point balance on this account |
account.savings | number | eq ne gt gte lt lte between | Total savings accumulated on this account |
account.spend | number | eq ne gt gte lt lte between | Total spend recorded on this account |
account.tier | string | eq ne in exists | Current tier name (e.g. Bronze, Silver, Gold) |
Customer namespace
Data from the customer's global profile.
| Attribute | Type | Operators | Description |
|---|---|---|---|
customer.cltv | number | eq ne gt gte lt lte between | Customer lifetime value across all places |
customer.country | string | eq ne in contains | Country code on the customer's profile |
customer.dob | date | eq ne gt gte lt lte between | Date of birth (YYYY-MM-DD) |
customer.earnedPoints | number | eq ne gt gte lt lte between | Lifetime points earned across all accounts |
customer.email | eq ne in contains exists | Customer email address | |
customer.firstName | string | eq ne in contains exists | Customer first name |
customer.gender | string | eq ne in exists | Gender value on the customer's profile |
customer.lastName | string | eq ne in contains exists | Customer last name |
customer.mobilePhone | phone | eq ne in contains exists | Customer mobile phone number |
customer.orders | number | eq ne gt gte lt lte between | Total orders placed across all places |
customer.redeemedPoints | number | eq ne gt gte lt lte between | Lifetime points redeemed across all accounts |
customer.savings | number | eq ne gt gte lt lte between | Total savings across all places |
Journey namespace
Data about an active journey execution for the customer.
| Attribute | Type | Operators | Description |
|---|---|---|---|
journey.daysSinceStarted | number | eq ne gt gte lt lte between | Whole days elapsed since the journey execution started |
journey.started | date | eq ne gt gte lt lte between | Date the journey execution was created (YYYY-MM-DD) |
Place namespace
| Attribute | Type | Operators | Description |
|---|---|---|---|
place.name | string | eq ne in contains exists | Title of the current place |
Date Values
Date attributes expect values as YYYY-MM-DD strings in rule definitions. Internally the engine converts them to UTC midnight millisecond timestamps for comparison, so the time component is ignored. For between, pass {"from": "2024-01-01", "to": "2024-12-31"}.
Practical Examples
1. High-value VIP customers
Customers in the Gold or Platinum tier who have spent more than 5,000.
// Rule Group 0 — tier must be Gold OR Platinum
{ attribute: "account.tier", operator: "in", value: ["Gold", "Platinum"], ruleGroup: 0 }
// Rule Group 1 — AND spend must be over 5000
{ attribute: "account.spend", operator: "gt", value: 5000, ruleGroup: 1 }2. At-risk lapsed customers
Customers who haven't ordered in over 60 days.
{ attribute: "account.daysSinceLastOrder", operator: "gt", value: 60, ruleGroup: 0 }3. Birthday month promotion
Customers born in a specific month. Use between with the month's date range.
{ attribute: "customer.dob", operator: "between", value: { from: "1900-07-01", to: "1900-07-31" }, ruleGroup: 0 }1900 as a placeholder works reliably for all customers.4. New customers (first 3 orders)
{ attribute: "account.orders", operator: "lte", value: 3, ruleGroup: 0 }5. Customers with a points balance to redeem
{ attribute: "account.points", operator: "gte", value: 100, ruleGroup: 0 }6. Exclude employees / test accounts
Create a segment matching a specific email domain, then assign it as mode: "exclude".
{ attribute: "customer.email", operator: "contains", value: "@yourcompany.com", ruleGroup: 0 }7. Customers active in a specific journey for over 7 days
{ attribute: "journey.daysSinceStarted", operator: "gt", value: 7, ruleGroup: 0 }8. Customers in a specific country who have never ordered
// Group 0
{ attribute: "customer.country", operator: "eq", value: "DK", ruleGroup: 0 }
// Group 1 — AND no orders yet
{ attribute: "account.orders", operator: "eq", value: 0, ruleGroup: 1 }API Quick Reference
All endpoints are under /places/{placeId}/targeting/ and require a valid Api-Key header.
| Method | Path | Description |
|---|---|---|
GET | /targeting/attributes | List all available attributes and their allowed operators |
GET | /targeting/segments | List segments (paginated) |
POST | /targeting/segments | Create a segment |
PUT | /targeting/segments/{id} | Update a segment |
DELETE | /targeting/segments/{id} | Delete a segment |
GET | /targeting/segments/{id}/rules | List rules for a segment |
POST | /targeting/segments/{id}/rules | Add a rule to a segment |
PUT | /targeting/segments/{id}/rules/{ruleId} | Update a rule |
DELETE | /targeting/segments/{id}/rules/{ruleId} | Delete a rule |
GET | /targeting/segments/{id}/assignments | List assignments for a segment |
POST | /targeting/segments/{id}/assignments | Create an assignment |
PUT | /targeting/segments/{id}/assignments/{aId} | Update an assignment |
DELETE | /targeting/segments/{id}/assignments/{aId} | Delete an assignment |
Evaluation Summary
| Scenario | Result |
|---|---|
| No assignments exist for the target | ✅ Visible to everyone |
| Only include assignments, customer matches one | ✅ Visible |
| Only include assignments, customer matches none | ❌ Hidden |
| Customer matches an exclude assignment | ❌ Hidden (even if also in an include segment) |
| Segment has no rules | ✅ Matches everyone |
Rule has negate: true and condition passes | Rule treated as failed |
Comments