> ## 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 An XP Feature

> Learn how to use Trophy to add an XP feature to your web or mobile app.

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

For illustration purposes we'll use the example of a study platform that uses XP to reward users for taking different interactions.

<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 any number of metrics that best represents the interactions you want to reward XP 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 [points page](https://app.trophy.so/points) and create a new points system called 'XP'.

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

Once created, you'll be taken to the configure page for the XP system where you can create 'triggers' for each of the ways you want to reward users with XP.

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

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 if the event is against a metric that is configured as part of any XP triggers.

If so Trophy will award the user with the appropriate amount of XP according to the trigger configuration.

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

<Tip>
  XP can also be awarded to users based on achievements, streaks and other
  triggers. See the dedicated [points docs](/features/points#points-triggers)
  for more information.
</Tip>

<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>

The response to this API call is the complete set of changes to any features you've built with Trophy, including any XP that was awarded to the user as a result of the event, and from what triggers it was awarded.

<Note>
  If you use [Points Levels](/features/points#points-levels), the `xp` object can include a **`level`** field **only when** the user's tier changed on this request; it may be omitted when their level stayed the same. See the [metric change API reference](/api-reference/endpoints/metrics/send-a-metric-change-event) for the full response schema.
</Note>

```json Response [expandable] theme={null}
{
  "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623",
  "eventId": "0040fe51-6bce-4b44-b0ad-bddc4e123534",
  "total": 900,
  ...,
  "points": {
    "xp": {
      "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534",
      "key": "xp",
      "name": "XP",
      "description": null,
      "badgeUrl": null,
      "maxPoints": null,
      "total": 450,
      "added": 50,
      "awards": [
        {
          "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534",
          "awarded": 50,
          "date": "2021-01-01T00:00:00Z",
          "total": 450,
          "trigger": {
            "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534",
            "type": "metric",
            "metricName": "Flashcards Flipped",
            "metricThreshold": 100,
            "points": 50
          }
        }
      ]
    }
  },
  ...
}
```

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

<h2 id="displaying-xp">
  Displaying XP
</h2>

You have a number of options for displaying XP 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 users pop-up notifications when users are awarded new XP.

Here's an example of this in action:

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

if (!response) {
  return;
}

const xp = response.points.xp;

// Show toast if user was awarded XP
if (xp.awards.length > 0) {
  const trigger = xp.awards[0].trigger;

  toast({
    title: `You gained ${xp.added} XP`,

    // e.g. "+20 XP for 10 flashcards flipped"
    description: `+${trigger.points} XP for ${
      trigger.metricThreshold
    } ${trigger.metricName.toLowercase()}`,
  });
}
```

<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-xp">
  Displaying User XP
</h3>

To fetch a users XP use the [user points API](/api-reference/endpoints/users/get-a-users-points).

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

  ```typescript Node theme={null}
  trophy.users.points("user-id", "points-system-key");
  ```

  ```python Python theme={null}
  client.users.points(
      id="user-id",
      key="points-system-key",
  )
  ```

  ```php PHP theme={null}
  $trophy->users->points("user-id", "points-system-key");
  ```

  ```java Java theme={null}
  client.users().points("user-id","points-system-key");
  ```

  ```go Go theme={null}
  response, err := client.Users.Points(
      "user-id",
      "points-system-key"
  )
  ```

  ```csharp C# theme={null}
  trophy.Users.PointsAsync("user-id", "points-system-key");
  ```

  ```ruby Ruby theme={null}
  result = client.users.points(
    :id => 'user-id',
    :key => "points-system-key"
  )
  ```
</CodeGroup>

This API returns data on the user's total XP but can be configured to also return between 1 and 100 of the user's most recent XP awards by using the `awards` query parameter.

```json Response [expandable] theme={null}
{
  "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534",
  "key": "xp",
  "name": "XP",
  "description": null,
  "badgeUrl": null,
  "maxPoints": null,
  "total": 100,
  "level": {
    "id": "1140fe51-6bce-4b44-b0ad-bddc4e123534",
    "key": "silver",
    "name": "Silver",
    "description": "Mid-tier level",
    "badgeUrl": null,
    "points": 50
  },
  "awards": [
    {
      "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534",
      "awarded": 10,
      "date": "2021-01-01T00:00:00Z",
      "total": 100,
      "trigger": {
        "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534",
        "type": "metric",
        "points": 10,
        "metricName": "Flashcards Flipped",
        "metricThreshold": 1000
      }
    }
  ]
}
```

Here's an example of a UI that shows users a list of their most recent awards based on the data returned from the user points API.

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

<h3 id="user-xp-chart">
  User XP Chart
</h3>

The [user points summary API](/api-reference/endpoints/users/get-a-users-points-summary) returns chart-ready historical data for displaying how a user's XP has changed over time.

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

  ```typescript Node theme={null}
  trophy.users.pointsEventSummary("user-id", "points-system-key", {
    aggregation: "daily",
    start_date: "2025-01-01",
    end_date: "2025-01-07"
  });
  ```

  ```python Python theme={null}
  client.users.points_event_summary(
    id="user-id",
    key="points-system-key",
    aggregation="daily",
    start_date="2025-01-01",
    end_date="2025-01-07"
  )
  ```

  ```php PHP theme={null}
  $trophy->users->pointsEventSummary("user-id", "points-system-key");
  ```

  ```java Java theme={null}
  client.users().pointsEventSummary("user-id","points-system-key");
  ```

  ```go Go theme={null}
  response, err := client.Users.PointsEventSummary(
      "user-id",
      "points-system-key"
  )
  ```

  ```csharp C# theme={null}
  trophy.Users.PointsEventSummaryAsync("user-id", "points-system-key");
  ```

  ```ruby Ruby theme={null}
  result = client.users.pointsEventSummary(
    :id => 'user-id',
    :key => "points-system-key"
  )
  ```
</CodeGroup>

Use the `aggregation`, `start_date` and `end_date` query parameters to control the data returned. Here's an example of XP data aggregated daily:

```json GET /users/{id}/points/{key}/event-summary — 200 response [expandable] theme={null}
[
  {
    "date": "2024-01-01",
    "total": 100,
    "change": 100
  },
  {
    "date": "2024-01-02",
    "total": 300,
    "change": 200
  },
  {
    "date": "2024-01-03",
    "total": 600,
    "change": 300
  }
]
```

And here's an example of the types of charts you can build with this data:

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

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

In Trophy your [xp system page](https://app.trophy.so/points), includes analytics charts that shows data on total XP earned and a breakdown of exactly what triggers award the most XP.

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

<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!
