> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trophy.so/llms.txt
> Use this file to discover all available pages before exploring further.

# How To Build A Streaks Feature

> Learn how to use Trophy to add a streaks feature to your web or mobile app.

The guide outlines the full process of adding a streaks feature to your web or mobile app using Trophy.

For illustration purposes we'll use the example of a study platform that uses a daily streak to incentivize and reward users for viewing flashcards.

<Tip>
  To see a fully working example of this in practice, check out the [live
  demo](https://examples.trophy.so) or [github
  repo](https://github.com/trophyso/example-study-platform/tree/demo).
</Tip>

<h2 id="pre-requisites">
  Pre-requisites
</h2>

* A [Trophy](https://app.trophy.so/sign-up) account
* About 10 minutes

<h2 id="trophy-setup">
  Trophy Setup
</h2>

In Trophy, [Metrics](/features/metrics) are the building blocks of gamification and model the different interactions users make with your product.

In this guide the interaction we're interested in is `flashcards-viewed`, but you can create a metric that best represents the interaction you want to drive streaks from.

In the Trophy dashboard, head to the [metrics page](https://app.trophy.so/metrics) and create a metric.

<Frame>
  <video autoPlay muted loop playsInline className="w-full aspect-video" src="https://mintcdn.com/trophy/2khPIyZFxPyE7xgA/assets/guides/achievements-feature/create_new_metric.mp4?fit=max&auto=format&n=2khPIyZFxPyE7xgA&q=85&s=8e66d7276d9648d449c4556340febf45" data-path="assets/guides/achievements-feature/create_new_metric.mp4" />
</Frame>

Once you've created your metric, head to the [streaks page](https://app.trophy.so/streaks/configure) and select the streak frequency you want (daily, weekly or monthly).

Then add your metric to the streak conditions and set the threshold users must meet (e.g. at least 1 to extend their streak each period).

You can also add multiple metrics and choose whether users must meet all of them or just one—see the [streak conditions docs](/features/streaks#streak-conditions) for details.

In Trophy you track user interactions by sending [Events](/features/events) from your code to Trophy APIs against a specific metric.

When events are recorded for a specific user, Trophy will automatically check whether they've met their streak conditions for the current period and update the user's streak accordingly.

This is what makes building gamified experiences with Trophy so easy, it does all the work for you behind the scenes.

<h2 id="installing-trophy-sdk">
  Installing Trophy SDK
</h2>

To interact with Trophy from your code you'll use the Trophy SDK available in most major [programming languages](/api-reference/client-libraries).

Install the Trophy SDK:

<CodeGroup>
  ```bash Node theme={null}
  npm install @trophyso/node
  ```

  ```bash Ruby theme={null}
  gem install trophy_api_client
  ```

  ```bash Python theme={null}
  pip install trophy
  ```

  ```bash PHP theme={null}
  composer require trophyso/php
  ```

  ```bash Java (Gradle) theme={null}
  implementation 'so.trophy:trophy-java:1.0.0'
  ```

  ```bash Java (Maven) theme={null}
  <dependency>
    <groupId>so.trophy</groupId>
    <artifactId>trophy-java</artifactId>
    <version>1.0.0</version>
  </dependency>
  ```

  ```bash Go theme={null}
  go get github.com/trophy-so/trophy-go
  ```

  ```bash .NET (C#) theme={null}
  // .NET Core CLI
  dotnet add package Trophy

  // Nuget Package Manager
  nuget install Trophy

  // Visual Studio
  Install-Package Trophy
  ```
</CodeGroup>

Next, grab your API key from the Trophy [integration page](https://app.trophy.so/integration) and add this as a **server-side only** environment variable.

```bash theme={null}
TROPHY_API_KEY='*******'
```

<Warning>
  Make sure you **don't** expose your API key in client-side code.
</Warning>

<h2 id="tracking-user-interactions">
  Tracking User Interactions
</h2>

To track an event (user interaction) against your metric, use the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event).

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://app.trophy.so/api/metrics/flashcards-flipped/event \
       -H "X-API-KEY: <apiKey>" \
       -H "Content-Type: application/json" \
       -d '{
    "user": {
      "id": "18",
      "email": "user@example.com",
      "tz": "Europe/London"
    },
    "value": 750
  }'
  ```

  ```typescript Node theme={null}
  trophy.metrics.event("flashcards-flipped", {
    user: {
      id: "18",
      email: "user@example.com",
      tz: "Europe/London",
    },
    value: 750,
  });
  ```

  ```python Python theme={null}
  client.metrics.event(
      key="flashcards-flipped",
      user=EventRequestUser(
          id="18",
          email="user@example.com",
          tz="Europe/London",
      ),
      value=750.0,
  )
  ```

  ```php PHP theme={null}
  $user = new EventRequestUser([
      'id' => '18',
      'email' => 'user@example.com'
  ]);

  $request = new MetricsEventRequest([
      'user' => $user,
      'value' => 750
  ]);

  $trophy->metrics->event("flashcards-flipped", $request);
  ```

  ```java Java theme={null}
  MetricsEventRequest request = MetricsEventRequest.builder()
        .user(
          EventRequestUser.builder()
            .id("18")
            .email("user@example.com")
            .build()
        )
        .value(750)
        .build();

  EventResponse response = client.metrics().event("flashcards-flipped", request);
  ```

  ```go Go theme={null}
  response, err := client.Metrics.Event(
      "flashcards-flipped",
      &api.MetricsEventRequest{
          User: &api.EventRequestUser{
              Id: "18",
              Email: "user@example.com",
          },
          Value: 750,
      },
  )
  ```

  ```csharp C# theme={null}
  var user = new EventRequestUser {
     Id = "18",
     Email = "user@example.com"
  };

  var request = new MetricsEventRequest {
     User = user,
     Value = 750
  };

  await trophy.Metrics.EventAsync("flashcards-flipped", request);
  ```

  ```ruby Ruby theme={null}
  result = client.metrics.event(
    :key => 'flashcards-flipped',
    :user => {
      :id => '18',
      :email => 'user@example.com'
    },
    :value => 750
  )
  ```
</CodeGroup>

<Tip>
  By including the user's timezone in the `tz` attribute, Trophy will
  automatically track streaks according to the user's local clock and handle
  awkward edge cases where users change time zones.
</Tip>

The response to this API call is the complete set of changes to any features you've built with Trophy, including the latest data on the users streak.

```json Response [expandable] theme={null}
{
  "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623",
  "eventId": "0040fe51-6bce-4b44-b0ad-bddc4e123534",
  "total": 750,
  ...,
  "currentStreak": {
    "length": 1,
    "frequency": "daily",
    "started": "2025-04-02",
    "periodStart": "2025-03-31",
    "periodEnd": "2025-04-05",
    "expires": "2025-04-12"
  },
  ...
}
```

Validate this is working by checking the Trophy [dashboard](https://app.trophy.so).

<h2 id="displaying-streaks">
  Displaying Streaks
</h2>

You have a number of options for displaying streaks in your application. Here we'll look at the most common options.

<h3 id="pop-up-notifications">
  Pop-up Notifications
</h3>

We can use the response of the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event) to show pop-up notifications (or 'toasts') when user extends their streak.

Here's an example of this in action:

```ts Streak Extended Pop-up theme={null}
// Sends event to Trophy
const response = await viewFlashcard();

if (!response) {
  return;
}

// Show toast if user has extended their streak
if (response.currentStreak?.extended) {
  toast({
    title: "You're on a roll!",
    description: `Keep going to keep your ${response.currentStreak.length} day streak!`,
  });
}
```

<Frame>
  <video autoPlay muted loop playsInline className="w-full aspect-video" src="https://mintcdn.com/trophy/2khPIyZFxPyE7xgA/assets/guides/streaks-feature/streak-toasts.mp4?fit=max&auto=format&n=2khPIyZFxPyE7xgA&q=85&s=fdabff0b481acd4f4df657b1b61d1734" data-path="assets/guides/streaks-feature/streak-toasts.mp4" />
</Frame>

<Tip>
  If you want to play sound effects, use the [HTML5 Audio
  API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) and feel
  free to steal these [audio
  files](https://github.com/trophyso/example-study-platform/tree/demo/public/sounds)
  we recommend.
</Tip>

<h3 id="displaying-user-streaks">
  Displaying User Streaks
</h3>

To fetch data on a user's streak use the [user streak API](/api-reference/endpoints/users/get-a-users-streak).

<CodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.trophy.so/v1/users/{id}/streak \
    --header 'X-API-KEY: <api-key>'
  ```

  ```typescript Node theme={null}
  trophy.users.streak("user-id", {
    historyPeriods: 14
  });
  ```

  ```python Python theme={null}
  client.users.streak(
      id="user-id",
      history_periods=14,
  )
  ```

  ```php PHP theme={null}
  $request = new UsersStreakRequest([
      'history_periods' => 14
  ]);

  $trophy->users->streak("user-id", $request);
  ```

  ```java Java theme={null}
  UsersStreakRequest request = UsersStreakRequest.builder()
        .historyPeriods(14)
        .build();

  UsersStreakResponse response = client.users().streak("user-id", request);
  ```

  ```go Go theme={null}
  response, err := client.Users.Streak(
      "user-id",
      &api.UsersStreakRequest{
          HistoryPeriods: 14,
      },
  )
  ```

  ```csharp C# theme={null}
  var request = new UsersStreakRequest {
     HistoryPeriods = 14
  };

  await trophy.Users.StreaksAsync("user-id", request);
  ```

  ```ruby Ruby theme={null}
  result = client.users.streak(
    :id => 'user-id',
    :history_periods => 14
  )
  ```
</CodeGroup>

This API not only returns data on the user's current streak like `length` and `expires`, but it can also return historical streak data which can be used to display any kind of streak UI you like through the `historyPeriods` parameter.

```json Response [expandable] theme={null}
{
  "length": 1,
  "frequency": "weekly",
  "started": "2025-04-02",
  "periodStart": "2025-03-31",
  "periodEnd": "2025-04-05",
  "expires": "2025-04-12",
  "streakHistory": [
    {
      "periodStart": "2025-03-30",
      "periodEnd": "2025-04-05",
      "length": 1
    },
    {
      "periodStart": "2025-04-06",
      "periodEnd": "2025-04-12",
      "length": 2
    },
    {
      "periodStart": "2025-04-13",
      "periodEnd": "2025-04-19",
      "length": 3
    },
    {
      "periodStart": "2025-04-20",
      "periodEnd": "2025-04-26",
      "length": 0
    },
    {
      "periodStart": "2025-04-27",
      "periodEnd": "2025-05-03",
      "length": 1
    },
    {
      "periodStart": "2025-05-04",
      "periodEnd": "2025-05-10",
      "length": 2
    },
    {
      "periodStart": "2025-05-11",
      "periodEnd": "2025-05-17",
      "length": 3
    }
  ]
}
```

Here's an example of a git-style streak calendar built using the data in the response above:

<Frame>
  <img height="200" width="100%" noZoom src="https://mintcdn.com/trophy/2khPIyZFxPyE7xgA/assets/guides/streaks-feature/streak-calendar.png?fit=max&auto=format&n=2khPIyZFxPyE7xgA&q=85&s=2e66434c47a2167f1744a4ea68396d61" data-path="assets/guides/streaks-feature/streak-calendar.png" />
</Frame>

<Tip>
  Check this [example
  repo](https://github.com/trophyso/example-study-platform/blob/demo/src/app/user-center/study-center/default-view.tsx)
  for a React component that drives this UI using data from Trophy.
</Tip>

<h2 id="analytics">
  Analytics
</h2>

The [streaks page](https://app.trophy.so/streaks) in Trophy shows data on active streaks, the average length of streaks and longest streaks.

<Frame>
  <img height="200" width="100%" noZoom src="https://mintcdn.com/trophy/2khPIyZFxPyE7xgA/assets/guides/streaks-feature/analytics.png?fit=max&auto=format&n=2khPIyZFxPyE7xgA&q=85&s=af29f93108d5e8a699cdbee0dce8257b" data-path="assets/guides/streaks-feature/analytics.png" />
</Frame>

<h2 id="streak-freezes">
  Streak Freezes
</h2>

Trophy also supports streak freezes which can help prevent users from losing their streak if they accidentally miss a period.

Learn more about streak freezes in the dedicated [streak freezes docs](/features/streaks#streak-freezes).

<h2 id="get-support">
  Get Support
</h2>

Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help!
