Phoenix Gamification
Achievements

Achievement Types and Configuration

The Phoenix Gamification Achievement System supports three types of progress tracking conditions—Count, Sum, and Distinct—each designed for different use cases, plus event filtering and reward integration.

Achievement Types and Configuration

The Phoenix Gamification Achievement System supports three types of progress tracking conditions, each designed for different use cases.

Overview

Achievements track user progress toward a goal by processing events. Each achievement definition specifies:

  • Event Types: Which events count toward the achievement
  • Expression (optional): Filters events based on attributes
  • Progress Condition: How to measure progress (Count, Sum, or Distinct)
  • Target: The value needed to complete the achievement
  • Repeatable: Whether the achievement can be completed multiple times

Progress Condition Types

1. Count Condition

Tracks the number of matching events. Use this for achievements like "Play 100 games" or "Complete 50 quests".

Configuration:

{
  "event_types": ["game.completed"],
  "condition": {
    "type": "count",
    "min": 100
  },
  "target": 100.0
}

Example Achievement:

  • Name: "Play 100 Games"
  • Description: "Play 100 games to unlock this achievement"
  • Progress: Counts each game.completed event
  • Completion: When count reaches 100

Use Cases:

  • Track number of actions performed
  • Count completions of a specific activity
  • Simple milestone tracking

2. Sum Condition

Tracks the sum of a numeric field from matching events. Use this for achievements like "Spend $1000" or "Earn 10,000 points".

Configuration:

{
  "event_types": ["transaction.completed"],
  "expression": {
    "condition": {
      "field": "attrs.status",
      "operator": "eq",
      "value": "success"
    }
  },
  "condition": {
    "type": "sum",
    "field": "attrs.amount",
    "min": 1000.0
  },
  "target": 1000.0
}

Key Components:

  • field: Path to the numeric field to sum (e.g., "attrs.amount", "attrs.score")
  • min: The minimum sum required (must match target)
  • expression (optional): Filter events before summing (e.g., only count successful transactions)

Example Achievement:

  • Name: "Spend $1000"
  • Description: "Spend a total of $1000 to unlock this achievement"
  • Progress: Sums attrs.amount from transaction.completed events where attrs.status == "success"
  • Completion: When sum reaches $1000

Use Cases:

  • Track total spending
  • Accumulate points or currency
  • Sum scores or values over time

3. Distinct Condition

Tracks the number of unique values for a field. Use this for achievements like "Play 10 different game types" or "Visit 5 different locations".

Configuration:

{
  "event_types": ["game.completed"],
  "condition": {
    "type": "distinct",
    "field": "attrs.game_type",
    "min": 10
  },
  "target": 10.0
}

Key Components:

  • field: Path to the field containing distinct values (e.g., "attrs.game_type", "attrs.location")
  • min: The minimum number of unique values required (must match target)

Example Achievement:

  • Name: "Play 10 Game Types"
  • Description: "Play 10 different game types to unlock this achievement"
  • Progress: Tracks unique values of attrs.game_type from game.completed events
  • Completion: When 10 distinct game types have been played

Use Cases:

  • Track variety of activities
  • Encourage exploration
  • Collect unique items or experiences

Event Filtering with Expressions

You can filter which events count toward an achievement using expressions. Expressions support AND/OR logic and field comparisons.

Simple Expression

Filter events based on a single condition:

{
  "event_types": ["transaction.completed"],
  "expression": {
    "condition": {
      "field": "attrs.status",
      "operator": "eq",
      "value": "success"
    }
  },
  "condition": {
    "type": "sum",
    "field": "attrs.amount",
    "min": 1000.0
  },
  "target": 1000.0
}

This only counts successful transactions.

Complex Expression (AND)

Filter events that meet multiple conditions:

{
  "event_types": ["transaction.completed"],
  "expression": {
    "and": {
      "expressions": [
        {
          "condition": {
            "field": "attrs.status",
            "operator": "eq",
            "value": "success"
          }
        },
        {
          "condition": {
            "field": "attrs.amount",
            "operator": "gte",
            "value": 10
          }
        }
      ]
    }
  },
  "condition": {
    "type": "count",
    "min": 50
  },
  "target": 50.0
}

This counts transactions that are successful AND have amount >= $10.

Complex Expression (OR)

Filter events that meet any of multiple conditions:

{
  "event_types": ["game.completed"],
  "expression": {
    "or": {
      "expressions": [
        {
          "condition": {
            "field": "attrs.game_type",
            "operator": "eq",
            "value": "puzzle"
          }
        },
        {
          "condition": {
            "field": "attrs.game_type",
            "operator": "eq",
            "value": "strategy"
          }
        }
      ]
    }
  },
  "condition": {
    "type": "count",
    "min": 20
  },
  "target": 20.0
}

This counts games that are either puzzle OR strategy type.

Supported Operators:

  • eq: Equal to
  • ne: Not equal to
  • gt: Greater than
  • gte: Greater than or equal to
  • lt: Less than
  • lte: Less than or equal to
  • in: Value is in array
  • contains: Field contains value (for strings/arrays)

Repeatable Achievements

By default, achievements can only be completed once. Set repeatable: true to allow multiple completions.

Configuration:

{
  "event_types": ["game.completed"],
  "condition": {
    "type": "count",
    "min": 10
  },
  "target": 10.0,
  "repeatable": true
}

Behavior:

  • When completed, progress resets to 0
  • completion_count increments
  • Achievement can be completed again
  • Useful for daily/weekly challenges

Reward Integration (Rewards v2)

Achievements can grant rewards from the rewards catalog upon completion. When an achievement is completed, the reward is automatically granted through the rewards service, which handles fulfillment (wallet currency, badges, manual fulfillment, or API webhooks).

Configuration:

{
  "event_types": ["game.completed"],
  "condition": {
    "type": "count",
    "min": 100
  },
  "target": 100.0,
  "repeatable": false,
  "reward_id": "uuid-of-reward-from-catalog"
}

How it works:

  1. Achievement completes → AchievementEventV1::Completed event published to NATS
  2. Rewards service consumer listens for completion events
  3. Reward order created automatically via reward_service.grant()
  4. Fulfillment executed based on reward type:
    • Wallet currency: Added to user's wallet balance
    • Wallet badge: Badge granted to user
    • Manual: Order status set to pending_claim for admin fulfillment
    • API: Webhook called to external service

Example: Currency Reward

{
  "name": "Play 100 Games",
  "config": {
    "event_types": ["game.completed"],
    "condition": { "type": "count", "min": 100 },
    "target": 100.0,
    "reward_id": "aad70d24-5cfa-44e5-9a2b-614f60fbd108"  // Reward from catalog
  }
}

The reward must exist in the rewards catalog before creating the achievement. The reward_id is validated when creating/updating achievement definitions.


Configuration Validation Rules

Required Fields

  • event_types: Must have at least one event type (supports wildcards like "game.*")
  • condition: Must specify one of: count, sum, or distinct
  • target: Must be positive and match the condition's min value

Condition-Specific Rules

Count:

  • min must be a positive integer
  • target must equal min

Sum:

  • field cannot be empty
  • min must be positive
  • target must equal min (within 0.001 tolerance)

Distinct:

  • field cannot be empty
  • min must be a positive integer
  • target must equal min

Expression Rules

  • Field paths must be valid (e.g., "attrs.field_name")
  • Operators must be supported
  • Values must match the field type

Complete Configuration Examples

Example 1: Simple Count Achievement

{
  "name": "Play 100 Games",
  "description": "Play 100 games to unlock this achievement",
  "config": {
    "event_types": ["game.completed"],
    "condition": {
      "type": "count",
      "min": 100
    },
    "target": 100.0,
    "repeatable": false
  }
}

Example 2: Sum Achievement with Filtering

{
  "name": "Spend $1000",
  "description": "Spend a total of $1000 on successful transactions",
  "config": {
    "event_types": ["transaction.completed"],
    "expression": {
      "condition": {
        "field": "attrs.status",
        "operator": "eq",
        "value": "success"
      }
    },
    "condition": {
      "type": "sum",
      "field": "attrs.amount",
      "min": 1000.0
    },
    "target": 1000.0,
    "repeatable": false
  }
}

Example 3: Distinct Achievement

{
  "name": "Play 10 Game Types",
  "description": "Play 10 different game types",
  "config": {
    "event_types": ["game.completed"],
    "condition": {
      "type": "distinct",
      "field": "attrs.game_type",
      "min": 10
    },
    "target": 10.0,
    "repeatable": false
  }
}

Example 4: Repeatable Achievement with Reward

{
  "name": "Daily 10 Games",
  "description": "Play 10 games each day (repeatable)",
  "config": {
    "event_types": ["game.completed"],
    "condition": {
      "type": "count",
      "min": 10
    },
    "target": 10.0,
    "repeatable": true,
    "reward_id": "uuid-of-reward-from-catalog"
  }
}

Note: Each time a repeatable achievement completes, the reward is granted again. This allows daily/weekly rewards that reset automatically.


Progress Tracking

The system tracks progress in a Progress object:

{
  "count": 75,           // For Count condition
  "sum": 750.0,          // For Sum condition
  "distinct": ["type1", "type2", "type3"]  // For Distinct condition
}

Only the relevant field is used based on the condition type.


API Usage

Create Achievement Definition

POST /v1/tenants/{tenant_id}/achievements
X-API-Key: {api_key}
Content-Type: application/json

{
  "name": "Play 100 Games",
  "description": "Play 100 games to unlock this achievement",
  "config": {
    "event_types": ["game.completed"],
    "condition": {
      "type": "count",
      "min": 100
    },
    "target": 100.0,
    "repeatable": false
  }
}

Query User Progress (Query Gateway)

GET /v1/achievements/user/{user_id}
Authorization: Bearer <JWT>

Optional: GET /v1/achievements/user/{user_id}/{definition_id} for a single achievement. Tenant and user are derived from the JWT; paths do not include tenant_id. Response includes current progress, target, completion status, and completion percentage.


Best Practices

  1. Choose the Right Condition Type

    • Use count for simple counting
    • Use sum for accumulating values
    • Use distinct for variety/collection achievements
  2. Use Expressions Wisely

    • Filter out invalid events (e.g., failed transactions)
    • Combine conditions with AND/OR for complex logic
    • Keep expressions simple for performance
  3. Set Appropriate Targets

    • Balance challenge with achievability
    • Consider user engagement patterns
    • Test with real event data
  4. Consider Repeatability

    • Use repeatable: true for daily/weekly challenges
    • Use repeatable: false for one-time achievements
    • Track completion_count for analytics
  5. Integrate Rewards

    • Create rewards in the rewards catalog first
    • Link achievements to rewards using reward_id
    • Provide meaningful incentives
    • Balance reward value with achievement difficulty
    • For repeatable achievements, rewards are granted on each completion

On this page