# Search user gamification logs

List one trader's XP and coin changes in your community, newest first, with optional date, time and action filters.

- Endpoint: `POST https://api.returning.ai/v1/gamifications/logs`
- Section: Gamification / General
- Authentication: `Authorization: Bearer <API_KEY>` (Community API key)
- Permission: `getUserStats` (Shown in the dashboard as "Get User Stats")
- Retries: Read-only; exact retries are safe
- Guide: hand-written
- Verified: live, 2026-09-27
- Last updated: 26 Sep 2026
- Web page: https://docs.returning.ai/api-reference/gamification/search-user-gamification-logs

## When to use this

- A trader asks why their coins went down, and support needs every change with its reason.
- Explain a day's total from Get daily user XP and coin changes, one entry at a time.
- Copy a trader's XP and coin history into your CRM.

**Instead:** Use [Get daily user XP and coin changes](https://docs.returning.ai/api-reference/gamification/get-daily-user-xp-and-coin-changes.md) instead to get one day's net totals for up to 100 traders at once.

## Authentication

- Header: `Authorization: Bearer <API_KEY>`
- Permission: `getUserStats`

Use a Community API key with `getUserStats`, shown in the dashboard as Get User Stats, and keep it on your server. The key decides the community: `user` only matches traders in it, and you only see changes made in it.

## Behaviour

This read changes nothing. Changes to a trader's XP and coins add entries here, including corrections made through Update User XP and Currency. An XP and coin change made in the same request can share one entry, with both `xp` and `currency` set.

Filters combine, and every date and time is UTC. `date` bounds take whole days. `time` bounds compare the time of day on every date, so `gte` `22:00` with `lte` `02:00` matches nothing. Other keys inside `date` or `time` are ignored.

A trader's history stays readable after they are deleted with Manage User Account.

New entries push older ones down while you page. To copy history, page through one finished day at a time and use `_id` to skip entries you already have.

## Request

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Authorization` | `string` | Yes | Community API key with `getUserStats`. (`Bearer <API_KEY>`) |
| `Content-Type` | `string` | Yes | Request body format. (`application/json`) |

### Body

Send `user`, plus any filters. `page` and `limit` must be JSON numbers; strings such as `"1"` are rejected. A `date` value that isn't a date returns `500`, so check dates before you send them.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `user` | `string` | Yes | The trader's email or platform user ID. Only traders in your community match, and emails ignore case. (Email or platform user ID) |
| `date` | `object` | No | Only entries on these UTC days. Combine the bounds you need. |
| `date.gte` | `string` | No | On or after this day. (`YYYY-MM-DD`) |
| `date.lte` | `string` | No | On or before this day. The whole day is included. (`YYYY-MM-DD`) |
| `date.gt` | `string` | No | After this day. (`YYYY-MM-DD`) |
| `date.lt` | `string` | No | Before this day. (`YYYY-MM-DD`) |
| `time` | `object` | No | Only entries at these times of day, in UTC, on any date. |
| `time.gt` | `string` | No | After this minute. (`HH:MM`, 24-hour) |
| `time.gte` | `string` | No | At or after this time. (`HH:MM`, 24-hour) |
| `time.lt` | `string` | No | Before this time. (`HH:MM`, 24-hour) |
| `time.lte` | `string` | No | At or before this minute. (`HH:MM`, 24-hour) |
| `action` | `string[]` | No | Only entries with one of these `action` labels, such as `Add` or `Adjusted by admin`. Matched exactly, including case. (Array of labels) |
| `page` | `integer` | No | Page number, starting at 1. (JSON number, 1 or more; default 1) |
| `limit` | `integer` | No | Entries per page. (Default 100) |

### Example request

```bash
curl --request POST \
  --url https://api.returning.ai/v1/gamifications/logs \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "user": "trader@example.com",
    "date": {
      "gte": "2026-09-01",
      "lte": "2026-09-30"
    },
    "limit": 50,
    "page": 1
  }'
```

## Response

A `200` returns the page in `data.data` and the totals in `data.pagination`. The body has no `message` or `code`; branch on the HTTP status. `xp`, `action` and `action_details` are left out of an entry that doesn't have them.

### Response fields

| Field | Type | Presence | Description |
| --- | --- | --- | --- |
| `status` | `string` | always | Result of the request. (`success`) |
| `data` | `object` | always | The page of entries and the paging totals. |
| `data.pagination` | `object` | always | Paging totals for everything that matches your filters. |
| `data.pagination.total` | `integer` | always | Entries that match, across all pages. (Min 0) |
| `data.pagination.currentPage` | `integer` | always | The page you asked for. (Min 1) |
| `data.pagination.totalPages` | `integer` | always | Pages at this `limit`. `0` when nothing matches. (Min 0) |
| `data.pagination.hasNextPage` | `boolean` | always | `true` when a later page has entries. |
| `data.pagination.hasPrevPage` | `boolean` | always | `true` when `page` is above 1. |
| `data.data` | `object[]` | always | Entries on this page, newest first. Empty when nothing matches. |
| `data.data._id` | `string` | always | The entry's ID. Use it to skip entries you already have. |
| `data.data.email` | `string` | always | The trader's email. (Email format) |
| `data.data.action` | `string` | - | What caused the change, such as `Add`, `Subtract` or `Adjusted by admin`. Can be missing on older entries. |
| `data.data.action_details` | `string` | - | More detail, such as `Added 150 XP` or `Overwrite 200 XP`. Can be missing on older entries. (Nullable) |
| `data.data.xp` | `number` | - | XP change. Negative when XP was taken away. |
| `data.data.currency` | `number` | always | Coin change. Negative when coins were taken away. `0` when only XP changed. |
| `data.data.date` | `string` | always | Day of the change in UTC, as `YYYY-MM-DD`. (Date) |
| `data.data.time` | `string` | always | Time of the change in UTC, as `HH:MM`. (`^([01]\d\|2[0-3]):[0-5]\d$`) |

### Example response (200)

```json
{
  "status": "success",
  "data": {
    "pagination": {
      "total": 2,
      "currentPage": 1,
      "totalPages": 1,
      "hasNextPage": false,
      "hasPrevPage": false
    },
    "data": [
      {
        "_id": "66f000000000000000000741",
        "email": "trader@example.com",
        "action": "Adjusted by admin",
        "action_details": "Overwrite 200 XP",
        "xp": 200,
        "currency": 0,
        "date": "2026-09-26",
        "time": "08:45"
      },
      {
        "_id": "66f000000000000000000740",
        "email": "trader@example.com",
        "action": "Add",
        "action_details": "Added 150 XP",
        "xp": 150,
        "currency": 25,
        "date": "2026-09-26",
        "time": "08:30"
      }
    ]
  }
}
```

## Errors

Errors on this endpoint have no machine-readable code; branch on the HTTP status. A key without `getUserStats` gets `401`, not `403`, with only a `message`. A validation `400` names each field in `detail`.

### Fix the request

| Status | Code | What to do |
| --- | --- | --- |
| 400 | - | A field is missing or has the wrong type: no `user`, `date` or `time` not an object, `action` not an array, `limit` outside 1-100, or `page` or `limit` sent as a string. `detail` names each field. |
| 401 | - | The key is missing, invalid or expired, it's a personal key, or it lacks `getUserStats` (`message` is then `Your api key does not have permission to access this action`). Send `Authorization: Bearer <API_KEY>` with a current Community API key, and add Get User Stats in Settings > Integration > API Keys. |

### Fix the data

| Status | Code | What to do |
| --- | --- | --- |
| 404 | - | `user` matches no trader in your community. Check the email or platform user ID. A trader with no changes returns `200` with an empty list, not `404`. |

### Retry with backoff

| Status | Code | What to do |
| --- | --- | --- |
| 500 | - | The entries could not be read, a `date` value isn't a date, or the key could not be checked (`Authentication failed`). Check the dates, then retry with exponential backoff. |

**Retries:** This endpoint is read-only and doesn't accept an `Idempotency-Key`. Retrying the exact same request is safe after a network error or a `500`; use bounded exponential backoff. Don't retry a `404` without changing `user`. Over the [rate limit](https://docs.returning.ai/how-it-works.md#rate-limits), requests return `429`; wait for the window to reset, then retry.

## Next step

- [Get daily user XP and coin changes](https://docs.returning.ai/api-reference/gamification/get-daily-user-xp-and-coin-changes.md): `POST /v1/users/activity/daily`. Get the net XP and coin change for a day, for one trader or many.
- [Read the trader's current balances with Get User Data](https://docs.returning.ai/api-reference/users/get-user-data.md): `POST /v1/users/info`.
