# Update an existing leaderboard

Change the settings of one leaderboard in the community that owns your API key, sending only the fields you want to change.

- Endpoint: `PATCH https://api.returning.ai/v1/leaderboards/{leaderboardID}`
- Section: Gamification / Leaderboards
- Authentication: `Authorization: Bearer <API_KEY>` (Community API key)
- Permission: `leaderboard` (Shown in the dashboard as "Leaderboard")
- Retries: No Idempotency-Key; read back before retrying
- Guide: hand-written
- Verified: code, 2026-09-27
- Last updated: 26 Sep 2026
- Web page: https://docs.returning.ai/api-reference/gamification-leaderboards/update-an-existing-leaderboard

## When to use this

- Extend a competition's end date or switch a leaderboard on or off.
- Change who can see or be ranked on a leaderboard.
- Add prizes, or change the columns traders see in the table.

**Instead:** Use [Create leaderboard](https://docs.returning.ai/api-reference/gamification-leaderboards/create-leaderboard.md) instead to start a new competition rather than reusing an old one.

**Whole objects:** The schedule, the rank lists, the view lists and the display flags are each replaced as a group. When you change one field in a group, send the rest of that group too, or it goes back to its default.

## Authentication

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

Send a Community API key with the `leaderboard` permission. The key decides the community, so a leaderboard in another community returns `404`.

## Behaviour

Only the fields you send change, and the change shows to traders straight away. Unknown fields are ignored, so check spelling. Each of these groups is replaced as a whole when you send any field in it:

- **Schedule**: `startDate`, `endDate`, `hasNoEndDate`, `timeZone`, `startTime`, `endTime`. `startDate` is required, and any part you leave out goes back to its default: no end date off, UTC, 12:00 AM to 11:59 PM. Read the leaderboard first and send the full schedule.
- **Rank lists** and **view lists**: sending one `allowed...ToRank` list empties the other two rank lists, and the same goes for view lists. Empty arrays are ignored, so a rank or view list can't be cleared completely.
- **Display flags**: `topRankOnly`, `topRankLimit`, `onlyPositiveRanks`, `includeDummyUsers`. The ones you leave out are switched off.
- **Lists and settings objects**: `displayedColumns`, `allowedTimeFilters`, `prizes`, `widget`, `banner`, `translations` and `userInformationDisplay` replace the current value.

`displayOrder` moves the leaderboard and shifts the others to make room. The Default Leaderboard can be renamed and switched on or off here, but its rank and view lists can only be changed in the dashboard. Switching it on or off also switches the leaderboard feature for your community, and each change to it is logged in `configurationHistory`.

## Request

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `leaderboardID` | `string` | Yes | The leaderboard's `_id`, from List leaderboards or Create leaderboard. (24-character hex ID) |

### Headers

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

### Body

Send only the fields you want to change. An empty body changes nothing and returns the leaderboard as it is. Several fields have an older alias, such as `rankBy` for `rankingField`; send one of each pair.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `string` | No | New name shown to traders. Other leaderboards' names aren't checked, so keep names unique yourself. (1-100 chars) |
| `slug` | `string` | No | New URL-friendly ID. Must be unique in your community. (1-100 chars; lowercase letters, numbers and single hyphens) |
| `description` | `string` | No | New description shown to traders. |
| `image` | `string` | No | Leaderboard image URL. Wins over `leaderImage` if you send both. (http or https URL) |
| `isEnabled` | `boolean` | No | `true` makes the leaderboard live for the traders who can see it; `false` hides it. |
| `rankingField` | `string` | No | What traders are ranked by: `XP`, `Coins`, `Referrals`, or the key of a user field in your community. Anything else returns `400`. (`XP`, `Coins`, `Referrals` or a user-field key; no spaces) |
| `displayedColumns` | `array` | No | The table's columns, in order, replacing the current ones. Send names, or objects with `column`, `order` and `visible` to hide one. `user` is always the first column. (Up to 50) |
| `startDate` | `string` | No | First day of the leaderboard. Required whenever you send any schedule field (`endDate`, `hasNoEndDate`, `timeZone`, `startTime`, `endTime`). (ISO 8601 or `DD-MM-YYYY`) |
| `endDate` | `string` | No | Last day of the leaderboard, or `null` for none. Send it with `startDate`. (ISO 8601, `DD-MM-YYYY` or `null`) |
| `hasNoEndDate` | `boolean` | No | `true` when the leaderboard has no end date. Reset to `false` if you change the schedule without it. |
| `timeZone` | `number` | No | Time zone as minutes from UTC. Reset to `0` (UTC) if you change the schedule without it. (Minutes, e.g. `480` for UTC+8) |
| `startTime` | `object` | No | Time of day the leaderboard and each reset period start, as `hours` (1-12), `minutes` (0-59) and `ampm`. Reset to 12:00 AM if you change the schedule without it. |
| `endTime` | `object` | No | Time of day the leaderboard ends on `endDate`, in the same format. Reset to 11:59 PM if you change the schedule without it. |
| `allowedTimeFilters` | `string[]` | No | Period tabs traders can switch between: `all-time`, `daily`, `weekly`, `monthly`, `yearly`. Replaces the current tabs. |
| `allowedRolesToRank` | `array` | No | Roles whose traders get ranked, by name or ID. `@all` covers every trader. Sending any rank list replaces all three; lists you leave out become empty. |
| `allowedUsersToRank` | `array` | No | Traders to rank one by one, by platform user ID, username or email. Each must be a member of your community. |
| `allowedTagsToRank` | `array` | No | Tags whose traders get ranked, by name or ID. |
| `allowedRolesToView` | `array` | No | Roles whose traders can see the leaderboard, by name or ID. Sending any view list replaces all three; lists you leave out become empty. |
| `allowedUsersToView` | `array` | No | Traders who can see the leaderboard, by platform user ID, username or email. |
| `allowedTagsToView` | `array` | No | Tags whose traders can see the leaderboard, by name or ID. |
| `displayOrder` | `number` | No | New position among your leaderboards, starting at 1. The others move to make room. `0` leaves the position unchanged. (Whole number, 0 or more) |
| `enableLeaderboardReset` | `boolean` | No | `true` restarts scores every period and keeps each finished period as history. |
| `leaderboardResetFrequency` | `string` | No | How often scores restart. Required when you send `enableLeaderboardReset` as `true`. (`daily`, `weekly`, `monthly`, `yearly`) |
| `enablePrizePool` | `boolean` | No | `true` pays the `prizes` rows to the winners when a period ends. |
| `prizes` | `object[]` | No | Prize rows by rank, replacing the current ones. Each row has the same fields as in Create leaderboard. |
| `topRankOnly` | `boolean` | No | `true` blurs rows below the top `topRankLimit` ranks. Sending any of `topRankOnly`, `topRankLimit`, `onlyPositiveRanks` or `includeDummyUsers` resets the ones you leave out to off. |
| `topRankLimit` | `number` | No | How many top ranks stay visible. Must be 1 or more when `topRankOnly` is `true`. (Whole number, 1 or more when `topRankOnly` is `true`) |
| `onlyPositiveRanks` | `boolean` | No | `true` shows only traders with a score above 0. |
| `includeDummyUsers` | `boolean` | No | Older setting for filling the table with placeholder traders. Leave it out. |
| `isPreviewEnabled` | `boolean` | No | `true` lets guests see the leaderboard, if your community allows guest preview. |
| `widget` | `object` | No | Embeddable widget settings, replacing the current ones. Most integrations leave this out. |
| `widget.enabled` | `boolean` | No | `true` when the widget can be embedded. |
| `widget.apiKey` | `string` | No | Key the embedded widget uses. |
| `widget.size` | `string` | No | Widget size setting. |
| `widget.width` | `number` | No | Widget width in pixels. |
| `widget.height` | `number` | No | Widget height in pixels. |
| `widget.theme` | `object` | No | Widget colours. |
| `widget.font` | `object` | No | Widget font. |
| `widget.ctaButton` | `object` | No | Call-to-action button settings. |
| `widget.domains` | `string[]` | No | Domains allowed to embed the widget. |
| `widget.communityThemeOverride` | `boolean` | No | `true` when the widget uses its own colours instead of your community theme. |
| `banner` | `object` | No | Banner settings, replacing the current ones. Most integrations leave this out. |
| `translations` | `object` | No | Translated names and descriptions, as `name` and `description` lists of `languageCode` and `translation`. Replaces the current translations. |
| `userInformationDisplay` | `object[]` | No | How much of each trader's details the table shows, per field, as `full` or `partial`. Replaces the current settings. |
| `leaderboardPageConfig` | `object` | No | Leaderboard page layout settings. Most integrations leave this out. |
| `leaderImage` | `string` | No | Alias for `image`. `image` wins if you send both. (Up to 2,000 chars) |
| `rankBy` | `string` | No | Alias for `rankingField`. `rankingField` wins if you send both. |
| `displayedFields` | `string[]` | No | Visible columns. Alias for `displayedColumns`; send it with `displayFieldsOrder`. |
| `displayFieldsOrder` | `string[]` | No | Column order. Alias for `displayedColumns`; send it with `displayedFields`. |
| `timeFilters` | `string[]` | No | Alias for `allowedTimeFilters`. `allowedTimeFilters` wins if you send both. |
| `duration` | `object` | No | The schedule as one object. Alias for `startDate`, `endDate`, `hasNoEndDate`, `timeZone`, `startTime` and `endTime`, which win if you send them. `start` is required. |
| `duration.start` | `string` | Yes | First day of the leaderboard. |
| `duration.end` | `string` | No | Last day, or `null`. (Nullable) |
| `duration.noEndDate` | `boolean` | No | `true` when there is no end date. |
| `duration.timeZone` | `number` | No | Minutes from UTC. |
| `duration.startTime` | `object` | No | Start time of day. |
| `duration.endTime` | `object` | No | End time of day. |
| `rankedUserRoles` | `object` | No | The rank lists as one object with `users`, `roles` and `tags`. Alias for the `allowed...ToRank` fields, which win if you send them. |
| `viewPermissionUserRoles` | `object` | No | The view lists as one object with `users`, `roles` and `tags`. Alias for the `allowed...ToView` fields, which win if you send them. |
| `performanceDisplay` | `object` | No | The display flags as one object, `showTop` (`enabled`, `value`), `showPositive` and `showDummy`. Alias for `topRankOnly`, `topRankLimit`, `onlyPositiveRanks` and `includeDummyUsers`, which win if you send them. |
| `enabled` | `boolean` | No | Alias for `isEnabled`. `isEnabled` wins if you send both. |
| `previewEnabled` | `boolean` | No | Alias for `isPreviewEnabled`. `isPreviewEnabled` wins if you send both. |
| `order` | `number` | No | Alias for `displayOrder`. `order` wins if you send both. |

### Example request

```bash
curl --request PATCH \
  --url https://api.returning.ai/v1/leaderboards/66f000000000000000000301 \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "description": "Top traders by trading volume each month. Prizes for the top 3.",
    "allowedTimeFilters": ["monthly", "all-time"],
    "isEnabled": true
  }'
```

## Response

A `200` returns the whole leaderboard after your change in `data`, with user, role and tag references expanded, in the same shape as Get leaderboard. Branch on the HTTP status.

### Response fields

| Field | Type | Presence | Description |
| --- | --- | --- | --- |
| `meta` | `object` | always | Status of the request. There is no `meta.code` on success. |
| `meta.status` | `string` | always | Result of the request. (`success`) |
| `meta.statusCode` | `integer` | always | The HTTP status, repeated. |
| `message` | `string` | always | Human-readable summary. Do not branch on it. |
| `data` | `object` | always | The leaderboard after your changes, in the same shape as Get leaderboard. |
| `data._id` | `string` | - | Leaderboard ID. |
| `data.communityID` | `string` | - | Your community ID. |
| `data.kind` | `string` | - | `default` for your community's built-in Default Leaderboard; `custom` for every other leaderboard. |
| `data.name` | `string` | - | Leaderboard name. |
| `data.slug` | `string` | - | URL-friendly ID. |
| `data.description` | `string` | - | Description shown to traders. |
| `data.image` | `string` | - | Leaderboard image URL, when one is set. |
| `data.enabled` | `boolean` | - | `true` when the leaderboard is live. |
| `data.displayOrder` | `number` | - | Position among your leaderboards. |
| `data.order` | `number` | - | Same value as `displayOrder`. |
| `data.rankBy` | `string` | - | What traders are ranked by. |
| `data.duration` | `object` | - | The schedule. `timeZone` is minutes from UTC. |
| `data.duration.start` | `string` | - | Start date. |
| `data.duration.end` | `string` | - | End date, or `null` when there is none. (Nullable) |
| `data.duration.noEndDate` | `boolean` | - | `true` when the leaderboard runs with no end date. |
| `data.duration.timeZone` | `number` | - | Time zone as minutes from UTC. |
| `data.duration.startTime` | `object` | - | Start time of day, as `hours` (1-12), `minutes` and `ampm`. |
| `data.duration.endTime` | `object` | - | End time of day, in the same format. |
| `data.timeFilters` | `string[]` | - | Period tabs. |
| `data.rankedUserRoles` | `object` | - | Rank lists, expanded: traders as `id` (numeric platform user ID), `_id`, `username` and `email`; roles and tags as `_id` and `name`. |
| `data.viewPermissionUserRoles` | `object` | - | View lists, in the same shape. |
| `data.enableLeaderboardReset` | `boolean` | - | `true` when scores restart every period. |
| `data.leaderboardResetFrequency` | `string` | - | How often scores restart. |
| `data.enablePrizePool` | `boolean` | - | `true` when prizes are paid. |
| `data.prizes` | `object[]` | - | Prize rows. |
| `data.banner` | `object` | - | Banner settings. |
| `data.userInformationDisplay` | `object[]` | - | Trader detail display settings. |
| `data.previewEnabled` | `boolean` | - | `true` when guests can see it. |
| `data.selected` | `boolean` | - | `true` for the one leaderboard your community has set as its active leaderboard. |
| `data.updatedAt` | `string` | - | When the leaderboard last changed, ISO 8601 UTC. |
| `data.createdAt` | `string` | - | When the leaderboard was created, ISO 8601 UTC. |
| `data.widget` | `object` | - | Widget settings, when set. |
| `data.translations` | `object` | - | Translations, when set. |
| `data.leaderboardPageConfig` | `object` | - | Page layout settings, when set. |
| `data.configurationHistory` | `object[]` | - | Log of settings changes. Present only on the Default Leaderboard after it has been changed. |
| `data.displayedFields` | `string[]` | - | Visible columns. `user` comes first. |
| `data.displayFieldsOrder` | `string[]` | - | Column order. |
| `data.performanceDisplay` | `object` | - | The display flags. |

### Example response (200)

```json
{
  "meta": {
    "status": "success",
    "statusCode": 200
  },
  "message": "Update leaderboard success.",
  "data": {
    "_id": "66f000000000000000000301",
    "communityID": "66f000000000000000000010",
    "kind": "custom",
    "name": "Monthly Trading Volume",
    "slug": "monthly-trading-volume",
    "description": "Top traders by trading volume each month. Prizes for the top 3.",
    "rankBy": "tradingvolume",
    "displayedFields": ["user", "tier", "level"],
    "displayFieldsOrder": ["user", "tier", "level"],
    "timeFilters": ["monthly", "all-time"],
    "performanceDisplay": {
      "showTop": {
        "enabled": false,
        "value": 0
      },
      "showPositive": {
        "enabled": false
      },
      "showDummy": {
        "enabled": false
      }
    },
    "duration": {
      "start": "2026-10-01T00:00:00.000Z",
      "end": null,
      "noEndDate": true,
      "timeZone": 0,
      "startTime": {
        "hours": 12,
        "minutes": 0,
        "ampm": "AM"
      },
      "endTime": {
        "hours": 11,
        "minutes": 59,
        "ampm": "PM"
      }
    },
    "rankedUserRoles": {
      "users": [],
      "roles": [
        {
          "_id": "66f000000000000000000502",
          "name": "@all"
        }
      ],
      "tags": []
    },
    "viewPermissionUserRoles": {
      "users": [],
      "roles": [
        {
          "_id": "66f000000000000000000502",
          "name": "@all"
        }
      ],
      "tags": []
    },
    "banner": {},
    "enableLeaderboardReset": true,
    "leaderboardResetFrequency": "monthly",
    "enablePrizePool": false,
    "prizes": [],
    "userInformationDisplay": [],
    "enabled": true,
    "previewEnabled": false,
    "selected": false,
    "displayOrder": 2,
    "order": 2,
    "updatedAt": "2026-09-27T09:00:00.000Z",
    "createdAt": "2026-09-26T08:30:00.000Z"
  }
}
```

## Errors

`401`, `403` and `400 VALIDATION_FAILED` errors carry a code in `meta.code`. Other errors from this endpoint have no code; branch on the HTTP status and read `detail`.

### Fix the request

| Status | Code | What to do |
| --- | --- | --- |
| 400 | - | The change can't be applied. There is no `meta.code`; `detail` says why, such as `Invalid rankBy value: ...`, `rankedUserRoles.users[0] could not be resolved for this community.`, an invalid date, or `Update Default Leaderboard access through Leaderboard Settings` (the Default Leaderboard's rank and view lists can only be changed in the dashboard). Nothing changed. |
| 400 | `VALIDATION_FAILED` | A field breaks a format rule, such as a malformed `leaderboardID`, a slug with capitals or spaces, a schedule change without `startDate` (`detail` names `payload.duration.start`), `topRankOnly` without a `topRankLimit` of 1 or more, or `enableLeaderboardReset` without `leaderboardResetFrequency`. `detail` names the field. Nothing changed. |
| 401 | `AUTH_API_KEY_REQUIRED` | No key was sent. Send `Authorization: Bearer <API_KEY>`. |
| 401 | `AUTH_API_KEY_INVALID` | The key is unknown, expired or malformed. Use a current Community API key. |
| 403 | `AUTH_PERMISSION_REQUIRED` | The key lacks `leaderboard`. Add the permission in Settings > Integration > API Keys. |

### Fix the data

| Status | Code | What to do |
| --- | --- | --- |
| 404 | - | No leaderboard with this ID in your community (`Leaderboard not found: <id>`). It may have been deleted, or belong to another community. Find the ID again with List leaderboards. |
| 409 | - | Another leaderboard in your community already has this slug (`Slug already exists: <slug>`). Choose another slug. |

### Retry with backoff

| Status | Code | What to do |
| --- | --- | --- |
| 500 | - | The update failed unexpectedly. A `displayOrder` change is saved before the other fields, so read the leaderboard with Get leaderboard before you send it again. |
| 502 | - | Leaderboards are briefly unavailable, and the outcome is unknown. Read the leaderboard with Get leaderboard, then retry with backoff if the change is missing. |
| 401 | `AUTH_API_KEY_VALIDATION_FAILED` | The key could not be checked just now, and nothing changed. Retry with backoff. |

**Retries:** This endpoint does not accept an `Idempotency-Key`. Sending the same change again gives the same result, so a retry is safe once you know the first request failed. After a timeout, a `500` or a `502`, read the leaderboard with Get leaderboard and send the change again only if it is missing. 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 leaderboard](https://docs.returning.ai/api-reference/gamification-leaderboards/get-leaderboard.md): `GET /v1/leaderboards/{leaderboardID}`. Read the leaderboard back to confirm the change.
- [Back to List leaderboards](https://docs.returning.ai/api-reference/gamification-leaderboards/list-leaderboards.md): `GET /v1/leaderboards`.
