diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..3781ac7d --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,5 @@ +{ + "permissions": { + "deny": ["Read(./.env)", "Read(./.env.local)", "Read(./.env.*.local)"] + } +} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..2ed1662e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,29 @@ +# Trophy Docs + +Mintlify documentation site for Trophy. Repo-root paths are the canonical English source; `es/**` is generated by the translation workflow. See `README.md` for Lingo and MCP setup (Cursor-specific steps there). + +## Writing style + +- Never abbreviate "leaderboard" to "board" in docs copy, comments or commit messages. Always write "leaderboard". + +## Localization Workflow + +- Treat repo-root docs paths as the canonical English source and keep identical relative file paths/filenames across all locales. +- For planned doc changes, default scope to English source files only. Do not plan or execute manual edits to non-English locale content (for example `es/**`) unless the user explicitly asks for localized updates; rely on the existing translation workflow to propagate English changes. +- Keep all locale navigation in `docs.json` under `navigation.languages`; do not reintroduce `navigation.global` or top-level `navbar`. +- Keep SEO metatags in English unless explicitly requested otherwise. +- Do not translate code snippets, inline code, URLs, route slugs, or API identifiers. +- Shared media lives under repo-root `assets/`. Default-language pages are at repo-root section paths (for example `
/.mdx`) and targets are under locale directories (for example `es/
/.mdx`), so relative `src` paths need one more `../` under each locale segment than in English. `scripts/localize-internal-links.mjs` rewrites markdown `](/...)` and JSX `href="/..."` only (absolute navigation paths). Relative repo-root `assets/` `src` and MDX `import` paths are normalized by `scripts/localize-mdx-paths.mjs` after Lingo. +- `scripts/localize-mdx-paths.mjs` rewrites: `/components/*.jsx` for the active locale; `from ".../snippets/..."`; and JSX `src=".../assets/..."` so `../` depth matches the file’s path. Run after Lingo in `translate:generate` and translate-on-main; CI checks via `translate:localize-mdx-paths:check`. +- Shared MDX snippets live under repo-root `snippets/` (not per-locale). Import with relative paths from each page; do not add shared MDX snippets to Lingo `i18n.json` buckets unless you intend to translate them. +- Locale-aware React components that can contain text live under `components/*.jsx` for default language and `/components/*.jsx` for targets, and are manually maintained per locale (do not add them to Lingo `i18n.json` buckets; do not keep these in shared `snippets/`). +- Endpoint and webhook MDX must pin a **repo-local** OpenAPI file in frontmatter (do **not** put remote OpenAPI URLs in frontmatter, and do **not** set tab/group-level `openapi` in `docs.json` navigation—tab `openapi` auto-populates endpoints and duplicates MDX nav entries; remote URLs in frontmatter are not reliably resolved by Mintlify). Specs live under `openapi/application.json` (Application API + webhooks) and `openapi/admin.json` (Admin API), also listed under `api.openapi` in `docs.json`. Forms: `openapi: "/openapi/application.json GET /path"`, `openapi: "/openapi/application.json webhook "`, `openapi: "/openapi/admin.json METHOD /path"`. Keep those lines identical across locales; Lingo must not alter them. +- English **nav titles** for those pages come from OpenAPI **`summary`** via `scripts/sync-openapi-titles.mjs` (`npm run translate:sync-openapi-titles`). It runs at the start of `translate:generate` and on the translate-on-main workflow. Lingo translates the `title` field for target locales; the script only fills a missing target `title` with English (bootstrap)—it does not overwrite existing translations. +- Mintlify custom heading IDs use markdown `## Title {#slug}` (see Mintlify docs). Slugs are aligned from English source root files via `scripts/sync-heading-anchors.mjs`. Never translate or alter `{#…}`; sync runs after Lingo in PIT/CI. Do not merge heading-count drift between source root and target locales without fixing structure first. +- For Mintlify `` components that may be deep-linked, always set explicit `id` values (do not rely on title-derived hashes) and keep those `id`s identical across locales. +- Do not use fragile OpenAPI parameter hash links like `#parameter-*` in docs links; link to the endpoint page and reference parameter names inline in prose/code. +- Treat `lingo/brand-voice.md` as the source of truth for **brand voice** (tone and style) only. Do not add structural or tooling rules there (for example Mintlify `{#slug}` handling); those stay in this rule, README, and optionally in the Lingo engine **Instructions** field—not brand voice. +- Do not add script-based brand voice sync; sync brand voice on demand via Lingo MCP tools from chat. +- Treat `lingo/glossary.csv` as the glossary source of truth; use MCP sync with canonical key `sourceLocale|targetLocale|type|sourceText`. +- For glossary sync, always do dry-run first, show create/update/delete counts, then apply on confirmation. +- When adding locales, update both `docs.json` language blocks and `i18n.json` `locale.targets`. diff --git a/assets/features/leaderboards/eligibility.png b/assets/features/leaderboards/eligibility.png new file mode 100644 index 00000000..64554a99 Binary files /dev/null and b/assets/features/leaderboards/eligibility.png differ diff --git a/es/features/leaderboards.mdx b/es/features/leaderboards.mdx index a0b2562e..a94e8c40 100644 --- a/es/features/leaderboards.mdx +++ b/es/features/leaderboards.mdx @@ -134,6 +134,46 @@ Para obtener las clasificaciones de un grupo particular de usuarios con una comb para que un usuario se incluya en las clasificaciones devueltas. +## Elegibilidad {#eligibility} + +La elegibilidad controla qué usuarios pueden participar en una clasificación. Úsala cuando una clasificación deba estar abierta solo a un grupo específico de usuarios, por ejemplo, clientes de pago, una región concreta o una cohorte beta. + +La elegibilidad se basa en [atributos de usuario personalizados](/es/features/users#custom-user-attributes). Un usuario solo puede participar en una clasificación, y aparecer en su ranking, si sus atributos cumplen todas las reglas de elegibilidad que definas. Las clasificaciones sin reglas de elegibilidad están abiertas a todos los usuarios. + +Para definir reglas de elegibilidad, ve a la página de configuración de la clasificación y añade uno o más atributos de usuario, junto con los valores que deben tener los usuarios, en el campo **Atributos de usuario** de la sección **Elegibilidad**. + + + + + + + Cuando defines varias reglas de elegibilidad, un usuario debe cumplirlas todas + para ser elegible. + + +Los usuarios que no son elegibles no reciben puntuación y no aparecen en los rankings. Las posiciones se calculan solo entre los usuarios elegibles, por lo que no quedan huecos donde habría estado un usuario no elegible. Si los atributos de un usuario cambian y ya no cumplen las reglas, Trophy lo elimina de la ejecución actual de la clasificación. + +Las ejecuciones históricas nunca se modifican. Una vez que una ejecución ha finalizado, sus rankings quedan fijados, independientemente de cómo cambien después los atributos de los usuarios. + + + Las reglas de elegibilidad solo se pueden cambiar mientras una clasificación está `Inactive`. Consulta + [estados de la clasificación](#leaderboard-statuses). + + +### Elegibilidad frente a desgloses {#eligibility-vs-breakdowns} + +Los [desgloses](#ranking-breakdowns) y la elegibilidad usan atributos de usuario, pero cumplen funciones distintas: + +- **Los desgloses** dividen a los participantes en grupos más pequeños. Todos los usuarios siguen clasificados, pero dentro de su propio grupo. +- **La elegibilidad** decide quién puede participar. Los usuarios que no cumplen las reglas quedan excluidos por completo. + +Puedes usar ambas opciones en la misma clasificación, por ejemplo, para limitar una clasificación a los usuarios de pago y luego agruparlos por país. + ## Fechas de Inicio y Fin {#start-end-dates} Usa fechas de inicio y fin para controlar la ventana dentro de la cual las clasificaciones están clasificando activamente a los usuarios. @@ -211,6 +251,8 @@ Si una clasificación ya tiene un número de participantes que coincide con su m La única excepción a esto es cuando se utilizan [atributos de desglose](#ranking-breakdowns) para agrupar a los participantes en cohortes más pequeñas. Al usar atributos de desglose, el límite de participantes se aplica a cada grupo, no de manera general. +Si una clasificación tiene reglas de [elegibilidad](#eligibility), el límite de participantes solo cuenta a los usuarios elegibles. + Si necesitas un límite de participantes mayor, considera usar [atributos de desglose](#ranking-breakdowns) para agrupar a los participantes en cohortes más pequeñas. O contáctanos en [support@trophy.so](mailto:support@trophy.so) y estaremos encantados de aumentar tus valores predeterminados. @@ -255,7 +297,7 @@ Para crear una clasificación, dirígete a la [página de clasificaciones](https - Guarda los cambios y dirígete a la página de configuración para establecer [fechas de inicio y fin](#start-end-dates), [programaciones de clasificaciones recurrentes](#repeating-leaderboards) y más. + Guarda los cambios y ve a la página de configuración para definir la [elegibilidad](#eligibility), las [fechas de inicio y fin](#start-end-dates), las [programaciones de clasificaciones recurrentes](#repeating-leaderboards) y más. @@ -365,6 +407,14 @@ Este gráfico es un histograma de las puntuaciones de los usuarios en una clasif {" "} + + Sí. Añade reglas de [elegibilidad](#eligibility) a la clasificación mediante atributos + de usuario personalizados, y solo los usuarios con los valores de atributo correspondientes podrán + participar y aparecer en la clasificación. + + +{" "} + Trophy admite la ejecución de clasificaciones repetitivas en cualquier número arbitrario de días. Así que una clasificación semanal sería simplemente una clasificación que se repite cada 7 diff --git a/es/features/users.mdx b/es/features/users.mdx index 501d226e..9e0b8f8e 100644 --- a/es/features/users.mdx +++ b/es/features/users.mdx @@ -187,6 +187,13 @@ Los atributos personalizados de usuario pueden utilizarse para personalizar logr > Controla qué usuarios reciben correos electrónicos gamificados. + + Limita las clasificaciones a grupos específicos de usuarios. + +## Eligibility {#eligibility} + +Eligibility controls which users are allowed to take part in a leaderboard at all. Use it when a leaderboard should only be open to a specific group of users, for example paying customers, a particular region or a beta cohort. + +Eligibility is based on [custom user attributes](/features/users#custom-user-attributes). A user can only take part in, and appear in the rankings of, a leaderboard if their attributes match every eligibility rule you set. Leaderboards with no eligibility rules are open to all users. + +To set eligibility rules, head to the leaderboard configuration page and add one or more user attributes and the values users must have in the **User attributes** field of the **Eligibility** section. + + + + + + + When you set multiple eligibility rules, a user must match all of them to be + eligible. + + +Users who aren't eligible are not scored and don't appear in rankings. Ranks are calculated across eligible users only, so there are no gaps where ineligible users would have been. If a user's attributes change and they no longer match, Trophy removes them from the current run of the leaderboard. + +Historical runs are never changed. Once a run has finished its rankings are fixed, regardless of how users' attributes change afterwards. + + + Eligibility rules can only be changed while a leaderboard is `Inactive`. See + [leaderboard statuses](#leaderboard-statuses). + + +### Eligibility vs Breakdowns {#eligibility-vs-breakdowns} + +[Breakdowns](#ranking-breakdowns) and eligibility both use user attributes but do different jobs: + +- **Breakdowns** split participants into smaller groups. Every user is still ranked, just within their own group. +- **Eligibility** decides who can participate. Users who don't match are excluded entirely. + +You can use both on the same leaderboard, for example to limit a leaderboard to paying users and then group them by country. + ## Start & End Dates {#start-end-dates} Use start and end dates to control the window within which leaderboards are actively ranking users. @@ -213,6 +253,8 @@ If a leaderboard already has a number of participants that matches its configure The only exception to this is when using [breakdown attributes](#ranking-breakdowns) to group participants into smaller cohorts. When using breakdown attributes the participant limit applies to each group, not overall. +If a leaderboard has [eligibility](#eligibility) rules, the participant limit only counts eligible users. + If you require a larger participant limit, considering using [breakdown attributes](#ranking-breakdowns) to group participants into smaller cohorts. Or please contact us at [support@trophy.so](mailto:support@trophy.so) and we'll be happy to increase your defaults. @@ -257,7 +299,7 @@ To create a leaderboard, head to the [leaderboards page](https://app.trophy.so/l - Hit save and head to the configure page to set up [start & end dates](#start-end-dates), [repeating leaderboard schedules](#repeating-leaderboards) and more. + Hit save and head to the configure page to set up [eligibility](#eligibility), [start & end dates](#start-end-dates), [repeating leaderboard schedules](#repeating-leaderboards) and more. @@ -367,6 +409,14 @@ This chart is a histogram of users' scores in a particular leaderboard. This is {" "} + + Yes. Add [eligibility](#eligibility) rules to the leaderboard using custom + user attributes, and only users with matching attribute values will be able + to take part and appear in the rankings. + + +{" "} + Trophy supports running repeating leaderboards on any arbitrary number of days. So a weekly leaderboard would just be a leaderboard that repeats every 7 diff --git a/features/users.mdx b/features/users.mdx index ff5dbc64..d8384a51 100644 --- a/features/users.mdx +++ b/features/users.mdx @@ -185,11 +185,18 @@ Custom user attributes can be used to personalize achievements, customize the wa Personalize email copy and subject lines with custom user attributes. + + Limit leaderboards to specific groups of users. + #### Data Aggregation and Filtering {#data-aggregation-and-filtering} diff --git a/i18n.lock b/i18n.lock index a83c0fdd..9683a391 100644 --- a/i18n.lock +++ b/i18n.lock @@ -2967,7 +2967,7 @@ checksums: content/41: 05641d90a0717b85be807cc98f584c6a content/42: 95e371be1e0fec6192b97fe249708847 content/43: 3ad13a1574f2de214f4c699755082c41 - content/44: 9cc84e456ef816bb07be30921d288061 + content/44: 0300a96edf67f441577e9218fa2c898e content/45: 3fe90af4210ad4881b08f5a23dc73802 content/46: 64254f469606a3fd7f45c6a3591f8458 content/47: c6f004129299c86e60eeeeae9306858c @@ -3482,76 +3482,92 @@ checksums: content/43: 218e40ee0c9c118800a6bbc8c22a0406 content/44: 73dc7193c431ee27face3e96cca59471 content/45: abdc26659c4e48ac2a87a447c8069e21 - content/46: 2da3bce39f011d227e7439bf39000c4b - content/47: 376db24492fd179249a97399cac62c7b - content/48: 1f65a0e76a538cfda34ef69ff7316d11 - content/49: be8ee42c61c2f8412a2223346fd1e5d7 - content/50: 19ddbf4a6a27d1217a35ff1ab7082883 - content/51: 6978c15affabcb936aeac6d02d2a9201 - content/52: f53d2373b481a20fe76d332f4a102b84 - content/53: 26f14651033af5a7e0da93c5dd653916 - content/54: 84be083ce8f618a49aa37c1b5ebae0ad - content/55: 0c38bac95149de65c170e1dc15518530 - content/56: 09b8810dc95ee7576ab78014537171b0 - content/57: caea7e41aac97a6ddc795b729dea0931 - content/58: 273ebc1d83cace35c22fa22d2197272c - content/59: a613f2df863b43e86ca0da44e6f15453 - content/60: b33e18f0f1d560693bb4dbd7e1721d4f - content/61: 7f6326115eac856bb1a7ff6880f71906 - content/62: d8f1e59bb6a7b4a47013dd4ac7685903 - content/63: 6b8ea2b42e1d06e799a5a8dd20f18e4c - content/64: f2795e78ad428a0fc0d12ad8186b899a - content/65: 68fd00d1807b1c3633aa5546dc4144d5 - content/66: f6e705bf5e8338559f9cdecad266d761 - content/67: 29eba1f6a44ec24ee2b0d97faff09564 - content/68: 99c2177f3abb1ba8d08e6d45f6f7fb47 - content/69: b51af007ac57c287f3cc3e85e77d6aec - content/70: 610db0f043625ec6ace32150e8b96a66 - content/71: 258c9459be6a0fc3f535b41a89922f50 - content/72: afef5bbb2a19c4fd4958de5066bc51c1 - content/73: 830481b98e88ae69a3971df3aff5d250 - content/74: 538df8a5de4562fa88ae14bd586adc97 - content/75: 13d00a5c8b9f29080a87b703527bda75 - content/76: b0655e68a1c5d9579fbcfdd75db04fce - content/77: 71d221bcbeb83b663ee8c0a0e4975b04 - content/78: a40d8cc248a02d4ce34ae16119b14bf8 - content/79: 4e5ddca85ed818337a2757ae6bc2dc64 - content/80: a27e8a0a72073744f925f2820a1b7684 - content/81: 73aea3ef20f5f9d1626fcc73fdea597d - content/82: a01a451f7aba1a80f0af7148df811dfc - content/83: 84a204a7da04ff738ebcc5f4d3a5ea35 - content/84: 3347863434fc4a0bd8f075b971428c95 - content/85: bb2ac20c712241859473f415c26a0a11 - content/86: e180f5d5909fef8296047e0782fe8a85 - content/87: 9c2bbabcea6c0a514182170b67f38183 - content/88: 214aa9e0d35236656ba1738c8be4c3f0 - content/89: 919b58f12a4f16d63b5a35d18fdf04bb - content/90: 2ca28a1219570c10ddeaf4cb1157bb6d - content/91: a6390089f6e92430098d3d97c610c17a - content/92: 300bcdb15fbcadfde3db772fafe63524 - content/93: 3d93f05e4ce92565d044836c948352a9 - content/94: 0d08ff106c532b64677561ea2c3bad1f - content/95: 116df75dc256a5019b4809510f35677c - content/96: 162d669fb5a317e3a9deb84a1745c08c - content/97: 0ab559de8fc24b6e6cca6175444cfe9a - content/98: fab414b91a7a42cd5e37a0c5ade9945a - content/99: b87703add8d1e584a1307b28c871a0db - content/100: 2d9538daa0f3bcd2d760598097ee5d80 - content/101: 9c3265eff155d4a823fd955cde7dba35 - content/102: 689a23c317009f8944c124a0058137bc - content/103: ed10345c824c6fc4c69c882e9983b12b - content/104: fb9ce2e2edb7815efec73b696b5d5e95 - content/105: afd2cbb809e9881ac70735964981a355 - content/106: 44d8170f535788a47c7874e25dedb768 - content/107: fa9f6f1b8a7829a8e68a1d0340a84533 - content/108: b38dbc626d4fb02a18b368e626ca7cb0 - content/109: 79524f874744893afda00224a0203ea6 - content/110: 0fa45599d835da6f2e916859554673eb - content/111: 450ad7f99b79b12599856f813b9ef758 - content/112: 4f9f49c4756aa59d40fcc76ca15669a9 - content/113: f151f31b2581a7bd7f7d8635bff09dc8 - content/114: 10e8e9a59847ddd720c6d06b12c7b120 - content/115: 9799b973234bf2d10d5c971b267f55a5 + content/46: 1b2ef030b4a9c9ed76c1dae370bea77f + content/47: f16e2f4ef0731081b33838c8cb17fc34 + content/48: e839364a016e77e989922c6ac8a76a02 + content/49: 095c75f86e61d82e96e1159397765fe4 + content/50: a564b0f8fe8c5dd0d5cfa9f1897d01b2 + content/51: 8d4047550c18a4f9f5274fb479694e97 + content/52: 2dbb9d26048c96ab4d272d573dbc0591 + content/53: 8f3a2c9e3e4cff919cf089384a3e0691 + content/54: e30b47ecd8885f1c5e558937cb87f856 + content/55: 41beacaa919d50752ac1b81b3f109563 + content/56: 7d4548e2d24c1055bb6d6d03201bc285 + content/57: 5e840fe671834ee5d385c9dc4ed6ee49 + content/58: 75e7f0244426fbf3fc98265d184dd8ea + content/59: 2da3bce39f011d227e7439bf39000c4b + content/60: 376db24492fd179249a97399cac62c7b + content/61: 1f65a0e76a538cfda34ef69ff7316d11 + content/62: be8ee42c61c2f8412a2223346fd1e5d7 + content/63: 19ddbf4a6a27d1217a35ff1ab7082883 + content/64: 6978c15affabcb936aeac6d02d2a9201 + content/65: f53d2373b481a20fe76d332f4a102b84 + content/66: 26f14651033af5a7e0da93c5dd653916 + content/67: 84be083ce8f618a49aa37c1b5ebae0ad + content/68: 0c38bac95149de65c170e1dc15518530 + content/69: 09b8810dc95ee7576ab78014537171b0 + content/70: caea7e41aac97a6ddc795b729dea0931 + content/71: 273ebc1d83cace35c22fa22d2197272c + content/72: a613f2df863b43e86ca0da44e6f15453 + content/73: b33e18f0f1d560693bb4dbd7e1721d4f + content/74: 7f6326115eac856bb1a7ff6880f71906 + content/75: d8f1e59bb6a7b4a47013dd4ac7685903 + content/76: 6b8ea2b42e1d06e799a5a8dd20f18e4c + content/77: f2795e78ad428a0fc0d12ad8186b899a + content/78: 68fd00d1807b1c3633aa5546dc4144d5 + content/79: f6e705bf5e8338559f9cdecad266d761 + content/80: 29eba1f6a44ec24ee2b0d97faff09564 + content/81: 99c2177f3abb1ba8d08e6d45f6f7fb47 + content/82: b51af007ac57c287f3cc3e85e77d6aec + content/83: 610db0f043625ec6ace32150e8b96a66 + content/84: 258c9459be6a0fc3f535b41a89922f50 + content/85: d7dfcd0c7c754b5ce13f1578a4f99954 + content/86: afef5bbb2a19c4fd4958de5066bc51c1 + content/87: 830481b98e88ae69a3971df3aff5d250 + content/88: 538df8a5de4562fa88ae14bd586adc97 + content/89: 13d00a5c8b9f29080a87b703527bda75 + content/90: b0655e68a1c5d9579fbcfdd75db04fce + content/91: 71d221bcbeb83b663ee8c0a0e4975b04 + content/92: a40d8cc248a02d4ce34ae16119b14bf8 + content/93: 4e5ddca85ed818337a2757ae6bc2dc64 + content/94: a27e8a0a72073744f925f2820a1b7684 + content/95: 70417ef01650ced9aba95003d897b92f + content/96: a01a451f7aba1a80f0af7148df811dfc + content/97: 84a204a7da04ff738ebcc5f4d3a5ea35 + content/98: 3347863434fc4a0bd8f075b971428c95 + content/99: bb2ac20c712241859473f415c26a0a11 + content/100: e180f5d5909fef8296047e0782fe8a85 + content/101: 9c2bbabcea6c0a514182170b67f38183 + content/102: 214aa9e0d35236656ba1738c8be4c3f0 + content/103: 919b58f12a4f16d63b5a35d18fdf04bb + content/104: 2ca28a1219570c10ddeaf4cb1157bb6d + content/105: a6390089f6e92430098d3d97c610c17a + content/106: 300bcdb15fbcadfde3db772fafe63524 + content/107: 3d93f05e4ce92565d044836c948352a9 + content/108: 0d08ff106c532b64677561ea2c3bad1f + content/109: 116df75dc256a5019b4809510f35677c + content/110: 162d669fb5a317e3a9deb84a1745c08c + content/111: 0ab559de8fc24b6e6cca6175444cfe9a + content/112: fab414b91a7a42cd5e37a0c5ade9945a + content/113: b87703add8d1e584a1307b28c871a0db + content/114: 2d9538daa0f3bcd2d760598097ee5d80 + content/115: 9c3265eff155d4a823fd955cde7dba35 + content/116: 689a23c317009f8944c124a0058137bc + content/117: ed10345c824c6fc4c69c882e9983b12b + content/118: fb9ce2e2edb7815efec73b696b5d5e95 + content/119: afd2cbb809e9881ac70735964981a355 + content/120: 44d8170f535788a47c7874e25dedb768 + content/121: fa9f6f1b8a7829a8e68a1d0340a84533 + content/122: b38dbc626d4fb02a18b368e626ca7cb0 + content/123: 79524f874744893afda00224a0203ea6 + content/124: 0fa45599d835da6f2e916859554673eb + content/125: 450ad7f99b79b12599856f813b9ef758 + content/126: 4f9f49c4756aa59d40fcc76ca15669a9 + content/127: 917173aa35b9a8d822a0dfe9220401f7 + content/128: 4f9f49c4756aa59d40fcc76ca15669a9 + content/129: f151f31b2581a7bd7f7d8635bff09dc8 + content/130: 10e8e9a59847ddd720c6d06b12c7b120 + content/131: 9799b973234bf2d10d5c971b267f55a5 6e661270fda5f62c58626640850c56b3: meta/title: d17179a947e9687dd2338ca524b26b63 meta/description: a491b9e76be8c65506d2af60e6c6b972 diff --git a/openapi/admin.json b/openapi/admin.json index a412e574..f804e86a 100644 --- a/openapi/admin.json +++ b/openapi/admin.json @@ -2,13 +2,13 @@ "openapi": "3.1.0", "info": { "title": "Trophy", - "version": "1.26.0" + "version": "1.27.0" }, "x-fern-global-headers": [ { "header": "X-SDK-VERSION", "name": "sdkVersion", - "x-fern-default": "1.26.0" + "x-fern-default": "1.27.0" } ], "paths": { @@ -4884,7 +4884,13 @@ "550e8400-e29b-41d4-a716-446655440010" ], "runUnit": "month", - "runInterval": 1 + "runInterval": 1, + "userAttributes": [ + { + "attributeId": "550e8400-e29b-41d4-a716-446655440010", + "attributeValue": "premium" + } + ] }, { "id": "550e8400-e29b-41d4-a716-446655440101", @@ -4894,7 +4900,8 @@ "rankBy": "streak", "maxParticipants": 1000, "start": "2026-04-27", - "breakdownAttributes": [] + "breakdownAttributes": [], + "userAttributes": [] } ] } @@ -5314,7 +5321,8 @@ "550e8400-e29b-41d4-a716-446655440010" ], "runUnit": "month", - "runInterval": 1 + "runInterval": 1, + "userAttributes": [] } }, "Streak leaderboard": { @@ -5326,7 +5334,8 @@ "rankBy": "streak", "maxParticipants": 1000, "start": "2026-04-27", - "breakdownAttributes": [] + "breakdownAttributes": [], + "userAttributes": [] } } } @@ -8149,7 +8158,7 @@ }, "userAttributes": { "type": "array", - "description": "User attribute filters applied to the achievement. Each `attributeId` must be an active user attribute.", + "description": "User attribute filters applied to the achievement.", "items": { "type": "object", "properties": { @@ -8471,7 +8480,7 @@ "array", "null" ], - "description": "Updated user attribute filters. Send `null` to clear. Each `attributeId` must be an active user attribute.", + "description": "Updated user attribute filters. Send `null` to clear.", "items": { "type": "object", "properties": { @@ -8726,6 +8735,26 @@ "minimum": 1, "description": "The number of `runUnit`s between repeats. Required when `runUnit` is set.", "example": 1 + }, + "userAttributes": { + "type": "array", + "description": "User attribute filters. Omit to allow every user.", + "items": { + "type": "object", + "properties": { + "attributeId": { + "type": "string", + "format": "uuid" + }, + "attributeValue": { + "type": "string" + } + }, + "required": [ + "attributeId", + "attributeValue" + ] + } } }, "required": [ @@ -8834,6 +8863,26 @@ "runInterval": { "type": "integer", "description": "The number of recurrence units between leaderboard runs." + }, + "userAttributes": { + "type": "array", + "description": "User attribute filters.", + "items": { + "type": "object", + "properties": { + "attributeId": { + "type": "string", + "format": "uuid" + }, + "attributeValue": { + "type": "string" + } + }, + "required": [ + "attributeId", + "attributeValue" + ] + } } }, "required": [ @@ -8844,7 +8893,8 @@ "rankBy", "maxParticipants", "start", - "breakdownAttributes" + "breakdownAttributes", + "userAttributes" ] }, "CreateLeaderboardsResponse": { @@ -9015,6 +9065,34 @@ "type": "integer", "minimum": 1, "description": "The updated recurrence interval." + }, + "userAttributes": { + "oneOf": [ + { + "type": "array", + "description": "Replacement user attribute filters.", + "items": { + "type": "object", + "properties": { + "attributeId": { + "type": "string", + "format": "uuid" + }, + "attributeValue": { + "type": "string" + } + }, + "required": [ + "attributeId", + "attributeValue" + ] + } + }, + { + "type": "null" + } + ], + "description": "Replacement user attribute filters, or `null` to clear them. Omit to leave the current filters unchanged. This can only be changed while the leaderboard is inactive." } }, "required": [ diff --git a/openapi/application.json b/openapi/application.json index 889318f1..9f4b847c 100644 --- a/openapi/application.json +++ b/openapi/application.json @@ -2,13 +2,13 @@ "openapi": "3.1.0", "info": { "title": "Trophy", - "version": "1.26.0" + "version": "1.27.0" }, "x-fern-global-headers": [ { "header": "X-SDK-VERSION", "name": "sdkVersion", - "x-fern-default": "1.26.0" + "x-fern-default": "1.27.0" } ], "paths": { @@ -79,7 +79,17 @@ "badgeUrl": "https://example.com/badge.png", "key": "finish-onboarding", "completions": 8, - "rarity": 80 + "rarity": 80, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + }, + { + "key": "region", + "value": "us-east" + } + ] }, { "id": "5100fe51-6bce-6j44-b0hs-bddc4e123683", @@ -141,7 +151,17 @@ "badgeUrl": "https://example.com/badge.png", "anniversaryYears": 1, "completions": 4, - "rarity": 40 + "rarity": 40, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + }, + { + "key": "region", + "value": "us-east" + } + ] } ] } @@ -242,11 +262,18 @@ "description": "Complete the onboarding process.", "badgeUrl": "https://example.com/badge.png", "key": "finish-onboarding", - "achievedAt": "2021-01-01T00:00:00Z" + "achievedAt": "2021-01-01T00:00:00Z", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, "points": { "points-system-key": { "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "points-system-key", "name": "XP", "description": null, "badgeUrl": null, @@ -262,7 +289,16 @@ "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", "type": "achievement", "achievementName": "Finish onboarding", - "points": 10 + "points": 10, + "status": "active", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ], + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z" } } ] @@ -334,7 +370,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true + "subscribeToEmails": true } } } @@ -366,11 +402,11 @@ "x-codeSamples": [ { "lang": "javascript", - "source": "import { TrophyApiClient } from '@trophyso/node';\n\nconst trophy = new TrophyApiClient({\n apiKey: 'YOUR_API_KEY'\n});\n\nconst response = await trophy.metrics.event(\n \"words-written\",\n {\n user: {\n id: 'user-id',\n email: 'user@example.com',\n tz: 'Europe/London',\n subscribedToEmails: true,\n attributes: {\n department: 'engineering',\n role: 'developer'\n }\n },\n value: 750,\n attributes: {\n category: 'writing',\n source: 'mobile-app'\n }\n }\n);\n" + "source": "import { TrophyApiClient } from '@trophyso/node';\n\nconst trophy = new TrophyApiClient({\n apiKey: 'YOUR_API_KEY'\n});\n\nconst response = await trophy.metrics.event(\n \"words-written\",\n {\n user: {\n id: 'user-id',\n email: 'user@example.com',\n tz: 'Europe/London',\n subscribeToEmails: true,\n attributes: {\n department: 'engineering',\n role: 'developer'\n }\n },\n value: 750,\n attributes: {\n category: 'writing',\n source: 'mobile-app'\n }\n }\n);\n" }, { "lang": "python", - "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nuser = UpsertedUser(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribedToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n\nresponse = client.metrics.event(\n \"words-written\",\n user=user,\n value=750,\n attributes={\n \"category\": \"writing\",\n \"source\": \"mobile-app\"\n})\n" + "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nuser = UpsertedUser(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribeToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n\nresponse = client.metrics.event(\n \"words-written\",\n user=user,\n value=750,\n attributes={\n \"category\": \"writing\",\n \"source\": \"mobile-app\"\n})\n" }, { "lang": "go", @@ -422,7 +458,13 @@ "metricValue": 500, "name": "500 words written", "description": "Write 500 words in the app.", - "achievedAt": "2020-01-01T00:00:00Z" + "achievedAt": "2020-01-01T00:00:00Z", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] } ], "currentStreak": { @@ -431,11 +473,13 @@ "started": "2025-04-02", "periodStart": "2025-03-31", "periodEnd": "2025-04-05", - "expires": "2025-04-12" + "expires": "2025-04-12", + "extended": true }, "points": { "xp": { "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "xp", "name": "XP", "description": null, "badgeUrl": null, @@ -460,7 +504,16 @@ "type": "metric", "metricName": "words written", "metricThreshold": 100, - "points": 10 + "points": 10, + "status": "active", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ], + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z" } } ] @@ -477,6 +530,8 @@ "runInterval": null, "maxParticipants": 100, "breakdownAttribute": null, + "breakdownAttributes": [], + "userAttributes": [], "metricName": "words written", "metricKey": "words-written", "threshold": 10, @@ -610,11 +665,11 @@ "x-codeSamples": [ { "lang": "javascript", - "source": "import { TrophyApiClient } from '@trophyso/node';\n\nconst trophy = new TrophyApiClient({\n apiKey: 'YOUR_API_KEY'\n});\n\nconst response = await trophy.users.create({\n id: 'user-id',\n email: 'user@example.com',\n tz: 'Europe/London',\n subscribedToEmails: true,\n attributes: {\n department: 'engineering',\n role: 'developer'\n }\n});\n" + "source": "import { TrophyApiClient } from '@trophyso/node';\n\nconst trophy = new TrophyApiClient({\n apiKey: 'YOUR_API_KEY'\n});\n\nconst response = await trophy.users.create({\n id: 'user-id',\n email: 'user@example.com',\n tz: 'Europe/London',\n subscribeToEmails: true,\n attributes: {\n department: 'engineering',\n role: 'developer'\n }\n});\n" }, { "lang": "python", - "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.users.create(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribedToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n" + "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.users.create(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribeToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n" }, { "lang": "go", @@ -651,7 +706,8 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, + "control": false, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -762,7 +818,8 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, + "control": false, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -821,7 +878,7 @@ }, { "lang": "python", - "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.users.identify(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribedToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n" + "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.users.identify(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribeToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n" }, { "lang": "go", @@ -873,6 +930,23 @@ "application/json": { "schema": { "$ref": "#/components/schemas/User" + }, + "examples": { + "Successful operation": { + "value": { + "id": "user-id", + "email": "user@example.com", + "tz": "Europe/London", + "subscribeToEmails": true, + "control": false, + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z", + "attributes": { + "department": "engineering", + "role": "developer" + } + } + } } } } @@ -923,7 +997,7 @@ }, { "lang": "python", - "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.users.update(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribedToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n" + "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.users.update(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribeToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n" }, { "lang": "go", @@ -975,6 +1049,23 @@ "application/json": { "schema": { "$ref": "#/components/schemas/User" + }, + "examples": { + "Successful operation": { + "value": { + "id": "user-id", + "email": "user@example.com", + "tz": "Europe/London", + "subscribeToEmails": true, + "control": false, + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z", + "attributes": { + "department": "engineering", + "role": "developer" + } + } + } } } } @@ -1392,7 +1483,13 @@ "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", "metricValue": 500, "achievedAt": "2021-01-01T00:00:00Z", - "badgeUrl": "https://example.com/badge1.png" + "badgeUrl": "https://example.com/badge1.png", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, { "id": "8a07f2d0-9c72-4de1-bf92-9530ae82b4b6", @@ -1402,7 +1499,13 @@ "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", "metricValue": 1000, "achievedAt": "2021-01-02T00:00:00Z", - "badgeUrl": "https://example.com/badge2.png" + "badgeUrl": "https://example.com/badge2.png", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, { "id": "2090d038-aa04-4048-ab2e-e2b7bf2d3b9f", @@ -1412,7 +1515,13 @@ "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", "metricValue": 2000, "achievedAt": null, - "badgeUrl": "null" + "badgeUrl": "null", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] } ] } @@ -1538,7 +1647,13 @@ "name": "Novice Writer", "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", "metricValue": 500, - "achievedAt": "2021-01-01T00:00:00Z" + "achievedAt": "2021-01-01T00:00:00Z", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, { "id": "8a07f2d0-9c72-4de1-bf92-9530ae82b4b6", @@ -1546,7 +1661,13 @@ "name": "Intermediate Writer", "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", "metricValue": 1000, - "achievedAt": "2021-01-02T00:00:00Z" + "achievedAt": "2021-01-02T00:00:00Z", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, { "id": "2090d038-aa04-4048-ab2e-e2b7bf2d3b9f", @@ -1555,7 +1676,13 @@ "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", "metricValue": 2000, "achievedAt": null, - "badgeUrl": "https://example.com/badge.png" + "badgeUrl": "https://example.com/badge.png", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] } ] } @@ -1886,7 +2013,13 @@ "achievedAt": "2021-01-01T00:00:00Z", "badgeUrl": "https://example.com/badge2.png", "completions": 100, - "rarity": 50 + "rarity": 50, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, { "id": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", @@ -1899,7 +2032,13 @@ "achievedAt": "2021-02-01T00:00:00Z", "badgeUrl": "https://example.com/badge1.png", "completions": 100, - "rarity": 50 + "rarity": 50, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, { "id": "d01dcbcb-d51e-4c12-b054-dc811dcdc624", @@ -1910,7 +2049,13 @@ "achievedAt": "2021-03-01T00:00:00Z", "badgeUrl": "https://example.com/badge2.png", "completions": 100, - "rarity": 50 + "rarity": 50, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, { "id": "d01dcbcb-d51e-4c12-b054-dc811dcdc626", @@ -1920,7 +2065,13 @@ "achievedAt": "2021-04-01T00:00:00Z", "badgeUrl": "https://example.com/badge3.png", "completions": 40, - "rarity": 20 + "rarity": 20, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] } ] } @@ -2340,8 +2491,17 @@ "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", "type": "metric", "points": 10, + "status": "active", "metricName": "words written", - "metricThreshold": 1000 + "metricThreshold": 1000, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ], + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z" } } ] @@ -2814,6 +2974,8 @@ "end": null, "maxParticipants": 100, "breakdownAttribute": null, + "breakdownAttributes": [], + "userAttributes": [], "runUnit": "day", "runInterval": 7, "rank": 2, @@ -3009,7 +3171,13 @@ "metricName": "Words Written", "achievedAt": "2024-03-15T14:30:00Z", "completions": 150, - "rarity": 15 + "rarity": 15, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] } ], "leaderboards": { @@ -3024,6 +3192,8 @@ "start": "2024-03-11", "end": "2024-03-17", "maxParticipants": 100, + "breakdownAttributes": [], + "userAttributes": [], "runUnit": "day", "runInterval": 7, "rank": 3, @@ -3119,7 +3289,13 @@ "metricName": "Words Written", "achievedAt": "2024-03-15T14:30:00Z", "completions": 150, - "rarity": 15 + "rarity": 15, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] } ], "leaderboards": {}, @@ -3446,6 +3622,12 @@ "points": 10, "status": "active", "streakLengthThreshold": 7, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ], "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z" }, @@ -3973,6 +4155,12 @@ "country", "plan" ], + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ], "runUnit": "day", "runInterval": 7 }, @@ -3990,6 +4178,7 @@ "maxParticipants": 50, "breakdownAttribute": null, "breakdownAttributes": [], + "userAttributes": [], "runUnit": null, "runInterval": null } @@ -4165,6 +4354,8 @@ "end": null, "maxParticipants": 100, "breakdownAttribute": null, + "breakdownAttributes": [], + "userAttributes": [], "runUnit": "day", "runInterval": 7, "rankings": [ @@ -4275,7 +4466,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -4346,7 +4537,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -4420,7 +4611,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -4494,7 +4685,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -4562,7 +4753,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -4631,7 +4822,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -4695,7 +4886,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -4822,7 +5013,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -5045,6 +5236,7 @@ "end": null, "maxParticipants": 100, "breakdownAttribute": null, + "userAttributes": [], "runUnit": "day", "runInterval": 7, "rankings": [ @@ -5116,6 +5308,7 @@ "end": null, "maxParticipants": 100, "breakdownAttribute": null, + "userAttributes": [], "runUnit": "day", "runInterval": 7, "rankings": [ @@ -5199,6 +5392,7 @@ "end": null, "maxParticipants": 100, "breakdownAttribute": null, + "userAttributes": [], "runUnit": "day", "runInterval": 7, "rankings": [ @@ -5502,7 +5696,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -5522,6 +5716,7 @@ "end": null, "maxParticipants": 100, "breakdownAttribute": null, + "userAttributes": [], "runUnit": "day", "runInterval": 7, "rank": 2, @@ -6410,6 +6605,29 @@ ], "description": "The interval between repetitions, relative to the start date and repetition type. Null for one-time leaderboards.", "example": 7 + }, + "userAttributes": { + "type": "array", + "description": "User attribute filters that a user must match to participate in this leaderboard and appear in its rankings.", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The key of the user attribute.", + "example": "plan-type" + }, + "value": { + "type": "string", + "description": "The required value of the user attribute.", + "example": "premium" + } + }, + "required": [ + "key", + "value" + ] + } } }, "required": [ @@ -6427,7 +6645,8 @@ "endTime", "maxParticipants", "runUnit", - "runInterval" + "runInterval", + "userAttributes" ] }, "LeaderboardResponseWithRankings": {