---
title: Best Practices
url: https://docs.kaltura.com/docs/events-collaboration/gamification-api/gamification-best-practices
---

## Best Practices

**Rule design — use groupBy wisely.** `groupBy: "kuserId"` accumulates across all entries. `groupBy: "kuserId,entryId"` accumulates per entry per user — use this for "watch X minutes per session" rules where you want per-session caps.

**Set maxPoints caps.** Without `maxPoints`, users can accumulate unlimited points from a single rule. Use caps to prevent gaming: `"maxPoints": "100"` per session for viewership, `"maxPoints": "50"` for chat.

**Sub-rule ordering.** Sub-rules execute before the root rule evaluates. Use `block_root_rule_if_exhausted` to cap total accumulation, and `block_root_rule_while_in_progress` for prerequisite logic.

**Status lifecycle.** Create game objects as `scheduled`, enable rules, then update to `enabled` when the event starts. Use `scheduledGameObject` for automated transitions.

**Event scoping.** Scope rules to specific events or content using `virtualEventId` and `categories` conditions. Without scoping, rules apply to all partner content.

**Certificate PDF design.** Test the PDF template with a real certificate before the event. The background image URL must be publicly accessible. Text overlay positions (`x`, `y`) are in pixels from the top-left corner.

**Participation policies.** Always exclude internal staff from leaderboards and lead scoring via `byEmailDomain` with policy `do_not_save`. Speakers can participate but should be hidden via `do_not_display`.

**External events.** Use `delta` mode for incremental score additions (booth visits, check-ins). Use `upsert` mode when the external system provides final scores (exam results, external quiz platforms).

### Common Integration Patterns

#### Virtual Conference Engagement Program

Set up a complete gamification program for a multi-day virtual conference.

```bash
# 1. Create leaderboard (scheduled)
LEADERBOARD=$(curl -s -X POST "$KALTURA_SCM_URL/leaderboard/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "objectType": "Leaderboard",
    "name": "Conference 2025",
    "status": "scheduled",
    "startDate": "2025-06-01T09:00:00Z",
    "endDate": "2025-06-03T18:00:00Z",
    "participationPolicy": {
      "userDefaultPolicy": "display",
      "policies": [
        {"policy": "do_not_save", "matchCriteria": "byEmailDomain", "values": ["internal.com"]}
      ]
    },
    "subLeaderboards": [{"name": "By Country", "filterPaths": ["country"], "id": 0}],
    "virtualEventIds": ["$VIRTUAL_EVENT_ID"]
  }' | jq -r '.id')

# 2. Create rules
# Viewership: 10 pts per 60 sec watched, max 100 per session
curl -s -X POST "$KALTURA_SCM_URL/rule/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "leaderboard",
    "gameObjectId": "'$LEADERBOARD'",
    "name": "Watch sessions",
    "conditions": [{"fact": "eventType", "operator": "equal", "value": "viewPeriod"}],
    "type": "sum",
    "mode": "distribute_points",
    "metric": "playTime",
    "groupBy": "kuserId,entryId",
    "goal": "60",
    "points": "10",
    "maxPoints": "100"
  }'

# Polls: 25 pts per poll answered
curl -s -X POST "$KALTURA_SCM_URL/rule/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "leaderboard",
    "gameObjectId": "'$LEADERBOARD'",
    "name": "Answer polls",
    "conditions": [{"fact": "eventType", "operator": "equal", "value": "pollAnswered"}],
    "type": "count",
    "mode": "distribute_points",
    "metric": "pollId",
    "groupBy": "kuserId",
    "goal": "1",
    "points": "25",
    "maxPoints": "unlimited"
  }'

# Chat: 5 pts per message, max 50
curl -s -X POST "$KALTURA_SCM_URL/rule/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "leaderboard",
    "gameObjectId": "'$LEADERBOARD'",
    "name": "Chat messages",
    "conditions": [{"fact": "eventType", "operator": "equal", "value": "comment"}],
    "type": "count",
    "mode": "distribute_points",
    "metric": "comment",
    "groupBy": "kuserId",
    "goal": "1",
    "points": "5",
    "maxPoints": "50"
  }'

# 3. Create badges
curl -s -X POST "$KALTURA_SCM_URL/badge/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Power Attendee",
    "description": "Watch 10 sessions",
    "virtualEventIds": ["$VIRTUAL_EVENT_ID"],
    "participationPolicy": {"userDefaultPolicy": "display", "policies": []},
    "rules": [{
      "name": "Watch 10 unique sessions",
      "conditions": [{"fact": "eventType", "operator": "equal", "value": "viewPeriod"}],
      "type": "countUnique",
      "metric": "entryId",
      "groupBy": "kuserId",
      "goal": "10",
      "points": "1",
      "maxPoints": "1"
    }]
  }'

# 4. Enable leaderboard when event starts
curl -s -X POST "$KALTURA_SCM_URL/leaderboard/update" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{"id": "'$LEADERBOARD'", "status": "enabled"}'
```

#### Partner Training Certification

Set up tiered certification with PDF certificates.

```bash
# 1. Create certificate with PDF template
CERT=$(curl -s -X POST "$KALTURA_SCM_URL/certificate/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Partner Certified Developer",
    "description": "Complete all training modules",
    "status": "disabled",
    "certificateEligibility": "once",
    "certifiedCreditsThreshold": 3,
    "host": "https://training.example.com",
    "participationPolicy": {"userDefaultPolicy": "display", "policies": []},
    "outputFileConfiguration": {
      "outputFileElements": [
        {"url": "https://cfvod.kaltura.com/p/'$PARTNER_ID'/thumbnail/entry_id/'$BG_ENTRY'/width/1397/height/1080"},
        {"textElementType": "userFullName", "fontSize": 30, "y": 440},
        {"textElementType": "certificationDate", "fontSize": 18, "x": 695, "y": 636},
        {"textElementType": "credits", "fontSize": 23, "x": 1060, "y": 699}
      ]
    }
  }' | jq -r '.id')

# 2. Create viewership rule for the certificate
RULE=$(curl -s -X POST "$KALTURA_SCM_URL/rule/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "certificate",
    "gameObjectId": "'$CERT'",
    "name": "Watch training module",
    "conditions": [{"fact": "eventType", "operator": "equal", "value": "viewPeriod"}],
    "type": "sum",
    "mode": "distribute_points",
    "metric": "playTime",
    "groupBy": "kuserId,entryId",
    "goal": "60",
    "points": "1",
    "maxPoints": "unlimited",
    "reportFormat": "default"
  }' | jq -r '.id')

# 3. Enable certificate with credits mapping
curl -s -X POST "$KALTURA_SCM_URL/certificate/update" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "'$CERT'",
    "status": "enabled",
    "creditsMapping": "credits,'$RULE'\n10.0,1\n11.5,2"
  }'
```

#### Post-Event Lead Scoring

Score attendees and export hot leads to CRM.

```bash
# 1. Create lead scoring profile
LEAD=$(curl -s -X POST "$KALTURA_SCM_URL/leadScoring/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Event Lead Scoring",
    "status": "scheduled",
    "startDate": "2025-06-01T00:00:00Z",
    "endDate": "2025-06-03T23:59:59Z",
    "participationPolicy": {
      "userDefaultPolicy": "display",
      "policies": [{"policy": "do_not_save", "matchCriteria": "byEmailDomain", "values": ["internal.com"]}]
    },
    "scoreGroups": [
      {"name": "Top", "range": [1000, 99999]},
      {"name": "Mid", "range": [500, 999]},
      {"name": "Low", "range": [0, 499]}
    ],
    "virtualEventIds": ["$VIRTUAL_EVENT_ID"]
  }' | jq -r '.id')

# 2. Create rules across engagement categories
# Registration
curl -s -X POST "$KALTURA_SCM_URL/rule/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "leadScoring",
    "gameObjectId": "'$LEAD'",
    "name": "Registration",
    "conditions": [{"fact": "eventType", "operator": "equal", "value": "registered"}],
    "type": "count",
    "mode": "distribute_points",
    "metric": "kuserId",
    "groupBy": "kuserId",
    "goal": "1",
    "points": "50",
    "maxPoints": "50"
  }'

# Viewership
curl -s -X POST "$KALTURA_SCM_URL/rule/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "leadScoring",
    "gameObjectId": "'$LEAD'",
    "name": "Session viewership",
    "conditions": [{"fact": "eventType", "operator": "equal", "value": "viewPeriod"}],
    "type": "sum",
    "mode": "distribute_points",
    "metric": "playTime",
    "groupBy": "kuserId,entryId",
    "goal": "60",
    "points": "10",
    "maxPoints": "unlimited"
  }'

# 3. After event — generate lead scoring report
curl -s -X POST "$KALTURA_SCM_URL/report/generate" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{"reportType": "userLeadScoring", "gameObjectId": "'$LEAD'"}'

# 4. List user lead scores
curl -s -X POST "$KALTURA_SCM_URL/userLeadScoring/list" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{"gameObjectId": "'$LEAD'", "pager": {"pageSize": 100, "pageIndex": 1}}'
```

#### Flash Challenges with Scheduled Transitions

Create time-limited challenges that auto-enable and auto-disable.

```bash
# Create challenge leaderboard
CHALLENGE=$(curl -s -X POST "$KALTURA_SCM_URL/leaderboard/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "objectType": "Leaderboard",
    "name": "Keynote Flash Challenge",
    "status": "scheduled",
    "startDate": "2025-06-01T14:00:00Z",
    "endDate": "2025-06-01T15:00:00Z",
    "participationPolicy": {"userDefaultPolicy": "display", "policies": []},
    "virtualEventIds": ["$VIRTUAL_EVENT_ID"]
  }' | jq -r '.id')

# Double points for this challenge
curl -s -X POST "$KALTURA_SCM_URL/rule/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "leaderboard",
    "gameObjectId": "'$CHALLENGE'",
    "name": "Speed polls (double points)",
    "conditions": [{"fact": "eventType", "operator": "equal", "value": "pollAnswered"}],
    "type": "count",
    "mode": "distribute_points",
    "metric": "pollId",
    "groupBy": "kuserId",
    "goal": "1",
    "points": "50",
    "maxPoints": "unlimited"
  }'

# Schedule auto-enable at challenge start
curl -s -X POST "$KALTURA_SCM_URL/scheduledGameObject/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "leaderboard",
    "gameObjectId": "'$CHALLENGE'",
    "scheduledAction": "enable",
    "scheduledDate": "2025-06-01T14:00:00Z"
  }'

# Schedule auto-disable at challenge end
curl -s -X POST "$KALTURA_SCM_URL/scheduledGameObject/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "leaderboard",
    "gameObjectId": "'$CHALLENGE'",
    "scheduledAction": "disable",
    "scheduledDate": "2025-06-01T15:00:00Z"
  }'
```

#### CPE / Continuing Education Credits

Track Continuing Professional Education (CPE) credits per session for accreditation bodies (medical, legal, accounting). Each session awards credits based on verified watch time, and a branded PDF certificate is generated for submission to the accreditation authority.

**Workflow:**
1. Create a certificate with `externalId` (accreditation program ID), `certificateEligibility: "perEntry"` (one certificate per session per user), and `certifiedCreditsThreshold` (minimum credits for certification)
2. Create a `sum` rule on `viewPeriod` scoped to a session category via a `categories` condition, so only content in that category contributes credits
3. Configure `creditsMapping` to map watch-time thresholds to credit values (e.g., 60 minutes = 1.0 credit, 90 minutes = 1.5 credits)
4. Set participation policies to exclude speakers from earning credits
5. Generate per-entry certificates for submission to the accreditation body

```bash
# 1. Create CPE certificate with per-entry eligibility
CERT=$(curl -s -X POST "$KALTURA_SCM_URL/certificate/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CPE Credit Certificate",
    "description": "Continuing Professional Education — 1 credit per qualifying session",
    "status": "disabled",
    "externalId": "NASBA-PROG-2025-4821",
    "certificateEligibility": "perEntry",
    "certifiedCreditsThreshold": 1,
    "host": "https://training.events.kaltura.com",
    "participationPolicy": {
      "userDefaultPolicy": "display",
      "policies": [
        {"policy": "do_not_save", "matchCriteria": "byGroup", "values": ["speakers"]}
      ]
    },
    "outputFileConfiguration": {
      "outputFileElements": [
        {"url": "https://cfvod.kaltura.com/p/'$PARTNER_ID'/thumbnail/entry_id/'$BG_ENTRY_ID'/width/1397/height/1080"},
        {"textElementType": "userFullName", "fontSize": 30, "y": 440},
        {"textElementType": "entryName", "fontSize": 30, "y": 570},
        {"textElementType": "credits", "fontSize": 23, "x": 1060, "y": 699},
        {"textElementType": "certificationDate", "fontSize": 18, "x": 695, "y": 636}
      ]
    }
  }' | jq -r '.id')

# 2. Create viewership rule scoped to accredited sessions category
RULE=$(curl -s -X POST "$KALTURA_SCM_URL/rule/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "certificate",
    "gameObjectId": "'$CERT'",
    "name": "CPE session watch time",
    "conditions": [
      {"fact": "eventType", "operator": "equal", "value": "viewPeriod"},
      {"fact": "categories", "operator": "in", "value": "'$CATEGORY_ID'"}
    ],
    "type": "sum",
    "mode": "distribute_points",
    "metric": "playTime",
    "groupBy": "kuserId,entryId",
    "goal": "60",
    "points": "1",
    "maxPoints": "unlimited",
    "reportFormat": "in_minutes"
  }' | jq -r '.id')

# 3. Enable certificate with credits mapping (watch-time to CPE credit tiers)
curl -s -X POST "$KALTURA_SCM_URL/certificate/update" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "'$CERT'",
    "status": "enabled",
    "creditsMapping": "credits,'$RULE'\n1.0,1\n1.5,2\n2.0,3"
  }'

# 4. Generate per-entry certificate report for accreditation submission
curl -s -X POST "$KALTURA_SCM_URL/report/generate" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{"reportType": "entryCertificate", "gameObjectId": "'$CERT'"}'
```

The `categories` condition in the rule ensures only sessions within the accredited category contribute watch time. The `certificateEligibility: "perEntry"` setting generates a separate certificate per session, so each CPE credit maps to a specific session for audit purposes.

> **See also:** [Events Platform](/docs/events-collaboration/events-platform-api/events-platform-overview) for virtual event and session category setup, [Custom Metadata](/docs/rich-media-cms-apis/content-management/custom-metadata-overview) for attaching accreditation metadata to entries.

#### External Score Import (Hybrid Events)

Hybrid events combine in-person and digital activities on a single leaderboard. Booth visits, physical check-ins, and external quiz platforms produce scores outside the Kaltura analytics pipeline. Import those scores via CSV so they appear alongside digital engagement on the same leaderboard.

**Workflow:**
1. Create an `external` type rule (metric: `"score"`) on the leaderboard so it accepts imported events
2. Prepare a CSV file with `userId`, `score`, and `eventType` columns
3. Upload via `event/sendExternalEventsFromCSV` with `delta` mode (add to existing) or `upsert` mode (replace existing)
4. Verify imported scores in `userScore/list`

```bash
# 1. Create external rule on the leaderboard
curl -s -X POST "$KALTURA_SCM_URL/rule/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "leaderboard",
    "gameObjectId": "'$LEADERBOARD_ID'",
    "name": "Booth visits and physical activities",
    "conditions": [{"fact": "eventType", "operator": "equal", "value": "boothVisit"}],
    "type": "external",
    "mode": "distribute_points",
    "metric": "score",
    "groupBy": "kuserId",
    "goal": "1",
    "points": "score",
    "maxPoints": "unlimited"
  }'

# 2. Import scores — delta mode (add to existing scores)
# CSV columns: userId, score, eventType
curl -s -X POST "$KALTURA_SCM_URL/event/sendExternalEventsFromCSV" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -F "eventAction=delta" \
  -F "csvFile=@-;filename=scores.csv" <<'CSVEOF'
userId,score,eventType
user1@example.com,50,boothVisit
user2@example.com,75,boothVisit
user3@example.com,100,boothVisit
CSVEOF

# 3. Import scores — upsert mode (replace existing scores)
# Use upsert when the external system provides final scores (e.g., quiz retake)
curl -s -X POST "$KALTURA_SCM_URL/event/sendExternalEventsFromCSV" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -F "eventAction=upsert" \
  -F "csvFile=@-;filename=quiz_finals.csv" <<'CSVEOF'
userId,score,eventType
user1@example.com,90,boothVisit
user2@example.com,85,boothVisit
CSVEOF
```

Use `delta` for incremental activities where each occurrence adds points (booth scans, check-ins). Use `upsert` when the external system provides a corrected or final score that should replace the previous value (exam retakes, final quiz grades).

> **See also:** Section 11 (External Events & CSV Import) for CSV format details, [Analytics Reports](/docs/rich-media-cms-apis/analytics/analytics-overview) for verifying imported data in analytics.

#### Sponsor Engagement Tracking & ROI Reports

Event sponsors need measurable engagement data to justify renewal. Organize sponsor content by category, track booth page visits via analytics events, score engagement on a per-sponsor leaderboard, and generate per-sponsor reports.

**Workflow:**
1. Create a category per sponsor using the [Categories & Entitlements API](/docs/rich-media-cms-apis/content-management/categories-overview)
2. Create leaderboard rules with `categories` conditions scoping to each sponsor's category
3. Track booth page engagement via `analytics.trackEvent` (eventType 10003 for page loads)
4. Pull per-sponsor analytics via `report.getTable` with `categoriesIdsIn` filter
5. Generate per-sponsor gamification reports

```bash
# 1. Create leaderboard rule scoped to sponsor category
curl -s -X POST "$KALTURA_SCM_URL/rule/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "leaderboard",
    "gameObjectId": "'$LEADERBOARD_ID'",
    "name": "Sponsor booth engagement",
    "conditions": [
      {"fact": "eventType", "operator": "equal", "value": "viewPeriod"},
      {"fact": "categories", "operator": "in", "value": "'$SPONSOR_CATEGORY_ID'"}
    ],
    "type": "sum",
    "mode": "distribute_points",
    "metric": "playTime",
    "groupBy": "kuserId,entryId",
    "goal": "30",
    "points": "15",
    "maxPoints": "150"
  }'

# 2. Track sponsor booth page visit via analytics.trackEvent (API v3)
curl -s -X POST "$KALTURA_SERVICE_URL/service/analytics/action/trackEvent" \
  -d "ks=$KALTURA_KS" \
  -d "format=1" \
  -d "event[objectType]=KalturaStatsEvent" \
  -d "event[eventType]=10003" \
  -d "event[entryId]=$SPONSOR_ENTRY_ID" \
  -d "event[partnerId]=$KALTURA_PARTNER_ID"

# 3. Pull per-sponsor engagement data via report.getTable (API v3)
curl -s -X POST "$KALTURA_SERVICE_URL/service/report/action/getTable" \
  -d "ks=$KALTURA_KS" \
  -d "format=1" \
  -d "reportType=38" \
  -d "reportInputFilter[objectType]=KalturaReportInputFilter" \
  -d "reportInputFilter[fromDate]=$FROM_TIMESTAMP" \
  -d "reportInputFilter[toDate]=$TO_TIMESTAMP" \
  -d "reportInputFilter[categoriesIdsIn]=$SPONSOR_CATEGORY_ID" \
  -d "pager[objectType]=KalturaFilterPager" \
  -d "pager[pageSize]=100" \
  -d "pager[pageIndex]=1"

# 4. Generate per-sponsor gamification report
curl -s -X POST "$KALTURA_SCM_URL/report/generate" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{"reportType": "userScore", "gameObjectId": "'$LEADERBOARD_ID'"}'
```

The `categories` condition on the rule ensures only content within the sponsor's category contributes engagement points. Combine the gamification `userScore` report with the API v3 `report.getTable` data (reportType 38 = Top Content) to build a comprehensive sponsor ROI package showing both engagement scores and detailed viewing analytics.

> **See also:** [Categories & Entitlements](/docs/rich-media-cms-apis/content-management/categories-overview) for sponsor category setup, [Events Collection](/docs/rich-media-cms-apis/analytics/analytics-events-overview) for analytics event tracking, [Analytics Reports](/docs/rich-media-cms-apis/analytics/analytics-overview) for report.getTable usage, [Events Platform](/docs/events-collaboration/events-platform-api/events-platform-overview) for virtual event structure.

#### Employee Onboarding Gamification

Make employee onboarding engaging with module-based scoring, milestone badges, and a branded completion certificate. HR managers track cohort progress and generate records for compliance.

**Workflow:**
1. Create a leaderboard with a 90-day cohort window (startDate/endDate)
2. Create `sum` rules per onboarding module category (company culture, IT security, benefits)
3. Create milestone badges with inline rules ("Day 1 Done", "Security Certified", "Fully Onboarded")
4. Create a completion certificate with HR-branded PDF template
5. Managers query `userScore/list` for cohort progress and `report/generate` for HR records

```bash
# 1. Create 90-day onboarding leaderboard
ONBOARD_LB=$(curl -s -X POST "$KALTURA_SCM_URL/leaderboard/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "objectType": "Leaderboard",
    "name": "Q2 Onboarding Cohort",
    "description": "New hire onboarding progress — 90-day window",
    "status": "scheduled",
    "startDate": "2025-04-01T00:00:00Z",
    "endDate": "2025-06-30T23:59:59Z",
    "participationPolicy": {"userDefaultPolicy": "display", "policies": []},
    "virtualEventIds": ["$VIRTUAL_EVENT_ID"]
  }' | jq -r '.id')

# 2. Create module completion rule (scoped to IT Security category)
curl -s -X POST "$KALTURA_SCM_URL/rule/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectType": "leaderboard",
    "gameObjectId": "'$ONBOARD_LB'",
    "name": "IT Security module completion",
    "conditions": [
      {"fact": "eventType", "operator": "equal", "value": "viewPeriod"},
      {"fact": "categories", "operator": "in", "value": "'$IT_SECURITY_CATEGORY_ID'"}
    ],
    "type": "sum",
    "mode": "distribute_points",
    "metric": "playTime",
    "groupBy": "kuserId,entryId",
    "goal": "60",
    "points": "20",
    "maxPoints": "100"
  }'

# 3. Create "Security Certified" milestone badge
curl -s -X POST "$KALTURA_SCM_URL/badge/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Security Certified",
    "description": "Complete all IT security training modules and pass the quiz",
    "virtualEventIds": ["$VIRTUAL_EVENT_ID"],
    "iconUrl": "https://example.com/badges/security-certified.png",
    "participationPolicy": {"userDefaultPolicy": "display", "policies": []},
    "rules": [
      {
        "name": "Watch all IT security sessions",
        "conditions": [
          {"fact": "eventType", "operator": "equal", "value": "viewPeriod"},
          {"fact": "categories", "operator": "in", "value": "'$IT_SECURITY_CATEGORY_ID'"}
        ],
        "type": "countUnique",
        "metric": "entryId",
        "groupBy": "kuserId",
        "goal": "5",
        "points": "1",
        "maxPoints": "1"
      },
      {
        "name": "Pass IT security quiz",
        "conditions": [
          {"fact": "eventType", "operator": "equal", "value": "quizSubmitted"},
          {"fact": "categories", "operator": "in", "value": "'$IT_SECURITY_CATEGORY_ID'"}
        ],
        "type": "count",
        "metric": "kuserId",
        "groupBy": "kuserId",
        "goal": "1",
        "points": "1",
        "maxPoints": "1"
      }
    ]
  }'

# 4. Create HR-branded onboarding completion certificate
ONBOARD_CERT=$(curl -s -X POST "$KALTURA_SCM_URL/certificate/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Onboarding Complete",
    "description": "New hire onboarding program — all modules completed",
    "status": "enabled",
    "certificateEligibility": "once",
    "certifiedCreditsThreshold": 3,
    "host": "https://onboarding.internal.example.com",
    "participationPolicy": {"userDefaultPolicy": "display", "policies": []},
    "outputFileConfiguration": {
      "outputFileElements": [
        {"url": "https://cfvod.kaltura.com/p/'$PARTNER_ID'/thumbnail/entry_id/'$HR_BG_ENTRY_ID'/width/1397/height/1080"},
        {"textElementType": "userFullName", "fontSize": 30, "y": 440},
        {"textElementType": "certificationDate", "fontSize": 18, "x": 695, "y": 636}
      ]
    }
  }' | jq -r '.id')

# 5. Generate onboarding completion report for HR records
curl -s -X POST "$KALTURA_SCM_URL/report/generate" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{"reportType": "userCertificate", "gameObjectId": "'$ONBOARD_CERT'"}'
```

The 90-day window on the leaderboard defines the cohort period. Each onboarding module maps to a category, so rules can target specific modules. The badge requires both watching all sessions (`countUnique` on `entryId`) and passing the quiz (`count` on `quizSubmitted`), ensuring comprehensive completion before awarding the milestone.

> **See also:** [Categories & Entitlements](/docs/rich-media-cms-apis/content-management/categories-overview) for organizing onboarding modules by category, [User Management](/docs/rich-media-cms-apis/content-management/user-management-overview) for managing new hire user accounts and group assignments.

#### Customer Education Academy with Badges

Reduce support tickets by building a customer education portal with visible achievement badges. Each product area has a badge requiring both content consumption and quiz completion. Track customer progress and export completion data to CRM.

**Workflow:**
1. Organize courses by product area categories (Admin Console, API Development, Analytics)
2. Create badges per module with inline rules — `countUnique` on `entryId` for content completion, `count` on `quizSubmitted` for knowledge verification
3. Create a "Certified Administrator" certificate for customers completing all modules
4. Query `userBadge/list` for per-customer progress
5. Generate badge completion reports for CRM integration

```bash
# 1. Create "API Expert" badge with content + quiz requirements
curl -s -X POST "$KALTURA_SCM_URL/badge/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "API Expert",
    "description": "Complete all API training courses and pass the certification quiz",
    "virtualEventIds": ["$VIRTUAL_EVENT_ID"],
    "iconUrl": "https://example.com/badges/api-expert.png",
    "participationPolicy": {"userDefaultPolicy": "display", "policies": []},
    "rules": [
      {
        "name": "Complete API training courses",
        "conditions": [
          {"fact": "eventType", "operator": "equal", "value": "viewPeriod"},
          {"fact": "categories", "operator": "in", "value": "'$API_TRAINING_CATEGORY_ID'"}
        ],
        "type": "countUnique",
        "metric": "entryId",
        "groupBy": "kuserId",
        "goal": "8",
        "points": "1",
        "maxPoints": "1"
      },
      {
        "name": "Pass API certification quiz",
        "conditions": [
          {"fact": "eventType", "operator": "equal", "value": "quizSubmitted"},
          {"fact": "categories", "operator": "in", "value": "'$API_TRAINING_CATEGORY_ID'"}
        ],
        "type": "count",
        "metric": "kuserId",
        "groupBy": "kuserId",
        "goal": "1",
        "points": "1",
        "maxPoints": "1"
      }
    ]
  }'

# 2. Query per-customer badge progress
curl -s -X POST "$KALTURA_SCM_URL/userBadge/list" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectId": "'$BADGE_ID'",
    "pager": {"pageSize": 100, "pageIndex": 1}
  }'

# 3. Generate badge completion report for CRM export
curl -s -X POST "$KALTURA_SCM_URL/report/generate" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{"reportType": "userBadge", "gameObjectId": "'$BADGE_ID'"}'
```

Each badge requires both content consumption (`countUnique` ensures the customer watched distinct courses, not the same one repeatedly) and quiz completion (`count` on `quizSubmitted`). The `userBadge/list` response includes per-rule `rulesData` with `progress` and `completed` fields, so your application can render a progress bar per badge. Export the `userBadge` report to your CRM to trigger follow-up workflows when customers achieve certifications.

> **See also:** [Categories & Entitlements](/docs/rich-media-cms-apis/content-management/categories-overview) for product area category structure, [eSearch](/docs/rich-media-cms-apis/content-management/esearch-overview) for searching entries and users by badge-related metadata.

#### Team-Based Competitions

Run department-level or regional competitions alongside individual rankings. Sub-leaderboards automatically aggregate individual scores by a user profile property (company, country, department), so team standings update in real time without separate team-level scoring logic.

**Workflow:**
1. Create a leaderboard with `subLeaderboards` using `filterPaths` referencing user profile properties (e.g., `["company"]` for department grouping)
2. Individual scores auto-aggregate within their team segment
3. Create a team-level badge to recognize the top-performing department
4. Query sub-leaderboard rankings via `userScore/list` with `gameObjectType: "leaderboard"`

```bash
# 1. Create leaderboard with department-based sub-leaderboards
TEAM_LB=$(curl -s -X POST "$KALTURA_SCM_URL/leaderboard/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "objectType": "Leaderboard",
    "name": "Sales Kickoff Competition",
    "description": "Individual and department rankings for SKO 2025",
    "status": "scheduled",
    "startDate": "2025-06-01T09:00:00Z",
    "endDate": "2025-06-03T18:00:00Z",
    "participationPolicy": {
      "userDefaultPolicy": "display",
      "policies": [
        {"policy": "do_not_display", "matchCriteria": "byGroup", "values": ["executives"]}
      ]
    },
    "subLeaderboards": [
      {"name": "By Department", "filterPaths": ["company"], "id": 0}
    ],
    "virtualEventIds": ["$VIRTUAL_EVENT_ID"]
  }' | jq -r '.id')

# 2. Create "Top Department" team badge
curl -s -X POST "$KALTURA_SCM_URL/badge/create" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Top Department",
    "description": "Awarded to every member of the highest-scoring department",
    "virtualEventIds": ["$VIRTUAL_EVENT_ID"],
    "iconUrl": "https://example.com/badges/top-department.png",
    "participationPolicy": {"userDefaultPolicy": "display", "policies": []},
    "rules": [
      {
        "name": "Department total engagement",
        "conditions": [{"fact": "eventType", "operator": "equal", "value": "viewPeriod"}],
        "type": "sum",
        "metric": "playTime",
        "groupBy": "kuserId",
        "goal": "300",
        "points": "1",
        "maxPoints": "1"
      }
    ]
  }'

# 3. Query team standings (sub-leaderboard rankings)
curl -s -X POST "$KALTURA_SCM_URL/userScore/list" \
  -H "Authorization: Bearer $KALTURA_KS" \
  -H "Content-Type: application/json" \
  -d '{
    "gameObjectId": "'$TEAM_LB'",
    "gameObjectType": "leaderboard",
    "pager": {"pageSize": 50, "pageIndex": 1}
  }'
```

The `filterPaths: ["company"]` setting on the sub-leaderboard groups users by the `company` property from their [User Profile](/docs/events-collaboration/user-profile-api/user-profile-overview). Individual scores accumulate normally, and the sub-leaderboard aggregates them per team automatically. Use `do_not_display` for executives so they participate (their scores count toward the department total) but do not appear in individual rankings.

> **See also:** [User Profile](/docs/events-collaboration/user-profile-api/user-profile-overview) for user properties that power `filterPaths` grouping, [Events Platform](/docs/events-collaboration/events-platform-api/events-platform-overview) for virtual event scoping.
