Using JSON in segments

Attributes and event data can contain nested (JSON) values—arrays, objects, and arrays of objects. You can use these nested values to match people in segments, filters, and trigger criteria. If you’re not familiar with JSON, we provide some simple options to help you traverse nested attributes. If you are familiar with JSON, you can use JSON dot notation as you normally would.

 New to JSON?

JSON is a standard, simple way to organize and structure data. Check out our introduction to JSON and learn how you can take advantage of JSON in

How it works

Attributes and events can contain nested JSON (JavaScript Object Notation) values, and you can evaluate these values when you build a segmentA group of people who match a series of conditions. People enter and exit the segment automatically when they match or stop matching conditions., filter, or trigger condition. If you’re not familiar with JSON, you might check out our introduction to JSON—but we’ll explain a bit about how it works on this page. Read on and you’ll be a pro in no time.

If you’re new to JSON (nested attributes and values), or just want a simplified experience, you can use the Has the property or Where at least one selectors to build segments based on nested values. See the If you’re new to JSON section below for more about these selectors.

use our simple editor to traverse json
use our simple editor to traverse json

If you’re already familiar with JSON, you can use dot notation to point to nested values. You might use the Advanced option to point right to the nested value you want to evaluate.

use our advanced editor to traverse json
use our advanced editor to traverse json

 You can only use JSON customer attributes and event properties in segment conditions

You can’t use nested device attributes, collection properties, or trigger properties (from an incoming webhook or an API-triggered broadcast) in segment conditions.

Example data

On this page, we’ll consider the following sample JSON data representing a complex attribute or event data. We’ll use it to demonstrate segment matches on this page. In both cases, we’ll call it shopper_history.

  "last_shopped": 1662587234,
  "location": {"city": "Montreal","province": "QC"}, //simple object
  "purchases": [ //array of objects
      "id": 123,
      "type": "computers",
      "name": "Monitor",
      "price": 25,
      "discount": 10,
      "shipping_address": {"city": "Calgary","province": "AB"},
      "coupons_applied": [
          "coupon_code": "AXXXXX",
          "discount": "10%"
          "coupon_code": "BXXXXX",
          "discount": "15%"
      "id": 456,
      "type": "computers",
      "name": "Mouse",
      "price": 15,
      "shipping_address": {"city": "Edmonton","province": "AB"}
  "stores_visited": ["Winnipeg","Toronto","Vancouver"], //simple string array
  "coupons_received": [5,10,20], //simple numeric array
  "children_ages": [1654099180,1654099181,1654099182], //simple timestamp array
  "lottery_tickets": //simple array of simple arrays

If you’re new to JSON…

We nest values under parents in two different formats: objects and arrays.

See the curly brackets surrounding the data above—the { on the first line and the } on the last line? That’s called an object. If we stored all of the data above in an attribute called shopper_history, you could filter people who last_shopped with you using the Has the property selector—because last_shopped resides within the shopper_history attribute.

traverse an object with the has the property operator
traverse an object with the has the property operator

See the square brackets surrounding the value after stores_visited above? That’s called an array. If we wanted to build a segment of all people who visited the Toronto store, we could build a segment of people with a shopper_history attribute that has the property stores_visited where at least one value is Toronto.

find a value in an array with the where at least one operator
find a value in an array with the where at least one operator

An array can also contain objects—that’s what’s going on with purchases above. If we wanted to find everybody who used a coupon on their purchase, we could set up a segment where: shopper_history has the property purchases where at least one property called coupons_applied exists.

use our simple editor to traverse json
use our simple editor to traverse json

Nested properties in the basic segment builder

The segment builder has two selectors that help you traverse complex values:

  • Has the property: Checks for a child of an object.
  • Where at least one: Checks for a matching value in an array.

When working with complex attributes and event values, you might want to open a representative person in another browser window—someone with data that you can reference as you build your segment.

Has the property (objects)

The has the property selector lets you traverse objects. Using our example data, you could create a condition stating Attribute location has the property city equal to Montreal. This evaluates to true. When you use the has the property selector, we nest child values, helping show the parent-child relationships between nested attributes.

nested properties in a segment
nested properties in a segment

At least one of (arrays)

The at least one of selector matches an item in an array. When you use it, you select whether you want to match a property or value in the array.

  • property: your array contains objects—like purchases in our example data, and you want to traverse that data to find a match.
  • value: your array only contains values, like stores_visited in our example data. Use this to match any value in the array.

For a simple array—a list of values in square brackets—simply use the contains or equals operators to match a value. Using the stores_visited array from our shopper_history example data, the following would evaluate to true.

shopper_history.stores_vistited contains Winnipeg is true, and the person represented by the data above would join the segment.

You cannot specify a position or “index” when you use the segment builder this way. If you want to match a value at a specific index, you’ll need to use JSON dot notation in the advanced editor.

 Equals and Contains have some minor differences

The equals operator searches for a matching value, and contains searches for a match within a value in the array. See our section below for more information.

Dot notation in the basic editor

You can use dot notation in the simplified, basic editor. However, you can’t specify an array index when you use the editor this way.

For example, if you wanted to filter or segment on a matching value in the stores_visited array, you could use Attribute shopper_history.stores_visited[] is equal to Winnipeg.

But you’d have to switch to the advanced editor if you only wanted to match the first item in the array—i.e. shopper_history.stores_visited[0].

JSON dot notation in the segment builder
JSON dot notation in the segment builder

The Advanced Editor

You can click Advanced to switch to an expanded JSON dot notation editor.

If you don’t provide an array index when you use dot notation, we’ll match any item in the array (e.g. array[]). If you provide an index, we’ll match a specific position in an array. Arrays are zero-indexed—e.g. array[0] matches the first item in the array.

Using our stores_visited example:

  • shopper_history.stores_visited[] contains Toronto is true
  • shopper_history.stores_visited[0] contains Toronto is false
use an array in a segment condition
use an array in a segment condition

This works with nested arrays and arrays of objects as well. For example if we wanted to create a segment of people who used a specific coupon, we might use shopper_history.purchases[].shopper_history.coupons_applied[].coupon_code. This would search against every object within purchases for an array called coupons_applied, and then for the coupon_code for every object within coupons_applied.

use an array of objects in a segment condition
use an array of objects in a segment condition

 Empty brackets don’t work in Liquid

You can’t use our empty bracket array syntax (array[]) to match any item in an array using liquidA syntax that supports variables, letting you personalize messages for your audience. For example, if you want to reference a person’s first name, you might use the variable {{customer.first_name}}.. Instead, you’ll need to use a for loop or provide a specific index in the array.

The ? operator: for arrays or objects

If you’re not sure whether an attribute is an object or an array, you can use the ? operator in place of square brackets.

When you use a ?, we’ll treat a value first as an array; if we don’t find a match, we’ll treat the value as an object. For example, if you wanted to access a key in purchases, but you weren’t sure if it was an object or an array of objects, you could use: shopper_history.purchases?.type.

If you use the where at least one property in the basic editor and then switch to Advanced, you’ll see that we use the ? operator rather than square brackets, segments aren’t aware of your attribute and event data structure until you save conditions and we begin processing the segment.

Traversing nested attributes

When you search within an object or an array, we’ll traverse the entire value until we find a match. For example, purchases[].coupons_applied[].coupon_code searches for the coupon_code key within any coupons found in purchases.

However, if you know your coupon code values are unique, you could also simply search purchases[] for your coupon code value. We’ll automatically search for a matching value in any child property of the purchases[] array.

Look for a unique value from a top-level array
Look for a unique value from a top-level array

Equals vs contains in an array

In most cases, the equals and contains conditions are functionally identical when you use the at least one of selector (e.g. you reference an array). But there is a minor difference: Equals searches for an exact match. Contains searches for a value containing a string value—so a partial string can match.

Using our stores_visited example data, these two statements are identical and true:

  • stores_visited[] contains Toronto
  • stores_visited[] equals Toronto

But these two statements are not identical:

  • stores_visited[] contains Toro is true
  • stores_visited[] equals Toro is false

If you don’t use array syntax—either the where at least one selector or []contains will work but equals will not. When you don’t use array syntax, we treat the value as a string; the stringified value of stores_visited contains Toronto, but it has more characters than just that word, so it can’t equal Toronto.

  • stores_visited contains Toronto is true
  • stores_visited equals Toronto is false

Use does not exist instead of an empty string

In general, you shouldn’t send attributes or event data properties with empty strings. It’s not necessarily a problem if you do, but we don’t store customer attributesA key-value pair that you associate with a person—like their name, the date they were created in your workspace, etc. Use attributes to target people and personalize messages. with empty values, and you can’t save a segment or filter condition with an empty value.

But, if you do pass us attributes or properties with empty values—like in event data—you can segment or filter against these conditions using the exists or does not exist operators.

use the does not exist condition to check for an empty value
use the does not exist condition to check for an empty value
Copied to clipboard!