# Team Achievements

**URL:** https://heroiclabs.com/docs/hiro/concepts/teams/achievements/
**Keywords:** achievements, team-achievements, teams
**Categories:** hiro, achievements, teams

---


# Team Achievements

## Overview

Team Achievements let teams work together toward shared goals and unlock rewards through group effort. Teams can tackle everything from simple one-time challenges to complex achievement chains that build on each other, plus seasonal content and recurring goals that reset automatically to keep players engaged long-term.

## Features

- **Flexible goal structures**: Create simple one-time achievements or complex multi-part challenges with sub-achievements and dependencies
- **Automated scheduling**: Set up recurring achievements with CRON expressions for daily, weekly, or seasonal content
- **Reward control options**: Choose between automatic reward distribution or manual admin approval for strategic resource management
- **Progress tracking**: Monitor team advancement with cumulative progress that persists across sessions

## Permissions

**Viewing Achievements**

- **All Members** can view achievement progress and status for their teams
- Players can only see achievements for teams where they are active members

**Managing Achievements**

- **All Members** can contribute progress to team achievements through gameplay actions
- **Admins Only** can manually claim completed achievements to receive rewards
- Individual contributions aggregate automatically into team-wide progress

<!-- For complete API documentation, see the [Team Achievements Reference Guide](#). -->

## Key Terms

**Sub-Achievements and Parent Goals**

Team achievements can contain nested sub-achievements, each with their own rewards and completion criteria. This creates layered progression where teams work toward multiple smaller goals that contribute to larger objectives.

**Individual vs Total Rewards**

- **Individual Rewards**: Granted when specific achievements or sub-achievements complete
- **Total Rewards**: Special bonuses unlocked only when all sub-achievements within a parent achievement are completed

**Auto-Claim vs Manual Claim**

- **Auto-Claim**: Rewards distribute automatically when achievements complete, ideal for routine progression
- **Manual Claim**: Requires explicit admin action, giving team leaders control over valuable or strategic resources

## Achievement Preconditions

Achievements can specify prerequisite achievements i.e., preconditions, that must be claimed before progress can continue. This enables multi-layered achievement trees and guided progression systems.

{{< code type="server" >}}

```json
{
  "advanced_combat": {
    "precondition_ids": ["basic_training", "first_victory"],
    "name": "Advanced Combat Training"
  }
}
```

{{< / code >}}

**Use preconditions for:**

- Tutorials that teach features step-by-step
- Gated content that unlocks advanced challenges after mastering basics
- Seasonal progression that builds complexity over time
- Skill-based unlockable trees that require players to prove their mettle

## Building with Team Achievements

**Publisher Events**

Achievement completions and progress updates generate detailed events for analytics, external integrations, and custom game logic triggers.

For example, say PvP wins are being tracked as a Team Stat. Here's how you might reward Teams for reaching certain stat thresholds and log a new achievement:

```go
func (p *TeamAchievementPublisher) Send(ctx context.Context, logger runtime.Logger, nk runtime.NakamaModule, userID string, events []*hiro.PublisherEvent) {
    for _, event := range events {
        if event.Name == "team_stat_updated" {
            teamID := event.Metadata["team_id"]
            statName := event.Metadata["stat_name"]
            newValue, _ := strconv.ParseInt(event.Metadata["new_value"], 10, 64)

            if statName == "pvp_wins" && newValue >= 10 {
                // Trigger achievement update server-side
                p.teamsSystem.UpdateAchievements(ctx, logger, nk, userID, teamID, map[string]int64{
                    "pvp_mastery": 1,
                })
            }
        }
    }
}
```

From the client, you simply update Team Stats as normal:

{{< code type="client C#" >}}

```csharp
await teamsSystem.UpdateStatsAsync(
    null, // private stats
    new List<UpdateStat> {
        new UpdateStat {
            Name = "pvp_wins",
            Value = 1,
            Operator = StatOperator.Increment
        }
    }
);
```

{{< / code >}}

Learn more in the [Publishers](../../publishers/) guide.

### Example: Monthly Team Challenges

Create engaging recurring content that keeps teams active throughout each month. The following example demonstrates sub-achievements, reset schedules, and progression checkpoints:

```csharp
public async Task CheckMonthlyProgress(string teamId)
{
    var achievementList = await teamsSystem.GetAchievementsAsync();
    var monthlyChallenge = achievementList.Achievements
        .FirstOrDefault(kvp => kvp.Key == "monthly_challenge");

    if (monthlyChallenge.Value != null && monthlyChallenge.Value.ClaimTimeSec > 0)
    {
        // Check if all sub-achievements are complete for bonus rewards
        var allSubsComplete = monthlyChallenge.Value.SubAchievements
            .All(sub => sub.Value.ClaimTimeSec > 0);

        if (allSubsComplete && monthlyChallenge.Value.TotalClaimTimeSec == 0)
        {
            // Claim total reward for completing everything
            await teamsSystem.ClaimAchievementsAsync(new[] { "monthly_challenge" });

            // Unlock next tier of challenges
            await UnlockEliteChallenges(teamId);
        }
    }
}

private async Task UpdateChallengeProgress(string teamId, string challengeType)
{
    var challengeIds = GetChallengesByType(challengeType);
    await teamsSystem.UpdateAchievementsAsync(challengeIds, 1);
}
```

**Monthly Challenge Structure**

- **Parent Achievement**: "Monthly Challenge" with total reward for completing everything
- **Sub-Achievements**: "Win 10 PvP battles," "Complete 5 dungeons," "Collect 1000 gold"
- **Reset Schedule**: Automatic monthly reset using CRON expressions
- **Progression Gates**: Completion unlocks "Elite Challenges" for advanced teams

This creates urgency (monthly targets) while providing multiple paths to success and escalating difficulty for engaged teams.

## What Ifs

_This section describes edge cases and scenarios that may be unintuitive to the developer._

**Negative values can be submitted for achievements**

While achievement progress is typically cumulative, the system does accept negative progress values for scenarios where progress might need to be reduced. However, there are important safeguards in place:

- Floor protection: Achievement counts cannot go below 0 - negative submissions that would result in negative progress are floored at zero
- Ceiling protection: Progress cannot exceed the achievement's maximum count (MaxCount)

Once progress is made toward an achievement, it persists until the achievement resets according to its schedule or expires, unless explicitly reduced through negative value updates.

**A precondition achievement is removed from config**

When a precondition achievement is removed from your configuration, any achievements that depend on it become inaccessible. Teams cannot make progress on dependent achievements until you either restore the missing precondition or update the dependency requirements.

**Parent and sub-achievements have different reset schedules**

If a parent achievement resets but its sub-achievements have different reset schedules, the sub-achievements maintain their individual progress. This can create scenarios where sub-achievements are partially complete when the parent resets, allowing teams to finish the parent achievement more quickly in the next cycle.

**A middle achievement in a precondition chain is removed**

When a middle achievement in a precondition chain is removed, all achievements that depend on it become unreachable. The system doesn't automatically reroute dependencies, so you'll need to manually update precondition requirements or restore the missing achievement.

**Teams want to claim the same achievement multiple times**

Teams can claim the same achievement multiple times if it has a reset schedule. Each reset creates a new opportunity to complete and claim the achievement, making recurring achievements valuable for ongoing team engagement.

**Achievements expire versus reset**

When achievements expire, any accumulated progress is lost permanently. When achievements reset according to their schedule, progress returns to zero but the achievement remains available for a new completion cycle. Teams lose progress in both cases, but resets indicate new opportunities while expiration indicates time limits were missed.

**Auto-claim fails when an achievement completes**

If auto-claim fails due to reward system errors, the achievement remains in a completed but unclaimed state. The system doesn't automatically retry failed auto-claims, so teams may need to manually claim the achievement or wait for server-side intervention to resolve the issue.

## Configuring Teams Achievements

**Achievements Config**

| Property       | Type                     | Definition                                       |
| :
------------- | :----------------------- | :----------------------------------------------- |
| `achievements` | `map[string]Achievement` | Individual achievements mapped by achievement ID |

**Individual Achievement Config**

Each achievement is defined by a unique ID (the key) and its configuration properties:

| Property           | Type                        | Definition                                       |
| :----------------- | :-------------------------- | :----------------------------------------------- |
| `name`             | `string`                    | Display name for the achievement                 |
| `description`      | `string`                    | Description of the achievement goals             |
| `count`            | `int`                       | Target progress value to complete                |
| `auto_claim`       | `bool`                      | Whether rewards auto-distribute on completion    |
| `reset_cronexpr`   | `string`                    | CRON expression for automatic resets             |
| `precondition_ids` | `[]string`                  | Required achievements that must be claimed first |
| `reward`           | `Reward`                    | Individual completion reward                     |
| `total_reward`     | `Reward`                    | Bonus reward for completing all sub-achievements |
| `sub_achievements` | `map[string]SubAchievement` | Nested achievements within this achievement      |

### Example: Team Achievements JSON

The JSON schema defines an `achievements` object which contains individual achievement objects for each achievement you wish to define in the system. You can configure as few or as many achievements as needed for your game.

```json
{
  // ... other Team configs
  "achievements": {
    "monthly_challenge": {
      "name": "Monthly Challenge",
      "description": "Complete all monthly objectives",
      "count": 3,
      "auto_claim": false,
      "reset_cronexpr": "0 0 1 * *",
      "reward": {
        "guaranteed": {
          "currencies": { "team_tokens": { "min": 1000 } }
        }
      },
      "total_reward": {
        "guaranteed": {
          "currencies": { "premium_tokens": { "min": 500 } },
          "items": { "exclusive_banner": { "min": 1 } }
        }
      },
      "sub_achievements": {
        "pvp_wins": {
          "name": "PvP Victory",
          "description": "Win 10 PvP battles",
          "count": 10,
          "auto_claim": true,
          "reward": {
            "guaranteed": {
              "currencies": { "battle_points": { "min": 100 } }
            }
          }
        }
      }
    }
  }
}
```
