Dev GuideAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunitySubmit a ticketLog In
GitHubNuGetDev CommunitySubmit a ticket

Roadmap for Mastering Real-Time Segments

This topic describes how to work with real-time segments in Optimizely Data Platform (ODP).

Real-time segments are streaming programs that ODP runs across all of your customers, all of the time. These programs can:

  1. Notify you when an interesting behavior occurs.
  2. Identify the full set of customers who have exhibited the behavior.
  3. Flag the set of interesting behaviors that a particular customer has exhibited.

When a real-time segment is installed in your account, it immediately starts running across all of your customers, and it keeps running until you uninstall it.

The simplest use for a real-time segment is to personalize an individual's experience on your website. To do this:

  1. Develop the segment
  2. Install the segment
  3. Query the segment membership of your visitor

Mastery of real-time segments develops through 2 levels of complexity:

  1. Attribute-only segments
  2. Behavioral segments

All of the components of a real-time segment are thoroughly documented here if you prefer a more direct road to mastery. This article is for those who prefer to learn by example.

The most foundational component of a real-time segment is the Expression. An expression is a set of operations that evaluate to some value. For example, customer.observation.lifetime_revenue > 100 evaluates to the boolean value true if ODP calculates that the lifetime value of a customer is greater than 100 (account default currency units). Expressions are not expressed as strings in this API and are instead expressed as a JSON tree for greater type safety and reduced ambiguity. You would encode the above expression as:

{
    "description": "Customers who have spent more than 100 Imperial Credits",
    "definition": {
        "root_condition": {
            "customer_condition": {
                "customer_filter": {
                    "comparison": {
                        "lhs": {
                            "path_reference": {
                                "value": "customer.observations.total_revenue"
                            }
                        },
                        "comparator": "GREATER_THAN",
                        "rhs": {
                            "number_literal": {
                                "value": "100"
                            }
                        }
                    }
                }
            }
        }
    }
}

The above JSON declares a top level expression that is the result of comparing the outputs from two nested expressions. The first nested expression (lhs) looks up a value from the customer profile. The second nested expression (rhs) encodes the constant 100.

An expression always takes the json form {expression_type: {expression parameters}}. There are three examples of this above.

  • comparison: Compares the result of evaluating the lhs expression to the rhs expression using the specified comparator.
    • lhs: The left hand side expression of the comparison
    • comparator: LESS_THAN, GREATER_THAN, and so on
    • rhs: The right hand side expression of the comparison
  • path_reference: Path for locating a string, number, or boolean starting from a customer profile or an event.
    • value: the path. The first element must be either customer or event. Event is only legal within a SequenceCondition
  • number_literal: A constant number expressed as a string to avoid floating point ambiguities and JSON number encoding limitations.
    • value: the number, expressed as a decimal string

The 13 other ways to encode and combine expressions are all enumerated in the OpenAPI spec and the exhaustive documentation.

To turn this expression into a complete, real-time segment, we need to wrap it in a condition.

Level 1: Attribute-based segments

The expression above lets us build a simple segment because we are only dealing with profile level data (data rooted at customer) so we can use CustomerCondition.

In JSON, this would be:

{
  "description": "Customers who have spent more than 100 Imperial Credits",
  "definition": {        
    "root_condition": {
          "customer_condition": {
        "customer_filter": {
            "comparison": {
              "lhs": {
                "path_reference": {
                  "value": "customer.observation.lifetime_revenue"
                }
              },
              "comparator": "GREATER_THAN",
              "rhs": {
                "number_literal": {
                  "value": "100"
              }
              }
          }
        }
      }
    }
  }
}

The above is a complete segment definition that you can send to the public-segments definition API. If you store the sample above in a file named segment_definition.json, you can install it with the following curl command:

curl -XPUT -H "x-api-key: $PRIVATE_KEY" \
    -H 'Content-Type': 'application/json' \
    https://api.zaius.com/v3/segments/big_spenders \
    -d @segment_definition.json

This will now flag individuals with their membership in this segment when a GraphQL audience query is performed.

This completes an example attribute-only segment. This is the simplest segment that you can create with real-time segments. You can modify the above example to declare segments from any logical combination of user attributes.

Level 2: Behavioral-based segments

A behavioral segment is a segment that makes decisions using a customer's events as input. An event is a bag of data that describes something the user did. Example events are user made a purchase, user opened an account, user completed a survey, user swiped their membership card. A behavioral segment might be looking for a single trigger event (customer swiped their membership card) or a meaningful sequence of events (customer swiped their membership card and then made a purchase on the same day). Behavioral segments have more complex definitions than attribute-only segments because of the potential for these segments to look for patterns across time.

The heart of a behavioral segment is a SequenceCondition.

A sequence condition is built from a sequence (which is the pattern detector itself) and the number of matches that sequence must observe before the condition is met. Optionally, you may specify a maximum number of matches after which the condition will no longer be met. For example:

  • Without a maximum: Customers who have completed checkout at least 3 times this month.
  • With a maximum: Customers who have completed checkout between 3 and 5 times this month.

The heart of a SequenceCondition is, of course, the Sequence. Sequences match patterns of event data.

When a sequence is active, its entry_event_filter considers every event that follows the time that the sequence was activated (sequences in a root_condition are always active). If the entry_event_filter finds a matching event, the sequence spawns a partial match. This partial match will contribute to the match count immediately if the sequence has no goal. However, if the sequence does have a goal, the goal becomes activated and the partial match will complete when the goal completes.

In summary, to match a set of events:

  • First, you must meet a sequence's entry expression. If a sequence has no goal, then the that partial match is complete and contributes to the match count immediately.
  • If that sequence also contains a goal, then you must meet that goal before the partial match can contribute to the match count.

Partial and complete matches are immediately removed the moment their disqualifier matches. Disqualifiers are used to represent concepts like timeouts (customer who added to cart in the last 7 days) and invalidating events (customers who added a product to cart and did not remove that product).

Below is a sequence with an entry condition of "blue event". When it detects its entry condition, it creates a partial match with a goal and a disqualifier. When that detector observes a purple event, its goal is met and the match count increases to 1. If the Sequence Condition specified 1 as its minimum number of matches, that sequence condition will now match. Later, when that detector observes another blue event, the match state (the detector) is discarded by its disqualifier but the sequences entry condition also observes the blue event and spawns a new detector.

This contrived example might represent customers who have made reviewed (purple) their most recent purchase (blue).

In pseudocode, the above animation might be written as:

{
  "entry_event_filter": "<blue event expression>",
  "goal": {
    "event_filter": "<purple event expression>"
  },
  "disqualifier": {
    "event_filter": "<blue event expression>"
  }
}

We are using a simple event_filter continuation as our goal and disqualifier. Sequences can also be continuations, which allows goals to chain into new sequences, which chain into new goals, and so on. You can also match sequences in parallel with the combination continuation and combine their results logically with AND and OR.

The classic cart abandon gives a great foundation for talking about other continuation types. Cart abandon is usually framed as user did (entry event) A (for example, add to cart) but did not (disqualifier) do B or C (for example, remove from cart or purchase) and this started more than 1 day ago (goal).