Expression JSON (expJSON)

When you create a loyalty promotion through the UI, the qualifying conditions defined are internally structured into a format called Expression JSON, or expJSON.

When using the Loyalty Promotions API to create or update a promotion programmatically, you must provide the expJSON field directly. Understanding this structure will help you define any rule or condition for a loyalty promotion without depending on the UI.

The expJSON field is required when creating activity-based promotions and this includes single-activity, milestone-based, and streak-based promotions. Broadcast promotions do not use qualifying conditions and therefore do not require an expJSON.

UI conditions and expJSON

Each condition you configure in the UI maps directly to a specific JSON structure. The easiest way to understand expJSON is to see what the UI condition builder produces behind the scenes.

No qualifying condition

When no qualifying condition is added, the activity applies to all transactions and the expJSON evaluates to a constant true value.

{
  "arity": "literal",
  "type": "boolean:primitive",
  "value": "true"
}

A single condition

When you add one condition, for example requiring the transaction value to be greater than 1000, the expJSON becomes a comparison operation between a transaction field and a fixed number.

{
  "arity": "binary_operation",
  "type": "boolean:primitive",
  "value": ">",
  "operands": [
    {
      "arity": "object_dereference",
      "type": "real:object:primitive",
      "operands": [
        {
          "arity": "name",
          "type": "tx:object:primitive",
          "value": "currentTxn"
        },
        {
          "arity": "name",
          "type": "real:object:primitive",
          "value": "value"
        }
      ]
    },
    {
      "arity": "literal",
      "type": "number:primitive",
      "value": "1000"
    }
  ]
}

Explanation of the JSON from top to bottom:

  • The root node is a binary_operation with "value": ">" this is the Greater Than operator selected in the UI.
  • Its first operand is an object_dereference this is the UI's Purchase attributes > Value selection, which reads the value field from the current transaction object (currentTxn).
  • Its second operand is a literal with "value": "1000" this is the number 1000 entered in the UI.

Multiple conditions combined with AND

When you add more than one condition using Add more, all conditions are joined with a logical AND. Every condition must be true for the promotion to apply.

{
  "arity": "binary_operation",
  "type": "boolean:primitive",
  "value": "&&",
  "operands": [
    {
      "arity": "binary_operation",
      "type": "boolean:primitive",
      "value": ">",
      "operands": [
        {
          "arity": "object_dereference",
          "type": "real:object:primitive",
          "operands": [
            {
              "arity": "name",
              "type": "tx:object:primitive",
              "value": "currentTxn"
            },
            {
              "arity": "name",
              "type": "real:object:primitive",
              "value": "value"
            }
          ]
        },
        {
          "arity": "literal",
          "type": "number:primitive",
          "value": "500"
        }
      ]
    },
    {
      "arity": "method_call",
      "type": "boolean:primitive",
      "operands": [
        {
          "arity": "object_dereference",
          "type": "string:object:primitive",
          "operands": [
            {
              "arity": "name",
              "type": "customer:object:primitive",
              "value": "currentCustomer"
            },
            {
              "arity": "name",
              "type": "string:object:primitive",
              "value": "slabName"
            }
          ]
        },
        {
          "arity": "name",
          "type": "anonymous:function:primitive",
          "value": "isOneOf"
        },
        {
          "arity": "literal",
          "type": "string:object:primitive",
          "value": "Gold"
        },
        {
          "arity": "literal",
          "type": "string:object:primitive",
          "value": "Sapphire"
        }
      ]
    }
  ]
}

Reading through this JSON:

  • The root node is a binary_operation with "value": "&&" this is the logical AND joining the two conditions.
  • The first operand (left branch) is the currentTxn.value > 500 check, identical in structure to the single-condition example above.
  • The second operand (right branch) is a method_call. This is how Is One Of works in the UI. It calls the isOneOf method on currentCustomer.slabName, passing each selected tier ("Gold", "Sapphire") as a separate literal operand.

How expJSON is structured

An expJSON is a tree of nodes. Each node represents one piece of the condition logic, and nodes can contain other nodes (called operands) to build up more complex expressions.

Every node has the following fields:

FieldData typeRequiredDescriptionExample
arityStringYesThe role this node plays in the expression. Determines how the node is evaluated. See Arity values for the full list."arity": "binary_operation"
typeStringYesThe data type of the value this node produces. For example, boolean:primitive for a true/false result, or number:primitive for a numeric value. See Type values for the full list."type": "boolean:primitive"
valueStringConditionalThe actual value for this node. Used for literal and name nodes. For binary_operation nodes, this is the operator symbol (for example, >, ==, &&). Not used in object_dereference nodes."value": ">"
operandsArray of nodesConditionalThe child nodes that this node operates on. Required for binary_operation, unary_operation, object_dereference, and method_call nodes. Not used in literal nodes."operands": [{ ... }, { ... }]
📘

Note:

The outermost node in an expJSON must always evaluate to a boolean:primitive. The promotions engine uses this final true/false result to decide whether the condition is met.

Arity values

The arity field defines the structural role of a node.

ArityDescriptionHas value?Has operands?
literalA constant value, such as a fixed number, string, or true/false.YesNo
nameA reference to a variable (such as currentTxn) or a property name (such as value).YesNo
binary_operationAn operation that takes exactly two operands and applies an operator between them. The value field holds the operator symbol.YesYes (exactly 2)
unary_operationAn operation that takes a single operand, such as logical NOT.YesYes (exactly 1)
object_dereferenceAccesses a property on an object. The first operand is the object; the second operand is the property name.NoYes (exactly 2)
method_callCalls a method on an object with optional arguments. The first operand is the object, the second is the method name, and any additional operands are the arguments.NoYes (2 or more)

Type values

The type field describes the data type of the value a node produces.

TypeDescription
boolean:primitiveA true/false value. The root node of an expJSON must always be this type.
number:primitiveA generic numeric value. Used for literal numbers in conditions.
integer:number:primitiveA whole number (integer).
real:object:primitiveA decimal (floating-point) number, used for monetary amounts and other fractional values.
string:object:primitiveA text string.
tx:object:primitiveThe current transaction object (currentTxn).
transaction:object:primitiveAn alias for the current transaction object (currentTransaction).
customer:object:primitiveThe current customer object (currentCustomer).
store:object:primitiveThe current store object (currentStore).

Context objects and their fields

When building conditions that reference live data, such as the amount of the current transaction, you use context objects. These are pre-defined variables available during promotion evaluation.

currentTxn / currentTransaction

Represents the transaction that triggered the promotion evaluation.

FieldTypeDescription
valuereal:object:primitiveThe net monetary value of the transaction.
grossAmountreal:object:primitiveThe gross amount of the transaction before discounts.
discountreal:object:primitiveThe total discount applied to the transaction.
basketSizeinteger:number:primitiveThe number of distinct line items in the transaction.
totalQtyinteger:number:primitiveThe total quantity of all items in the transaction.
datedate:object:primitiveThe date and time of the transaction.
tenderIncludesboolean:primitiveChecks whether the transaction includes a specific tender (payment method) code.
tenderSumreal:object:primitiveThe sum of amounts paid using tenders matching a regex pattern.
basketIncludesboolean:primitiveChecks whether the basket contains an item matching a regex pattern.
categorySumreal:object:primitiveThe total value of items belonging to a category matching a regex pattern.
categoryCountinteger:number:primitiveThe number of distinct items in a category matching a regex pattern.
categoryQuantityinteger:number:primitiveThe total quantity of items in a category matching a regex pattern.
brandIncludesboolean:primitiveChecks whether the basket contains an item from a brand matching a regex pattern.
brandSumreal:object:primitiveThe total value of items from a brand matching a regex pattern.
brandCountinteger:number:primitiveThe number of distinct items from a brand matching a regex pattern.
brandQuantityinteger:number:primitiveThe total quantity of items from a brand matching a regex pattern.

currentLineItem

Represents an individual line item within the transaction. Use this for item-level conditions.

FieldTypeDescription
valuereal:object:primitiveThe monetary value of the line item.
discountreal:object:primitiveThe discount applied to the line item.
codestring:object:primitiveThe item code or SKU.
quantityinteger:number:primitiveThe quantity of this item in the transaction.
categoryMatchesboolean:primitiveChecks whether the item's category matches a given regex pattern.
brandMatchesboolean:primitiveChecks whether the item's brand matches a given regex pattern.

currentCustomer

Represents the member associated with the transaction.

FieldTypeDescription
slabNamestring:object:primitiveThe name of the member's current loyalty tier (slab).
slabNumberinteger:number:primitiveThe numeric index of the member's current loyalty tier.
currentPointsreal:object:primitiveThe member's current points balance.
lifetimePointsreal:object:primitiveThe total points earned by the member across their lifetime.
lifetimePurchasereal:object:primitiveThe total purchase value across the member's lifetime.
joinDatedate:object:primitiveThe date the member enrolled in the loyalty programme.
numberOfVisitsinteger:number:primitiveThe total number of store visits recorded for the member.
numberOfTxnsinteger:number:primitiveThe total number of transactions made by the member.
avgSpendPerVisitreal:object:primitiveThe member's average spend per visit.
avgBasketSizeinteger:number:primitiveThe member's average basket size across transactions.
slabChangeDatedate:object:primitiveThe date the member's loyalty tier last changed.
slabExpiryDatedate:object:primitiveThe date on which the member's current loyalty tier expires.
loyaltyTypelstring:string:object:primitiveThe loyalty type of the member (for example, LOYAL, NON_LOYAL).

currentStore

Represents the store where the transaction occurred.

FieldTypeDescription
namestring:object:primitiveThe name of the store.
codestring:object:primitiveThe unique code identifying the store.

Operators

For binary_operation nodes, the value field specifies the operator. The available operators depend on the types of the operands.

OperatorApplicable typesDescription
>NumberGreater than
<NumberLess than
>=NumberGreater than or equal to
<=NumberLess than or equal to
==Number, StringEquals
!=Number, StringNot equals
&&BooleanLogical AND: both conditions must be true
||BooleanLogical OR: at least one condition must be true