# Retrieve and export community logs

Read your community's gamification, milestone, API or mini-game logs for a date range, as JSON pages or as a CSV file to download.

- Endpoint: `GET https://api.returning.ai/v1/community-logs`
- Section: Community / Logs
- Authentication: `Authorization: Bearer <API_KEY>` (Community API key)
- Permission: `readCommunityLogs` (Shown in the dashboard as "Get Community Logs")
- Retries: Read-only; exact retries are safe
- Guide: hand-written
- Verified: live, 2026-09-30
- Last updated: 30 Sep 2026
- Web page: https://docs.returning.ai/api-reference/community-logs/retrieve-and-export-community-logs

## When to use this

- Pull the XP and coin activity for a period into your own reporting or data warehouse.
- Check which API calls your integration made, which failed and why, without opening the dashboard.
- Export milestone completions or Spin the Wheel and quiz history as CSV for a campaign review.

**Instead:** Use [Search user gamification logs](https://docs.returning.ai/api-reference/gamification/search-user-gamification-logs.md) instead to read one trader's gamification history, or [List user streak logs](https://docs.returning.ai/api-reference/gamification-streaks-and-mini-games/list-user-streak-logs.md) for streak logs, which this endpoint does not serve yet.

**Four log types today:** `gamification`, `milestone`, `api` and `miniGames` return data. `streaks`, `raffles`, `referral`, `email`, `misconduct` and `audit` are accepted but return `501` until they are available.

## Authentication

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

Use a Community API key with the `readCommunityLogs` permission, and keep it on your server. The key decides the community, so never send a `communityId`; an unknown query key returns `400`. Personal API keys are refused with `403`.

## Behaviour

Records come newest first. Filters combine: a record must match every filter you send, and any one of the values within a filter. Each filter takes up to 100 values of up to 500 characters. Send one value as `filters[userId]=3247779`, or several by repeating `filters[userId][]=`.

`action` and `location` filters match the values stored when the entry was recorded. For most entries these are the labels in the response, but some gamification entries show a different label, so a filter can return more or fewer records than the labels suggest.

Each log type takes the five shared filters (`action`, `location`, `userId`, `email`, `customFieldIdentifier`). `miniGames` also takes `gameType` and `miniGameId`, and `milestone` also takes `milestones`. Any other filter returns `400`.

## Request

### Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `logType` | `string` | Yes | The log to read. `streaks`, `raffles`, `referral`, `email`, `misconduct` and `audit` are accepted but return `501` until they are available. (`gamification`, `milestone`, `api` or `miniGames`) |
| `startDate` | `string` | Yes | Start of the range, inclusive. API logs are matched on when the request arrived, quiz attempts on when they were completed, and everything else on when the entry was created. (UTC timestamp ending in `Z`) |
| `endDate` | `string` | Yes | End of the range, inclusive. There is no maximum span. (UTC timestamp ending in `Z`; not before `startDate`) |
| `format` | `string` | No | `json` returns one page of records. `csv` returns a link to a CSV file with every match, and ignores `page` and `limit`. (`json` (default) or `csv`) |
| `page` | `integer` | No | JSON only. The page to return, starting at `1`. A page past the end returns an empty `records` list. (Whole number from 1; default `1`) |
| `limit` | `integer` | No | JSON only. Records per page. (Default 100; max 500) |
| `filters[action]` | `string` | No | Only these actions. API logs: the record's `name`, such as `Create New User`. Gamification: the action as recorded, usually the record's `action`. Milestones: `Stage Completed` or `stage_completed` and the other milestone actions. Spin the Wheel: `Spin Earned`, `Spin Claimed` and the other wheel actions. Quiz: `Completed`, `Passed` or `Failed`. (String, or repeat `filters[action][]=` for several) |
| `filters[location]` | `string` | No | Only these locations. API logs: the request path with an `/apis` prefix, such as `/apis/v1/users` for Create User. Gamification: the location as recorded, usually the record's `action_location`. Spin the Wheel: the game or channel name. Quiz: the game name. Milestone logs have no location, so any value returns no records. (String or array) |
| `filters[userId]` | `string` | No | Only these traders, by platform user ID. (Digits; string or array) |
| `filters[email]` | `string` | No | Only traders with these emails. Exact match, not case-sensitive. API logs also match an email sent in the request body. (String or array) |
| `filters[customFieldIdentifier]` | `string` | No | Only traders with these values in your community's active identifier field, usually the broker customer ID. Text values match exactly, not case-sensitive. (String or array) |
| `filters[gameType]` | `string` | No | `miniGames` only. Sending it with another log type returns `400`. (`spin_the_wheel` (default) or `quiz`) |
| `filters[miniGameId]` | `string` | No | `miniGames` only. One game's ID. (24 hex characters) |
| `filters[milestones]` | `string` | No | `milestone` only. Only these milestones, by exact name. (String or array) |

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Authorization` | `string` | Yes | Community API key with `readCommunityLogs`. The key decides the community. (`Bearer <API_KEY>`) |

### Example request

```bash
curl --request GET \
  --url 'https://api.returning.ai/v1/community-logs?logType=gamification&startDate=2026-09-01T00:00:00Z&endDate=2026-09-30T23:59:59Z&page=1&limit=100' \
  --header 'Authorization: Bearer <API_KEY>'
```

## Response

With `format=json`, a `200` returns one page in `data.records`. To read everything, start at `page=1` and ask for the next page while `data.hasNextPage` is `true`. Dates in `date` and `time` are UTC (`DD/MM/YYYY` and `HH:MM`); the dashboard shows the same entries in your local time zone. Records by `logType`:

- `gamification`: `id`, `userId` (a number), `createdAt`, `user` (display name), `email`, `action`, `action_details`, `action_location`, `roles` (role names), `xp`, `currency`, `date` and `time`. `identifier` appears when your community has an active identifier field (`null` when the trader has no value), `premiumCurrency` when premium currency awards are on, and `coinExpiry` (`TRUE` or empty) when coin expiry is on.
- `milestone`: `_id`, `milestoneId`, `milestoneName`, `userId` (a string), `user`, `email` (`-` when unknown), `identifierValue`, `action` (such as `Stage Completed`), `actionDetails`, `createdAt`, `updatedAt`, `date` and `time`, plus the rewards given: `xps`, `currencies`, `multiplier`, `multiplierAwarded`, `roles` and `tags` (names under `add`, `remove` and `overwrite`), `badges` and `customRewards`.
- `api`: `id`, `name` (the operation, or the name you gave a bulk job), `receivedAt`, `apiKeyName`, `duration` (such as `0.70 seconds`, or empty), `status` (`Queued`, `In progress`, `Complete`, `Complete with errors`, `Error` or `Cancelled`), `progress` (0 to 100 for bulk jobs, otherwise `null`), `statusCode` (`null` when none was recorded) and `error`.
- `miniGames` with `spin_the_wheel`: `id`, `userId` (a string), `createdAt`, `user`, `identifier`, `email`, `gameName`, `roles`, `date`, `time`, `action` (such as `Spin Earned` or `Spin Claimed`), `actionDetails` and `spinCount`.
- `miniGames` with `quiz`: the same first ten fields, with `createdAt` set to when the attempt was completed, plus `attemptNumber`, `score` (such as `3/5`) and `result` (`Passed`, `Failed`, or `Completed` for quizzes without a pass mark).

With `format=csv`, a `200` returns a link instead of records. `page` and `limit` are ignored, and the file holds every match:

```json
{
  "meta": { "status": "success", "statusCode": 200 },
  "message": "Get community logs success.",
  "data": {
    "downloadUrl": "https://media-cdn.returning.ai/community-logs/66f000000000000000000010/<fileId>_gamification-logs.csv",
    "fileName": "gamification-logs.csv",
    "fileSizeBytes": 6429,
    "totalRecords": 55
  }
}
```

The file is UTF-8 with a byte-order mark, so spreadsheet apps open it correctly. Text cells that start with `=`, `+`, `-` or `@` get a leading `'`, so `@all` reads `'@all`. With no matches, the file holds the header row only. Treat `downloadUrl` as confidential and keep it on your server.

CSV columns follow the dashboard's own log exports. The identifier column is headed with your identifier field's name, and the XP and coin columns with your community's unit names:

- `gamification`: Ref ID (`#1` is the oldest row), User, your identifier, Email, Action, Action Details, Action Location, Role, XP Activity, Coins Activity, then Premium Activity and Coin Expiry when those are on, Date, Time.
- `milestone`: User, your identifier, Email, Milestone Name, Action, Action Details, Rewards, Date, Time. Rewards lists roles, tags and custom rewards by ID.
- `api`: Request ID, Request received, Received at (UTC), API key name, Duration, Status, Progress (as a percentage), Status code, Error.
- `miniGames`: Ref ID, User, your identifier, Email, then Action, Action Details and Spin Count for the wheel or Attempt, Score and Result for quizzes, Game Name, Roles, Date, Time.

A CSV export over 10,000 rows or 10 MB, or one that takes too long to build, returns `202` with `meta.code` `EXPORT_ACCEPTED` and `data.exportId`. The file is then prepared in the background, and you download it from Settings > Logs > Export Histories in the dashboard. Only one large export per log type runs at a time.

### Response fields

| Field | Type | Presence | Description |
| --- | --- | --- | --- |
| `meta` | `object` | always | Result details. |
| `meta.status` | `string` | always | `success`. |
| `meta.statusCode` | `integer` | always | `200`. |
| `message` | `string` | always | Human-readable summary. Do not branch on it. |
| `data` | `object` | always | A page of records for `json`, or the download link for `csv`. |
| `data.communityId` | `string` | json only | Your community's ID, taken from the API key. |
| `data.logType` | `string` | json only | The `logType` you sent. (`gamification`, `milestone`, `api`, `miniGames`, `raffles`) |
| `data.startDate` | `string` | json only | The `startDate` you sent. |
| `data.endDate` | `string` | json only | The `endDate` you sent. |
| `data.filters` | `object` | json only | The filters you sent, or `{}`. For `miniGames` it includes `gameType`, even when you left it out. |
| `data.totalRecords` | `integer` | always | Records that match, across all pages. For `csv`, the rows in the file. |
| `data.currentPage` | `integer` | json only | The page returned. |
| `data.pageSize` | `integer` | json only | The `limit` used. |
| `data.totalPages` | `integer` | json only | Pages at this `limit`. `0` when nothing matches. |
| `data.hasNextPage` | `boolean` | json only | `true` when there are more records after this page. Ask for `page + 1`. |
| `data.hasPreviousPage` | `boolean` | json only | `true` on any page after the first, when something matches. |
| `data.records` | `object[]` | json only | This page of records, newest first. The fields depend on `logType`; see Response below. |
| `data.downloadUrl` | `string` | csv only | HTTPS link to the CSV file. Download it with a plain `GET`; it needs no API key. |
| `data.fileName` | `string` | csv only | The file name, such as `gamification-logs.csv`. |
| `data.fileSizeBytes` | `integer` | csv only | The file size in bytes. |

### Example response (200)

```json
{
  "meta": {
    "status": "success",
    "statusCode": 200
  },
  "message": "Get community logs success.",
  "data": {
    "communityId": "66f000000000000000000010",
    "logType": "gamification",
    "startDate": "2026-09-01T00:00:00Z",
    "endDate": "2026-09-30T23:59:59Z",
    "filters": {},
    "totalRecords": 55,
    "currentPage": 1,
    "pageSize": 100,
    "totalPages": 1,
    "hasNextPage": false,
    "hasPreviousPage": false,
    "records": [
      {
        "id": "66f000000000000000000301",
        "userId": 3247779,
        "createdAt": "2026-09-20T12:30:00.000Z",
        "user": "Sample Trader",
        "identifier": "<brokerCustomerId>",
        "email": "trader@example.com",
        "action": "Adjusted by admin",
        "action_details": "Added 10 XP",
        "action_location": "User List",
        "roles": ["Gold Tier", "@all"],
        "xp": 10,
        "currency": 0,
        "date": "20/09/2026",
        "time": "12:30"
      }
    ]
  }
}
```

## Errors

A request that fails validation returns `400` with `code` `VALIDATION_FAILED` at the top level and a `detail` object naming each field, and no `meta`. Every other error carries its code in `meta.code`, with a readable `detail`.

### Fix the request

| Status | Code | What to do |
| --- | --- | --- |
| 400 | `VALIDATION_FAILED` | A query value is missing or breaks its rule, such as a date without `Z`, `endDate` before `startDate`, `limit` over 500, an unknown query key or a filter this log type doesn't take. `detail` names each field. |
| 400 | `VALIDATION_ERROR` | `page` × `limit` is too large, or `customFieldIdentifier` isn't a number when your identifier field is numeric. Lower `page`, or send numeric identifiers. |
| 400 | `FILTER_TOO_BROAD` | `email` or `customFieldIdentifier` matches more than 1,000 traders, or on milestone logs `userId` and `email` together select more than 100. Send fewer or more specific values. |
| 401 | `AUTHENTICATION_REQUIRED` | The key is missing, invalid or expired. Send `Authorization: Bearer <API_KEY>` with a current Community API key. |
| 403 | `API_KEY_PERMISSION_DENIED` | The key lacks `readCommunityLogs`, or it is a personal API key. Use a Community API key and add Get Community Logs in Settings > Integration > API Keys. |
| 403 | `COMMUNITY_ACCESS_DENIED` | A large CSV export was refused because the key lost `readCommunityLogs` or expired. Check the key, then request the export again. |

### Fix the data

| Status | Code | What to do |
| --- | --- | --- |
| 400 | `CUSTOM_IDENTIFIER_UNAVAILABLE` | Your community has no active custom identifier field. Filter by `userId` or `email` instead, or set up the identifier field. |
| 404 | `COMMUNITY_NOT_FOUND` | The community this key belongs to no longer exists. Contact Returning.AI support. |

### Retry with backoff

| Status | Code | What to do |
| --- | --- | --- |
| 429 | `EXPORT_IN_PROGRESS` | Another large export of this log type is still running. Wait for it in Settings > Logs > Export Histories, then request again. |
| 503 | `QUERY_TIMEOUT` | The read took too long. Narrow the date range or add filters, then retry. |
| 503 | `EXPORT_UPLOAD_FAILED` | The CSV file could not be stored. Retry the same request after a few seconds. |
| 503 | `EXPORT_STORAGE_UNAVAILABLE` | CSV downloads are unavailable. Use `format=json`, or retry later and contact Returning.AI support if it persists. |
| 503 | `EXPORT_QUEUE_UNAVAILABLE` | A large export may or may not have been queued. Check Settings > Logs > Export Histories before you request it again. |
| 500 | `INTERNAL_ERROR` | The logs could not be read. Retry the same request with exponential backoff. |
| 500 | `AUTHENTICATION_FAILED` | The key could not be checked. Retry the same request with exponential backoff. |

### Do not retry

| Status | Code | What to do |
| --- | --- | --- |
| 501 | `NOT_IMPLEMENTED` | This log type is not available yet. Use `gamification`, `milestone`, `api` or `miniGames`. |

**Retries:** This endpoint only reads, so retrying the exact same request is safe after a network error, a `500` or a `503`. Use bounded exponential backoff. `EXPORT_UPLOAD_FAILED` is usually brief, and the same request succeeds a few seconds later. Repeating a large CSV request while its export is still pending returns the same `exportId`. New entries arrive at the top, so pages can shift while you page through a busy log; match records by ID to skip ones you've already read. 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 User Data](https://docs.returning.ai/api-reference/users/get-user-data.md): `POST /v1/users/info`. Look up the trader behind a record's `userId` or `email`.
- [Or read one trader's history with Search user gamification logs](https://docs.returning.ai/api-reference/gamification/search-user-gamification-logs.md): `POST /v1/gamifications/logs`.
