Skip to main content

What is a Policy Rule?

A Policy Rule is a condition → actions pair. When the input facts satisfy the condition, the rule’s actions fire. Rules are evaluated in priority order (0 = highest priority).
IF condition matches → THEN execute actions
Each rule belongs to a specific policy version and has a name, a priority, a condition (tree-structured logic), and one or more actions.

Condition Syntax

Conditions use a tree structure with two node types: SINGLE and GROUP.
LexQ uses its own condition DTO format with type: "SINGLE" / type: "GROUP", field, operator, value, and valueType fields. Do not confuse this with the Console’s internal form representation which may use different field names. Always use the engine format when calling the API or CLI.

SINGLE Condition

A leaf node that compares a single fact against a value:
{
  "type": "SINGLE",
  "field": "payment_amount",
  "operator": "GREATER_THAN_OR_EQUAL",
  "value": 100000,
  "valueType": "NUMBER"
}

GROUP Condition

A branch node that combines child conditions with AND or OR:
{
  "type": "GROUP",
  "operator": "AND",
  "children": [
    { "type": "SINGLE", "field": "customer_tier", "operator": "EQUALS", "value": "VIP", "valueType": "STRING" },
    { "type": "SINGLE", "field": "payment_amount", "operator": "GREATER_THAN", "value": 50000, "valueType": "NUMBER" }
  ]
}

Operators

OperatorCompatible TypesDescription
EQUALSAllExact match
NOT_EQUALSAllNegation
GREATER_THANNUMBER>
GREATER_THAN_OR_EQUALNUMBER>=
LESS_THANNUMBER<
LESS_THAN_OR_EQUALNUMBER<=
CONTAINSSTRINGSubstring match
INSTRING, NUMBERValue is in the provided list
NOT_INSTRING, NUMBERValue is not in the provided list

Value Types

TypeJSON ValueExample
STRING"string""VIP"
NUMBERnumber100000
BOOLEANtrue / falsetrue
LIST_STRING["a", "b"]["KR", "US"]
LIST_NUMBER[1, 2][10000, 20000]

Nested Conditions Example

(customer_tier = "VIP" AND payment_amount >= 100000) OR region IN ["KR", "JP"]
{
  "type": "GROUP",
  "operator": "OR",
  "children": [
    {
      "type": "GROUP",
      "operator": "AND",
      "children": [
        { "type": "SINGLE", "field": "customer_tier", "operator": "EQUALS", "value": "VIP", "valueType": "STRING" },
        { "type": "SINGLE", "field": "payment_amount", "operator": "GREATER_THAN_OR_EQUAL", "value": 100000, "valueType": "NUMBER" }
      ]
    },
    { "type": "SINGLE", "field": "region", "operator": "IN", "value": ["KR", "JP"], "valueType": "LIST_STRING" }
  ]
}
In the example above, customer_tier and region are custom facts that must be registered in Fact Definitions before use. payment_amount is a system fact available by default.

Action Types

Each rule can have multiple actions. Actions fire sequentially when the condition matches.
TypeDescriptionKey Parameters
DISCOUNTApply a discount to a reference amountrefVar, method, rate or value
POINTAward points based on a reference amountrefVar, method, rate or value, integrationId
COUPON_ISSUEIssue a couponcouponId, integrationId
BLOCKBlock the transactionreason
NOTIFICATIONSend notification (SMS, EMAIL, PUSH)channel, templateId, integrationId
WEBHOOKCall an external URLurl, method, payloadTemplate (optional)
SET_FACTSet an output variable to a fixed valuekey, value
ADD_TAGAdd a tag to the execution resulttag

DISCOUNT — Percentage

Apply a 10% discount based on payment_amount:
{
  "type": "DISCOUNT",
  "parameters": {
    "method": "PERCENTAGE",
    "rate": 10,
    "refVar": "payment_amount"
  }
}
If payment_amount is 200,000 → discount is 20,000.

DISCOUNT — Fixed Amount

{
  "type": "DISCOUNT",
  "parameters": {
    "method": "AMOUNT",
    "value": 5000,
    "refVar": "payment_amount"
  }
}

SET_FACT — Output Variable

SET_FACT is a literal value setter, not an expression evaluator. It assigns a fixed value to a key in the output variables. It does not support arithmetic expressions or references to other facts.
{
  "type": "SET_FACT",
  "parameters": { "key": "risk_level", "value": "HIGH" }
}

BLOCK — Transaction Block

{
  "type": "BLOCK",
  "parameters": { "reason": "Suspected fraud" }
}

POINT — Percentage

Earn 1% of payment_amount as points:
{
  "type": "POINT",
  "parameters": {
    "method": "PERCENTAGE",
    "rate": 1,
    "refVar": "payment_amount",
    "targetVar": "total_point",
    "integrationId": "<your-point-integration-id>"
  }
}
If payment_amount is 100,000 → earns 1,000 points. total_point is incremented and last_earned_point is set in the output.

POINT — Fixed Amount

{
  "type": "POINT",
  "parameters": {
    "method": "AMOUNT",
    "value": 500,
    "refVar": "payment_amount",
    "integrationId": "<your-point-integration-id>"
  }
}

COUPON_ISSUE

{
  "type": "COUPON_ISSUE",
  "parameters": {
    "couponId": "WELCOME_VIP_2026",
    "integrationId": "<your-coupon-integration-id>"
  }
}
Requires user_id in input facts to identify the recipient. No output variables are modified.

NOTIFICATION

{
  "type": "NOTIFICATION",
  "parameters": {
    "channel": "SMS",
    "templateId": "ORDER_CONFIRM_001",
    "targetVar": "phone_number",
    "integrationId": "<your-notification-integration-id>"
  }
}
Channels: SMS, EMAIL, PUSH. The targetVar specifies which fact contains the recipient address (defaults to phone_number).

ADD_TAG

{
  "type": "ADD_TAG",
  "parameters": {
    "tag": "VIP_VERIFIED",
    "targetVar": "user_tags"
  }
}
Appends the tag to the list fact. Duplicates are automatically prevented. If targetVar is omitted, defaults to user_tags.

WEBHOOK — Basic

{
  "type": "WEBHOOK",
  "parameters": {
    "url": "https://api.example.com/webhooks/orders",
    "method": "POST"
  }
}
Without payloadTemplate, the engine sends all input/output facts as the request body (system variables excluded).

WEBHOOK — With Payload Template

Use payloadTemplate to customize the request body. Template variables are resolved at execution time.
{
  "type": "WEBHOOK",
  "parameters": {
    "url": "<integration-id-or-direct-url>",
    "method": "POST",
    "payloadTemplate": {
      "text": "🔔 Rule {{ruleName}} fired\nCustomer: {{fact.customer_tier}}\nAmount: {{output.payment_amount}}"
    }
  }
}

Available Template Variables

VariableDescriptionExample Value
{{fact.xxx}}Input fact value{{fact.payment_amount}}150000
{{output.xxx}}Output variable (after actions){{output.last_discount_amount}}15000
{{timestamp}}Execution time (ISO-8601)2026-04-14T05:30:00Z
{{ruleName}}Matched rule nameVIP 20% Discount
{{groupName}}Policy group namePayment Policy
{{versionNo}}Executed version number5
{{xxx}}Direct fact lookup (shorthand){{customer_tier}}VIP
Slack requires {"text": "..."}, Discord requires {"content": "..."}. Use payloadTemplate to match each platform’s expected format.
{{fact.xxx}} and {{output.xxx}} reference the same data map. If an earlier action (e.g., DISCOUNT) modifies payment_amount, both {{fact.payment_amount}} and {{output.payment_amount}} reflect the modified value.

Mutex — Rule-Level Conflict Resolution

Within a single version, rules can belong to a mutex group to control how many matching rules fire.
FieldDescription
mutexGroupA string key grouping related rules (e.g., "best-discount")
mutexModeNONE (default) or EXCLUSIVE
mutexStrategyFIRST_MATCH, HIGHEST_PRIORITY, or MAX_BENEFIT
mutexLimitMax number of rules to fire from this group (for EXCLUSIVE)
When mutexMode is EXCLUSIVE, only the winning rule fires. Other matching rules in the same mutex group are skipped. This is logged in the Decision Trace as BLOCKED_BY_MUTEX.

Complete Rule Example

{
  "name": "VIP 20% Discount",
  "priority": 0,
  "condition": {
    "type": "GROUP",
    "operator": "AND",
    "children": [
      { "type": "SINGLE", "field": "customer_tier", "operator": "EQUALS", "value": "VIP", "valueType": "STRING" },
      { "type": "SINGLE", "field": "payment_amount", "operator": "GREATER_THAN_OR_EQUAL", "value": 100000, "valueType": "NUMBER" }
    ]
  },
  "actions": [
    { "type": "DISCOUNT", "parameters": { "method": "PERCENTAGE", "rate": 20, "refVar": "payment_amount" } },
    { "type": "SET_FACT", "parameters": { "key": "discount_applied", "value": true } }
  ],
  "mutexGroup": "best-discount",
  "mutexMode": "EXCLUSIVE",
  "mutexStrategy": "HIGHEST_PRIORITY",
  "isEnabled": true
}

Next Steps

Fact Definitions

Define the input variables your rules expect.

Dry Run

Test your rules before publishing.