---
title: Rules Engine
url: https://docs.kaltura.com/docs/events-collaboration/gamification-api/gamification-rules-engine
---

## Rules Engine

The rules engine evaluates analytics events against configured rules and triggers scoring, achievement, and certification actions. Rules are the core abstraction that ties all gamification features together.

### 6.1 Rule Entity

| Field | Required | Description |
|-------|----------|-------------|
| `gameObjectType` | Yes | `leaderboard`, `certificate`, `badge`, `leadScoring` |
| `gameObjectId` | Yes | ID of the parent game object |
| `name` | Yes | Rule display name |
| `conditions` | Yes | Array of `{fact, operator, value}` — all must match (AND) |
| `type` | Yes | `sum`, `count`, `countUnique`, `countBoolean`, `external`, `override` |
| `mode` | Yes | `distribute_points`, `dont_distribute_points`, `block_root_rule_while_in_progress`, `block_root_rule_if_exhausted` |
| `metric` | Yes | Event field to evaluate: `playTime`, `kuserId`, `like`, `score`, `pollId` |
| `groupBy` | Yes | Scope fields, comma-separated: `kuserId`, `kuserId,entryId` |
| `goal` | Yes | Threshold to trigger point distribution |
| `points` | Yes | Fixed value (`"50"`) or dynamic event field name (`"score"`) |
| `maxPoints` | No | Cap on total points. Fixed, dynamic, or `"unlimited"` |
| `reportFormat` | No | `default` or `in_minutes` |
| `status` | No | `enabled` or `disabled` |
| `subRules` | No | Array of child rules for multi-stage logic |
| `userIdOverride` | No | Map points to a different user (e.g., upvote recipient) |

### 6.2 Rule Types

| Type | Description | Example |
|------|-------------|---------|
| `sum` | Sum the metric value across events | Sum `playTime` — when total reaches `goal` seconds, distribute points |
| `count` | Count matching events | Count `pollAnswered` events — when count reaches `goal`, distribute points |
| `countUnique` | Count unique metric values | Count unique `entryId` values — "watch N different sessions" |
| `countBoolean` | Count events where metric is truthy | Count events where `like` is true |
| `external` | Accept external/3rd-party scores | Metric must be `"score"`; points come from the event's score field |
| `override` | Replace existing score entirely | Override with new value instead of accumulating |

### 6.3 Rule Modes

| Mode | Description |
|------|-------------|
| `distribute_points` | When goal is met, distribute `points` to the user's score |
| `dont_distribute_points` | Track progress without adding to score (for badge rules that track activity separately) |
| `block_root_rule_while_in_progress` | Sub-rule must complete before the root rule can accumulate |
| `block_root_rule_if_exhausted` | Once this sub-rule reaches its max, the root rule stops accumulating |

### 6.4 Condition Operators

`equal`, `notEqual`, `lessThan`, `greaterThan`, `in`, `notIn`, `contains`, `doesNotContain`

### 6.5 Condition Facts (Event Fields)

| Fact | Description |
|------|-------------|
| `eventType` | `viewPeriod`, `quizSubmitted`, `pollAnswered`, `comment`, `like`, `registered`, etc. |
| `entryId` | Kaltura entry ID |
| `categories` | Category IDs the entry belongs to |
| `virtualEventId` | Virtual event ID |

**Single category:**
```json
{"fact": "categories", "operator": "in", "value": "325222712"}
```

**Multiple categories (OR):**
```json
{
  "any": [
    {"fact": "categories", "operator": "contains", "value": "$CATEGORY_ID_1"},
    {"fact": "categories", "operator": "contains", "value": "$CATEGORY_ID_2"}
  ]
}
```

### 6.6 Analytics Events That Feed Rules

| Source | Event Types | Example Rules |
|--------|------------|---------------|
| Playback | `viewPeriod` | Watch duration, per-session completion |
| In-Video Quizzes | `quizSubmitted` | Quiz completion, first-to-complete bonus |
| Chat & Collaboration | `pollAnswered`, `comment`, `like`/`upvote` | Poll participation, chat engagement |
| Live Q&A | `questionAsked`, `questionPromoted` | Q&A participation |
| KME | `participated`, `quizAnswer` | Meeting attendance |
| Registration | `registered` | Event registration, first-to-register bonus |

### 6.7 Rule CRUD

```bash
# Create rule for a leaderboard
curl -X POST "$KALTURA_SCM_URL/rule/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "leaderboard",
    "gameObjectId": "'$LEADERBOARD_ID'",
    "name": "Watch sessions",
    "description": "10 points per 60 seconds watched",
    "conditions": [{"fact": "eventType", "operator": "equal", "value": "viewPeriod"}],
    "type": "sum",
    "mode": "distribute_points",
    "metric": "playTime",
    "groupBy": "kuserId,entryId",
    "goal": "60",
    "points": "10",
    "maxPoints": "100",
    "reportFormat": "default"
  }'

# List rules for a game object
curl -X POST "$KALTURA_SCM_URL/rule/list" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{"gameObjectType": "leaderboard", "gameObjectId": "'$LEADERBOARD_ID'"}'

# Update rule (enable)
curl -X POST "$KALTURA_SCM_URL/rule/update" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{"id": "'$RULE_ID'", "status": "enabled"}'

# Delete rule
curl -X POST "$KALTURA_SCM_URL/rule/delete" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{"id": "'$RULE_ID'"}'
```

### 6.8 Sub-Rules

Sub-rules enable multi-stage logic. Example: root rule awards points per upvote, sub-rule caps total upvotes per user.

```bash
curl -X POST "$KALTURA_SCM_URL/rule/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "leaderboard",
    "gameObjectId": "'$LEADERBOARD_ID'",
    "name": "Upvotes on comments",
    "conditions": [{"fact": "eventType", "operator": "equal", "value": "like"}],
    "type": "count",
    "mode": "distribute_points",
    "metric": "like",
    "groupBy": "kuserId,entryId",
    "goal": "1",
    "points": "20",
    "maxPoints": "100",
    "subRules": [
      {
        "name": "Cap at 5 upvotes",
        "type": "count",
        "metric": "commentId",
        "groupBy": "kuserId",
        "goal": "1",
        "maxPoints": "5",
        "mode": "block_root_rule_if_exhausted"
      }
    ]
  }'
```
