From 40362d24465f36c822b1ae988a92ec0f349f0797 Mon Sep 17 00:00:00 2001 From: Charlie Hopkins-Brinicombe Date: Mon, 30 Mar 2026 13:05:12 +0100 Subject: [PATCH 01/16] Base setup for translations --- .cursor/rules/localization-workflow.mdc | 14 + .cursorignore | 1 + .env.example | 8 + .github/workflows/translate-on-main.yml | 71 + .github/workflows/validate-translations.yml | 58 + .gitignore | 3 +- README.md | 86 + docs.json | 695 +- {account => en/account}/billing.mdx | 0 {account => en/account}/branding.mdx | 0 {account => en/account}/members.mdx | 0 {account => en/account}/overview.mdx | 0 .../endpoints/points/archive-a-boost.mdx | 0 .../endpoints/points/archive-boosts-batch.mdx | 0 .../endpoints/points/create-boosts.mdx | 0 .../endpoints/streaks/grant-freezes.mdx | 0 .../endpoints/streaks/restore-streaks.mdx | 0 {admin-api => en/admin-api}/introduction.mdx | 0 .../api-reference}/authentication.mdx | 0 .../api-reference}/client-libraries.mdx | 0 .../achievements/all-achievements.mdx | 0 .../mark-an-achievement-as-completed.mdx | 0 .../get-all-active-leaderboards.mdx | 0 .../leaderboards/get-leaderboard.mdx | 0 .../metrics/send-a-metric-change-event.mdx | 0 .../endpoints/points/get-points-boosts.mdx | 0 .../points/get-points-level-summary.mdx | 0 .../endpoints/points/get-points-levels.mdx | 0 .../endpoints/points/get-points-summary.mdx | 0 .../endpoints/points/get-points.mdx | 0 .../endpoints/streaks/get-streak-rankings.mdx | 0 .../endpoints/streaks/get-streaks.mdx | 0 .../endpoints/users/create-a-user.mdx | 0 ...single-metric-event-summary-for-a-user.mdx | 0 .../users/get-a-single-metric-for-a-user.mdx | 0 .../endpoints/users/get-a-single-user.mdx | 0 .../get-a-users-completed-achievements.mdx | 0 .../users/get-a-users-leaderboard.mdx | 0 .../users/get-a-users-points-boosts.mdx | 0 .../users/get-a-users-points-summary.mdx | 0 .../endpoints/users/get-a-users-points.mdx | 0 .../endpoints/users/get-a-users-streak.mdx | 0 .../endpoints/users/get-a-users-wrapped.mdx | 0 .../users/get-all-metrics-for-a-user.mdx | 0 .../endpoints/users/get-user-preferences.mdx | 0 .../endpoints/users/identify-a-user.mdx | 0 .../endpoints/users/update-a-user.mdx | 0 .../users/update-user-preferences.mdx | 0 .../api-reference}/idempotency.mdx | 0 .../api-reference}/introduction.mdx | 0 .../api-reference}/openapi.yml | 0 .../api-reference}/rate-limiting.mdx | 0 .../experimentation}/engagement.mdx | 0 .../experimentation}/overview.mdx | 0 .../experimentation}/retention.mdx | 0 .../getting-started}/introduction.mdx | 0 .../getting-started}/quickstart.mdx | 0 .../guides}/gamified-fitness-platform.mdx | 0 .../guides}/gamified-study-platform.mdx | 0 .../how-to-build-a-leaderboards-feature.mdx | 0 .../how-to-build-a-streaks-feature.mdx | 0 .../how-to-build-an-achievements-feature.mdx | 0 .../how-to-build-an-energy-feature.mdx | 0 .../guides}/how-to-build-an-xp-feature.mdx | 0 {platform => en/platform}/achievements.mdx | 0 {platform => en/platform}/emails.mdx | 0 {platform => en/platform}/events.mdx | 0 {platform => en/platform}/leaderboards.mdx | 0 {platform => en/platform}/metrics.mdx | 0 {platform => en/platform}/overview.mdx | 0 {platform => en/platform}/points.mdx | 0 .../platform}/push-notifications.mdx | 0 {platform => en/platform}/streaks.mdx | 0 {platform => en/platform}/users.mdx | 0 .../snippets}/add-env-var-block.mdx | 0 .../all-achievements-request-block.mdx | 0 .../all-achievements-response-block.mdx | 0 .../snippets}/control-flag-block.mdx | 0 .../event-attributes-request-block.mdx | 0 ...et-user-notification-preferences-block.mdx | 0 .../snippets}/idempotent-event-tracking.mdx | 0 .../snippets}/identify-user-request-block.mdx | 0 ...-user-with-device-tokens-request-block.mdx | 0 ...d-rankings-request-multiple-attributes.mdx | 0 ...oard-rankings-request-single-attribute.mdx | 0 .../leaderboard-rankings-request.mdx | 0 .../leaderboard-rankings-response.mdx | 0 ...hange-event-points-level-excerpt-block.mdx | 0 .../snippets}/metric-change-request-block.mdx | 0 .../metric-change-response-block.mdx | 0 ...event-with-device-tokens-request-block.mdx | 0 {snippets => en/snippets}/plan-badge.jsx | 0 ...oints-histogram-summary-response-block.mdx | 0 .../points-level-summary-response-block.mdx | 0 .../points-levels-list-response-block.mdx | 0 .../points-system-response-block.mdx | 0 .../snippets}/rate-limit-badge.jsx | 0 .../snippets}/sdk-install-command.mdx | 0 {snippets => en/snippets}/snippet-intro.mdx | 0 .../snippets}/streak-request-block.mdx | 0 .../snippets}/streak-response-block.mdx | 0 ...te-user-notification-preferences-block.mdx | 0 .../user-achievements-request-block.mdx | 0 .../snippets}/user-energy-response-block.mdx | 0 .../user-leaderboard-rankings-request.mdx | 0 .../user-leaderboard-rankings-response.mdx | 0 .../snippets}/user-points-request-block.mdx | 0 .../snippets}/user-points-response-block.mdx | 0 .../user-points-summary-request-block.mdx | 0 .../user-points-summary-response-block.mdx | 0 ...ook-points-level-changed-payload-block.mdx | 0 .../achievements/achievement-completed.mdx | 0 .../leaderboards/leaderboard-changed.mdx | 0 .../leaderboards/leaderboard-finished.mdx | 0 .../leaderboards/leaderboard-rank-changed.mdx | 0 .../leaderboards/leaderboard-started.mdx | 0 .../events/points/points-boost-finished.mdx | 0 .../events/points/points-boost-started.mdx | 0 .../events/points/points-changed.mdx | 0 .../events/points/points-level-changed.mdx | 0 .../events/streaks/streak-extended.mdx | 0 .../events/streaks/streak-freeze-consumed.mdx | 0 .../events/streaks/streak-freeze-earned.mdx | 0 .../webhooks}/events/streaks/streak-lost.mdx | 0 .../events/streaks/streak-started.mdx | 0 {webhooks => en/webhooks}/idempotency.mdx | 0 {webhooks => en/webhooks}/introduction.mdx | 0 {webhooks => en/webhooks}/observability.mdx | 0 {webhooks => en/webhooks}/quickstart.mdx | 0 {webhooks => en/webhooks}/retries.mdx | 0 {webhooks => en/webhooks}/security.mdx | 0 es/account/billing.mdx | 129 + es/account/branding.mdx | 24 + es/account/members.mdx | 57 + es/account/overview.mdx | 23 + .../endpoints/points/archive-a-boost.mdx | 9 + .../endpoints/points/archive-boosts-batch.mdx | 9 + .../endpoints/points/create-boosts.mdx | 9 + .../endpoints/streaks/grant-freezes.mdx | 9 + .../endpoints/streaks/restore-streaks.mdx | 9 + es/admin-api/introduction.mdx | 24 + es/api-reference/authentication.mdx | 83 + es/api-reference/client-libraries.mdx | 99 + .../achievements/all-achievements.mdx | 9 + .../mark-an-achievement-as-completed.mdx | 9 + .../get-all-active-leaderboards.mdx | 9 + .../leaderboards/get-leaderboard.mdx | 9 + .../metrics/send-a-metric-change-event.mdx | 9 + .../endpoints/points/get-points-boosts.mdx | 9 + .../points/get-points-level-summary.mdx | 9 + .../endpoints/points/get-points-levels.mdx | 9 + .../endpoints/points/get-points-summary.mdx | 9 + .../endpoints/points/get-points.mdx | 9 + .../endpoints/streaks/get-streak-rankings.mdx | 9 + .../endpoints/streaks/get-streaks.mdx | 9 + .../endpoints/users/create-a-user.mdx | 9 + ...single-metric-event-summary-for-a-user.mdx | 9 + .../users/get-a-single-metric-for-a-user.mdx | 9 + .../endpoints/users/get-a-single-user.mdx | 9 + .../get-a-users-completed-achievements.mdx | 9 + .../users/get-a-users-leaderboard.mdx | 9 + .../users/get-a-users-points-boosts.mdx | 9 + .../users/get-a-users-points-summary.mdx | 9 + .../endpoints/users/get-a-users-points.mdx | 9 + .../endpoints/users/get-a-users-streak.mdx | 9 + .../endpoints/users/get-a-users-wrapped.mdx | 9 + .../users/get-all-metrics-for-a-user.mdx | 9 + .../endpoints/users/get-user-preferences.mdx | 9 + .../endpoints/users/identify-a-user.mdx | 9 + .../endpoints/users/update-a-user.mdx | 9 + .../users/update-user-preferences.mdx | 9 + es/api-reference/idempotency.mdx | 69 + es/api-reference/introduction.mdx | 35 + es/api-reference/openapi.yml | 6288 +++++++++++++++++ es/api-reference/rate-limiting.mdx | 26 + es/experimentation/engagement.mdx | 56 + es/experimentation/overview.mdx | 70 + es/experimentation/retention.mdx | 38 + es/getting-started/introduction.mdx | 45 + es/getting-started/quickstart.mdx | 127 + es/guides/gamified-fitness-platform.mdx | 1227 ++++ es/guides/gamified-study-platform.mdx | 2314 ++++++ .../how-to-build-a-leaderboards-feature.mdx | 238 + es/guides/how-to-build-a-streaks-feature.mdx | 209 + .../how-to-build-an-achievements-feature.mdx | 252 + es/guides/how-to-build-an-energy-feature.mdx | 217 + es/guides/how-to-build-an-xp-feature.mdx | 263 + es/platform/achievements.mdx | 399 ++ es/platform/emails.mdx | 843 +++ es/platform/events.mdx | 221 + es/platform/leaderboards.mdx | 353 + es/platform/metrics.mdx | 112 + es/platform/overview.mdx | 74 + es/platform/points.mdx | 504 ++ es/platform/push-notifications.mdx | 272 + es/platform/streaks.mdx | 225 + es/platform/users.mdx | 435 ++ es/snippets/add-env-var-block.mdx | 5 + .../all-achievements-request-block.mdx | 36 + .../all-achievements-response-block.mdx | 48 + es/snippets/control-flag-block.mdx | 15 + .../event-attributes-request-block.mdx | 108 + ...et-user-notification-preferences-block.mdx | 36 + es/snippets/idempotent-event-tracking.mdx | 115 + es/snippets/identify-user-request-block.mdx | 14 + ...-user-with-device-tokens-request-block.mdx | 98 + ...d-rankings-request-multiple-attributes.mdx | 82 + ...oard-rankings-request-single-attribute.mdx | 82 + es/snippets/leaderboard-rankings-request.mdx | 47 + es/snippets/leaderboard-rankings-response.mdx | 41 + ...hange-event-points-level-excerpt-block.mdx | 21 + es/snippets/metric-change-request-block.mdx | 105 + es/snippets/metric-change-response-block.mdx | 83 + ...event-with-device-tokens-request-block.mdx | 116 + es/snippets/plan-badge.jsx | 26 + ...oints-histogram-summary-response-block.mdx | 17 + .../points-level-summary-response-block.mdx | 39 + .../points-levels-list-response-block.mdx | 30 + es/snippets/points-system-response-block.mdx | 49 + es/snippets/rate-limit-badge.jsx | 35 + es/snippets/sdk-install-command.mdx | 45 + es/snippets/snippet-intro.mdx | 4 + es/snippets/streak-request-block.mdx | 61 + es/snippets/streak-response-block.mdx | 49 + ...te-user-notification-preferences-block.mdx | 85 + .../user-achievements-request-block.mdx | 38 + es/snippets/user-energy-response-block.mdx | 39 + .../user-leaderboard-rankings-request.mdx | 41 + .../user-leaderboard-rankings-response.mdx | 45 + es/snippets/user-points-request-block.mdx | 45 + es/snippets/user-points-response-block.mdx | 36 + .../user-points-summary-request-block.mdx | 52 + .../user-points-summary-response-block.mdx | 21 + ...ook-points-level-changed-payload-block.mdx | 44 + .../achievements/achievement-completed.mdx | 8 + .../leaderboards/leaderboard-changed.mdx | 3 + .../leaderboards/leaderboard-finished.mdx | 15 + .../leaderboards/leaderboard-rank-changed.mdx | 3 + .../leaderboards/leaderboard-started.mdx | 12 + .../events/points/points-boost-finished.mdx | 9 + .../events/points/points-boost-started.mdx | 12 + es/webhooks/events/points/points-changed.mdx | 8 + .../events/points/points-level-changed.mdx | 7 + .../events/streaks/streak-extended.mdx | 8 + .../events/streaks/streak-freeze-consumed.mdx | 3 + .../events/streaks/streak-freeze-earned.mdx | 3 + es/webhooks/events/streaks/streak-lost.mdx | 3 + es/webhooks/events/streaks/streak-started.mdx | 3 + es/webhooks/idempotency.mdx | 57 + es/webhooks/introduction.mdx | 53 + es/webhooks/observability.mdx | 27 + es/webhooks/quickstart.mdx | 237 + es/webhooks/retries.mdx | 21 + es/webhooks/security.mdx | 67 + i18n.json | 137 + lingo/brand-voice.md | 4 + lingo/glossary.csv | 16 + lingo/instructions.global.txt | 21 + package.json | 6 +- scripts/generate-translations.mjs | 85 + scripts/translate-docs-json.mjs | 103 + scripts/validate-translations.mjs | 96 + 262 files changed, 19252 insertions(+), 212 deletions(-) create mode 100644 .cursor/rules/localization-workflow.mdc create mode 100644 .cursorignore create mode 100644 .env.example create mode 100644 .github/workflows/translate-on-main.yml create mode 100644 .github/workflows/validate-translations.yml rename {account => en/account}/billing.mdx (100%) rename {account => en/account}/branding.mdx (100%) rename {account => en/account}/members.mdx (100%) rename {account => en/account}/overview.mdx (100%) rename {admin-api => en/admin-api}/endpoints/points/archive-a-boost.mdx (100%) rename {admin-api => en/admin-api}/endpoints/points/archive-boosts-batch.mdx (100%) rename {admin-api => en/admin-api}/endpoints/points/create-boosts.mdx (100%) rename {admin-api => en/admin-api}/endpoints/streaks/grant-freezes.mdx (100%) rename {admin-api => en/admin-api}/endpoints/streaks/restore-streaks.mdx (100%) rename {admin-api => en/admin-api}/introduction.mdx (100%) rename {api-reference => en/api-reference}/authentication.mdx (100%) rename {api-reference => en/api-reference}/client-libraries.mdx (100%) rename {api-reference => en/api-reference}/endpoints/achievements/all-achievements.mdx (100%) rename {api-reference => en/api-reference}/endpoints/achievements/mark-an-achievement-as-completed.mdx (100%) rename {api-reference => en/api-reference}/endpoints/leaderboards/get-all-active-leaderboards.mdx (100%) rename {api-reference => en/api-reference}/endpoints/leaderboards/get-leaderboard.mdx (100%) rename {api-reference => en/api-reference}/endpoints/metrics/send-a-metric-change-event.mdx (100%) rename {api-reference => en/api-reference}/endpoints/points/get-points-boosts.mdx (100%) rename {api-reference => en/api-reference}/endpoints/points/get-points-level-summary.mdx (100%) rename {api-reference => en/api-reference}/endpoints/points/get-points-levels.mdx (100%) rename {api-reference => en/api-reference}/endpoints/points/get-points-summary.mdx (100%) rename {api-reference => en/api-reference}/endpoints/points/get-points.mdx (100%) rename {api-reference => en/api-reference}/endpoints/streaks/get-streak-rankings.mdx (100%) rename {api-reference => en/api-reference}/endpoints/streaks/get-streaks.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/create-a-user.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/get-a-single-metric-for-a-user.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/get-a-single-user.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/get-a-users-completed-achievements.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/get-a-users-leaderboard.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/get-a-users-points-boosts.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/get-a-users-points-summary.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/get-a-users-points.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/get-a-users-streak.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/get-a-users-wrapped.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/get-all-metrics-for-a-user.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/get-user-preferences.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/identify-a-user.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/update-a-user.mdx (100%) rename {api-reference => en/api-reference}/endpoints/users/update-user-preferences.mdx (100%) rename {api-reference => en/api-reference}/idempotency.mdx (100%) rename {api-reference => en/api-reference}/introduction.mdx (100%) rename {api-reference => en/api-reference}/openapi.yml (100%) rename {api-reference => en/api-reference}/rate-limiting.mdx (100%) rename {experimentation => en/experimentation}/engagement.mdx (100%) rename {experimentation => en/experimentation}/overview.mdx (100%) rename {experimentation => en/experimentation}/retention.mdx (100%) rename {getting-started => en/getting-started}/introduction.mdx (100%) rename {getting-started => en/getting-started}/quickstart.mdx (100%) rename {guides => en/guides}/gamified-fitness-platform.mdx (100%) rename {guides => en/guides}/gamified-study-platform.mdx (100%) rename {guides => en/guides}/how-to-build-a-leaderboards-feature.mdx (100%) rename {guides => en/guides}/how-to-build-a-streaks-feature.mdx (100%) rename {guides => en/guides}/how-to-build-an-achievements-feature.mdx (100%) rename {guides => en/guides}/how-to-build-an-energy-feature.mdx (100%) rename {guides => en/guides}/how-to-build-an-xp-feature.mdx (100%) rename {platform => en/platform}/achievements.mdx (100%) rename {platform => en/platform}/emails.mdx (100%) rename {platform => en/platform}/events.mdx (100%) rename {platform => en/platform}/leaderboards.mdx (100%) rename {platform => en/platform}/metrics.mdx (100%) rename {platform => en/platform}/overview.mdx (100%) rename {platform => en/platform}/points.mdx (100%) rename {platform => en/platform}/push-notifications.mdx (100%) rename {platform => en/platform}/streaks.mdx (100%) rename {platform => en/platform}/users.mdx (100%) rename {snippets => en/snippets}/add-env-var-block.mdx (100%) rename {snippets => en/snippets}/all-achievements-request-block.mdx (100%) rename {snippets => en/snippets}/all-achievements-response-block.mdx (100%) rename {snippets => en/snippets}/control-flag-block.mdx (100%) rename {snippets => en/snippets}/event-attributes-request-block.mdx (100%) rename {snippets => en/snippets}/get-user-notification-preferences-block.mdx (100%) rename {snippets => en/snippets}/idempotent-event-tracking.mdx (100%) rename {snippets => en/snippets}/identify-user-request-block.mdx (100%) rename {snippets => en/snippets}/identify-user-with-device-tokens-request-block.mdx (100%) rename {snippets => en/snippets}/leaderboard-rankings-request-multiple-attributes.mdx (100%) rename {snippets => en/snippets}/leaderboard-rankings-request-single-attribute.mdx (100%) rename {snippets => en/snippets}/leaderboard-rankings-request.mdx (100%) rename {snippets => en/snippets}/leaderboard-rankings-response.mdx (100%) rename {snippets => en/snippets}/metric-change-event-points-level-excerpt-block.mdx (100%) rename {snippets => en/snippets}/metric-change-request-block.mdx (100%) rename {snippets => en/snippets}/metric-change-response-block.mdx (100%) rename {snippets => en/snippets}/metric-event-with-device-tokens-request-block.mdx (100%) rename {snippets => en/snippets}/plan-badge.jsx (100%) rename {snippets => en/snippets}/points-histogram-summary-response-block.mdx (100%) rename {snippets => en/snippets}/points-level-summary-response-block.mdx (100%) rename {snippets => en/snippets}/points-levels-list-response-block.mdx (100%) rename {snippets => en/snippets}/points-system-response-block.mdx (100%) rename {snippets => en/snippets}/rate-limit-badge.jsx (100%) rename {snippets => en/snippets}/sdk-install-command.mdx (100%) rename {snippets => en/snippets}/snippet-intro.mdx (100%) rename {snippets => en/snippets}/streak-request-block.mdx (100%) rename {snippets => en/snippets}/streak-response-block.mdx (100%) rename {snippets => en/snippets}/update-user-notification-preferences-block.mdx (100%) rename {snippets => en/snippets}/user-achievements-request-block.mdx (100%) rename {snippets => en/snippets}/user-energy-response-block.mdx (100%) rename {snippets => en/snippets}/user-leaderboard-rankings-request.mdx (100%) rename {snippets => en/snippets}/user-leaderboard-rankings-response.mdx (100%) rename {snippets => en/snippets}/user-points-request-block.mdx (100%) rename {snippets => en/snippets}/user-points-response-block.mdx (100%) rename {snippets => en/snippets}/user-points-summary-request-block.mdx (100%) rename {snippets => en/snippets}/user-points-summary-response-block.mdx (100%) rename {snippets => en/snippets}/webhook-points-level-changed-payload-block.mdx (100%) rename {webhooks => en/webhooks}/events/achievements/achievement-completed.mdx (100%) rename {webhooks => en/webhooks}/events/leaderboards/leaderboard-changed.mdx (100%) rename {webhooks => en/webhooks}/events/leaderboards/leaderboard-finished.mdx (100%) rename {webhooks => en/webhooks}/events/leaderboards/leaderboard-rank-changed.mdx (100%) rename {webhooks => en/webhooks}/events/leaderboards/leaderboard-started.mdx (100%) rename {webhooks => en/webhooks}/events/points/points-boost-finished.mdx (100%) rename {webhooks => en/webhooks}/events/points/points-boost-started.mdx (100%) rename {webhooks => en/webhooks}/events/points/points-changed.mdx (100%) rename {webhooks => en/webhooks}/events/points/points-level-changed.mdx (100%) rename {webhooks => en/webhooks}/events/streaks/streak-extended.mdx (100%) rename {webhooks => en/webhooks}/events/streaks/streak-freeze-consumed.mdx (100%) rename {webhooks => en/webhooks}/events/streaks/streak-freeze-earned.mdx (100%) rename {webhooks => en/webhooks}/events/streaks/streak-lost.mdx (100%) rename {webhooks => en/webhooks}/events/streaks/streak-started.mdx (100%) rename {webhooks => en/webhooks}/idempotency.mdx (100%) rename {webhooks => en/webhooks}/introduction.mdx (100%) rename {webhooks => en/webhooks}/observability.mdx (100%) rename {webhooks => en/webhooks}/quickstart.mdx (100%) rename {webhooks => en/webhooks}/retries.mdx (100%) rename {webhooks => en/webhooks}/security.mdx (100%) create mode 100644 es/account/billing.mdx create mode 100644 es/account/branding.mdx create mode 100644 es/account/members.mdx create mode 100644 es/account/overview.mdx create mode 100644 es/admin-api/endpoints/points/archive-a-boost.mdx create mode 100644 es/admin-api/endpoints/points/archive-boosts-batch.mdx create mode 100644 es/admin-api/endpoints/points/create-boosts.mdx create mode 100644 es/admin-api/endpoints/streaks/grant-freezes.mdx create mode 100644 es/admin-api/endpoints/streaks/restore-streaks.mdx create mode 100644 es/admin-api/introduction.mdx create mode 100644 es/api-reference/authentication.mdx create mode 100644 es/api-reference/client-libraries.mdx create mode 100644 es/api-reference/endpoints/achievements/all-achievements.mdx create mode 100644 es/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx create mode 100644 es/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx create mode 100644 es/api-reference/endpoints/leaderboards/get-leaderboard.mdx create mode 100644 es/api-reference/endpoints/metrics/send-a-metric-change-event.mdx create mode 100644 es/api-reference/endpoints/points/get-points-boosts.mdx create mode 100644 es/api-reference/endpoints/points/get-points-level-summary.mdx create mode 100644 es/api-reference/endpoints/points/get-points-levels.mdx create mode 100644 es/api-reference/endpoints/points/get-points-summary.mdx create mode 100644 es/api-reference/endpoints/points/get-points.mdx create mode 100644 es/api-reference/endpoints/streaks/get-streak-rankings.mdx create mode 100644 es/api-reference/endpoints/streaks/get-streaks.mdx create mode 100644 es/api-reference/endpoints/users/create-a-user.mdx create mode 100644 es/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx create mode 100644 es/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx create mode 100644 es/api-reference/endpoints/users/get-a-single-user.mdx create mode 100644 es/api-reference/endpoints/users/get-a-users-completed-achievements.mdx create mode 100644 es/api-reference/endpoints/users/get-a-users-leaderboard.mdx create mode 100644 es/api-reference/endpoints/users/get-a-users-points-boosts.mdx create mode 100644 es/api-reference/endpoints/users/get-a-users-points-summary.mdx create mode 100644 es/api-reference/endpoints/users/get-a-users-points.mdx create mode 100644 es/api-reference/endpoints/users/get-a-users-streak.mdx create mode 100644 es/api-reference/endpoints/users/get-a-users-wrapped.mdx create mode 100644 es/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx create mode 100644 es/api-reference/endpoints/users/get-user-preferences.mdx create mode 100644 es/api-reference/endpoints/users/identify-a-user.mdx create mode 100644 es/api-reference/endpoints/users/update-a-user.mdx create mode 100644 es/api-reference/endpoints/users/update-user-preferences.mdx create mode 100644 es/api-reference/idempotency.mdx create mode 100644 es/api-reference/introduction.mdx create mode 100644 es/api-reference/openapi.yml create mode 100644 es/api-reference/rate-limiting.mdx create mode 100644 es/experimentation/engagement.mdx create mode 100644 es/experimentation/overview.mdx create mode 100644 es/experimentation/retention.mdx create mode 100644 es/getting-started/introduction.mdx create mode 100644 es/getting-started/quickstart.mdx create mode 100644 es/guides/gamified-fitness-platform.mdx create mode 100644 es/guides/gamified-study-platform.mdx create mode 100644 es/guides/how-to-build-a-leaderboards-feature.mdx create mode 100644 es/guides/how-to-build-a-streaks-feature.mdx create mode 100644 es/guides/how-to-build-an-achievements-feature.mdx create mode 100644 es/guides/how-to-build-an-energy-feature.mdx create mode 100644 es/guides/how-to-build-an-xp-feature.mdx create mode 100644 es/platform/achievements.mdx create mode 100644 es/platform/emails.mdx create mode 100644 es/platform/events.mdx create mode 100644 es/platform/leaderboards.mdx create mode 100644 es/platform/metrics.mdx create mode 100644 es/platform/overview.mdx create mode 100644 es/platform/points.mdx create mode 100644 es/platform/push-notifications.mdx create mode 100644 es/platform/streaks.mdx create mode 100644 es/platform/users.mdx create mode 100644 es/snippets/add-env-var-block.mdx create mode 100644 es/snippets/all-achievements-request-block.mdx create mode 100644 es/snippets/all-achievements-response-block.mdx create mode 100644 es/snippets/control-flag-block.mdx create mode 100644 es/snippets/event-attributes-request-block.mdx create mode 100644 es/snippets/get-user-notification-preferences-block.mdx create mode 100644 es/snippets/idempotent-event-tracking.mdx create mode 100644 es/snippets/identify-user-request-block.mdx create mode 100644 es/snippets/identify-user-with-device-tokens-request-block.mdx create mode 100644 es/snippets/leaderboard-rankings-request-multiple-attributes.mdx create mode 100644 es/snippets/leaderboard-rankings-request-single-attribute.mdx create mode 100644 es/snippets/leaderboard-rankings-request.mdx create mode 100644 es/snippets/leaderboard-rankings-response.mdx create mode 100644 es/snippets/metric-change-event-points-level-excerpt-block.mdx create mode 100644 es/snippets/metric-change-request-block.mdx create mode 100644 es/snippets/metric-change-response-block.mdx create mode 100644 es/snippets/metric-event-with-device-tokens-request-block.mdx create mode 100644 es/snippets/plan-badge.jsx create mode 100644 es/snippets/points-histogram-summary-response-block.mdx create mode 100644 es/snippets/points-level-summary-response-block.mdx create mode 100644 es/snippets/points-levels-list-response-block.mdx create mode 100644 es/snippets/points-system-response-block.mdx create mode 100644 es/snippets/rate-limit-badge.jsx create mode 100644 es/snippets/sdk-install-command.mdx create mode 100644 es/snippets/snippet-intro.mdx create mode 100644 es/snippets/streak-request-block.mdx create mode 100644 es/snippets/streak-response-block.mdx create mode 100644 es/snippets/update-user-notification-preferences-block.mdx create mode 100644 es/snippets/user-achievements-request-block.mdx create mode 100644 es/snippets/user-energy-response-block.mdx create mode 100644 es/snippets/user-leaderboard-rankings-request.mdx create mode 100644 es/snippets/user-leaderboard-rankings-response.mdx create mode 100644 es/snippets/user-points-request-block.mdx create mode 100644 es/snippets/user-points-response-block.mdx create mode 100644 es/snippets/user-points-summary-request-block.mdx create mode 100644 es/snippets/user-points-summary-response-block.mdx create mode 100644 es/snippets/webhook-points-level-changed-payload-block.mdx create mode 100644 es/webhooks/events/achievements/achievement-completed.mdx create mode 100644 es/webhooks/events/leaderboards/leaderboard-changed.mdx create mode 100644 es/webhooks/events/leaderboards/leaderboard-finished.mdx create mode 100644 es/webhooks/events/leaderboards/leaderboard-rank-changed.mdx create mode 100644 es/webhooks/events/leaderboards/leaderboard-started.mdx create mode 100644 es/webhooks/events/points/points-boost-finished.mdx create mode 100644 es/webhooks/events/points/points-boost-started.mdx create mode 100644 es/webhooks/events/points/points-changed.mdx create mode 100644 es/webhooks/events/points/points-level-changed.mdx create mode 100644 es/webhooks/events/streaks/streak-extended.mdx create mode 100644 es/webhooks/events/streaks/streak-freeze-consumed.mdx create mode 100644 es/webhooks/events/streaks/streak-freeze-earned.mdx create mode 100644 es/webhooks/events/streaks/streak-lost.mdx create mode 100644 es/webhooks/events/streaks/streak-started.mdx create mode 100644 es/webhooks/idempotency.mdx create mode 100644 es/webhooks/introduction.mdx create mode 100644 es/webhooks/observability.mdx create mode 100644 es/webhooks/quickstart.mdx create mode 100644 es/webhooks/retries.mdx create mode 100644 es/webhooks/security.mdx create mode 100644 i18n.json create mode 100644 lingo/brand-voice.md create mode 100644 lingo/glossary.csv create mode 100644 lingo/instructions.global.txt create mode 100644 scripts/generate-translations.mjs create mode 100644 scripts/translate-docs-json.mjs create mode 100644 scripts/validate-translations.mjs diff --git a/.cursor/rules/localization-workflow.mdc b/.cursor/rules/localization-workflow.mdc new file mode 100644 index 0000000..7fc6443 --- /dev/null +++ b/.cursor/rules/localization-workflow.mdc @@ -0,0 +1,14 @@ +--- +description: Localization workflow and Lingo guardrails +alwaysApply: true +--- + +# Localization Workflow + +- Treat `en/` as the canonical source locale and keep identical file paths/filenames across all locales. +- 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. +- Treat `lingo/brand-voice.md` as the source of truth for brand voice text. +- Do not add script-based brand voice sync; sync brand voice on demand via Lingo MCP tools from chat. +- When adding locales, update both `docs.json` language blocks and `i18n.json` `locale.targets`. diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.cursorignore @@ -0,0 +1 @@ +.env diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a38fe1a --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +# Lingo.dev configuration +# Copy this file to .env and replace placeholders with real values. + +# Lingo.dev API key (used by CLI and CI workflows) +LINGODOTDEV_API_KEY=ldv_your_api_key_here + +# Lingo.dev localization engine id +LINGO_ENGINE_ID=eng_your_engine_id_here diff --git a/.github/workflows/translate-on-main.yml b/.github/workflows/translate-on-main.yml new file mode 100644 index 0000000..34b424f --- /dev/null +++ b/.github/workflows/translate-on-main.yml @@ -0,0 +1,71 @@ +name: Translate on main + +on: + push: + branches: + - main + +permissions: + contents: write + pull-requests: write + +jobs: + translate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Inject Lingo engine id into i18n config + env: + LINGO_ENGINE_ID: ${{ secrets.LINGO_ENGINE_ID }} + run: | + if [ -z "${LINGO_ENGINE_ID}" ]; then + echo "LINGO_ENGINE_ID secret is required." + exit 1 + fi + node -e 'const fs=require("fs");const p="i18n.json";const j=JSON.parse(fs.readFileSync(p,"utf8"));j.engineId=process.env.LINGO_ENGINE_ID;fs.writeFileSync(p,JSON.stringify(j,null,2)+"\n");' + + - name: Run Lingo translation + env: + LINGO_API_KEY: ${{ secrets.LINGODOTDEV_API_KEY }} + run: | + locales=$(node -e 'const fs=require("fs");const j=JSON.parse(fs.readFileSync("i18n.json","utf8"));console.log((j.locale?.targets||[]).join(" "));') + if [ -z "$locales" ]; then + echo "No target locales configured in i18n.json locale.targets" + exit 1 + fi + for locale in $locales; do + echo "Running Lingo translation for $locale" + npx lingo.dev@latest run --target-locale "$locale" + done + + - name: Translate docs.json language-specific navigation + env: + LINGO_ENGINE_ID: ${{ secrets.LINGO_ENGINE_ID }} + LINGO_API_KEY: ${{ secrets.LINGODOTDEV_API_KEY }} + run: | + locales=$(node -e 'const fs=require("fs");const j=JSON.parse(fs.readFileSync("i18n.json","utf8"));console.log((j.locale?.targets||[]).join(" "));') + for locale in $locales; do + echo "Translating docs.json labels for $locale" + node scripts/translate-docs-json.mjs --target "$locale" + done + + - name: Create translation PR + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore(i18n): update translations from main" + title: "chore(i18n): update translations from main" + body: "Automated translation update from main branch push." + branch: "chore/i18n/auto-translations" + delete-branch: true diff --git a/.github/workflows/validate-translations.yml b/.github/workflows/validate-translations.yml new file mode 100644 index 0000000..7b7f1fb --- /dev/null +++ b/.github/workflows/validate-translations.yml @@ -0,0 +1,58 @@ +name: Validate translations + +on: + pull_request: + paths: + - "docs.json" + - "i18n.json" + - "en/**" + - "es/**" + - ".github/workflows/translate-on-main.yml" + - ".github/workflows/validate-translations.yml" + - "scripts/validate-translations.mjs" + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Inject Lingo engine id into i18n config + env: + LINGO_ENGINE_ID: ${{ secrets.LINGO_ENGINE_ID }} + run: | + if [ -z "${LINGO_ENGINE_ID}" ]; then + echo "LINGO_ENGINE_ID is not set, skipping engine injection." + else + node -e 'const fs=require("fs");const p="i18n.json";const j=JSON.parse(fs.readFileSync(p,"utf8"));j.engineId=process.env.LINGO_ENGINE_ID;fs.writeFileSync(p,JSON.stringify(j,null,2)+"\n");' + fi + + - name: Validate translation parity and frontmatter + run: node scripts/validate-translations.mjs + + - name: Mintlify validate + run: npx mint@latest validate + + - name: Check broken links + run: npx mint@latest broken-links --check-anchors --check-snippets + + - name: Translation freshness gate + env: + LINGO_API_KEY: ${{ secrets.LINGODOTDEV_API_KEY }} + LINGO_ENGINE_ID: ${{ secrets.LINGO_ENGINE_ID }} + run: | + if [ -z "${LINGO_API_KEY}" ] || [ -z "${LINGO_ENGINE_ID}" ]; then + echo "LINGODOTDEV_API_KEY or LINGO_ENGINE_ID is not set, skipping frozen translation gate." + else + npx lingo.dev@latest run --frozen + fi diff --git a/.gitignore b/.gitignore index 28f1ba7..9bdf355 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -.DS_Store \ No newline at end of file +.DS_Store +.env \ No newline at end of file diff --git a/README.md b/README.md index 41bccb6..35fd08c 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,89 @@ npm run dev Our docs are written in [MDX](https://mdxjs.com/), which if you haven't used it before is a bit if markdown and React had a baby. See the [full guide](https://mintlify.com/docs/content/components) from Mintlify on writing MDX and the components that are available for how best to contribute. + +### Localization (Lingo.dev) + +This repository uses Lingo.dev for static-content localization of docs pages and language-specific navigation labels in `docs.json`. + +#### Install Lingo MCP in Cursor + +1. Open your Cursor MCP config file: + - `~/.cursor/mcp.json` +2. Add a Lingo MCP server entry: + +```json +{ + "mcpServers": { + "lingo": { + "url": "https://mcp.lingo.dev/account", + "headers": { + "x-api-key": "${env:LINGODOTDEV_API_KEY}" + } + } + } +} +``` + +3. Ensure the API key is available to the Cursor app process as an environment variable: + - `LINGODOTDEV_API_KEY` +4. Restart Cursor fully after updating `mcp.json` or environment variables. +5. Verify by asking Cursor chat to list engines from the Lingo MCP server. + +Notes: +- This repo ignores `.env` via `.cursorignore`, so Cursor agents should not read API keys from project files. +- If Cursor was launched before env vars were set, restart Cursor from a shell where env vars are already exported. + +#### Localization files + +- `lingo/glossary.csv`: Terms that must stay fixed or use specific translations. +- `lingo/brand-voice.md`: Single brand voice used for all locales. +- `lingo/instructions.global.txt`: Global structural rules (preserve MDX, never translate code, keep links stable). +- `scripts/translate-docs-json.mjs`: Translates language-specific `docs.json` navigation labels directly in the source-of-truth `docs.json`. + +#### Apply in Lingo.dev engine + +1. Configure `LINGO_ENGINE_ID` as an environment variable/secret. + - Local: `export LINGO_ENGINE_ID=eng_...` + - CI: set repository secret `LINGO_ENGINE_ID` + - `i18n.json` uses `"engineId": "${LINGO_ENGINE_ID}"` and workflows/scripts inject the real value at runtime. +2. In the Lingo.dev engine: + - import/create glossary entries from `lingo/glossary.csv` + - sync wildcard brand voice from `lingo/brand-voice.md` via Cursor chat + Lingo MCP (on demand) + - add instructions from `lingo/instructions.global.txt` (target locale `*` unless locale-specific) +3. Run: + - `npx lingo.dev@latest run` to generate/update translations + - `node scripts/translate-docs-json.mjs --target es` to translate language-specific labels in `docs.json` + - `npx lingo.dev@latest run --frozen` to enforce no pending translation deltas + +Brand voice sync workflow (manual via Cursor + MCP): +- Update `lingo/brand-voice.md`. +- In Cursor chat ask: "Sync `lingo/brand-voice.md` to Lingo brand voice for engine ``." +- The agent will use MCP tools to find the engine brand voice id and update it. + +#### Add a new language + +1. Add the locale to `docs.json`: + - In `navigation.languages`, add a new object with `language: ""`. + - Copy the structure from `en` (tabs/groups/pages) and prefix pages with `/...`. + - Add language-specific `anchors` and `navbar` fields in that locale block. +2. Add the locale to `i18n.json`: + - In `locale.targets`, append the locale code (for example `fr` or `de`). +3. Create the language content directory: + - Mirror the `en/` structure under `/` with the same filenames. + - Example: `en/platform/overview.mdx` -> `fr/platform/overview.mdx`. +4. Configure Lingo.dev engine controls for the new locale: + - Add/update brand voice for that locale. + - Add locale-specific glossary entries or instructions if needed. +5. Generate translations: + - One locale: `node scripts/generate-translations.mjs --target ` + - Multiple locales: `node scripts/generate-translations.mjs --target es,fr,de` + - All configured targets: `node scripts/generate-translations.mjs` +6. Validate before merge: + - `npm run translate:validate` + - `npx mint@latest validate` + - `npx mint@latest broken-links --check-anchors --check-snippets` + +Notes: +- The workflow `.github/workflows/translate-on-main.yml` automatically reads locales from `i18n.json` `locale.targets`. +- Keep `en` as source/default and preserve identical file paths across locales. diff --git a/docs.json b/docs.json index 8eb40e4..f4d5bf5 100644 --- a/docs.json +++ b/docs.json @@ -35,271 +35,546 @@ } }, "contextual": { - "options": ["copy", "view", "chatgpt", "claude", "perplexity"] + "options": [ + "copy", + "view", + "chatgpt", + "claude", + "perplexity" + ] }, "navigation": { - "tabs": [ + "languages": [ { - "tab": "Home", - "icon": "house", - "groups": [ + "language": "en", + "default": true, + "tabs": [ { - "group": "Getting Started", - "pages": [ - "getting-started/introduction", - "getting-started/quickstart" + "tab": "Home", + "icon": "house", + "groups": [ + { + "group": "Getting Started", + "pages": [ + "en/getting-started/introduction", + "en/getting-started/quickstart" + ] + }, + { + "group": "Platform", + "pages": [ + "en/platform/overview", + "en/platform/metrics", + "en/platform/events", + "en/platform/users", + "en/platform/achievements", + "en/platform/streaks", + "en/platform/points", + "en/platform/leaderboards", + "en/platform/emails", + "en/platform/push-notifications" + ] + }, + { + "group": "Experimentation", + "pages": [ + "en/experimentation/overview", + "en/experimentation/retention", + "en/experimentation/engagement" + ] + }, + { + "group": "Account", + "pages": [ + "en/account/overview", + "en/account/members", + "en/account/branding", + "en/account/billing" + ] + } ] }, { - "group": "Platform", - "pages": [ - "platform/overview", - "platform/metrics", - "platform/events", - "platform/users", - "platform/achievements", - "platform/streaks", - "platform/points", - "platform/leaderboards", - "platform/emails", - "platform/push-notifications" + "tab": "Guides", + "icon": "rocket", + "description": "Gamification how-to guides", + "groups": [ + { + "group": "Feature Guides", + "pages": [ + "en/guides/how-to-build-an-achievements-feature", + "en/guides/how-to-build-a-streaks-feature", + "en/guides/how-to-build-an-xp-feature", + "en/guides/how-to-build-an-energy-feature", + "en/guides/how-to-build-a-leaderboards-feature" + ] + }, + { + "group": "Use Cases", + "pages": [ + "en/guides/gamified-study-platform", + "en/guides/gamified-fitness-platform" + ] + } ] }, { - "group": "Experimentation", - "pages": [ - "experimentation/overview", - "experimentation/retention", - "experimentation/engagement" + "tab": "Application API", + "icon": "globe", + "groups": [ + { + "group": "Overview", + "pages": [ + "en/api-reference/introduction", + "en/api-reference/authentication", + "en/api-reference/rate-limiting", + "en/api-reference/idempotency", + "en/api-reference/client-libraries" + ] + }, + { + "group": "Achievements", + "pages": [ + "en/api-reference/endpoints/achievements/mark-an-achievement-as-completed", + "en/api-reference/endpoints/achievements/all-achievements" + ] + }, + { + "group": "Streaks", + "pages": [ + "en/api-reference/endpoints/streaks/get-streaks", + "en/api-reference/endpoints/streaks/get-streak-rankings" + ] + }, + { + "group": "Metrics", + "pages": [ + "en/api-reference/endpoints/metrics/send-a-metric-change-event" + ] + }, + { + "group": "Points", + "pages": [ + "en/api-reference/endpoints/points/get-points", + "en/api-reference/endpoints/points/get-points-boosts", + "en/api-reference/endpoints/points/get-points-levels", + "en/api-reference/endpoints/points/get-points-level-summary", + "en/api-reference/endpoints/points/get-points-summary" + ] + }, + { + "group": "Leaderboards", + "pages": [ + "en/api-reference/endpoints/leaderboards/get-all-active-leaderboards", + "en/api-reference/endpoints/leaderboards/get-leaderboard" + ] + }, + { + "group": "Users", + "pages": [ + "en/api-reference/endpoints/users/identify-a-user", + "en/api-reference/endpoints/users/create-a-user", + "en/api-reference/endpoints/users/get-a-single-user", + "en/api-reference/endpoints/users/update-a-user", + "en/api-reference/endpoints/users/get-all-metrics-for-a-user", + "en/api-reference/endpoints/users/get-a-single-metric-for-a-user", + "en/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user", + "en/api-reference/endpoints/users/get-a-users-completed-achievements", + "en/api-reference/endpoints/users/get-a-users-streak", + "en/api-reference/endpoints/users/get-a-users-points", + "en/api-reference/endpoints/users/get-a-users-points-boosts", + "en/api-reference/endpoints/users/get-a-users-points-summary", + "en/api-reference/endpoints/users/get-a-users-leaderboard", + "en/api-reference/endpoints/users/get-a-users-wrapped", + "en/api-reference/endpoints/users/get-user-preferences", + "en/api-reference/endpoints/users/update-user-preferences" + ] + } ] }, { - "group": "Account", - "pages": [ - "account/overview", - "account/members", - "account/branding", - "account/billing" - ] - } - ] - }, - { - "tab": "Guides", - "icon": "rocket", - "description": "Gamification how-to guides", - "groups": [ - { - "group": "Feature Guides", - "pages": [ - "guides/how-to-build-an-achievements-feature", - "guides/how-to-build-a-streaks-feature", - "guides/how-to-build-an-xp-feature", - "guides/how-to-build-an-energy-feature", - "guides/how-to-build-a-leaderboards-feature" + "tab": "Admin API", + "icon": "globe-lock", + "groups": [ + { + "group": "Overview", + "pages": [ + "en/admin-api/introduction" + ] + }, + { + "group": "Streaks", + "pages": [ + "en/admin-api/endpoints/streaks/grant-freezes", + "en/admin-api/endpoints/streaks/restore-streaks" + ] + }, + { + "group": "Points", + "pages": [ + "en/admin-api/endpoints/points/create-boosts", + "en/admin-api/endpoints/points/archive-boosts-batch", + "en/admin-api/endpoints/points/archive-a-boost" + ] + } ] }, { - "group": "Use Cases", - "pages": [ - "guides/gamified-study-platform", - "guides/gamified-fitness-platform" + "tab": "Webhooks", + "icon": "webhook", + "groups": [ + { + "group": "Overview", + "pages": [ + "en/webhooks/introduction", + "en/webhooks/quickstart", + "en/webhooks/security", + "en/webhooks/retries", + "en/webhooks/idempotency", + "en/webhooks/observability" + ] + }, + { + "group": "Achievements", + "pages": [ + "en/webhooks/events/achievements/achievement-completed" + ] + }, + { + "group": "Streaks", + "pages": [ + "en/webhooks/events/streaks/streak-started", + "en/webhooks/events/streaks/streak-extended", + "en/webhooks/events/streaks/streak-lost", + "en/webhooks/events/streaks/streak-freeze-consumed", + "en/webhooks/events/streaks/streak-freeze-earned" + ] + }, + { + "group": "Points", + "pages": [ + "en/webhooks/events/points/points-changed", + "en/webhooks/events/points/points-level-changed", + "en/webhooks/events/points/points-boost-started", + "en/webhooks/events/points/points-boost-finished" + ] + }, + { + "group": "Leaderboards", + "pages": [ + "en/webhooks/events/leaderboards/leaderboard-started", + "en/webhooks/events/leaderboards/leaderboard-changed", + "en/webhooks/events/leaderboards/leaderboard-rank-changed", + "en/webhooks/events/leaderboards/leaderboard-finished" + ] + } ] } - ] - }, - { - "tab": "Application API", - "icon": "globe", - "groups": [ + ], + "anchors": [ { - "group": "Overview", - "pages": [ - "api-reference/introduction", - "api-reference/authentication", - "api-reference/rate-limiting", - "api-reference/idempotency", - "api-reference/client-libraries" - ] + "anchor": "GitHub", + "href": "https://github.com/trophyso", + "icon": "github" }, { - "group": "Achievements", - "pages": [ - "api-reference/endpoints/achievements/mark-an-achievement-as-completed", - "api-reference/endpoints/achievements/all-achievements" - ] + "anchor": "Status", + "href": "https://status.trophy.so", + "icon": "radio-tower" }, { - "group": "Streaks", - "pages": [ - "api-reference/endpoints/streaks/get-streaks", - "api-reference/endpoints/streaks/get-streak-rankings" - ] + "anchor": "llms.txt", + "href": "https://docs.trophy.so/llms-full.txt", + "icon": "bot" }, { - "group": "Metrics", - "pages": [ - "api-reference/endpoints/metrics/send-a-metric-change-event" - ] - }, + "anchor": "Help", + "href": "mailto:support@trophy.so", + "icon": "life-buoy" + } + ], + "navbar": { + "links": [ + { + "label": "Dashboard", + "href": "https://app.trophy.so" + } + ], + "primary": { + "type": "button", + "label": "Start for free", + "href": "https://app.trophy.so/sign-up?utm_source=docs&utm_medium=navbar-links" + } + } + }, + { + "language": "es", + "tabs": [ { - "group": "Points", - "pages": [ - "api-reference/endpoints/points/get-points", - "api-reference/endpoints/points/get-points-boosts", - "api-reference/endpoints/points/get-points-levels", - "api-reference/endpoints/points/get-points-level-summary", - "api-reference/endpoints/points/get-points-summary" + "tab": "Inicio", + "icon": "house", + "groups": [ + { + "group": "Primeros pasos", + "pages": [ + "es/getting-started/introduction", + "es/getting-started/quickstart" + ] + }, + { + "group": "Plataforma", + "pages": [ + "es/platform/overview", + "es/platform/metrics", + "es/platform/events", + "es/platform/users", + "es/platform/achievements", + "es/platform/streaks", + "es/platform/points", + "es/platform/leaderboards", + "es/platform/emails", + "es/platform/push-notifications" + ] + }, + { + "group": "Experimentación", + "pages": [ + "es/experimentation/overview", + "es/experimentation/retention", + "es/experimentation/engagement" + ] + }, + { + "group": "Cuenta", + "pages": [ + "es/account/overview", + "es/account/members", + "es/account/branding", + "es/account/billing" + ] + } ] }, { - "group": "Leaderboards", - "pages": [ - "api-reference/endpoints/leaderboards/get-all-active-leaderboards", - "api-reference/endpoints/leaderboards/get-leaderboard" + "tab": "Guías", + "icon": "rocket", + "description": "Guías prácticas de gamificación", + "groups": [ + { + "group": "Guías de funcionalidades", + "pages": [ + "es/guides/how-to-build-an-achievements-feature", + "es/guides/how-to-build-a-streaks-feature", + "es/guides/how-to-build-an-xp-feature", + "es/guides/how-to-build-an-energy-feature", + "es/guides/how-to-build-a-leaderboards-feature" + ] + }, + { + "group": "Casos de uso", + "pages": [ + "es/guides/gamified-study-platform", + "es/guides/gamified-fitness-platform" + ] + } ] }, { - "group": "Users", - "pages": [ - "api-reference/endpoints/users/identify-a-user", - "api-reference/endpoints/users/create-a-user", - "api-reference/endpoints/users/get-a-single-user", - "api-reference/endpoints/users/update-a-user", - "api-reference/endpoints/users/get-all-metrics-for-a-user", - "api-reference/endpoints/users/get-a-single-metric-for-a-user", - "api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user", - "api-reference/endpoints/users/get-a-users-completed-achievements", - "api-reference/endpoints/users/get-a-users-streak", - "api-reference/endpoints/users/get-a-users-points", - "api-reference/endpoints/users/get-a-users-points-boosts", - "api-reference/endpoints/users/get-a-users-points-summary", - "api-reference/endpoints/users/get-a-users-leaderboard", - "api-reference/endpoints/users/get-a-users-wrapped", - "api-reference/endpoints/users/get-user-preferences", - "api-reference/endpoints/users/update-user-preferences" + "tab": "API de Aplicación", + "icon": "globe", + "groups": [ + { + "group": "Resumen", + "pages": [ + "es/api-reference/introduction", + "es/api-reference/authentication", + "es/api-reference/rate-limiting", + "es/api-reference/idempotency", + "es/api-reference/client-libraries" + ] + }, + { + "group": "Logros", + "pages": [ + "es/api-reference/endpoints/achievements/mark-an-achievement-as-completed", + "es/api-reference/endpoints/achievements/all-achievements" + ] + }, + { + "group": "Rachas", + "pages": [ + "es/api-reference/endpoints/streaks/get-streaks", + "es/api-reference/endpoints/streaks/get-streak-rankings" + ] + }, + { + "group": "Métricas", + "pages": [ + "es/api-reference/endpoints/metrics/send-a-metric-change-event" + ] + }, + { + "group": "Puntos", + "pages": [ + "es/api-reference/endpoints/points/get-points", + "es/api-reference/endpoints/points/get-points-boosts", + "es/api-reference/endpoints/points/get-points-levels", + "es/api-reference/endpoints/points/get-points-level-summary", + "es/api-reference/endpoints/points/get-points-summary" + ] + }, + { + "group": "Clasificaciones", + "pages": [ + "es/api-reference/endpoints/leaderboards/get-all-active-leaderboards", + "es/api-reference/endpoints/leaderboards/get-leaderboard" + ] + }, + { + "group": "Usuarios", + "pages": [ + "es/api-reference/endpoints/users/identify-a-user", + "es/api-reference/endpoints/users/create-a-user", + "es/api-reference/endpoints/users/get-a-single-user", + "es/api-reference/endpoints/users/update-a-user", + "es/api-reference/endpoints/users/get-all-metrics-for-a-user", + "es/api-reference/endpoints/users/get-a-single-metric-for-a-user", + "es/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user", + "es/api-reference/endpoints/users/get-a-users-completed-achievements", + "es/api-reference/endpoints/users/get-a-users-streak", + "es/api-reference/endpoints/users/get-a-users-points", + "es/api-reference/endpoints/users/get-a-users-points-boosts", + "es/api-reference/endpoints/users/get-a-users-points-summary", + "es/api-reference/endpoints/users/get-a-users-leaderboard", + "es/api-reference/endpoints/users/get-a-users-wrapped", + "es/api-reference/endpoints/users/get-user-preferences", + "es/api-reference/endpoints/users/update-user-preferences" + ] + } ] - } - ] - }, - { - "tab": "Admin API", - "icon": "globe-lock", - "groups": [ - { - "group": "Overview", - "pages": ["admin-api/introduction"] }, { - "group": "Streaks", - "pages": [ - "admin-api/endpoints/streaks/grant-freezes", - "admin-api/endpoints/streaks/restore-streaks" + "tab": "API de Administración", + "icon": "globe-lock", + "groups": [ + { + "group": "Resumen", + "pages": [ + "es/admin-api/introduction" + ] + }, + { + "group": "Rachas", + "pages": [ + "es/admin-api/endpoints/streaks/grant-freezes", + "es/admin-api/endpoints/streaks/restore-streaks" + ] + }, + { + "group": "Puntos", + "pages": [ + "es/admin-api/endpoints/points/create-boosts", + "es/admin-api/endpoints/points/archive-boosts-batch", + "es/admin-api/endpoints/points/archive-a-boost" + ] + } ] }, { - "group": "Points", - "pages": [ - "admin-api/endpoints/points/create-boosts", - "admin-api/endpoints/points/archive-boosts-batch", - "admin-api/endpoints/points/archive-a-boost" + "tab": "Webhooks", + "icon": "webhook", + "groups": [ + { + "group": "Resumen", + "pages": [ + "es/webhooks/introduction", + "es/webhooks/quickstart", + "es/webhooks/security", + "es/webhooks/retries", + "es/webhooks/idempotency", + "es/webhooks/observability" + ] + }, + { + "group": "Logros", + "pages": [ + "es/webhooks/events/achievements/achievement-completed" + ] + }, + { + "group": "Rachas", + "pages": [ + "es/webhooks/events/streaks/streak-started", + "es/webhooks/events/streaks/streak-extended", + "es/webhooks/events/streaks/streak-lost", + "es/webhooks/events/streaks/streak-freeze-consumed", + "es/webhooks/events/streaks/streak-freeze-earned" + ] + }, + { + "group": "Puntos", + "pages": [ + "es/webhooks/events/points/points-changed", + "es/webhooks/events/points/points-level-changed", + "es/webhooks/events/points/points-boost-started", + "es/webhooks/events/points/points-boost-finished" + ] + }, + { + "group": "Clasificaciones", + "pages": [ + "es/webhooks/events/leaderboards/leaderboard-started", + "es/webhooks/events/leaderboards/leaderboard-changed", + "es/webhooks/events/leaderboards/leaderboard-rank-changed", + "es/webhooks/events/leaderboards/leaderboard-finished" + ] + } ] } - ] - }, - { - "tab": "Webhooks", - "icon": "webhook", - "groups": [ + ], + "anchors": [ { - "group": "Overview", - "pages": [ - "webhooks/introduction", - "webhooks/quickstart", - "webhooks/security", - "webhooks/retries", - "webhooks/idempotency", - "webhooks/observability" - ] + "anchor": "GitHub", + "href": "https://github.com/trophyso", + "icon": "github" }, { - "group": "Achievements", - "pages": ["webhooks/events/achievements/achievement-completed"] + "anchor": "Estado", + "href": "https://status.trophy.so", + "icon": "radio-tower" }, { - "group": "Streaks", - "pages": [ - "webhooks/events/streaks/streak-started", - "webhooks/events/streaks/streak-extended", - "webhooks/events/streaks/streak-lost", - "webhooks/events/streaks/streak-freeze-consumed", - "webhooks/events/streaks/streak-freeze-earned" - ] - }, - { - "group": "Points", - "pages": [ - "webhooks/events/points/points-changed", - "webhooks/events/points/points-level-changed", - "webhooks/events/points/points-boost-started", - "webhooks/events/points/points-boost-finished" - ] + "anchor": "llms.txt", + "href": "https://docs.trophy.so/llms-full.txt", + "icon": "bot" }, { - "group": "Leaderboards", - "pages": [ - "webhooks/events/leaderboards/leaderboard-started", - "webhooks/events/leaderboards/leaderboard-changed", - "webhooks/events/leaderboards/leaderboard-rank-changed", - "webhooks/events/leaderboards/leaderboard-finished" - ] + "anchor": "Ayuda", + "href": "mailto:support@trophy.so", + "icon": "life-buoy" + } + ], + "navbar": { + "links": [ + { + "label": "Panel", + "href": "https://app.trophy.so" + } + ], + "primary": { + "type": "button", + "label": "Comenzar gratis", + "href": "https://app.trophy.so/sign-up?utm_source=docs&utm_medium=navbar-links" } - ] - } - ], - "global": { - "anchors": [ - { - "anchor": "GitHub", - "href": "https://github.com/trophyso", - "icon": "github" - }, - { - "anchor": "Status", - "href": "https://status.trophy.so", - "icon": "radio-tower" - }, - { - "anchor": "llms.txt", - "href": "https://docs.trophy.so/llms-full.txt", - "icon": "bot" - }, - { - "anchor": "Help", - "href": "mailto:support@trophy.so", - "icon": "life-buoy" } - ] - } + } + ] }, "logo": { "light": "/logo/logo_light.svg", "dark": "/logo/logo_dark.svg", "href": "https://trophy.so" }, - "navbar": { - "links": [ - { - "label": "Dashboard", - "href": "https://app.trophy.so" - } - ], - "primary": { - "type": "button", - "label": "Start for free", - "href": "https://app.trophy.so/sign-up?utm_source=docs&utm_medium=navbar-links" - } - }, "footer": { "socials": { "x": "https://x.com/trophylabs", diff --git a/account/billing.mdx b/en/account/billing.mdx similarity index 100% rename from account/billing.mdx rename to en/account/billing.mdx diff --git a/account/branding.mdx b/en/account/branding.mdx similarity index 100% rename from account/branding.mdx rename to en/account/branding.mdx diff --git a/account/members.mdx b/en/account/members.mdx similarity index 100% rename from account/members.mdx rename to en/account/members.mdx diff --git a/account/overview.mdx b/en/account/overview.mdx similarity index 100% rename from account/overview.mdx rename to en/account/overview.mdx diff --git a/admin-api/endpoints/points/archive-a-boost.mdx b/en/admin-api/endpoints/points/archive-a-boost.mdx similarity index 100% rename from admin-api/endpoints/points/archive-a-boost.mdx rename to en/admin-api/endpoints/points/archive-a-boost.mdx diff --git a/admin-api/endpoints/points/archive-boosts-batch.mdx b/en/admin-api/endpoints/points/archive-boosts-batch.mdx similarity index 100% rename from admin-api/endpoints/points/archive-boosts-batch.mdx rename to en/admin-api/endpoints/points/archive-boosts-batch.mdx diff --git a/admin-api/endpoints/points/create-boosts.mdx b/en/admin-api/endpoints/points/create-boosts.mdx similarity index 100% rename from admin-api/endpoints/points/create-boosts.mdx rename to en/admin-api/endpoints/points/create-boosts.mdx diff --git a/admin-api/endpoints/streaks/grant-freezes.mdx b/en/admin-api/endpoints/streaks/grant-freezes.mdx similarity index 100% rename from admin-api/endpoints/streaks/grant-freezes.mdx rename to en/admin-api/endpoints/streaks/grant-freezes.mdx diff --git a/admin-api/endpoints/streaks/restore-streaks.mdx b/en/admin-api/endpoints/streaks/restore-streaks.mdx similarity index 100% rename from admin-api/endpoints/streaks/restore-streaks.mdx rename to en/admin-api/endpoints/streaks/restore-streaks.mdx diff --git a/admin-api/introduction.mdx b/en/admin-api/introduction.mdx similarity index 100% rename from admin-api/introduction.mdx rename to en/admin-api/introduction.mdx diff --git a/api-reference/authentication.mdx b/en/api-reference/authentication.mdx similarity index 100% rename from api-reference/authentication.mdx rename to en/api-reference/authentication.mdx diff --git a/api-reference/client-libraries.mdx b/en/api-reference/client-libraries.mdx similarity index 100% rename from api-reference/client-libraries.mdx rename to en/api-reference/client-libraries.mdx diff --git a/api-reference/endpoints/achievements/all-achievements.mdx b/en/api-reference/endpoints/achievements/all-achievements.mdx similarity index 100% rename from api-reference/endpoints/achievements/all-achievements.mdx rename to en/api-reference/endpoints/achievements/all-achievements.mdx diff --git a/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx b/en/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx similarity index 100% rename from api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx rename to en/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx diff --git a/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx b/en/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx similarity index 100% rename from api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx rename to en/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx diff --git a/api-reference/endpoints/leaderboards/get-leaderboard.mdx b/en/api-reference/endpoints/leaderboards/get-leaderboard.mdx similarity index 100% rename from api-reference/endpoints/leaderboards/get-leaderboard.mdx rename to en/api-reference/endpoints/leaderboards/get-leaderboard.mdx diff --git a/api-reference/endpoints/metrics/send-a-metric-change-event.mdx b/en/api-reference/endpoints/metrics/send-a-metric-change-event.mdx similarity index 100% rename from api-reference/endpoints/metrics/send-a-metric-change-event.mdx rename to en/api-reference/endpoints/metrics/send-a-metric-change-event.mdx diff --git a/api-reference/endpoints/points/get-points-boosts.mdx b/en/api-reference/endpoints/points/get-points-boosts.mdx similarity index 100% rename from api-reference/endpoints/points/get-points-boosts.mdx rename to en/api-reference/endpoints/points/get-points-boosts.mdx diff --git a/api-reference/endpoints/points/get-points-level-summary.mdx b/en/api-reference/endpoints/points/get-points-level-summary.mdx similarity index 100% rename from api-reference/endpoints/points/get-points-level-summary.mdx rename to en/api-reference/endpoints/points/get-points-level-summary.mdx diff --git a/api-reference/endpoints/points/get-points-levels.mdx b/en/api-reference/endpoints/points/get-points-levels.mdx similarity index 100% rename from api-reference/endpoints/points/get-points-levels.mdx rename to en/api-reference/endpoints/points/get-points-levels.mdx diff --git a/api-reference/endpoints/points/get-points-summary.mdx b/en/api-reference/endpoints/points/get-points-summary.mdx similarity index 100% rename from api-reference/endpoints/points/get-points-summary.mdx rename to en/api-reference/endpoints/points/get-points-summary.mdx diff --git a/api-reference/endpoints/points/get-points.mdx b/en/api-reference/endpoints/points/get-points.mdx similarity index 100% rename from api-reference/endpoints/points/get-points.mdx rename to en/api-reference/endpoints/points/get-points.mdx diff --git a/api-reference/endpoints/streaks/get-streak-rankings.mdx b/en/api-reference/endpoints/streaks/get-streak-rankings.mdx similarity index 100% rename from api-reference/endpoints/streaks/get-streak-rankings.mdx rename to en/api-reference/endpoints/streaks/get-streak-rankings.mdx diff --git a/api-reference/endpoints/streaks/get-streaks.mdx b/en/api-reference/endpoints/streaks/get-streaks.mdx similarity index 100% rename from api-reference/endpoints/streaks/get-streaks.mdx rename to en/api-reference/endpoints/streaks/get-streaks.mdx diff --git a/api-reference/endpoints/users/create-a-user.mdx b/en/api-reference/endpoints/users/create-a-user.mdx similarity index 100% rename from api-reference/endpoints/users/create-a-user.mdx rename to en/api-reference/endpoints/users/create-a-user.mdx diff --git a/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx b/en/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx similarity index 100% rename from api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx rename to en/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx diff --git a/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx b/en/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx similarity index 100% rename from api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx rename to en/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx diff --git a/api-reference/endpoints/users/get-a-single-user.mdx b/en/api-reference/endpoints/users/get-a-single-user.mdx similarity index 100% rename from api-reference/endpoints/users/get-a-single-user.mdx rename to en/api-reference/endpoints/users/get-a-single-user.mdx diff --git a/api-reference/endpoints/users/get-a-users-completed-achievements.mdx b/en/api-reference/endpoints/users/get-a-users-completed-achievements.mdx similarity index 100% rename from api-reference/endpoints/users/get-a-users-completed-achievements.mdx rename to en/api-reference/endpoints/users/get-a-users-completed-achievements.mdx diff --git a/api-reference/endpoints/users/get-a-users-leaderboard.mdx b/en/api-reference/endpoints/users/get-a-users-leaderboard.mdx similarity index 100% rename from api-reference/endpoints/users/get-a-users-leaderboard.mdx rename to en/api-reference/endpoints/users/get-a-users-leaderboard.mdx diff --git a/api-reference/endpoints/users/get-a-users-points-boosts.mdx b/en/api-reference/endpoints/users/get-a-users-points-boosts.mdx similarity index 100% rename from api-reference/endpoints/users/get-a-users-points-boosts.mdx rename to en/api-reference/endpoints/users/get-a-users-points-boosts.mdx diff --git a/api-reference/endpoints/users/get-a-users-points-summary.mdx b/en/api-reference/endpoints/users/get-a-users-points-summary.mdx similarity index 100% rename from api-reference/endpoints/users/get-a-users-points-summary.mdx rename to en/api-reference/endpoints/users/get-a-users-points-summary.mdx diff --git a/api-reference/endpoints/users/get-a-users-points.mdx b/en/api-reference/endpoints/users/get-a-users-points.mdx similarity index 100% rename from api-reference/endpoints/users/get-a-users-points.mdx rename to en/api-reference/endpoints/users/get-a-users-points.mdx diff --git a/api-reference/endpoints/users/get-a-users-streak.mdx b/en/api-reference/endpoints/users/get-a-users-streak.mdx similarity index 100% rename from api-reference/endpoints/users/get-a-users-streak.mdx rename to en/api-reference/endpoints/users/get-a-users-streak.mdx diff --git a/api-reference/endpoints/users/get-a-users-wrapped.mdx b/en/api-reference/endpoints/users/get-a-users-wrapped.mdx similarity index 100% rename from api-reference/endpoints/users/get-a-users-wrapped.mdx rename to en/api-reference/endpoints/users/get-a-users-wrapped.mdx diff --git a/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx b/en/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx similarity index 100% rename from api-reference/endpoints/users/get-all-metrics-for-a-user.mdx rename to en/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx diff --git a/api-reference/endpoints/users/get-user-preferences.mdx b/en/api-reference/endpoints/users/get-user-preferences.mdx similarity index 100% rename from api-reference/endpoints/users/get-user-preferences.mdx rename to en/api-reference/endpoints/users/get-user-preferences.mdx diff --git a/api-reference/endpoints/users/identify-a-user.mdx b/en/api-reference/endpoints/users/identify-a-user.mdx similarity index 100% rename from api-reference/endpoints/users/identify-a-user.mdx rename to en/api-reference/endpoints/users/identify-a-user.mdx diff --git a/api-reference/endpoints/users/update-a-user.mdx b/en/api-reference/endpoints/users/update-a-user.mdx similarity index 100% rename from api-reference/endpoints/users/update-a-user.mdx rename to en/api-reference/endpoints/users/update-a-user.mdx diff --git a/api-reference/endpoints/users/update-user-preferences.mdx b/en/api-reference/endpoints/users/update-user-preferences.mdx similarity index 100% rename from api-reference/endpoints/users/update-user-preferences.mdx rename to en/api-reference/endpoints/users/update-user-preferences.mdx diff --git a/api-reference/idempotency.mdx b/en/api-reference/idempotency.mdx similarity index 100% rename from api-reference/idempotency.mdx rename to en/api-reference/idempotency.mdx diff --git a/api-reference/introduction.mdx b/en/api-reference/introduction.mdx similarity index 100% rename from api-reference/introduction.mdx rename to en/api-reference/introduction.mdx diff --git a/api-reference/openapi.yml b/en/api-reference/openapi.yml similarity index 100% rename from api-reference/openapi.yml rename to en/api-reference/openapi.yml diff --git a/api-reference/rate-limiting.mdx b/en/api-reference/rate-limiting.mdx similarity index 100% rename from api-reference/rate-limiting.mdx rename to en/api-reference/rate-limiting.mdx diff --git a/experimentation/engagement.mdx b/en/experimentation/engagement.mdx similarity index 100% rename from experimentation/engagement.mdx rename to en/experimentation/engagement.mdx diff --git a/experimentation/overview.mdx b/en/experimentation/overview.mdx similarity index 100% rename from experimentation/overview.mdx rename to en/experimentation/overview.mdx diff --git a/experimentation/retention.mdx b/en/experimentation/retention.mdx similarity index 100% rename from experimentation/retention.mdx rename to en/experimentation/retention.mdx diff --git a/getting-started/introduction.mdx b/en/getting-started/introduction.mdx similarity index 100% rename from getting-started/introduction.mdx rename to en/getting-started/introduction.mdx diff --git a/getting-started/quickstart.mdx b/en/getting-started/quickstart.mdx similarity index 100% rename from getting-started/quickstart.mdx rename to en/getting-started/quickstart.mdx diff --git a/guides/gamified-fitness-platform.mdx b/en/guides/gamified-fitness-platform.mdx similarity index 100% rename from guides/gamified-fitness-platform.mdx rename to en/guides/gamified-fitness-platform.mdx diff --git a/guides/gamified-study-platform.mdx b/en/guides/gamified-study-platform.mdx similarity index 100% rename from guides/gamified-study-platform.mdx rename to en/guides/gamified-study-platform.mdx diff --git a/guides/how-to-build-a-leaderboards-feature.mdx b/en/guides/how-to-build-a-leaderboards-feature.mdx similarity index 100% rename from guides/how-to-build-a-leaderboards-feature.mdx rename to en/guides/how-to-build-a-leaderboards-feature.mdx diff --git a/guides/how-to-build-a-streaks-feature.mdx b/en/guides/how-to-build-a-streaks-feature.mdx similarity index 100% rename from guides/how-to-build-a-streaks-feature.mdx rename to en/guides/how-to-build-a-streaks-feature.mdx diff --git a/guides/how-to-build-an-achievements-feature.mdx b/en/guides/how-to-build-an-achievements-feature.mdx similarity index 100% rename from guides/how-to-build-an-achievements-feature.mdx rename to en/guides/how-to-build-an-achievements-feature.mdx diff --git a/guides/how-to-build-an-energy-feature.mdx b/en/guides/how-to-build-an-energy-feature.mdx similarity index 100% rename from guides/how-to-build-an-energy-feature.mdx rename to en/guides/how-to-build-an-energy-feature.mdx diff --git a/guides/how-to-build-an-xp-feature.mdx b/en/guides/how-to-build-an-xp-feature.mdx similarity index 100% rename from guides/how-to-build-an-xp-feature.mdx rename to en/guides/how-to-build-an-xp-feature.mdx diff --git a/platform/achievements.mdx b/en/platform/achievements.mdx similarity index 100% rename from platform/achievements.mdx rename to en/platform/achievements.mdx diff --git a/platform/emails.mdx b/en/platform/emails.mdx similarity index 100% rename from platform/emails.mdx rename to en/platform/emails.mdx diff --git a/platform/events.mdx b/en/platform/events.mdx similarity index 100% rename from platform/events.mdx rename to en/platform/events.mdx diff --git a/platform/leaderboards.mdx b/en/platform/leaderboards.mdx similarity index 100% rename from platform/leaderboards.mdx rename to en/platform/leaderboards.mdx diff --git a/platform/metrics.mdx b/en/platform/metrics.mdx similarity index 100% rename from platform/metrics.mdx rename to en/platform/metrics.mdx diff --git a/platform/overview.mdx b/en/platform/overview.mdx similarity index 100% rename from platform/overview.mdx rename to en/platform/overview.mdx diff --git a/platform/points.mdx b/en/platform/points.mdx similarity index 100% rename from platform/points.mdx rename to en/platform/points.mdx diff --git a/platform/push-notifications.mdx b/en/platform/push-notifications.mdx similarity index 100% rename from platform/push-notifications.mdx rename to en/platform/push-notifications.mdx diff --git a/platform/streaks.mdx b/en/platform/streaks.mdx similarity index 100% rename from platform/streaks.mdx rename to en/platform/streaks.mdx diff --git a/platform/users.mdx b/en/platform/users.mdx similarity index 100% rename from platform/users.mdx rename to en/platform/users.mdx diff --git a/snippets/add-env-var-block.mdx b/en/snippets/add-env-var-block.mdx similarity index 100% rename from snippets/add-env-var-block.mdx rename to en/snippets/add-env-var-block.mdx diff --git a/snippets/all-achievements-request-block.mdx b/en/snippets/all-achievements-request-block.mdx similarity index 100% rename from snippets/all-achievements-request-block.mdx rename to en/snippets/all-achievements-request-block.mdx diff --git a/snippets/all-achievements-response-block.mdx b/en/snippets/all-achievements-response-block.mdx similarity index 100% rename from snippets/all-achievements-response-block.mdx rename to en/snippets/all-achievements-response-block.mdx diff --git a/snippets/control-flag-block.mdx b/en/snippets/control-flag-block.mdx similarity index 100% rename from snippets/control-flag-block.mdx rename to en/snippets/control-flag-block.mdx diff --git a/snippets/event-attributes-request-block.mdx b/en/snippets/event-attributes-request-block.mdx similarity index 100% rename from snippets/event-attributes-request-block.mdx rename to en/snippets/event-attributes-request-block.mdx diff --git a/snippets/get-user-notification-preferences-block.mdx b/en/snippets/get-user-notification-preferences-block.mdx similarity index 100% rename from snippets/get-user-notification-preferences-block.mdx rename to en/snippets/get-user-notification-preferences-block.mdx diff --git a/snippets/idempotent-event-tracking.mdx b/en/snippets/idempotent-event-tracking.mdx similarity index 100% rename from snippets/idempotent-event-tracking.mdx rename to en/snippets/idempotent-event-tracking.mdx diff --git a/snippets/identify-user-request-block.mdx b/en/snippets/identify-user-request-block.mdx similarity index 100% rename from snippets/identify-user-request-block.mdx rename to en/snippets/identify-user-request-block.mdx diff --git a/snippets/identify-user-with-device-tokens-request-block.mdx b/en/snippets/identify-user-with-device-tokens-request-block.mdx similarity index 100% rename from snippets/identify-user-with-device-tokens-request-block.mdx rename to en/snippets/identify-user-with-device-tokens-request-block.mdx diff --git a/snippets/leaderboard-rankings-request-multiple-attributes.mdx b/en/snippets/leaderboard-rankings-request-multiple-attributes.mdx similarity index 100% rename from snippets/leaderboard-rankings-request-multiple-attributes.mdx rename to en/snippets/leaderboard-rankings-request-multiple-attributes.mdx diff --git a/snippets/leaderboard-rankings-request-single-attribute.mdx b/en/snippets/leaderboard-rankings-request-single-attribute.mdx similarity index 100% rename from snippets/leaderboard-rankings-request-single-attribute.mdx rename to en/snippets/leaderboard-rankings-request-single-attribute.mdx diff --git a/snippets/leaderboard-rankings-request.mdx b/en/snippets/leaderboard-rankings-request.mdx similarity index 100% rename from snippets/leaderboard-rankings-request.mdx rename to en/snippets/leaderboard-rankings-request.mdx diff --git a/snippets/leaderboard-rankings-response.mdx b/en/snippets/leaderboard-rankings-response.mdx similarity index 100% rename from snippets/leaderboard-rankings-response.mdx rename to en/snippets/leaderboard-rankings-response.mdx diff --git a/snippets/metric-change-event-points-level-excerpt-block.mdx b/en/snippets/metric-change-event-points-level-excerpt-block.mdx similarity index 100% rename from snippets/metric-change-event-points-level-excerpt-block.mdx rename to en/snippets/metric-change-event-points-level-excerpt-block.mdx diff --git a/snippets/metric-change-request-block.mdx b/en/snippets/metric-change-request-block.mdx similarity index 100% rename from snippets/metric-change-request-block.mdx rename to en/snippets/metric-change-request-block.mdx diff --git a/snippets/metric-change-response-block.mdx b/en/snippets/metric-change-response-block.mdx similarity index 100% rename from snippets/metric-change-response-block.mdx rename to en/snippets/metric-change-response-block.mdx diff --git a/snippets/metric-event-with-device-tokens-request-block.mdx b/en/snippets/metric-event-with-device-tokens-request-block.mdx similarity index 100% rename from snippets/metric-event-with-device-tokens-request-block.mdx rename to en/snippets/metric-event-with-device-tokens-request-block.mdx diff --git a/snippets/plan-badge.jsx b/en/snippets/plan-badge.jsx similarity index 100% rename from snippets/plan-badge.jsx rename to en/snippets/plan-badge.jsx diff --git a/snippets/points-histogram-summary-response-block.mdx b/en/snippets/points-histogram-summary-response-block.mdx similarity index 100% rename from snippets/points-histogram-summary-response-block.mdx rename to en/snippets/points-histogram-summary-response-block.mdx diff --git a/snippets/points-level-summary-response-block.mdx b/en/snippets/points-level-summary-response-block.mdx similarity index 100% rename from snippets/points-level-summary-response-block.mdx rename to en/snippets/points-level-summary-response-block.mdx diff --git a/snippets/points-levels-list-response-block.mdx b/en/snippets/points-levels-list-response-block.mdx similarity index 100% rename from snippets/points-levels-list-response-block.mdx rename to en/snippets/points-levels-list-response-block.mdx diff --git a/snippets/points-system-response-block.mdx b/en/snippets/points-system-response-block.mdx similarity index 100% rename from snippets/points-system-response-block.mdx rename to en/snippets/points-system-response-block.mdx diff --git a/snippets/rate-limit-badge.jsx b/en/snippets/rate-limit-badge.jsx similarity index 100% rename from snippets/rate-limit-badge.jsx rename to en/snippets/rate-limit-badge.jsx diff --git a/snippets/sdk-install-command.mdx b/en/snippets/sdk-install-command.mdx similarity index 100% rename from snippets/sdk-install-command.mdx rename to en/snippets/sdk-install-command.mdx diff --git a/snippets/snippet-intro.mdx b/en/snippets/snippet-intro.mdx similarity index 100% rename from snippets/snippet-intro.mdx rename to en/snippets/snippet-intro.mdx diff --git a/snippets/streak-request-block.mdx b/en/snippets/streak-request-block.mdx similarity index 100% rename from snippets/streak-request-block.mdx rename to en/snippets/streak-request-block.mdx diff --git a/snippets/streak-response-block.mdx b/en/snippets/streak-response-block.mdx similarity index 100% rename from snippets/streak-response-block.mdx rename to en/snippets/streak-response-block.mdx diff --git a/snippets/update-user-notification-preferences-block.mdx b/en/snippets/update-user-notification-preferences-block.mdx similarity index 100% rename from snippets/update-user-notification-preferences-block.mdx rename to en/snippets/update-user-notification-preferences-block.mdx diff --git a/snippets/user-achievements-request-block.mdx b/en/snippets/user-achievements-request-block.mdx similarity index 100% rename from snippets/user-achievements-request-block.mdx rename to en/snippets/user-achievements-request-block.mdx diff --git a/snippets/user-energy-response-block.mdx b/en/snippets/user-energy-response-block.mdx similarity index 100% rename from snippets/user-energy-response-block.mdx rename to en/snippets/user-energy-response-block.mdx diff --git a/snippets/user-leaderboard-rankings-request.mdx b/en/snippets/user-leaderboard-rankings-request.mdx similarity index 100% rename from snippets/user-leaderboard-rankings-request.mdx rename to en/snippets/user-leaderboard-rankings-request.mdx diff --git a/snippets/user-leaderboard-rankings-response.mdx b/en/snippets/user-leaderboard-rankings-response.mdx similarity index 100% rename from snippets/user-leaderboard-rankings-response.mdx rename to en/snippets/user-leaderboard-rankings-response.mdx diff --git a/snippets/user-points-request-block.mdx b/en/snippets/user-points-request-block.mdx similarity index 100% rename from snippets/user-points-request-block.mdx rename to en/snippets/user-points-request-block.mdx diff --git a/snippets/user-points-response-block.mdx b/en/snippets/user-points-response-block.mdx similarity index 100% rename from snippets/user-points-response-block.mdx rename to en/snippets/user-points-response-block.mdx diff --git a/snippets/user-points-summary-request-block.mdx b/en/snippets/user-points-summary-request-block.mdx similarity index 100% rename from snippets/user-points-summary-request-block.mdx rename to en/snippets/user-points-summary-request-block.mdx diff --git a/snippets/user-points-summary-response-block.mdx b/en/snippets/user-points-summary-response-block.mdx similarity index 100% rename from snippets/user-points-summary-response-block.mdx rename to en/snippets/user-points-summary-response-block.mdx diff --git a/snippets/webhook-points-level-changed-payload-block.mdx b/en/snippets/webhook-points-level-changed-payload-block.mdx similarity index 100% rename from snippets/webhook-points-level-changed-payload-block.mdx rename to en/snippets/webhook-points-level-changed-payload-block.mdx diff --git a/webhooks/events/achievements/achievement-completed.mdx b/en/webhooks/events/achievements/achievement-completed.mdx similarity index 100% rename from webhooks/events/achievements/achievement-completed.mdx rename to en/webhooks/events/achievements/achievement-completed.mdx diff --git a/webhooks/events/leaderboards/leaderboard-changed.mdx b/en/webhooks/events/leaderboards/leaderboard-changed.mdx similarity index 100% rename from webhooks/events/leaderboards/leaderboard-changed.mdx rename to en/webhooks/events/leaderboards/leaderboard-changed.mdx diff --git a/webhooks/events/leaderboards/leaderboard-finished.mdx b/en/webhooks/events/leaderboards/leaderboard-finished.mdx similarity index 100% rename from webhooks/events/leaderboards/leaderboard-finished.mdx rename to en/webhooks/events/leaderboards/leaderboard-finished.mdx diff --git a/webhooks/events/leaderboards/leaderboard-rank-changed.mdx b/en/webhooks/events/leaderboards/leaderboard-rank-changed.mdx similarity index 100% rename from webhooks/events/leaderboards/leaderboard-rank-changed.mdx rename to en/webhooks/events/leaderboards/leaderboard-rank-changed.mdx diff --git a/webhooks/events/leaderboards/leaderboard-started.mdx b/en/webhooks/events/leaderboards/leaderboard-started.mdx similarity index 100% rename from webhooks/events/leaderboards/leaderboard-started.mdx rename to en/webhooks/events/leaderboards/leaderboard-started.mdx diff --git a/webhooks/events/points/points-boost-finished.mdx b/en/webhooks/events/points/points-boost-finished.mdx similarity index 100% rename from webhooks/events/points/points-boost-finished.mdx rename to en/webhooks/events/points/points-boost-finished.mdx diff --git a/webhooks/events/points/points-boost-started.mdx b/en/webhooks/events/points/points-boost-started.mdx similarity index 100% rename from webhooks/events/points/points-boost-started.mdx rename to en/webhooks/events/points/points-boost-started.mdx diff --git a/webhooks/events/points/points-changed.mdx b/en/webhooks/events/points/points-changed.mdx similarity index 100% rename from webhooks/events/points/points-changed.mdx rename to en/webhooks/events/points/points-changed.mdx diff --git a/webhooks/events/points/points-level-changed.mdx b/en/webhooks/events/points/points-level-changed.mdx similarity index 100% rename from webhooks/events/points/points-level-changed.mdx rename to en/webhooks/events/points/points-level-changed.mdx diff --git a/webhooks/events/streaks/streak-extended.mdx b/en/webhooks/events/streaks/streak-extended.mdx similarity index 100% rename from webhooks/events/streaks/streak-extended.mdx rename to en/webhooks/events/streaks/streak-extended.mdx diff --git a/webhooks/events/streaks/streak-freeze-consumed.mdx b/en/webhooks/events/streaks/streak-freeze-consumed.mdx similarity index 100% rename from webhooks/events/streaks/streak-freeze-consumed.mdx rename to en/webhooks/events/streaks/streak-freeze-consumed.mdx diff --git a/webhooks/events/streaks/streak-freeze-earned.mdx b/en/webhooks/events/streaks/streak-freeze-earned.mdx similarity index 100% rename from webhooks/events/streaks/streak-freeze-earned.mdx rename to en/webhooks/events/streaks/streak-freeze-earned.mdx diff --git a/webhooks/events/streaks/streak-lost.mdx b/en/webhooks/events/streaks/streak-lost.mdx similarity index 100% rename from webhooks/events/streaks/streak-lost.mdx rename to en/webhooks/events/streaks/streak-lost.mdx diff --git a/webhooks/events/streaks/streak-started.mdx b/en/webhooks/events/streaks/streak-started.mdx similarity index 100% rename from webhooks/events/streaks/streak-started.mdx rename to en/webhooks/events/streaks/streak-started.mdx diff --git a/webhooks/idempotency.mdx b/en/webhooks/idempotency.mdx similarity index 100% rename from webhooks/idempotency.mdx rename to en/webhooks/idempotency.mdx diff --git a/webhooks/introduction.mdx b/en/webhooks/introduction.mdx similarity index 100% rename from webhooks/introduction.mdx rename to en/webhooks/introduction.mdx diff --git a/webhooks/observability.mdx b/en/webhooks/observability.mdx similarity index 100% rename from webhooks/observability.mdx rename to en/webhooks/observability.mdx diff --git a/webhooks/quickstart.mdx b/en/webhooks/quickstart.mdx similarity index 100% rename from webhooks/quickstart.mdx rename to en/webhooks/quickstart.mdx diff --git a/webhooks/retries.mdx b/en/webhooks/retries.mdx similarity index 100% rename from webhooks/retries.mdx rename to en/webhooks/retries.mdx diff --git a/webhooks/security.mdx b/en/webhooks/security.mdx similarity index 100% rename from webhooks/security.mdx rename to en/webhooks/security.mdx diff --git a/es/account/billing.mdx b/es/account/billing.mdx new file mode 100644 index 0000000..1e9e263 --- /dev/null +++ b/es/account/billing.mdx @@ -0,0 +1,129 @@ +--- +title: Billing +description: Learn how Trophy controls costs by charging based on monthly active users. +"og:description": Learn how Trophy controls costs by charging based on monthly active users. +icon: gauge +--- + +import { PlanBadge } from "/snippets/plan-badge.jsx"; + +## Usage-based Billing + +Trophy follows a usage-based pricing model where customers only pay for the units of usage they consume. For Trophy, a unit of usage corresponds to a single [Monthly Active User](##monthly-active-users-maus) (MAU). + + + See our [pricing page](https://trophy.so/pricing) to get an estimate of your + costs based on your expected usage. + + +## Monthly Active Users (MAUs) + +Trophy defines an MAU as a single user that sends at least one [metric event](/platform/events) to Trophy in a given month. + + + Bear in mind **you never pay for churned users**. If a user signs up for your + product in a given month but doesn't return, you only pay for that user once + and never again. + + +## Free Tier + +The free tier allows teams to test and evaluate Trophy up to **100 MAUs** without accruing usage charges. + + + Once you exceed the free plan, your account will continue to function and + we'll reach out to you directly with a friendly reminder to upgrade. + + +## Paid Plans + +Trophy has two paid plans, [Starter](#starter-plan) and [Pro](#pro-plan). + +### Starter Plan + +The starter plan is for customers who have graduated from the testing and evaluation phase and are using Trophy in small-scale production deployments. + +Unlike the [Free Tier](#free-tier), the starter plan has no limits on MAUs. + + + Beyond ~`16,000` MAUs it's usually more cost-efficient to upgrade to the [Pro + Plan](#pro-plan). + + +### Pro Plan + +The pro plan is for customers who are using Trophy in larger scale deployments or who require more advanced [features](#features). + +### Plan Allowances + +Here's a comparison of the allowances of each paid plan: + +| Item | Starter | Pro | +| --------------------------- | ------- | -------- | +| Base price | `$99` | `$299` | +| Included MAUs | `1,000` | `10,000` | +| Included Emails | `5,000` | `50,000` | +| Included Push Notifications | `5,000` | `50,000` | + +### Overages + +Overages are charged on paid plans above the included [allowances](#plan-allowances) at the following rates: + +| Item | Starter | Pro | +| ------------------------ | -------- | -------- | +| 1 MAU | `$0.015` | `$0.015` | +| 1,000 Emails | `$2.50` | `$2.00` | +| 1,000 Push Notifications | `$1.50` | `$1.00` | + +Volume discounts are available as part of [custom contracts](#custom-contracts). + +## Features + +Here's a list of all Trophy features and the plan they become available on: + + + +- [Achievements](/platform/achievements) +- [Streaks](/platform/streaks) +- [Points](/platform/points) +- [Leaderboards](/platform/leaderboards) +- [Emails](/platform/emails) +- [Push Notifications](/platform/push-notifications) + + + +- [DNS Verification](/platform/emails#dns-verification-advanced) + + + +- [Webhooks](/webhooks) +- [Custom Attributes](/platform/users#custom-user-attributes) + +## Custom Contracts + +If you have more than 100K MAUs and would like to discuss custom contracts including volume discounts to suit your business needs then please [get in touch](mailto:hello@trophy.so) and we'll be happy to help. + +## Viewing Your Usage + +You can view your usage for the current billing period on the [billing page](https://app.trophy.so/billing) of the Trophy dashboard and view all past invoices in your billing portal. + + + + + +## Frequently Asked Questions + + + + We charge all customers on the 1st of the month for usage in arrears. + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/account/branding.mdx b/es/account/branding.mdx new file mode 100644 index 0000000..1d1ced1 --- /dev/null +++ b/es/account/branding.mdx @@ -0,0 +1,24 @@ +--- +title: Branding +description: Configure your logo and brand colors used across features built with Trophy. +"og:description": Learn how Trophy tracks usage according to the number of users that use your product. +icon: palette +--- + +## Configure Branding + +The [branding page](https://app.trophy.so/branding) in the Trophy dashboard allows you to configure the following account-level settings. These setting will be used in any communications you configure Trophy to send to users, like [Emails](/platform/emails). + + + + + +- **Logo**: Upload your brand’s logo to be displayed in the header of Trophy emails. We recommend using a horizontal logo with a transparent background or a plain background color with rounded corners. +- **Font**: The default font used in Trophy emails. +- **Brand Color**: Choose a primary color for your brand. Trophy will use this color for buttons and other elements in your emails. +- **App Name**: Enter the name of your app or service. Trophy will use this name in various places in your emails. +- **App URL**: Enter a default URL to use when not overridden on a per-email basis. Usually this is the URL of your app or service's sign in page. + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/account/members.mdx b/es/account/members.mdx new file mode 100644 index 0000000..ab75c53 --- /dev/null +++ b/es/account/members.mdx @@ -0,0 +1,57 @@ +--- +title: Members +description: Give team members access to Trophy and manage your account settings. +"og:description": Give team members access to Trophy and manage your account settings. +icon: user-plus +--- + +## Invite Your team + +Trophy supports up to 5 team members per organization. However if you feel you need more [just ask](mailto:support@trophy.so) and we'll be happy to give you more room. + +To invite a team member to your Trophy organization, open up the organization management dialog and head into the _Members_ tab: + + + + + +Hit _Invite_ and add the email address of the team member(s) you want to invite. If you're an account admin, you can add other admins, otherwise you'll only be able to add other members. + +Once you're happy, hit _Send invitations_ and each team member will receive an email invitation to Trophy. + +## Manage User Roles + +Only organization admins can manage user roles + +Every team member in your Trophy organization has one of two roles: + +- Admin: Can perform all account operations, including organization management and adding new admins. +- Member: Can perform all account operations, and add new members. + +By default, the person who first set up Trophy will become the first admin. However you can change the role of any user at any time. + +To manage roles, open up the organization management dialog and head into the _Members_ tab: + + + + + +Here you can promote any existing member to an admin, or demote any existing admin to a member. + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/account/overview.mdx b/es/account/overview.mdx new file mode 100644 index 0000000..4b42ea2 --- /dev/null +++ b/es/account/overview.mdx @@ -0,0 +1,23 @@ +--- +title: Overview +description: Learn how to manage your Trophy account. +--- + +From inviting team members to understanding billing, this section of the documentation is dedicated to managing your Trophy account. + + + + Give team members access to Trophy and manage your account settings. + + + Configure your logo and brand colors used across features built with Trophy. + + + Learn how Trophy tracks usage according to the number of users that use your + product. + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/admin-api/endpoints/points/archive-a-boost.mdx b/es/admin-api/endpoints/points/archive-a-boost.mdx new file mode 100644 index 0000000..e80a6fd --- /dev/null +++ b/es/admin-api/endpoints/points/archive-a-boost.mdx @@ -0,0 +1,9 @@ +--- +openapi: delete /points/boosts/{id} +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + diff --git a/es/admin-api/endpoints/points/archive-boosts-batch.mdx b/es/admin-api/endpoints/points/archive-boosts-batch.mdx new file mode 100644 index 0000000..1a54ad0 --- /dev/null +++ b/es/admin-api/endpoints/points/archive-boosts-batch.mdx @@ -0,0 +1,9 @@ +--- +openapi: delete /points/boosts +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + diff --git a/es/admin-api/endpoints/points/create-boosts.mdx b/es/admin-api/endpoints/points/create-boosts.mdx new file mode 100644 index 0000000..34f2532 --- /dev/null +++ b/es/admin-api/endpoints/points/create-boosts.mdx @@ -0,0 +1,9 @@ +--- +openapi: post /points/boosts +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + diff --git a/es/admin-api/endpoints/streaks/grant-freezes.mdx b/es/admin-api/endpoints/streaks/grant-freezes.mdx new file mode 100644 index 0000000..e16879d --- /dev/null +++ b/es/admin-api/endpoints/streaks/grant-freezes.mdx @@ -0,0 +1,9 @@ +--- +openapi: post /streaks/freezes +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + diff --git a/es/admin-api/endpoints/streaks/restore-streaks.mdx b/es/admin-api/endpoints/streaks/restore-streaks.mdx new file mode 100644 index 0000000..b60cf8c --- /dev/null +++ b/es/admin-api/endpoints/streaks/restore-streaks.mdx @@ -0,0 +1,9 @@ +--- +openapi: post /streaks/restore +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + diff --git a/es/admin-api/introduction.mdx b/es/admin-api/introduction.mdx new file mode 100644 index 0000000..0a47a0c --- /dev/null +++ b/es/admin-api/introduction.mdx @@ -0,0 +1,24 @@ +--- +title: Introduction +description: Learn about the Trophy Admin API and how to use it to drive custom workflows using Trophy data. +subtitle: Learn about the Trophy Admin API and how to use it to drive custom workflows using Trophy data. +--- + +The Trophy Admin API is a set of endpoints for building custom gamification workflows. + +While the [Application API](/api-reference/introduction) models the data and interactions required to deliver a gamification experience in-app, the Admin API models administrative actions that are only performed by your team or business logic outside of your application. + +The Admin API is accessible through **the same SDKs** as the Application API or, for those who manage their own HTTP clients, is available at the following base URL. + +```bash Base URL +https://admin.trophy.so/v1 +``` + + + Have a use case you think you need an admin API for? [Tell us about + it](mailto:support@trophy.so) and we'll build it! + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/api-reference/authentication.mdx b/es/api-reference/authentication.mdx new file mode 100644 index 0000000..7187fba --- /dev/null +++ b/es/api-reference/authentication.mdx @@ -0,0 +1,83 @@ +--- +title: Authentication +description: Integrate securely with the API using account-level API keys transmitted in the `X-API-KEY` header. +"og:description": Integrate securely with the Trophy API using account-level API keys transmitted in the `X-API-KEY` header. +icon: key +--- + +## API Keys + +Every account can create a maximum of 3 API keys from the [Integration Page](https://app.trophy.so/integration). If you've already signed up, you'll have created one during onboarding. + +Trophy keeps track of and displays when each API key in your account was created and when it was last used so you can easily keep track of usage. + + + + + +### Anatomy of an API key + +Each API key is made up of 2 parts separated by a period: + +```bash +{prefix}•{body} +``` + +- The _prefix_ is the first 8 characters. It's readable and will always stay the same so it's easily recognisable. +- The _body_ is the secret part, and is only shown to you once when you create the key. + + + When using the API, both parts of your API key must be sent in the `X-API-KEY` + header. + + +### Authenticating Requests + +When making requests to the API, make sure to include **both** parts of your API key in the `X-API-KEY` header as in this example: + +```bash +curl https://app.trophy.so/api/users//metrics/ \ + -H "X-API-KEY: ********.***********************" +``` + +If you do not pass an API key, or your API key is invalid, you'll receive a `401` response code. + +## Managing API keys + +There are a few different operations you can perform on API keys from within your Trophy dashboard to manage your integration. + + + + + +### Rotating keys + +API keys can be rotated if you want to change them for any reason. At the point of rotation, the original API key will no longer function and any requests still using it will begin receiving `401` responses immediately. + + + Note that when rotating keys, both the prefix and the body will change. + + +### Revoking keys + +API keys can also be revoked entirely at which point they become _Inactive_. At the point of revocation, the API key will no longer function and any requests still using it will begin receiving `401` responses immediately. + +Once revoked you can re-activate the API key at any time. + + + Neither the prefix or the body of the key change when revoked or re-activated. + + +### Deleting API keys + +If you're 100% sure you no longer need an API key, they can be deleted. + +Once API keys are deleted, they cannot be recovered. + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/api-reference/client-libraries.mdx b/es/api-reference/client-libraries.mdx new file mode 100644 index 0000000..f025f29 --- /dev/null +++ b/es/api-reference/client-libraries.mdx @@ -0,0 +1,99 @@ +--- +title: Client Libraries +description: Use Trophy's type-safe SDKs to integrate with the API. +"og:description": Use Trophy's type-safe SDKs to integrate with the API and start building gamified product experiences. +icon: code-xml +--- + +Trophy currently offers the following SDKs for interacting with the Trophy API. + + + + + + } + href="https://github.com/trophyso/trophy-go" + /> + + + + } + href="https://github.com/trophyso/trophy-java" + /> + + + + } + href="https://github.com/trophyso/trophy-dotnet" + /> + + + + } + href="https://github.com/trophyso/trophy-node" + /> + + + + } + href="https://github.com/trophyso/trophy-php" + /> + + + + } + href="https://github.com/trophyso/trophy-python" + /> + + + + } + href="https://github.com/trophyso/trophy-ruby" + /> + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/api-reference/endpoints/achievements/all-achievements.mdx b/es/api-reference/endpoints/achievements/all-achievements.mdx new file mode 100644 index 0000000..f90d566 --- /dev/null +++ b/es/api-reference/endpoints/achievements/all-achievements.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /achievements +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx b/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx new file mode 100644 index 0000000..4aa9860 --- /dev/null +++ b/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx @@ -0,0 +1,9 @@ +--- +openapi: post /achievements/{key}/complete +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx b/es/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx new file mode 100644 index 0000000..7decf5d --- /dev/null +++ b/es/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /leaderboards +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/leaderboards/get-leaderboard.mdx b/es/api-reference/endpoints/leaderboards/get-leaderboard.mdx new file mode 100644 index 0000000..12fae63 --- /dev/null +++ b/es/api-reference/endpoints/leaderboards/get-leaderboard.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /leaderboards/{key} +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/metrics/send-a-metric-change-event.mdx b/es/api-reference/endpoints/metrics/send-a-metric-change-event.mdx new file mode 100644 index 0000000..1cbbbe1 --- /dev/null +++ b/es/api-reference/endpoints/metrics/send-a-metric-change-event.mdx @@ -0,0 +1,9 @@ +--- +openapi: post /metrics/{key}/event +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/points/get-points-boosts.mdx b/es/api-reference/endpoints/points/get-points-boosts.mdx new file mode 100644 index 0000000..1e06e1f --- /dev/null +++ b/es/api-reference/endpoints/points/get-points-boosts.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /points/{key}/boosts +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/points/get-points-level-summary.mdx b/es/api-reference/endpoints/points/get-points-level-summary.mdx new file mode 100644 index 0000000..3d464be --- /dev/null +++ b/es/api-reference/endpoints/points/get-points-level-summary.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /points/{key}/level-summary +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + diff --git a/es/api-reference/endpoints/points/get-points-levels.mdx b/es/api-reference/endpoints/points/get-points-levels.mdx new file mode 100644 index 0000000..c86f5eb --- /dev/null +++ b/es/api-reference/endpoints/points/get-points-levels.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /points/{key}/levels +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + diff --git a/es/api-reference/endpoints/points/get-points-summary.mdx b/es/api-reference/endpoints/points/get-points-summary.mdx new file mode 100644 index 0000000..9c2b1c7 --- /dev/null +++ b/es/api-reference/endpoints/points/get-points-summary.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /points/{key}/summary +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/points/get-points.mdx b/es/api-reference/endpoints/points/get-points.mdx new file mode 100644 index 0000000..c6a7a9b --- /dev/null +++ b/es/api-reference/endpoints/points/get-points.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /points/{key} +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/streaks/get-streak-rankings.mdx b/es/api-reference/endpoints/streaks/get-streak-rankings.mdx new file mode 100644 index 0000000..a127442 --- /dev/null +++ b/es/api-reference/endpoints/streaks/get-streak-rankings.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /streaks/rankings +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/streaks/get-streaks.mdx b/es/api-reference/endpoints/streaks/get-streaks.mdx new file mode 100644 index 0000000..a27e533 --- /dev/null +++ b/es/api-reference/endpoints/streaks/get-streaks.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /streaks +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/create-a-user.mdx b/es/api-reference/endpoints/users/create-a-user.mdx new file mode 100644 index 0000000..ec6082d --- /dev/null +++ b/es/api-reference/endpoints/users/create-a-user.mdx @@ -0,0 +1,9 @@ +--- +openapi: post /users +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx b/es/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx new file mode 100644 index 0000000..a8a4ec6 --- /dev/null +++ b/es/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /users/{id}/metrics/{key}/event-summary +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx b/es/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx new file mode 100644 index 0000000..b25d138 --- /dev/null +++ b/es/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /users/{id}/metrics/{key} +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-single-user.mdx b/es/api-reference/endpoints/users/get-a-single-user.mdx new file mode 100644 index 0000000..c0f0d59 --- /dev/null +++ b/es/api-reference/endpoints/users/get-a-single-user.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /users/{id} +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-users-completed-achievements.mdx b/es/api-reference/endpoints/users/get-a-users-completed-achievements.mdx new file mode 100644 index 0000000..fa1bfe7 --- /dev/null +++ b/es/api-reference/endpoints/users/get-a-users-completed-achievements.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /users/{id}/achievements +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-users-leaderboard.mdx b/es/api-reference/endpoints/users/get-a-users-leaderboard.mdx new file mode 100644 index 0000000..d12ec33 --- /dev/null +++ b/es/api-reference/endpoints/users/get-a-users-leaderboard.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /users/{id}/leaderboards/{key} +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-users-points-boosts.mdx b/es/api-reference/endpoints/users/get-a-users-points-boosts.mdx new file mode 100644 index 0000000..1c91874 --- /dev/null +++ b/es/api-reference/endpoints/users/get-a-users-points-boosts.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /users/{id}/points/{key}/boosts +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-users-points-summary.mdx b/es/api-reference/endpoints/users/get-a-users-points-summary.mdx new file mode 100644 index 0000000..bc6bec2 --- /dev/null +++ b/es/api-reference/endpoints/users/get-a-users-points-summary.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /users/{id}/points/{key}/event-summary +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-users-points.mdx b/es/api-reference/endpoints/users/get-a-users-points.mdx new file mode 100644 index 0000000..4df1d5d --- /dev/null +++ b/es/api-reference/endpoints/users/get-a-users-points.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /users/{id}/points/{key} +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-users-streak.mdx b/es/api-reference/endpoints/users/get-a-users-streak.mdx new file mode 100644 index 0000000..64fb237 --- /dev/null +++ b/es/api-reference/endpoints/users/get-a-users-streak.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /users/{id}/streak +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-users-wrapped.mdx b/es/api-reference/endpoints/users/get-a-users-wrapped.mdx new file mode 100644 index 0000000..e30ddaf --- /dev/null +++ b/es/api-reference/endpoints/users/get-a-users-wrapped.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /users/{id}/wrapped +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx b/es/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx new file mode 100644 index 0000000..f26742f --- /dev/null +++ b/es/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /users/{id}/metrics +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-user-preferences.mdx b/es/api-reference/endpoints/users/get-user-preferences.mdx new file mode 100644 index 0000000..398c9c6 --- /dev/null +++ b/es/api-reference/endpoints/users/get-user-preferences.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /users/{id}/preferences +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/identify-a-user.mdx b/es/api-reference/endpoints/users/identify-a-user.mdx new file mode 100644 index 0000000..7e7d2e3 --- /dev/null +++ b/es/api-reference/endpoints/users/identify-a-user.mdx @@ -0,0 +1,9 @@ +--- +openapi: put /users/{id} +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/update-a-user.mdx b/es/api-reference/endpoints/users/update-a-user.mdx new file mode 100644 index 0000000..3ee1fff --- /dev/null +++ b/es/api-reference/endpoints/users/update-a-user.mdx @@ -0,0 +1,9 @@ +--- +openapi: patch /users/{id} +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/endpoints/users/update-user-preferences.mdx b/es/api-reference/endpoints/users/update-user-preferences.mdx new file mode 100644 index 0000000..1346b04 --- /dev/null +++ b/es/api-reference/endpoints/users/update-user-preferences.mdx @@ -0,0 +1,9 @@ +--- +openapi: patch /users/{id}/preferences +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +**Rate Limits** + + \ No newline at end of file diff --git a/es/api-reference/idempotency.mdx b/es/api-reference/idempotency.mdx new file mode 100644 index 0000000..b37981e --- /dev/null +++ b/es/api-reference/idempotency.mdx @@ -0,0 +1,69 @@ +--- +title: Idempotency +description: Prevent unintended side effects when retrying requests with idempotency controls built into the Trophy API and SDKs. +"og:description": Prevent unintended side effects when retrying requests with idempotency built into the Trophy API and SDKs using the `Idempotency-Key` request header. +icon: repeat +--- + +import IdempotentEventTracking from "/snippets/idempotent-event-tracking.mdx"; + +## What is Idempotency? + +When describing APIs, [idempotence](https://en.wikipedia.org/wiki/Idempotence) is a property of a particular operation whereby subsequent invocations after the first have no additional effect on the state of the system. + +Trophy's [event tracking API](/api-reference/endpoints/metrics/send-a-metric-change-event) can be used to enforce idempotency preventing client retries from having unintended side effects such as overcounting user interactions, or awarding points to users multiple times for the same action. + +This is particularly important where rewards are tied to user actions to prevent users from 'gaming the system'. + +## Sending Idempotent Requests + +To ensure idempotency is respected when sending events to Trophy, include an `Idempotency-Key` header when using the [event tracking API](/api-reference/endpoints/metrics/send-a-metric-change-event). Additionally, all [client SDKs](/api-reference/client-libraries) support idempotency with built-in type safety. + +You can choose what to use as your idempotency key, but it should reflect the level of 'uniqueness' that you want Trophy to respect. + + + For example, if you use Trophy to reward users for completing lessons, and + want to make sure each user can only redeem rewards once for each lesson, use + the unique ID of the lesson as your idempotency key. + + +Here's an example of what using an idempotency key looks like: + + + +## How Idempotency Works + +When Trophy detects an idempotency key has been sent with an event, it will first check if the user the event relates to has used it before. It will then proceed to take one of the following actions: + +- If the user has used the idempotency key before, Trophy will not process the event, returning a `202 Accepted` response. The response will reflect the current state of the system, but will not increase the users metric total, complete any achievements, award any points, extend the streak, etc. +- If instead Trophy detects the user hasn't used the idempotency key before, it will process the event as usual, returning a `201 Created` response. Finally Trophy will store the idempotency key for lookup during any subsequent requests. + + + All Trophy [metrics](/platform/metrics) manage idempotency in isolation. + Trophy will accept a user using the same idempotency key for events against + different metrics as separate isolated requests. + + +Additionally, when using an idempotency key the response will contain two properties to help clients manage replayed requests effectively: + +{/* vale off */} + +```json +{ + ..., + "idempotentReplayed": true, // true if replayed, false otherwise + "idempotencyKey": "test" // the original idempotency key +} +``` + +{/* vale on */} + + + By default Trophy uses an infinite time window for detecting duplicate events. + If you feel you need different behavior, please [get in + touch](mailto:support@trophy.so) and we'll happily set that up for you. + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/api-reference/introduction.mdx b/es/api-reference/introduction.mdx new file mode 100644 index 0000000..7a9fcc3 --- /dev/null +++ b/es/api-reference/introduction.mdx @@ -0,0 +1,35 @@ +--- +title: Introduction +description: Learn about the Trophy API and get started building your integration. +subtitle: Learn about the Trophy API and get started building your integration. +--- + +The Trophy Application API is a set of endpoints that provide simple interfaces for building gamification experiences and covers the full range of actions that an end user of your application can perform. + +It does not include global administrative functions or actions that should only be performed by your internal systems. If you are looking for admin functionality, please refer to the [Admin API](/admin-api/introduction). + +The Application API is accessible through [SDKs](/api-reference/client-libraries) available in most major programming languages or, for those who wish manage their own HTTP clients, is available at the following base URL. + +```bash Base URL +https://api.trophy.so/v1 +``` + + + + Securely integrate your applications with the Trophy API using API keys. + + + Learn about the Trophy API and request rate limiting. + + + Use Trophy’s type-safe SDKs to integrate with the API. + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/api-reference/openapi.yml b/es/api-reference/openapi.yml new file mode 100644 index 0000000..f244f0b --- /dev/null +++ b/es/api-reference/openapi.yml @@ -0,0 +1,6288 @@ +openapi: 3.1.0 +info: + title: Trophy + version: '1.2.0' +paths: + # APPLICATION API ------------------------------------------------------- + + /achievements: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get all achievements and their completion stats. + operationId: achievements_all + x-fern-server-name: api + tags: + - Achievements + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.achievements.all(); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.achievements.all() + parameters: + - name: userAttributes + in: query + description: Optional colon-delimited user attributes in the format attribute:value,attribute:value. Only achievements accessible to a user with the provided attributes will be returned. + required: false + schema: + type: string + example: plan-type:premium,region:us-east + responses: + '200': + description: Successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/AchievementWithStatsResponse' + examples: + Successful operation: + value: + - id: 5100fe51-6bce-6j44-b0hs-bddc4e123682 + trigger: api + name: Finish onboarding + description: Complete the onboarding process. + badgeUrl: https://example.com/badge.png + key: finish-onboarding + completions: 8 + rarity: 80 + - id: 5100fe51-6bce-6j44-b0hs-bddc4e123683 + trigger: metric + name: 500 words written + description: Write 500 words in the app. + badgeUrl: https://example.com/badge.png + metricId: 5100fe51-6bce-6j44-b0hs-bddc4e123683 + metricName: words written + metricValue: 500 + completions: 6 + rarity: 60 + userAttributes: + - key: plan-type + value: premium + - key: region + value: us-east + eventAttribute: + key: source + value: mobile-app + - id: 5100fe51-6bce-6j44-b0hs-bddc4e123684 + trigger: streak + name: 10 days of exercise + description: Exercise at least once a day for 10 days in a row. + badgeUrl: https://example.com/badge.png + streakLength: 10 + completions: 2 + rarity: 20 + userAttributes: + - key: plan-type + value: premium + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get all achievements and their completion stats + security: + - ApiKeyAuth: [] + /achievements/{key}/complete: + servers: + - url: https://api.trophy.so/v1 + description: Application API + post: + description: Mark an achievement as completed for a user. + operationId: achievements_complete + x-fern-server-name: api + tags: + - Achievements + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.achievements.complete("achievement-key", { + user: { + id: "user-id", + } + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + user = UpsertedUser(id="123") + + response = client.achievements.complete("achievement-key", user=user) + parameters: + - name: key + in: path + description: Unique reference of the achievement as set when created. + required: true + schema: + type: string + example: finish-onboarding + responses: + '201': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/AchievementCompletionResponse' + examples: + Successful operation: + value: + completionId: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + achievement: + id: 5100fe51-6bce-6j44-b0hs-bddc4e123682 + trigger: api + name: Finish onboarding + description: Complete the onboarding process. + badgeUrl: https://example.com/badge.png + key: finish-onboarding + achievedAt: '2021-01-01T00:00:00Z' + points: + points-system-key: + id: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + name: XP + description: null + badgeUrl: null + total: 10 + added: 10 + awards: + - id: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + awarded: 10 + date: '2021-01-01T00:00:00Z' + total: 10 + trigger: + id: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + type: achievement + achievementName: Finish onboarding + points: 10 + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Achievement Not Found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Mark an achievement as completed + security: + - ApiKeyAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + user: + $ref: '#/components/schemas/UpsertedUser' + description: The user that completed the achievement. + required: + - user + examples: + Successful operation: + value: + user: + id: user-id + email: user@example.com + tz: Europe/London + subscribedToEmails: true + /metrics/{key}/event: + servers: + - url: https://api.trophy.so/v1 + description: Application API + post: + description: Increment or decrement the value of a metric for a user. + operationId: metrics_event + x-fern-server-name: api + tags: + - Metrics + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.metrics.event( + "words-written", + { + user: { + id: 'user-id', + email: 'user@example.com', + tz: 'Europe/London', + subscribedToEmails: true, + attributes: { + department: 'engineering', + role: 'developer' + } + }, + value: 750, + attributes: { + category: 'writing', + source: 'mobile-app' + } + } + ); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + user = UpsertedUser( + id="123", + email="user@example.com", + tz="Europe/London", + subscribedToEmails=True, + attributes={ + "department": "engineering", + "role": "developer" + } + ) + + response = client.metrics.event( + "words-written", + user=user, + value=750, + attributes={ + "category": "writing", + "source": "mobile-app" + }) + parameters: + - name: key + in: path + description: Unique reference of the metric as set when created. + required: true + schema: + type: string + example: words-written + - name: Idempotency-Key + in: header + description: The idempotency key for the event. + required: false + schema: + type: string + example: e4296e4b-8493-4bd1-9c30-5a1a9ac4d78f + responses: + '201': + description: Created event + content: + application/json: + schema: + $ref: '#/components/schemas/EventResponse' + examples: + Successful operation: + value: + metricId: d01dcbcb-d51e-4c12-b054-dc811dcdc623 + eventId: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + total: 750 + achievements: + - id: 5100fe51-6bce-6j44-b0hs-bddc4e123682 + trigger: metric + metricId: 5100fe51-6bce-6j44-b0hs-bddc4e123682 + metricName: words written + metricValue: 500 + name: 500 words written + description: Write 500 words in the app. + achievedAt: '2020-01-01T00:00:00Z' + currentStreak: + length: 1 + frequency: daily + started: '2025-04-02' + periodStart: '2025-03-31' + periodEnd: '2025-04-05' + expires: '2025-04-12' + points: + xp: + id: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + name: XP + description: null + badgeUrl: null + total: 10 + level: + id: 1140fe51-6bce-4b44-b0ad-bddc4e123534 + key: bronze + name: Bronze + description: Starting level + badgeUrl: null + points: 0 + added: 10 + awards: + - id: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + awarded: 10 + date: '2021-01-01T00:00:00Z' + total: 10 + trigger: + id: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + type: metric + metricName: words written + metricThreshold: 100 + points: 10 + leaderboards: + all-time: + id: 0040fe51-6bce-4b44-b0ad-bddc4e123535 + key: all-time + name: All-Time Leaderboard + description: null + rankBy: metric + runUnit: null + runInterval: null + maxParticipants: 100 + breakdownAttribute: null + metricName: words written + metricKey: words-written + threshold: 10 + start: '2025-01-01' + end: null + previousRank: null + rank: 100 + '400': + description: 'Bad Request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Send a metric change event + security: + - ApiKeyAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + user: + $ref: '#/components/schemas/UpsertedUser' + description: The user that triggered the event. + value: + type: number + format: double + description: >- + The value to add to the user's current total for the given + metric. + example: 750 + attributes: + type: object + additionalProperties: + type: string + description: Event attributes as key-value pairs. Keys must match existing event attributes set up in the Trophy dashboard. + example: + category: writing + source: mobile-app + required: + - user + - value + examples: + Successful operation: + value: + user: + email: user@example.com + tz: Europe/London + id: '18' + attributes: + department: engineering + role: developer + value: 750 + attributes: + category: writing + source: mobile-app + /users: + servers: + - url: https://api.trophy.so/v1 + description: Application API + post: + description: Create a new user. + operationId: users_create + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.create({ + id: 'user-id', + email: 'user@example.com', + tz: 'Europe/London', + subscribedToEmails: true, + attributes: { + department: 'engineering', + role: 'developer' + } + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.create( + id="123", + email="user@example.com", + tz="Europe/London", + subscribedToEmails=True, + attributes={ + "department": "engineering", + "role": "developer" + } + ) + summary: Create a new user + security: + - ApiKeyAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + $ref: '#/components/schemas/UpsertedUser' + description: The user object. + responses: + '201': + description: Identified user + content: + application/json: + schema: + $ref: '#/components/schemas/User' + examples: + Successful operation: + value: + id: user-id + email: user@example.com + tz: Europe/London + subscribedToEmails: true + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + attributes: + department: engineering + role: developer + '400': + description: 'Bad Request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + /users/{id}: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a single user. + operationId: users_get + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.get("user-id"); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.get("user-id") + summary: Get a single user + parameters: + - name: id + in: path + description: ID of the user to get. + required: true + schema: + type: string + example: userId + security: + - ApiKeyAuth: [] + responses: + '200': + description: Found user + content: + application/json: + schema: + $ref: '#/components/schemas/User' + examples: + Successful operation: + value: + id: user-id + email: user@example.com + tz: Europe/London + subscribedToEmails: true + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + attributes: + department: engineering + role: developer + '400': + description: 'Bad Request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + put: + description: Identify a user. + operationId: users_identify + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.identify("user-id", { + email: 'user@example.com', + tz: 'Europe/London', + attributes: { + department: 'engineering', + role: 'developer' + } + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.identify( + id="123", + email="user@example.com", + tz="Europe/London", + subscribedToEmails=True, + attributes={ + "department": "engineering", + "role": "developer" + } + ) + summary: Identify a user + security: + - ApiKeyAuth: [] + parameters: + - name: id + in: path + description: ID of the user to identify. + required: true + schema: + type: string + example: id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdatedUser' + description: The user object. + example: + email: user@example.com + tz: Europe/London + attributes: + department: engineering + role: developer + responses: + '200': + description: Upserted user + content: + application/json: + schema: + $ref: '#/components/schemas/User' + examples: + Successful operation: + value: + id: user-id + email: user@example.com + tz: Europe/London + subscribedToEmails: true + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + '400': + description: 'Bad Request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + patch: + description: Update a user. + operationId: users_update + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.update("user-id", { + email: 'user@example.com', + tz: 'Europe/London', + attributes: { + department: 'engineering', + role: 'developer' + } + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.update( + id="123", + email="user@example.com", + tz="Europe/London", + subscribedToEmails=True, + attributes={ + "department": "engineering", + "role": "developer" + } + ) + summary: Update a user + security: + - ApiKeyAuth: [] + parameters: + - name: id + in: path + description: ID of the user to update. + required: true + schema: + type: string + example: id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdatedUser' + description: The user object. + example: + id: user-id + email: user@example.com + tz: Europe/London + attributes: + department: engineering + role: developer + responses: + '200': + description: Updated user + content: + application/json: + schema: + $ref: '#/components/schemas/User' + examples: + Successful operation: + value: + id: user-id + email: user@example.com + tz: Europe/London + subscribedToEmails: true + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + '400': + description: 'Bad Request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'User Not Found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + /users/{id}/preferences: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a user's notification preferences. + operationId: users_get_preferences + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.getPreferences("user-123"); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.get_preferences(id="user-123") + parameters: + - name: id + in: path + description: The user's ID in your database. + required: true + schema: + type: string + example: user-123 + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/UserPreferencesResponse' + examples: + Successful operation: + value: + notifications: + achievement_completed: + - email + - push + recap: + - email + reactivation: + - push + streak_reminder: + - email + - push + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'User not found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get a user's preferences + security: + - ApiKeyAuth: [] + patch: + description: Update a user's notification preferences. + operationId: users_update_preferences + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.updatePreferences("user-123", { + notifications: { + recap: ["email"], + streak_reminder: [] + } + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.update_preferences( + id="user-123", + notifications={ + "recap": ["email"], + "streak_reminder": [] + } + ) + parameters: + - name: id + in: path + description: The user's ID in your database. + required: true + schema: + type: string + example: user-123 + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/UserPreferencesResponse' + examples: + Successful operation: + value: + notifications: + achievement_completed: + - email + - push + recap: + - email + reactivation: + - email + - push + streak_reminder: [] + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'User not found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Update a user's preferences + security: + - ApiKeyAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateUserPreferencesRequest' + examples: + Disable streak reminders: + value: + notifications: + streak_reminder: [] + Email only for recaps: + value: + notifications: + recap: + - email + Enable all for achievements: + value: + notifications: + achievement_completed: + - email + - push + /users/{id}/metrics: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a single user's progress against all active metrics. + operationId: users_all_metrics + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.allMetrics("user-id"); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.all_metrics("user-id") + parameters: + - name: id + in: path + description: ID of the user + required: true + schema: + type: string + example: userId + responses: + '200': + description: Successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/MetricResponse' + examples: + Successful operation: + value: + - id: d01dcbcb-d51e-4c12-b054-dc811dcdc623 + key: words-written + name: Words written + status: active + current: 4500 + achievements: + - id: abe3120f-5ca9-4344-92c8-5b891643a04b + trigger: metric + name: Novice Writer + description: 'null' + metricId: d01dcbcb-d51e-4c12-b054-dc811dcdc623 + metricValue: 500 + achievedAt: '2021-01-01T00:00:00Z' + badgeUrl: https://example.com/badge1.png + - id: 8a07f2d0-9c72-4de1-bf92-9530ae82b4b6 + trigger: metric + name: Intermediate Writer + description: 'null' + metricId: d01dcbcb-d51e-4c12-b054-dc811dcdc623 + metricValue: 1000 + achievedAt: '2021-01-02T00:00:00Z' + badgeUrl: https://example.com/badge2.png + - id: 2090d038-aa04-4048-ab2e-e2b7bf2d3b9f + trigger: metric + name: Expert Writer + description: 'null' + metricId: d01dcbcb-d51e-4c12-b054-dc811dcdc623 + metricValue: 2000 + achievedAt: null + badgeUrl: 'null' + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'User Not Found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get all metrics for a user + security: + - ApiKeyAuth: [] + /users/{id}/metrics/{key}: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a user's progress against a single active metric. + operationId: users_single_metric + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.singleMetric("user-id", "words-written"); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.single_metric(id="user-id", key="words-written") + parameters: + - name: id + in: path + description: ID of the user. + required: true + schema: + type: string + example: userId + - name: key + in: path + description: Unique key of the metric. + required: true + schema: + type: string + example: key + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/MetricResponse' + examples: + Successful operation: + value: + id: d01dcbcb-d51e-4c12-b054-dc811dcdc623 + key: words-written + name: Words written + status: active + current: 1500 + achievements: + - id: abe3120f-5ca9-4344-92c8-5b891643a04b + trigger: metric + name: Novice Writer + metricId: d01dcbcb-d51e-4c12-b054-dc811dcdc623 + metricValue: 500 + achievedAt: '2021-01-01T00:00:00Z' + - id: 8a07f2d0-9c72-4de1-bf92-9530ae82b4b6 + trigger: metric + name: Intermediate Writer + metricId: d01dcbcb-d51e-4c12-b054-dc811dcdc623 + metricValue: 1000 + achievedAt: '2021-01-02T00:00:00Z' + - id: 2090d038-aa04-4048-ab2e-e2b7bf2d3b9f + trigger: metric + name: Expert Writer + metricId: d01dcbcb-d51e-4c12-b054-dc811dcdc623 + metricValue: 2000 + achievedAt: null + badgeUrl: https://example.com/badge.png + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Not Found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get a single metric for a user + security: + - ApiKeyAuth: [] + /users/{id}/metrics/{key}/event-summary: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a summary of metric events over time for a user. + operationId: users_metric_event_summary + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.metricEventSummary("user-id", "words-written", { + aggregation: "daily", + startDate: "2024-01-01", + endDate: "2024-01-31" + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.metric_event_summary( + id="user-id", + key="words-written", + aggregation="daily", + start_date="2024-01-01", + end_date="2024-01-31" + ) + parameters: + - name: id + in: path + description: ID of the user. + required: true + schema: + type: string + example: userId + - name: key + in: path + description: Unique key of the metric. + required: true + schema: + type: string + example: words-written + - name: aggregation + in: query + description: The time period over which to aggregate the event data. + required: true + schema: + type: string + enum: + - daily + - weekly + - monthly + example: daily + - name: startDate + in: query + description: The start date for the data range in YYYY-MM-DD format. The startDate must be before the endDate, and the date range must not exceed 400 days. + required: true + schema: + type: string + format: date + example: '2024-01-01' + - name: endDate + in: query + description: The end date for the data range in YYYY-MM-DD format. The endDate must be after the startDate, and the date range must not exceed 400 days. + required: true + schema: + type: string + format: date + example: '2024-01-31' + responses: + '200': + description: Successful operation + content: + application/json: + schema: + type: array + items: + type: object + properties: + date: + type: string + format: date + description: The date of the data point. For weekly or monthly aggregations, this is the first date of the period. + example: '2024-01-01' + total: + type: number + format: double + description: The user's total for this metric at the end of this date. + example: 100 + change: + type: number + format: double + description: The change in the user's total for this metric during this period. + example: 50 + required: + - date + - total + - change + examples: + Successful operation: + value: + - date: '2024-01-01' + total: 100 + change: 100 + - date: '2024-01-02' + total: 300 + change: 200 + - date: '2024-01-03' + total: 600 + change: 300 + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Not Found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get a summary of metric events over time + security: + - ApiKeyAuth: [] + /users/{id}/achievements: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a user's achievements. + operationId: users_achievements + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + label: Get a user's completed achievements + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.achievements("user-id"); + - lang: javascript + label: Get a user's achievements (include incomplete) + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.achievements("user-id", { + includeIncomplete: true + }); + - lang: python + label: Get a user's completed achievements + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.achievements(id="user-id") + - lang: python + label: Get a user's achievements (include incomplete) + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.achievements(id="user-id", include_incomplete="true") + parameters: + - name: id + in: path + description: ID of the user. + required: true + schema: + type: string + example: userId + - name: includeIncomplete + in: query + description: When set to 'true', returns both completed and incomplete achievements for the user. When omitted or set to any other value, returns only completed achievements. + required: false + schema: + type: string + enum: ['true'] + example: 'true' + responses: + '200': + description: Successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/UserAchievementWithStatsResponse' + examples: + Successful operation: + value: + - id: d01dcbcb-d51e-4c12-b054-dc811dcdc625 + name: Completed Onboarding + trigger: api + key: completed-onboarding + achievedAt: '2021-01-01T00:00:00Z' + badgeUrl: https://example.com/badge2.png + completions: 100 + rarity: 50 + - id: d01dcbcb-d51e-4c12-b054-dc811dcdc623 + trigger: metric + key: novice-writer + metricId: d01dcbcb-d51e-4c12-b054-dc811dcdc619 + metricValue: 500 + metricName: words written + name: Novice Writer + achievedAt: '2021-02-01T00:00:00Z' + badgeUrl: https://example.com/badge1.png + completions: 100 + rarity: 50 + - id: d01dcbcb-d51e-4c12-b054-dc811dcdc624 + trigger: streak + key: 3-day-streak + streakLength: 3 + name: 3-Day Streak + achievedAt: '2021-03-01T00:00:00Z' + badgeUrl: https://example.com/badge2.png + completions: 100 + rarity: 50 + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Not Found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get a user's achievements + security: + - ApiKeyAuth: [] + /users/{id}/streak: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a user's streak data. + operationId: users_streak + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.streak("user-id", { + historyPeriods: 14 + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.streak(id="user-id", history_periods=14) + parameters: + - name: id + in: path + description: ID of the user. + required: true + schema: + type: string + example: userId + - in: query + name: historyPeriods + schema: + type: integer + default: 7 + description: >- + The number of past streak periods to include in the streakHistory field of the + response. + responses: + '200': + description: Successful operation + content: + application/json: + schema: + type: object + $ref: '#/components/schemas/StreakResponse' + examples: + Successful operation: + value: + length: 1 + frequency: weekly + started: '2025-04-02' + periodStart: '2025-03-31' + periodEnd: '2025-04-05' + expires: '2025-04-12' + rank: 5 + streakHistory: + - periodStart: '2025-03-30' + periodEnd: '2025-04-05' + length: 1 + - periodStart: '2025-04-06' + periodEnd: '2025-04-12' + length: 2 + - periodStart: '2025-04-13' + periodEnd: '2025-04-19' + length: 3 + - periodStart: '2025-04-20' + periodEnd: '2025-04-26' + length: 0 + - periodStart: '2025-04-27' + periodEnd: '2025-05-03' + length: 1 + - periodStart: '2025-05-04' + periodEnd: '2025-05-10' + length: 2 + - periodStart: '2025-05-11' + periodEnd: '2025-05-17' + length: 3 + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Not Found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get a user's streak status + security: + - ApiKeyAuth: [] + /streaks: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get the streak lengths of a list of users, ranked by streak length from longest to shortest. + operationId: streaks_list + x-fern-server-name: api + tags: + - Streaks + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const response = await trophy.streaks.list({ + userIds: ['user-123', 'user-456', 'user-789'] + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.streaks.list(user_ids=["user-123", "user-456", "user-789"]) + parameters: + - name: userIds + in: query + description: A list of up to 100 user IDs. + required: true + schema: + type: array + items: + type: string + example: 'user-123,user-456,user-789' + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/BulkStreakResponse' + examples: + Successful operation: + value: + - userId: user-123 + streakLength: 15 + extended: '2025-01-01T05:03:00Z' + - userId: user-456 + streakLength: 12 + extended: '2025-01-01T08:43:00Z' + - userId: user-789 + streakLength: 0 + extended: null + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get the streak lengths of a list of users + security: + - ApiKeyAuth: [] + /streaks/rankings: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get the top users by streak length (active or longest). + operationId: streaks_rankings + x-fern-server-name: api + tags: + - Streaks + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.streaks.rankings({ + limit: 20, + type: 'active' + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.streaks.rankings(limit=20, type="active") + parameters: + - name: limit + in: query + description: Number of users to return. Must be between 1 and 100. + required: false + schema: + type: integer + minimum: 1 + maximum: 100 + default: 10 + example: 20 + - name: type + in: query + description: Whether to rank users by active streaks or longest streaks ever achieved. + required: false + schema: + type: string + enum: ['active', 'longest'] + default: 'active' + example: 'active' + responses: + '200': + description: Successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/StreakRankingUser' + examples: + Successful operation: + value: + - userId: user-123 + name: Alice Johnson + streakLength: 15 + - userId: user-456 + name: Bob Smith + streakLength: 12 + - userId: user-789 + name: Charlie Brown + streakLength: 8 + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get top users by streak length + security: + - ApiKeyAuth: [] + /users/{id}/points/{key}: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a user's points for a specific points system. + operationId: users_points + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.points("user-id", "points-system-key"); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.points(id="user-id", key="points-system-key") + parameters: + - name: id + in: path + description: ID of the user. + required: true + schema: + type: string + example: userId + - name: key + in: path + description: Key of the points system. + required: true + schema: + type: string + example: points-system-key + - in: query + name: awards + schema: + type: integer + default: 10 + minimum: 1 + maximum: 100 + description: The number of recent point awards to return. + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/GetUserPointsResponse' + examples: + Successful operation: + value: + 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: words written + metricThreshold: 1000 + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Not Found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get a user's points data + security: + - ApiKeyAuth: [] + /users/{id}/points/{key}/boosts: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get active points boosts for a user in a specific points system. Returns both global boosts the user is eligible for and user-specific boosts. + operationId: users_points_boosts + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.pointsBoosts("user-id", "points-system-key"); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.points_boosts(id="user-id", key="points-system-key") + parameters: + - name: id + in: path + description: ID of the user. + required: true + schema: + type: string + example: userId + - name: key + in: path + description: Key of the points system. + required: true + schema: + type: string + example: points-system-key + responses: + '200': + description: Successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PointsBoost' + examples: + Successful operation: + value: + - id: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + name: Double XP Weekend + status: active + start: '2025-01-01' + end: '2025-01-03' + multiplier: 2 + rounding: 'down' + - id: 0040fe51-6bce-4b44-b0ad-bddc4e123535 + name: VIP Bonus + status: active + start: '2025-01-01' + end: null + multiplier: 1.5 + rounding: 'nearest' + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Not Found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get a user's active points boosts + security: + - ApiKeyAuth: [] + /users/{id}/points/{key}/event-summary: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a summary of points awards over time for a user for a specific points system. + operationId: users_points_event_summary + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.pointsEventSummary("user-id", "points-system-key", { + aggregation: "daily", + startDate: "2024-01-01", + endDate: "2024-01-31" + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.points_event_summary( + id="user-id", + key="points-system-key", + aggregation="daily", + start_date="2024-01-01", + end_date="2024-01-31" + ) + parameters: + - name: id + in: path + description: ID of the user. + required: true + schema: + type: string + example: userId + - name: key + in: path + description: Key of the points system. + required: true + schema: + type: string + example: points-system-key + - name: aggregation + in: query + description: The time period over which to aggregate the event data. + required: true + schema: + type: string + enum: + - daily + - weekly + - monthly + example: daily + - name: startDate + in: query + description: The start date for the data range in YYYY-MM-DD format. The startDate must be before the endDate, and the date range must not exceed 400 days. + required: true + schema: + type: string + format: date + example: '2024-01-01' + - name: endDate + in: query + description: The end date for the data range in YYYY-MM-DD format. The endDate must be after the startDate, and the date range must not exceed 400 days. + required: true + schema: + type: string + format: date + example: '2024-01-31' + responses: + '200': + description: Successful operation + content: + application/json: + schema: + type: array + items: + type: object + properties: + date: + type: string + format: date + description: The date of the data point. For weekly or monthly aggregations, this is the first date of the period. + example: '2024-01-01' + total: + type: number + format: double + description: The user's total points at the end of this date. + example: 100 + change: + type: number + format: double + description: The change in the user's total points during this period. + example: 50 + required: + - date + - total + - change + examples: + Successful operation: + value: + - date: '2024-01-01' + total: 100 + change: 100 + - date: '2024-01-02' + total: 300 + change: 200 + - date: '2024-01-03' + total: 600 + change: 300 + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Not Found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get a summary of points events over time + security: + - ApiKeyAuth: [] + /users/{id}/leaderboards/{key}: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a user's rank, value, and history for a specific leaderboard. + operationId: users_leaderboard + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.leaderboards("user-123", "weekly-words", { + run: "2025-01-15" + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.leaderboards( + user_id="user-123", + key="weekly-words", + run="2025-01-15" + ) + parameters: + - name: id + in: path + description: The user's ID in your database. + required: true + schema: + type: string + example: user-123 + - name: key + in: path + description: Unique key of the leaderboard as set when created. + required: true + schema: + type: string + example: weekly-words + - name: run + in: query + description: Specific run date in YYYY-MM-DD format. If not provided, returns the current run. + required: false + schema: + type: string + format: date + example: '2025-01-15' + - name: numEvents + in: query + description: The number of events to return in the history array. + required: false + schema: + type: integer + default: 10 + minimum: 1 + maximum: 100 + example: 10 + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/UserLeaderboardResponseWithHistory' + examples: + Successful operation: + value: + id: 5100fe51-6bce-6j44-b0hs-bddc4e123682 + name: Weekly Word Count Challenge + key: weekly-words + rankBy: metric + metricKey: words-written + metricName: Words Written + description: Compete weekly to see who writes the most words + start: '2025-01-01' + end: null + maxParticipants: 100 + breakdownAttribute: null + runUnit: day + runInterval: 7 + rank: 2 + value: 4500 + history: + - timestamp: '2025-01-15T10:30:00Z' + previousRank: null + rank: 5 + previousValue: null + value: 1000 + - timestamp: '2025-01-15T14:15:00Z' + previousRank: 5 + rank: 3 + previousValue: 1000 + value: 3000 + - timestamp: '2025-01-15T18:45:00Z' + previousRank: 3 + rank: 2 + previousValue: 3000 + value: 4500 + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'User or leaderboard not found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get a user's leaderboard data + security: + - ApiKeyAuth: [] + /users/{id}/wrapped: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a user's year-in-review wrapped data. + operationId: users_wrapped + x-fern-server-name: api + tags: + - Users + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.users.wrapped("user-123", { + year: 2024 + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.users.wrapped(id="user-123", year=2024) + parameters: + - name: id + in: path + description: The user's ID in your database. + required: true + schema: + type: string + example: user-123 + - name: year + in: query + description: The year to get wrapped data for. Defaults to the current year. Must be an integer between 1 and the current year. + required: false + schema: + type: integer + minimum: 1 + example: 2024 + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WrappedResponse' + examples: + Successful operation: + value: + user: + id: user-123 + email: user@example.com + name: John Doe + tz: America/New_York + subscribeToEmails: true + created: '2024-01-15T10:30:00Z' + updated: '2024-06-20T14:45:00Z' + control: false + attributes: + plan-type: premium + region: us-east + activity: + daysActive: 156 + weeksActive: 42 + monthsActive: 11 + mostActiveDay: + date: '2024-03-15' + metrics: + words-written: + name: Words Written + units: words + currentTotal: 15000 + changeThisPeriod: 2500 + percentChange: 20 + byAttribute: {} + points: + xp-system: + name: Experience Points + description: Points earned through activity + currentTotal: 5000 + changeThisPeriod: 500 + percentChange: 11.1 + achievements: + - id: 5100fe51-6bce-6j44-b0hs-bddc4e123682 + name: 500 Words Written + trigger: metric + description: Write 500 words in a single day + badgeUrl: https://example.com/badge.png + key: 500-words + metricId: metric-123 + metricValue: 500 + metricName: Words Written + achievedAt: '2024-03-15T14:30:00Z' + completions: 150 + rarity: 15 + leaderboards: + weekly-words: + id: leaderboard-123 + name: Weekly Word Count + key: weekly-words + rankBy: metric + metricKey: words-written + metricName: Words Written + description: Weekly writing competition + start: '2024-03-11' + end: '2024-03-17' + maxParticipants: 100 + runUnit: day + runInterval: 7 + rank: 3 + value: 2500 + mostActiveWeek: + start: '2024-03-11' + end: '2024-03-17' + metrics: + words-written: + name: Words Written + units: words + currentTotal: 15000 + changeThisPeriod: 8500 + percentChange: 130 + percentileThisPeriod: 95 + byAttribute: {} + points: + xp-system: + name: Experience Points + description: Points earned through activity + currentTotal: 5000 + changeThisPeriod: 1200 + percentChange: 31.5 + percentileThisPeriod: 88 + achievements: [] + leaderboards: {} + mostActiveMonth: + month: 2 + metrics: + words-written: + name: Words Written + units: words + currentTotal: 15000 + changeThisPeriod: 12000 + percentChange: 400 + percentileThisPeriod: 92 + byAttribute: {} + points: + xp-system: + name: Experience Points + description: Points earned through activity + currentTotal: 5000 + changeThisPeriod: 2000 + percentChange: 66.6 + percentileThisPeriod: 85 + achievements: [] + leaderboards: {} + entireYear: + metrics: + words-written: + name: Words Written + units: words + currentTotal: 150000 + changeThisPeriod: 150000 + percentChange: 100 + percentileThisPeriod: 78 + byAttribute: {} + points: + xp-system: + name: Experience Points + description: Points earned through activity + currentTotal: 25000 + changeThisPeriod: 25000 + percentChange: 100 + percentileThisPeriod: 82 + achievements: + - id: 5100fe51-6bce-6j44-b0hs-bddc4e123682 + name: 500 Words Written + trigger: metric + description: Write 500 words in a single day + badgeUrl: https://example.com/badge.png + key: 500-words + metricId: metric-123 + metricValue: 500 + metricName: Words Written + achievedAt: '2024-03-15T14:30:00Z' + completions: 150 + rarity: 15 + leaderboards: {} + longestStreak: + length: 45 + frequency: daily + periodStart: '2024-02-01' + periodEnd: '2024-03-17' + started: '2024-02-01' + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'User not found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get a user's wrapped data + security: + - ApiKeyAuth: [] + /points/{key}/summary: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a breakdown of the number of users with points in each range. + operationId: points_summary + x-fern-server-name: api + tags: + - Points + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.points.summary("points-system-key", { + userAttributes: "plan-type:premium,region:us-east" + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.points.summary( + key="points-system-key", + user_attributes="plan-type:premium,region:us-east" + ) + parameters: + - name: key + in: path + description: Key of the points system. + required: true + schema: + type: string + example: points-system-key + - name: userAttributes + in: query + description: Optional colon-delimited user attribute filters in the format attribute:value,attribute:value. Only users matching ALL specified attributes will be included in the points breakdown. + required: false + schema: + type: string + example: plan-type:premium,region:us-east + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/PointsSummaryResponse' + examples: + Successful operation: + value: + - from: 0 + to: 0 + users: 5012 + - from: 1 + to: 100 + users: 1501 + - from: 101 + to: 200 + users: 1007 + - from: 201 + to: 300 + users: 584 + - from: 301 + to: 400 + users: 201 + - from: 401 + to: 500 + users: 102 + - from: 501 + to: 600 + users: 25 + - from: 601 + to: 700 + users: 0 + - from: 701 + to: 800 + users: 0 + - from: 801 + to: 900 + users: 0 + - from: 901 + to: 1000 + users: 0 + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Not Found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get a breakdown of users by points + security: + - ApiKeyAuth: [] + /points/{key}: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a points system with its triggers. + operationId: points_system + x-fern-server-name: api + tags: + - Points + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.points.system("points-system-key"); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.points.system(key="points-system-key") + parameters: + - name: key + in: path + description: Key of the points system. + required: true + schema: + type: string + example: points-system-key + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/PointsSystemResponse' + examples: + Successful operation: + value: + id: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + name: XP System + description: Experience points for user engagement + badgeUrl: https://example.com/badge.png + maxPoints: null + triggers: + - id: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + type: metric + points: 10 + status: active + metricId: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + metricName: words written + metricThreshold: 1000 + userAttributes: + - key: plan-type + value: premium + - key: region + value: us-east + eventAttribute: + key: source + value: mobile-app + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + - id: 0040fe51-6bce-4b44-b0ad-bddc4e123536 + type: streak + points: 10 + status: active + streakLengthThreshold: 7 + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + - id: 0040fe51-6bce-4b44-b0ad-bddc4e123535 + type: achievement + points: 50 + status: active + achievementId: 0040fe51-6bce-4b44-b0ad-bddc4e123535 + achievementName: finish onboarding + userAttributes: + - key: plan-type + value: premium + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Points system not found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get a points system with its triggers + security: + - ApiKeyAuth: [] + /points/{key}/boosts: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get all global boosts for a points system. Finished boosts are excluded by default. + operationId: points_boosts + x-fern-server-name: api + tags: + - Points + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.points.boosts("points-system-key"); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.points.boosts(key="points-system-key") + parameters: + - name: key + in: path + description: Key of the points system. + required: true + schema: + type: string + example: points-system-key + - name: includeFinished + in: query + description: When set to 'true', boosts that have finished (past their end date) will be included in the response. By default, finished boosts are excluded. + required: false + schema: + type: boolean + default: false + responses: + '200': + description: Successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PointsBoost' + examples: + Successful operation: + value: + - id: 0040fe51-6bce-4b44-b0ad-bddc4e123537 + name: Double XP Weekend + status: active + start: '2025-01-01' + end: '2025-01-03' + multiplier: 2 + rounding: 'down' + - id: 0040fe51-6bce-4b44-b0ad-bddc4e123538 + name: Holiday Bonus + status: finished + start: '2024-12-25' + end: '2024-12-31' + multiplier: 1.5 + rounding: 'nearest' + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Points system not found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get boosts for a points system + security: + - ApiKeyAuth: [] + /points/{key}/levels: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get all levels for a points system. + operationId: points_levels + x-fern-server-name: api + tags: + - Points + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.points.levels("points-system-key"); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.points.levels(key="points-system-key") + parameters: + - name: key + in: path + description: Key of the points system. + required: true + schema: + type: string + example: points-system-key + responses: + '200': + description: Successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PointsLevel' + examples: + Successful operation: + value: + - id: 1140fe51-6bce-4b44-b0ad-bddc4e123534 + key: bronze + name: Bronze + description: Starting level + badgeUrl: https://example.com/bronze.png + points: 0 + - id: 2240fe51-6bce-4b44-b0ad-bddc4e123534 + key: silver + name: Silver + description: Mid-tier level + badgeUrl: null + points: 50 + - id: 3340fe51-6bce-4b44-b0ad-bddc4e123534 + key: gold + name: Gold + description: Top level + badgeUrl: https://example.com/gold.png + points: 200 + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Points system not found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get levels for a points system + security: + - ApiKeyAuth: [] + /points/{key}/level-summary: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a breakdown of the number of users at each level in a points system. + operationId: points_level_summary + x-fern-server-name: api + tags: + - Points + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.points.levelSummary("points-system-key"); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.points.level_summary(key="points-system-key") + parameters: + - name: key + in: path + description: Key of the points system. + required: true + schema: + type: string + example: points-system-key + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/PointsLevelSummaryResponse' + examples: + Successful operation: + value: + - level: + id: 1140fe51-6bce-4b44-b0ad-bddc4e123534 + key: bronze + name: Bronze + description: Starting level + badgeUrl: https://example.com/bronze.png + points: 0 + users: 5012 + - level: + id: 2240fe51-6bce-4b44-b0ad-bddc4e123534 + key: silver + name: Silver + description: Mid-tier level + badgeUrl: null + points: 50 + users: 1501 + - level: + id: 3340fe51-6bce-4b44-b0ad-bddc4e123534 + key: gold + name: Gold + description: Top level + badgeUrl: https://example.com/gold.png + points: 200 + users: 102 + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Points system not found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'No levels configured on the points system' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get level summary for a points system + security: + - ApiKeyAuth: [] + /leaderboards: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get all leaderboards for your organization. Finished leaderboards are excluded by default. + operationId: leaderboards_all + x-fern-server-name: api + tags: + - Leaderboards + parameters: + - name: includeFinished + in: query + description: When set to 'true', leaderboards with status 'finished' will be included in the response. By default, finished leaderboards are excluded. + required: false + schema: + type: boolean + default: false + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.leaderboards.all(); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.leaderboards.all() + responses: + '200': + description: Successful operation + content: + application/json: + schema: + type: array + items: + allOf: + - $ref: '#/components/schemas/LeaderboardResponse' + - type: object + properties: + status: + type: string + enum: ['active', 'scheduled', 'finished'] + description: The status of the leaderboard. + example: active + required: + - status + examples: + Successful operation: + value: + - id: 5100fe51-6bce-6j44-b0hs-bddc4e123682 + name: Weekly Word Count Challenge + key: weekly-words + rankBy: metric + metricKey: words-written + metricName: Words Written + description: Compete weekly to see who writes the most words + status: active + start: '2025-01-01' + end: null + maxParticipants: 100 + breakdownAttribute: null + runUnit: day + runInterval: 7 + - id: 5100fe51-6bce-6j44-b0hs-bddc4e123683 + name: XP Leaderboard + key: xp-board + rankBy: points + pointsSystemKey: xp-system + pointsSystemName: Experience Points + description: Overall ranking by XP earned + status: active + start: '2025-01-01' + end: null + maxParticipants: 50 + breakdownAttribute: null + runUnit: null + runInterval: null + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get all leaderboards + security: + - ApiKeyAuth: [] + /leaderboards/{key}: + servers: + - url: https://api.trophy.so/v1 + description: Application API + get: + description: Get a specific leaderboard by its key. + operationId: leaderboards_get + x-fern-server-name: api + tags: + - Leaderboards + x-codeSamples: + - lang: javascript + label: Get rankings + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.leaderboards.get("weekly-words", { + offset: 0, + limit: 10, + run: "2025-01-15" + }); + - lang: javascript + label: Get rankings by user attribute + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.leaderboards.get("weekly-words", { + offset: 0, + limit: 10, + run: "2025-01-15", + userAttributes: "city:london" + }); + - lang: python + label: Get rankings + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.leaderboards.get( + key="weekly-words", + offset=0, + limit=10, + run="2025-01-15" + ) + - lang: python + label: Get rankings by user attribute + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.leaderboards.get( + key="weekly-words", + offset=0, + limit=10, + run="2025-01-15", + user_attributes="city:london" + ) + parameters: + - name: key + in: path + description: Unique key of the leaderboard as set when created. + required: true + schema: + type: string + example: weekly-words + - name: offset + in: query + description: Number of rankings to skip for pagination. + required: false + schema: + type: integer + minimum: 0 + default: 0 + example: 20 + - name: limit + in: query + description: Maximum number of rankings to return. Cannot be greater than the size of the leaderboard. + required: false + schema: + type: integer + minimum: 0 + default: 10 + example: 50 + - name: run + in: query + description: Specific run date in YYYY-MM-DD format. If not provided, returns the current run. + required: false + schema: + type: string + format: date + example: '2025-01-15' + - name: userId + in: query + description: When provided, offset is relative to this user's position on the leaderboard. If the user is not found in the leaderboard, returns empty rankings array. + required: false + schema: + type: string + example: 'user-123' + - name: userAttributes + in: query + description: Attribute key and value to filter the rankings by, separated by a colon. For example, `city:London`. This parameter is required, and only valid for leaderboards with a breakdown attribute. + required: false + schema: + type: string + example: city:London + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/LeaderboardResponseWithRankings' + examples: + Successful operation: + value: + id: 5100fe51-6bce-6j44-b0hs-bddc4e123682 + name: Weekly Word Count Challenge + key: weekly-words + rankBy: metric + metricKey: words-written + metricName: Words Written + pointsSystemKey: null + pointsSystemName: null + description: Compete weekly to see who writes the most words + status: active + start: '2025-01-01' + end: null + maxParticipants: 100 + breakdownAttribute: null + runUnit: day + runInterval: 7 + rankings: + - userId: user-123 + userName: Alice Johnson + rank: 1 + value: 5000 + - userId: user-456 + userName: Bob Smith + rank: 2 + value: 4500 + - userId: user-789 + userName: Charlie Brown + rank: 3 + value: 4200 + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Leaderboard not found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Get a single leaderboard + security: + - ApiKeyAuth: [] + + # ADMIN API ------------------------------------------------------------ + + /streaks/freezes: + servers: + - url: https://admin.trophy.so/v1 + description: Admin API + post: + description: Create streak freezes for multiple users. + operationId: admin_streaks_freezes_create + x-fern-server-name: admin + x-fern-sdk-group-name: + - admin + - streaks + - freezes + x-fern-sdk-method-name: create + tags: + - Admin + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.admin.streaks.freezes.create({ + freezes: [ + { userId: 'user-123' }, + { userId: 'user-456' }, + ] + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.admin.streaks.freezes.create({ + "freezes": [ + {"userId": "user-123"}, + {"userId": "user-456"} + ] + }) + requestBody: + description: Array of freezes to create + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateStreakFreezesRequest' + examples: + Create freezes for multiple users: + value: + freezes: + - userId: user-123 + - userId: user-456 + - userId: user-123 + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/CreateStreakFreezesResponse' + examples: + Success with no issues: + value: + issues: [] + Success with warnings: + value: + issues: + - userId: user-789 + level: warning + reason: Would exceed maximum freeze limit + Mixed success and errors: + value: + issues: + - userId: non-existent-user + level: error + reason: User does not exist + - userId: user-456 + level: warning + reason: Would exceed maximum freeze limit + '400': + description: 'Bad Request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Create streak freezes for multiple users + security: + - ApiKeyAuth: [] + + /streaks/restore: + servers: + - url: https://admin.trophy.so/v1 + description: Admin API + post: + description: Restore streaks for multiple users to the maximum length in the last 90 days (in the case of daily streaks), one year (in the case of weekly streaks), or two years (in the case of monthly streaks). + operationId: admin_streaks_restore + x-fern-server-name: admin + x-fern-sdk-group-name: + - admin + - streaks + x-fern-sdk-method-name: restore + tags: + - Admin + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.admin.streaks.restore({ + users: [{ id: 'user-123' }, { id: 'user-456' }] + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.admin.streaks.restore({ + "users": [{"id": "user-123"}, {"id": "user-456"}] + }) + requestBody: + description: Array of users to restore streaks for + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/RestoreStreaksRequest' + examples: + Restore streaks for multiple users: + value: + users: + - id: user-123 + - id: user-456 + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/RestoreStreaksResponse' + examples: + Success with no issues: + value: + restoredUsers: + - user-123 + - user-456 + issues: [] + Success with warnings: + value: + restoredUsers: + - user-123 + issues: + - userId: user-456 + level: warning + reason: No streak to restore + Mixed success and errors: + value: + restoredUsers: + - user-123 + issues: + - userId: non-existent-user + level: error + reason: User does not exist + - userId: user-789 + level: warning + reason: Streak is already at maximum length + '400': + description: 'Bad Request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Restore streaks for multiple users + security: + - ApiKeyAuth: [] + + /points/boosts: + servers: + - url: https://admin.trophy.so/v1 + description: Admin API + post: + description: Create points boosts for multiple users. + operationId: admin_points_boosts_create + x-fern-server-name: admin + x-fern-sdk-group-name: + - admin + - points + - boosts + x-fern-sdk-method-name: create + tags: + - Admin + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.admin.points.boosts.create({ + systemKey: 'xp', + boosts: [ + { + userId: 'user-123', + name: 'Double XP Weekend', + start: '2024-01-01', + end: '2024-01-03', + multiplier: 2 + }, + { + userId: 'user-456', + name: 'Holiday Bonus', + start: '2024-12-25', + multiplier: 1.5, + rounding: 'up' + } + ] + }); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.admin.points.boosts.create({ + "systemKey": "xp", + "boosts": [ + { + "userId": "user-123", + "name": "Double XP Weekend", + "start": "2024-01-01", + "end": "2024-01-03", + "multiplier": 2 + }, + { + "userId": "user-456", + "name": "Holiday Bonus", + "start": "2024-12-25", + "multiplier": 1.5, + "rounding": "up" + } + ] + }) + requestBody: + description: The points system key and array of boosts to create + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreatePointsBoostsRequest' + examples: + Create boosts for multiple users: + value: + systemKey: xp + boosts: + - userId: user-123 + name: Double XP Weekend + start: '2024-01-01' + end: '2024-01-03' + multiplier: 2 + - userId: user-456 + name: Holiday Bonus + start: '2024-12-25' + multiplier: 1.5 + rounding: up + responses: + '200': + description: Successful operation (no boosts created) + content: + application/json: + schema: + $ref: '#/components/schemas/CreatePointsBoostsResponse' + examples: + All requests had errors: + value: + created: [] + issues: + - userId: non-existent-user + level: error + reason: User does not exist + '201': + description: Created (at least one boost created) + content: + application/json: + schema: + $ref: '#/components/schemas/CreatePointsBoostsResponse' + examples: + Success with no issues: + value: + created: + - id: '550e8400-e29b-41d4-a716-446655440000' + name: Double XP Weekend + status: active + start: '2024-01-01' + end: '2024-01-03' + multiplier: 2 + rounding: down + userId: user-123 + issues: [] + Mixed success and errors: + value: + created: + - id: '550e8400-e29b-41d4-a716-446655440001' + name: Valid Boost + status: active + start: '2024-01-15' + end: null + multiplier: 1.5 + rounding: down + userId: user-456 + issues: + - userId: non-existent-user + level: error + reason: User does not exist + '400': + description: 'Bad Request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Not Found (points system not found)' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '422': + description: 'Unprocessible Entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Create points boosts for multiple users + security: + - ApiKeyAuth: [] + delete: + description: Archive multiple points boosts by ID. + operationId: admin_points_boosts_batch_archive + x-fern-server-name: admin + x-fern-sdk-group-name: + - admin + - points + - boosts + x-fern-sdk-method-name: batchArchive + tags: + - Admin + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + const response = await trophy.admin.points.boosts.batchArchive({ + ids: ['boost-uuid-1', 'boost-uuid-2', 'boost-uuid-3'] + }); + + console.log(`Archived ${response.archivedCount} boosts`); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + response = client.admin.points.boosts.batch_archive( + ids=['boost-uuid-1', 'boost-uuid-2', 'boost-uuid-3'] + ) + + print(f"Archived {response.archived_count} boosts") + parameters: + - name: ids + in: query + description: A list of up to 100 boost IDs. + required: true + schema: + type: array + items: + type: string + minItems: 1 + maxItems: 100 + example: 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11,b1ffcd00-0d1c-4ef9-cc7e-7cc0ce491b22' + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ArchivePointsBoostsResponse' + examples: + All boosts archived: + value: + archivedCount: 3 + Some boosts not found: + value: + archivedCount: 1 + No boosts found: + value: + archivedCount: 0 + '400': + description: 'Bad Request (no IDs provided or invalid UUID format)' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Archive multiple points boosts + security: + - ApiKeyAuth: [] + + /points/boosts/{id}: + servers: + - url: https://admin.trophy.so/v1 + description: Admin API + delete: + description: Archive a points boost by ID. + operationId: admin_points_boosts_archive + x-fern-server-name: admin + x-fern-sdk-group-name: + - admin + - points + - boosts + x-fern-sdk-method-name: archive + tags: + - Admin + x-codeSamples: + - lang: javascript + source: | + import { TrophyApiClient } from '@trophyso/node'; + + const trophy = new TrophyApiClient({ + apiKey: 'YOUR_API_KEY' + }); + + await trophy.admin.points.boosts.archive('boost-uuid-here'); + - lang: python + source: | + from trophy import TrophyApi + + client = TrophyApi(api_key='YOUR_API_KEY') + + client.admin.points.boosts.archive('boost-uuid-here') + parameters: + - name: id + in: path + required: true + description: The UUID of the points boost to archive + schema: + type: string + format: uuid + responses: + '204': + description: Successfully archived the points boost + '401': + description: 'Unauthorized' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: 'Not Found' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + summary: Archive a points boost + security: + - ApiKeyAuth: [] + +webhooks: + achievement.completed: + post: + summary: Achievement completed + operationId: webhooks_achievement_completed + description: Triggered when a user completes an achievement. + requestBody: + description: The webhook event. + content: + application/json: + schema: + properties: + type: + type: string + enum: ['achievement.completed'] + description: The webhook event type. + user: + $ref: '#/components/schemas/User' + description: The user who completed the achievement. + achievement: + $ref: '#/components/schemas/UserAchievementResponse' + description: The achievement completion that occurred. + required: + - type + - user + - achievement + examples: + Achievement completed: + value: + type: achievement.completed + user: + id: user-id + email: user@example.com + tz: Europe/London + subscribedToEmails: true + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + attributes: + department: engineering + role: developer + achievement: + id: d01dcbcb-d51e-4c12-b054-dc811dcdc625 + name: Completed Onboarding + trigger: api + description: null + key: completed-onboarding + achievedAt: '2021-01-01T00:00:00Z' + badgeUrl: https://example.com/badge2.png + + responses: + '200': + description: Return a 200 status to indicate the webhook was received and processed. + streak.started: + post: + summary: Streak started + operationId: webhooks_streak_started + description: Triggered when a user starts a streak. + requestBody: + description: The webhook event. + content: + application/json: + schema: + properties: + type: + type: string + enum: ['streak.started'] + description: The webhook event type. + user: + $ref: '#/components/schemas/User' + description: The user who started the streak. + streak: + $ref: '#/components/schemas/BaseStreakResponse' + description: The streak that was started. + required: + - type + - user + - streak + examples: + Streak started: + value: + type: streak.started + user: + id: user-id + email: user@example.com + tz: Europe/London + subscribedToEmails: true + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + attributes: + department: engineering + role: developer + streak: + length: 1 + frequency: daily + periodStart: '2025-04-02' + periodEnd: '2025-04-02' + started: '2025-04-02' + expires: '2025-04-03' + freezes: 0 + maxFreezes: 3 + freezeAutoEarnInterval: 7 + freezeAutoEarnAmount: 1 + responses: + '200': + description: Return a 200 status to indicate the webhook was received and processed. + streak.extended: + post: + summary: Streak extended + operationId: webhooks_streak_extended + description: Triggered when a user extends an existing active streak. + requestBody: + description: The webhook event. + content: + application/json: + schema: + properties: + type: + type: string + enum: ['streak.extended'] + description: The webhook event type. + user: + $ref: '#/components/schemas/User' + description: The user who extended the streak. + streak: + $ref: '#/components/schemas/BaseStreakResponse' + description: The streak that was extended. + required: + - type + - user + - streak + examples: + Streak extended: + value: + type: streak.extended + user: + id: user-id + email: user@example.com + tz: Europe/London + subscribedToEmails: true + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + attributes: + department: engineering + role: developer + streak: + length: 2 + frequency: daily + periodStart: '2025-04-03' + periodEnd: '2025-04-03' + started: '2025-04-02' + expires: '2025-04-05' + freezes: 0 + maxFreezes: 3 + freezeAutoEarnInterval: 7 + freezeAutoEarnAmount: 1 + responses: + '200': + description: Return a 200 status to indicate the webhook was received and processed. + streak.lost: + post: + summary: Streak lost + operationId: webhooks_streak_lost + description: Triggered when a user loses their streak. + requestBody: + description: The webhook event. + content: + application/json: + schema: + properties: + type: + type: string + enum: ['streak.lost'] + description: The webhook event type. + user: + $ref: '#/components/schemas/User' + description: The user who lost the streak. + length: + type: integer + description: The length of the streak that was lost. + required: + - type + - user + - length + examples: + Streak lost: + value: + type: streak.lost + user: + id: user-id + email: user@example.com + tz: Europe/London + subscribedToEmails: true + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + attributes: + department: engineering + role: developer + length: 7 + responses: + '200': + description: Return a 200 status to indicate the webhook was received and processed. + streak.freeze_consumed: + post: + summary: Streak freeze consumed + operationId: webhooks_streak_freeze_consumed + description: Triggered when a user consumes a streak freeze. + requestBody: + description: The webhook event. + content: + application/json: + schema: + properties: + type: + type: string + enum: ['streak.freeze_consumed'] + description: The webhook event type. + user: + $ref: '#/components/schemas/User' + description: The user whose streak freeze was consumed. + consumed: + type: integer + description: The number of freezes consumed. + freezes: + type: integer + description: The total number of freezes the user has left after the consumption. + required: + - type + - user + - consumed + - freezes + examples: + Streak freeze consumed: + value: + type: streak.freeze_consumed + user: + id: user-id + email: user@example.com + tz: Europe/London + subscribedToEmails: true + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + attributes: + department: engineering + role: developer + consumed: 1 + freezes: 2 + responses: + '200': + description: Return a 200 status to indicate the webhook was received and processed. + streak.freeze_earned: + post: + summary: Streak freeze earned + operationId: webhooks_streak_freeze_earned + description: Triggered when a user earns streak freezes. + requestBody: + description: The webhook event. + content: + application/json: + schema: + properties: + type: + type: string + enum: ['streak.freeze_earned'] + description: The webhook event type. + user: + $ref: '#/components/schemas/User' + description: The user who earned streak freezes. + earned: + type: integer + description: The number of freezes earned. + freezes: + type: integer + description: The total number of freezes the user has after the event. + required: + - type + - user + - earned + - freezes + examples: + Streak freeze earned: + value: + type: streak.freeze_earned + user: + id: user-id + email: user@example.com + tz: Europe/London + subscribedToEmails: true + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + attributes: + department: engineering + role: developer + earned: 1 + freezes: 2 + responses: + '200': + description: Return a 200 status to indicate the webhook was received and processed. + points.changed: + post: + summary: Points changed + operationId: webhooks_points_changed + description: Triggered when a user is awarded or loses points. This event is fired a maximum of once per user per points system per minute. + requestBody: + description: The webhook event. + content: + application/json: + schema: + properties: + type: + type: string + enum: ['points.changed'] + description: The webhook event type. + user: + $ref: '#/components/schemas/User' + description: The user whose points increased or decreased. + points: + $ref: '#/components/schemas/MetricEventPointsResponse' + description: The user's points after the event (includes added amount for this event). + required: + - type + - user + - points + examples: + Points changed: + value: + type: points.changed + user: + id: user-id + email: user@example.com + tz: Europe/London + subscribedToEmails: true + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + attributes: + department: engineering + role: developer + points: + id: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + key: xp + name: XP + description: null + badgeUrl: null + maxPoints: null + total: 100 + added: 10 + 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: words written + metricThreshold: 1000 + responses: + '200': + description: Return a 200 status to indicate the webhook was received and processed. + points.level_changed: + post: + summary: Points level changed + operationId: webhooks_points_level_changed + description: Triggered when a user's level changes within a points system as a result of earning or losing points. + requestBody: + description: The webhook event. + content: + application/json: + schema: + properties: + type: + type: string + enum: ['points.level_changed'] + description: The webhook event type. + user: + $ref: '#/components/schemas/User' + description: The user whose level changed. + points: + description: The points system in which the level changed. + allOf: + - $ref: '#/components/schemas/PointsResponse' + - type: object + properties: + total: + type: integer + description: The user's total points in this system. + required: + - total + previousLevel: + description: The user's previous level, or null if the user had no level. + oneOf: + - $ref: '#/components/schemas/PointsLevel' + - type: 'null' + newLevel: + description: The user's new level, or null if the user no longer has a level. + oneOf: + - $ref: '#/components/schemas/PointsLevel' + - type: 'null' + required: + - type + - user + - points + - previousLevel + - newLevel + examples: + Level changed: + value: + type: points.level_changed + user: + id: user-id + email: user@example.com + tz: Europe/London + subscribedToEmails: true + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + attributes: + department: engineering + role: developer + points: + id: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + key: xp + name: XP + description: Experience points + badgeUrl: null + maxPoints: null + total: 100 + previousLevel: + id: 1140fe51-6bce-4b44-b0ad-bddc4e123534 + key: bronze + name: Bronze + description: Starting level + badgeUrl: https://example.com/bronze.png + points: 0 + newLevel: + id: 2240fe51-6bce-4b44-b0ad-bddc4e123534 + key: silver + name: Silver + description: Mid-tier level + badgeUrl: null + points: 50 + responses: + '200': + description: Return a 200 status to indicate the webhook was received and processed. + points.boost_started: + post: + summary: Points boost started + operationId: webhooks_points_boost_started + description: Triggered when a points boost goes live (its start time has been reached). + requestBody: + description: The webhook event. + content: + application/json: + schema: + properties: + type: + type: string + enum: ['points.boost_started'] + description: The webhook event type. + timestamp: + type: string + format: date-time + description: When the event occurred (ISO 8601). + boost: + $ref: '#/components/schemas/PointsBoostWebhookPayload' + description: The points boost that started. + required: + - type + - timestamp + - boost + examples: + Points boost started: + value: + type: points.boost_started + timestamp: '2025-01-15T00:00:00Z' + boost: + id: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + name: Double XP Weekend + status: active + userId: null + pointsSystemId: 0040fe51-6bce-4b44-b0ad-bddc4e123535 + pointsSystemKey: xp + pointsSystemName: XP + start: '2025-01-15' + end: '2025-01-17' + multiplier: 2 + rounding: down + responses: + '200': + description: Return a 200 status to indicate the webhook was received and processed. + points.boost_finished: + post: + summary: Points boost finished + operationId: webhooks_points_boost_finished + description: Triggered when a points boost ends (its end time has been reached). + requestBody: + description: The webhook event. + content: + application/json: + schema: + properties: + type: + type: string + enum: ['points.boost_finished'] + description: The webhook event type. + timestamp: + type: string + format: date-time + description: When the event occurred (ISO 8601). + boost: + $ref: '#/components/schemas/PointsBoostWebhookPayload' + description: The points boost that finished. + required: + - type + - timestamp + - boost + examples: + Points boost finished: + value: + type: points.boost_finished + timestamp: '2025-01-17T23:59:59Z' + boost: + id: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + name: Double XP Weekend + status: finished + userId: null + pointsSystemId: 0040fe51-6bce-4b44-b0ad-bddc4e123535 + pointsSystemKey: xp + pointsSystemName: XP + start: '2025-01-15' + end: '2025-01-17' + multiplier: 2 + rounding: down + responses: + '200': + description: Return a 200 status to indicate the webhook was received and processed. + leaderboard.started: + post: + summary: Leaderboard started + operationId: webhooks_leaderboard_started + description: Triggered when a run of a leaderboard begins. + requestBody: + description: The webhook event. + content: + application/json: + schema: + properties: + type: + type: string + enum: ['leaderboard.started'] + description: The webhook event type. + leaderboard: + $ref: '#/components/schemas/LeaderboardResponseWithRankings' + description: The leaderboard run that started and its initial rankings. + required: + - type + - leaderboard + examples: + Leaderboard started: + value: + type: leaderboard.started + leaderboard: + id: 5100fe51-6bce-6j44-b0hs-bddc4e123682 + name: Weekly Word Count Challenge + key: weekly-words + rankBy: metric + metricKey: words-written + metricName: Words Written + pointsSystemKey: null + pointsSystemName: null + description: Compete weekly to see who writes the most words + status: active + start: '2025-01-01' + end: null + maxParticipants: 100 + breakdownAttribute: null + runUnit: day + runInterval: 7 + rankings: + - userId: user-123 + userName: Alice Johnson + rank: 1 + value: 1 + responses: + '200': + description: Return a 200 status to indicate the webhook was received and processed. + leaderboard.changed: + post: + summary: Leaderboard changed + operationId: webhooks_leaderboard_changed + description: Triggered when leaderboard rankings change. + requestBody: + description: The webhook event. + content: + application/json: + schema: + properties: + type: + type: string + enum: ['leaderboard.changed'] + description: The webhook event type. + leaderboard: + $ref: '#/components/schemas/LeaderboardResponseWithRankings' + description: The leaderboard run that changed. + required: + - type + - leaderboard + examples: + Leaderboard changed: + value: + type: leaderboard.changed + leaderboard: + id: 5100fe51-6bce-6j44-b0hs-bddc4e123682 + name: Weekly Word Count Challenge + key: weekly-words + rankBy: metric + metricKey: words-written + metricName: Words Written + pointsSystemKey: null + pointsSystemName: null + description: Compete weekly to see who writes the most words + status: active + start: '2025-01-01' + end: null + maxParticipants: 100 + breakdownAttribute: null + runUnit: day + runInterval: 7 + rankings: + - userId: user-123 + userName: Alice Johnson + rank: 1 + value: 10 + - userId: user-456 + userName: Bob Smith + rank: 2 + value: 6 + - userId: user-789 + userName: Charlie Brown + rank: 3 + value: 4 + responses: + '200': + description: Return a 200 status to indicate the webhook was received and processed. + leaderboard.finished: + post: + summary: Leaderboard finished + operationId: webhooks_leaderboard_finished + description: Triggered when a run of a leaderboard finishes. + requestBody: + description: The webhook event. + content: + application/json: + schema: + properties: + type: + type: string + enum: ['leaderboard.finished'] + description: The webhook event type. + leaderboard: + $ref: '#/components/schemas/LeaderboardResponseWithRankings' + description: The leaderboard run that finished and its final rankings. + required: + - type + - leaderboard + examples: + Leaderboard finished: + value: + type: leaderboard.finished + leaderboard: + id: 5100fe51-6bce-6j44-b0hs-bddc4e123682 + name: Weekly Word Count Challenge + key: weekly-words + rankBy: metric + metricKey: words-written + metricName: Words Written + pointsSystemKey: null + pointsSystemName: null + description: Compete weekly to see who writes the most words + status: active + start: '2025-01-01' + end: null + maxParticipants: 100 + breakdownAttribute: null + runUnit: day + runInterval: 7 + rankings: + - userId: user-123 + userName: Alice Johnson + rank: 1 + value: 10 + - userId: user-456 + userName: Bob Smith + rank: 2 + value: 6 + - userId: user-789 + userName: Charlie Brown + rank: 3 + value: 4 + responses: + '200': + description: Return a 200 status to indicate the webhook was received and processed. + leaderboard.rank_changed: + post: + summary: Leaderboard rank changed + operationId: webhooks_leaderboard_rank_changed + description: Triggered when a user's leaderboard rank changes. + requestBody: + description: The webhook event. + content: + application/json: + schema: + properties: + type: + type: string + enum: ['leaderboard.rank_changed'] + description: The webhook event type. + user: + $ref: '#/components/schemas/User' + description: The user whose rank changed. + leaderboard: + $ref: '#/components/schemas/WebhookUserLeaderboardResponse' + description: The user's leaderboard data that changed. + required: + - type + - user + - leaderboard + examples: + Leaderboard rank changed: + value: + type: leaderboard.rank_changed + user: + id: user-id + email: user@example.com + tz: Europe/London + subscribedToEmails: true + created: '2021-01-01T00:00:00Z' + updated: '2021-01-01T00:00:00Z' + attributes: + department: engineering + role: developer + leaderboard: + id: 5100fe51-6bce-6j44-b0hs-bddc4e123682 + name: Weekly Word Count Challenge + key: weekly-words + rankBy: metric + metricKey: words-written + metricName: Words Written + description: Compete weekly to see who writes the most words + start: '2025-01-01' + end: null + maxParticipants: 100 + breakdownAttribute: null + runUnit: day + runInterval: 7 + rank: 2 + value: 4500 + previousRank: 1 + previousValue: 4500 + responses: + '200': + description: Return a 200 status to indicate the webhook was received and processed. +components: + schemas: + MetricStatus: + title: MetricStatus + type: string + enum: + - archived + - active + description: The status of the achievement. + StreakFrequency: + title: StreakFrequency + type: string + enum: + - daily + - weekly + - monthly + BaseStreakResponse: + title: Streak Response + type: object + properties: + length: + type: integer + description: The length of the user's current streak. + frequency: + $ref: '#/components/schemas/StreakFrequency' + description: The frequency of the streak. + started: + type: + - string + - 'null' + format: date + description: The date the streak started. + periodStart: + type: + - string + - 'null' + format: date + description: The start date of the current streak period. + periodEnd: + type: + - string + - 'null' + format: date + description: The end date of the current streak period. + expires: + type: + - string + - 'null' + format: date + description: The date the streak will expire if the user does not increment a metric. + freezes: + type: integer + description: The number of available streak freezes. Only present if the organization has enabled streak freezes. + maxFreezes: + type: integer + description: The maximum number of streak freezes a user can have. Only present if the organization has enabled streak freezes. + freezeAutoEarnInterval: + type: integer + description: The interval at which the user will earn streak freezes, in days. Only present if the organization has enabled streak freeze auto-earn. + freezeAutoEarnAmount: + type: integer + description: The amount of streak freezes the user will earn per interval. Only present if the organization has enabled streak freeze auto-earn. + required: + - length + - frequency + - started + - periodStart + - periodEnd + - expires + BulkStreakResponse: + title: Bulk Streak Response + type: array + items: + properties: + userId: + type: string + description: The ID of the user. + streakLength: + type: integer + description: The length of the user's streak. + extended: + type: + - string + - 'null' + description: The timestamp the streak was extended, as a string. Null if the streak is not active. + required: + - userId + - streakLength + - extended + MetricEventStreakResponse: + title: Streak Response (Metric Event) + type: object + description: An object representing the user's streak after sending a metric event. + allOf: + - $ref: '#/components/schemas/BaseStreakResponse' + - type: object + properties: + extended: + type: boolean + description: Whether this metric event increased the user's streak length. + required: + - extended + StreakResponse: + title: Streak Response + type: object + description: An object representing the user's streak. + allOf: + - $ref: '#/components/schemas/BaseStreakResponse' + - type: object + properties: + streakHistory: + type: array + description: >- + A list of the user's past streak periods up through the current period. Each + period includes the start and end dates and the length of the streak. + items: + type: object + description: >- + An object representing a past streak period. + properties: + periodStart: + type: string + format: date + description: The date this streak period started. + example: '2025-03-31' + periodEnd: + type: string + format: date + description: The date this streak period ended. + example: '2025-04-05' + length: + type: integer + description: The length of the user's streak during this period. + example: 1 + usedFreeze: + type: boolean + description: Whether the user used a streak freeze during this period. Only present if the organization has enabled streak freezes. + example: false + required: + - periodStart + - periodEnd + - length + rank: + type: + - integer + - 'null' + description: The user's rank across all users. Null if the user has no active streak. + example: 5 + required: + - rank + PointsTrigger: + title: PointsTrigger + type: object + properties: + id: + type: string + description: The ID of the trigger + type: + type: string + description: The type of trigger + enum: ['metric', 'achievement', 'streak', 'time', 'user_creation'] + points: + type: integer + description: The points awarded by this trigger. + metricName: + type: string + description: If the trigger has type 'metric', the name of the metric + metricThreshold: + type: integer + description: If the trigger has type 'metric', the threshold of the metric that triggers the points + streakLengthThreshold: + type: integer + description: If the trigger has type 'streak', the threshold of the streak that triggers the points + achievementName: + type: string + description: If the trigger has type 'achievement', the name of the achievement + timeUnit: + type: string + enum: ['hour', 'day'] + description: If the trigger has type 'time', the unit of time after which to award points + timeInterval: + type: integer + description: If the trigger has type 'time', the numer of units of timeUnit after which to award points + PointsAward: + title: PointsAward + type: object + properties: + id: + type: string + description: The ID of the trigger award + awarded: + type: integer + description: The points awarded by this trigger + date: + type: string + description: The date these points were awarded, in ISO 8601 format. + total: + type: integer + description: The user's total points after this award occurred. + trigger: + $ref: '#/components/schemas/PointsTrigger' + boosts: + type: array + description: Array of points boosts that applied to this award. + items: + $ref: '#/components/schemas/PointsBoost' + PointsBoost: + title: PointsBoost + type: object + properties: + id: + type: string + description: The ID of the points boost + name: + type: string + description: The name of the points boost + status: + type: string + enum: ['active', 'scheduled', 'finished'] + description: The status of the points boost + start: + type: string + description: The start date of the points boost + end: + type: + - string + - 'null' + description: The end date of the points boost + multiplier: + type: number + description: The multiplier of the points boost + rounding: + type: string + enum: ['down', 'up', 'nearest'] + description: The rounding method of the points boost + required: + - id + - name + - status + - start + - end + - multiplier + - rounding + PointsBoostWebhookPayload: + title: PointsBoostWebhookPayload + type: object + description: Points boost payload sent in points.boost_started and points.boost_finished webhook events. + properties: + id: + type: string + description: The ID of the points boost. + name: + type: string + description: The name of the points boost. + status: + type: string + enum: ['active', 'finished'] + description: The status of the points boost. + userId: + type: + - string + - 'null' + description: The customer-facing user ID that the boost is scoped to, or null for global boosts. + pointsSystemId: + type: string + description: The ID of the points system this boost applies to. + pointsSystemKey: + type: string + description: The key of the points system this boost applies to. + pointsSystemName: + type: string + description: The name of the points system this boost applies to. + start: + type: string + format: date + description: The start date of the points boost (YYYY-MM-DD). + end: + type: + - string + - 'null' + format: date + description: The end date of the points boost (YYYY-MM-DD), or null if open-ended. + multiplier: + type: number + description: The multiplier applied to points during the boost. + rounding: + type: string + enum: ['down', 'up', 'nearest'] + description: The rounding method applied to boosted points. + required: + - id + - name + - status + - userId + - pointsSystemId + - pointsSystemKey + - pointsSystemName + - start + - end + - multiplier + - rounding + PointsResponse: + title: PointsResponse + type: object + description: Base points system fields shared across responses. + properties: + id: + type: string + description: The ID of the points system + key: + type: string + description: The key of the points system + name: + type: string + description: The name of the points system + description: + type: + - string + - 'null' + description: The description of the points system + badgeUrl: + type: + - string + - 'null' + description: The URL of the badge image for the points system + maxPoints: + type: + - number + - 'null' + description: The maximum number of points a user can be awarded in this points system + required: + - id + - key + - name + - description + - badgeUrl + - maxPoints + GetUserPointsResponse: + title: GetUserPointsResponse + type: object + allOf: + - $ref: '#/components/schemas/PointsResponse' + - type: object + properties: + total: + type: integer + description: The user's total points + level: + description: The user's current level in this points system, or null if no levels are configured or the user hasn't reached any level yet. + oneOf: + - $ref: '#/components/schemas/PointsLevel' + - type: 'null' + awards: + type: array + description: Array of trigger awards that added points. + items: + $ref: '#/components/schemas/PointsAward' + required: + - total + - level + - awards + PointsLevelSummaryResponse: + title: PointsLevelSummaryResponse + type: array + description: A breakdown of users by level in a points system. + items: + type: object + properties: + level: + $ref: '#/components/schemas/PointsLevel' + users: + type: integer + description: The number of users currently at this level + required: + - level + - users + PointsLevel: + title: PointsLevel + type: object + description: A level within a points system. + properties: + id: + type: string + description: The ID of the level + key: + type: string + description: The unique key of the level + name: + type: string + description: The name of the level + description: + type: string + description: The description of the level + badgeUrl: + type: + - string + - 'null' + description: The URL of the badge image for the level + points: + type: integer + description: The points threshold required to reach this level + required: + - id + - key + - name + - description + - badgeUrl + - points + LeaderboardResponse: + title: LeaderboardResponse + type: object + description: A leaderboard with its configuration details. + properties: + id: + type: string + description: The unique ID of the leaderboard. + example: 5100fe51-6bce-6j44-b0hs-bddc4e123682 + name: + type: string + description: The user-facing name of the leaderboard. + example: Weekly Word Count Challenge + key: + type: string + description: The unique key used to reference the leaderboard in APIs. + example: weekly-words + rankBy: + type: string + enum: ['points', 'streak', 'metric'] + description: What the leaderboard ranks by. + example: metric + breakdownAttribute: + type: + - string + - 'null' + description: The key of the attribute to break down this leaderboard by. + example: country + metricKey: + type: string + description: The key of the metric to rank by, if rankBy is 'metric'. + example: words-written + metricName: + type: string + description: The name of the metric to rank by, if rankBy is 'metric'. + example: Words Written + pointsSystemKey: + type: string + description: The key of the points system to rank by, if rankBy is 'points'. + example: xp-system + pointsSystemName: + type: string + description: The name of the points system to rank by, if rankBy is 'points'. + example: Experience Points + description: + type: + - string + - 'null' + description: The user-facing description of the leaderboard. + example: Compete weekly to see who writes the most words + start: + type: string + format: date + description: The start date of the leaderboard in YYYY-MM-DD format. + example: '2025-01-01' + end: + type: + - string + - 'null' + format: date + description: The end date of the leaderboard in YYYY-MM-DD format, or null if it runs forever. + example: '2025-12-31' + maxParticipants: + type: integer + description: The maximum number of participants in the leaderboard. + example: 100 + runUnit: + type: + - string + - 'null' + enum: ['day', 'month', 'year', null] + description: The repetition type for recurring leaderboards, or null for one-time leaderboards. + example: day + runInterval: + type: + - integer + - 'null' + description: The interval between repetitions, relative to the start date and repetition type. Null for one-time leaderboards. + example: 7 + required: + - id + - name + - key + - status + - description + - rankBy + - breakdownAttribute + - start + - end + - maxParticipants + - runUnit + - runInterval + LeaderboardResponseWithRankings: + title: LeaderboardResponseWithRankings + type: object + allOf: + - $ref: '#/components/schemas/LeaderboardResponse' + - type: object + properties: + status: + type: string + enum: ['active', 'scheduled', 'finished'] + description: The status of the leaderboard. + example: active + rankings: + type: array + description: Array of user rankings for the leaderboard. + items: + $ref: '#/components/schemas/LeaderboardRanking' + required: + - rankings + - status + MetricEventPointsResponse: + title: MetricEventPointsResponse + type: object + description: Points system response for metric events and achievement completions. + allOf: + - $ref: '#/components/schemas/PointsResponse' + - type: object + properties: + total: + type: integer + description: The user's total points + level: + description: The user's new level, included only when the level changed as a result of this event. + oneOf: + - $ref: '#/components/schemas/PointsLevel' + - type: 'null' + added: + type: integer + description: The points added by this event. + example: 10 + awards: + type: array + description: Array of trigger awards that added points. + items: + $ref: '#/components/schemas/PointsAward' + required: + - total + - added + - awards + MetricEventLeaderboardResponse: + title: MetricEventLeaderboardResponse + type: object + allOf: + - $ref: '#/components/schemas/LeaderboardResponse' + - type: object + properties: + start: + type: string + format: date + description: The start date of the current run of the leaderboard. + example: '2025-01-01' + end: + type: + - string + - 'null' + format: date + description: The end date of the current run of the leaderboard, or null if the run never ends. + example: '2025-12-31' + rank: + type: + - integer + - 'null' + description: The user's rank in the leaderboard, or null if the user is not on the leaderboard. + example: 100 + previousRank: + type: + - integer + - 'null' + description: The user's rank in the leaderboard before the event, or null if the user was not on the leaderboard before the event. + example: 100 + threshold: + type: integer + description: The minimum value required to enter the leaderboard according to its current rankings. + example: 25000 + breakdownAttributeValue: + type: string + description: For leaderboards with a breakdown attribute, the value of the attribute for the user. + example: USA + required: + - start + - end + - threshold + - rank + - previousRank + AchievementResponse: + title: AchievementResponse + type: object + properties: + id: + type: string + description: The unique ID of the achievement. + name: + type: string + description: The name of this achievement. + trigger: + type: string + enum: ['metric', 'streak', 'api', 'achievement'] + description: The trigger of the achievement. + description: + type: + - string + - 'null' + description: The description of this achievement. + badgeUrl: + type: + - string + - 'null' + description: >- + The URL of the badge image for the achievement, if one has been + uploaded. + key: + type: string + description: The key used to reference this achievement in the API (only applicable if trigger = 'api') + streakLength: + type: integer + description: The length of the streak required to complete the achievement (only applicable if trigger = 'streak') + achievementIds: + type: array + items: + type: string + description: The IDs of the prerequisite achievements that must be completed to earn this achievement (only applicable if trigger = 'achievement') + metricId: + type: string + description: The ID of the metric associated with this achievement (only applicable if trigger = 'metric') + metricValue: + type: number + format: double + description: >- + The value of the metric required to complete the achievement (only applicable if trigger = 'metric') + metricName: + type: string + description: The name of the metric associated with this achievement (only applicable if trigger = 'metric') + userAttributes: + type: array + description: User attribute filters that must be met for this achievement to be completed. Only present if the achievement has user attribute filters configured. + items: + type: object + properties: + key: + type: string + description: The key of the user attribute. + example: plan-type + value: + type: string + description: The value of the user attribute. + example: premium + required: + - key + - value + eventAttribute: + type: object + description: Event attribute filter that must be met for this achievement to be completed. Only present if the achievement has an event filter configured. + properties: + key: + type: string + description: The key of the event attribute. + example: source + value: + type: string + description: The value of the event attribute. + example: mobile-app + required: + - key + - value + required: + - id + - name + - trigger + - description + - badgeUrl + UserAchievementResponse: + title: UserAchievementResponse + type: object + allOf: + - $ref: '#/components/schemas/AchievementResponse' + - type: object + properties: + achievedAt: + type: + - string + - 'null' + format: date-time + description: The date and time the achievement was completed, in ISO 8601 format. Null if the achievement has not been completed. + required: + - achievedAt + UserAchievementWithStatsResponse: + title: UserAchievementWithStatsResponse + type: object + allOf: + - $ref: '#/components/schemas/AchievementWithStatsResponse' + - type: object + properties: + achievedAt: + type: + - string + - 'null' + format: date-time + description: The date and time the achievement was completed, in ISO 8601 format. Null if the achievement has not been completed. + required: + - achievedAt + AchievementWithStatsResponse: + title: AchievementWithStatsResponse + type: object + allOf: + - $ref: '#/components/schemas/AchievementResponse' + - type: object + properties: + completions: + type: integer + description: The number of users who have completed this achievement. + rarity: + type: number + format: double + description: The percentage of all users who have completed this achievement. + required: + - completions + - rarity + MetricResponse: + title: MetricResponse + type: object + properties: + id: + type: string + description: The unique ID of the metric. + example: d01dcbcb-d51e-4c12-b054-dc811dcdc623 + key: + type: string + description: The unique key of the metric. + example: words-written + name: + type: string + description: The name of the metric. + example: Words written + status: + $ref: '#/components/schemas/MetricStatus' + description: The status of the metric. + current: + type: number + format: double + description: The user's current total for the metric. + example: 1500 + achievements: + type: array + items: + $ref: '#/components/schemas/UserAchievementResponse' + description: >- + A list of the metric's achievements and the user's progress towards + each. + required: + - id + - key + - name + - status + - current + - achievements + UpdatedUser: + title: Updated User + type: object + description: An object with editable user fields. + properties: + email: + type: string + description: The user's email address. Required if subscribeToEmails is true. + example: user@example.com + name: + type: string + description: The name to refer to the user by in emails. + example: User + tz: + type: + - string + - 'null' + description: The user's timezone (used for email scheduling). + example: Europe/London + deviceTokens: + type: + - array + - 'null' + description: The user's device tokens, used for push notifications. + items: + type: string + description: The device token. + example: ['token1', 'token2'] + subscribeToEmails: + type: boolean + default: true + description: Whether the user should receive Trophy-powered emails. If false, Trophy will not store the user's email address. + example: true + attributes: + type: object + additionalProperties: + type: string + description: User attributes as key-value pairs. Keys must match existing user attributes set up in the Trophy dashboard. + example: + department: engineering + role: developer + UpsertedUser: + title: Upserted User + type: object + description: An object with editable user fields. + allOf: + - $ref: '#/components/schemas/UpdatedUser' + - type: object + properties: + id: + type: string + description: The ID of the user in your database. Must be a string. + example: user-id + required: + - id + User: + title: User + type: object + description: A user of your application. + properties: + id: + type: string + description: The ID of the user in your database. Must be a string. + example: user-id + email: + type: + - string + - 'null' + description: The user's email address. + example: user@example.com + name: + type: + - string + - 'null' + description: The name of the user. + example: John Doe + tz: + type: + - string + - 'null' + description: The user's timezone. + example: Europe/London + deviceTokens: + type: + - array + - 'null' + description: The user's device tokens. + items: + type: string + description: The device token. + example: ['token1', 'token2'] + subscribeToEmails: + type: boolean + description: Whether the user is opted into receiving Trophy-powered emails. + example: true + attributes: + type: object + additionalProperties: + type: string + description: User attributes as key-value pairs. Keys must match existing user attributes set up in the Trophy dashboard. + example: + department: engineering + role: developer + control: + type: boolean + description: Whether the user is in the control group, meaning they do not receive emails or other communications from Trophy. + example: false + created: + type: string + format: date-time + description: The date and time the user was created, in ISO 8601 format. + example: '2021-01-01T00:00:00Z' + updated: + type: string + format: date-time + description: The date and time the user was last updated, in ISO 8601 format. + example: '2021-01-01T00:00:00Z' + required: + - id + - email + - name + - tz + - subscribeToEmails + - attributes + - control + - created + - updated + NotificationChannel: + title: NotificationChannel + type: string + enum: + - email + - push + description: A notification delivery channel. + NotificationType: + title: NotificationType + type: string + enum: + - achievement_completed + - recap + - reactivation + - streak_reminder + description: A type of notification that can be configured. + NotificationPreferences: + title: NotificationPreferences + type: object + description: Notification preferences for each notification type. + properties: + achievement_completed: + type: array + items: + $ref: '#/components/schemas/NotificationChannel' + description: Channels to receive achievement completion notifications on. + recap: + type: array + items: + $ref: '#/components/schemas/NotificationChannel' + description: Channels to receive recap notifications on. + reactivation: + type: array + items: + $ref: '#/components/schemas/NotificationChannel' + description: Channels to receive reactivation notifications on. + streak_reminder: + type: array + items: + $ref: '#/components/schemas/NotificationChannel' + description: Channels to receive streak reminder notifications on. + UserPreferencesResponse: + title: UserPreferencesResponse + type: object + description: A user's preferences. + properties: + notifications: + $ref: '#/components/schemas/NotificationPreferences' + required: + - notifications + UpdateUserPreferencesRequest: + title: UpdateUserPreferencesRequest + type: object + description: Request body for updating user preferences. + properties: + notifications: + $ref: '#/components/schemas/NotificationPreferences' + ErrorBody: + title: ErrorBody + type: object + properties: + error: + type: string + required: + - error + AchievementCompletionResponse: + title: AchievementCompletionResponse + type: object + properties: + completionId: + type: string + description: The unique ID of the completion. + example: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + achievement: + $ref: '#/components/schemas/UserAchievementResponse' + points: + type: object + additionalProperties: + $ref: '#/components/schemas/MetricEventPointsResponse' + description: >- + A map of points systems by key that were affected by this achievement completion. + required: + - completionId + - achievement + - points + EventResponse: + title: EventResponse + type: object + properties: + eventId: + type: string + description: The unique ID of the event. + example: 0040fe51-6bce-4b44-b0ad-bddc4e123534 + metricId: + type: string + description: The unique ID of the metric that was updated. + example: d01dcbcb-d51e-4c12-b054-dc811dcdc623 + total: + type: number + format: double + description: The user's new total progress against the metric. + example: 750 + achievements: + type: array + items: + $ref: '#/components/schemas/UserAchievementResponse' + description: Achievements completed as a result of this event. + currentStreak: + $ref: '#/components/schemas/MetricEventStreakResponse' + description: >- + The user's current streak. + points: + type: object + additionalProperties: + $ref: '#/components/schemas/MetricEventPointsResponse' + description: >- + A map of points systems by key. Only contains points systems that were affected by the event. + leaderboards: + type: object + additionalProperties: + $ref: '#/components/schemas/MetricEventLeaderboardResponse' + description: >- + A map of leaderboards by key. Only contains leaderboards that were affected by the event. + idempotencyKey: + type: string + description: The idempotency key used for the event, if one was provided. + idempotentReplayed: + type: boolean + description: Whether the event was replayed due to idempotency. + required: + - eventId + - metricId + - total + - achievements + - currentStreak + - points + - leaderboards + PointsRange: + title: PointsRange + type: object + properties: + from: + type: integer + description: The start of the points range. Inclusive. + to: + type: integer + description: The end of the points range. Inclusive. + users: + type: integer + description: The number of users in this points range. + required: + - from + - to + - users + PointsSummaryResponse: + title: PointsSummaryResponse + type: array + description: >- + A list of eleven points ranges, with the first starting and ending at 0, + and the remaining 10 being calculated as 10 equally sized ranges from 1 + to the greatest number of points a user has, rounded up to the nearest + power of 10. + items: + $ref: '#/components/schemas/PointsRange' + PointsTriggerResponse: + title: PointsTriggerResponse + type: object + properties: + id: + type: string + description: The unique ID of the trigger. + type: + type: string + enum: ['metric', 'achievement', 'streak', 'time', 'user_creation'] + description: The type of trigger. + points: + type: integer + description: The points awarded by this trigger. + status: + type: string + enum: ['active', 'archived'] + description: The status of the trigger. + achievementId: + type: string + description: The unique ID of the achievement associated with this trigger, if the trigger is an achievement. + metricId: + type: string + description: The unique ID of the metric associated with this trigger, if the trigger is a metric. + metricThreshold: + type: integer + description: The amount that a user must increase the metric to earn the points, if the trigger is a metric. + streakLengthThreshold: + type: integer + description: The number of consecutive streak periods that a user must complete to earn the points, if the trigger is a streak. + metricName: + type: string + description: The name of the metric associated with this trigger, if the trigger is a metric. + achievementName: + type: string + description: The name of the achievement associated with this trigger, if the trigger is an achievement. + timeUnit: + type: string + enum: ['hour', 'day'] + description: The time unit of the trigger, if the trigger is a time interval. + timeInterval: + type: integer + description: The interval of the trigger in the time unit, if the trigger is a time interval. + userAttributes: + type: array + description: User attribute filters that must be met for this trigger to activate. Only present if the trigger has user attribute filters configured. + items: + type: object + properties: + key: + type: string + description: The key of the user attribute. + example: plan-type + value: + type: string + description: The value of the user attribute. + example: premium + required: + - key + - value + eventAttribute: + type: object + description: Event attribute filter that must be met for this trigger to activate. Only present if the trigger has an event filter configured. + properties: + key: + type: string + description: The key of the event attribute. + example: source + value: + type: string + description: The required value of the event attribute. + example: mobile-app + required: + - key + - value + created: + type: string + format: date-time + description: The date and time the trigger was created, in ISO 8601 format. + updated: + type: string + format: date-time + description: The date and time the trigger was last updated, in ISO 8601 format. + required: + - id + - type + - points + - status + - created + - updated + PointsSystemResponse: + title: PointsSystemResponse + type: object + properties: + id: + type: string + description: The unique ID of the points system. + name: + type: string + description: The name of the points system. + description: + type: + - string + - 'null' + description: The description of the points system. + badgeUrl: + type: + - string + - 'null' + description: The URL of the badge image for the points system, if one has been uploaded. + maxPoints: + type: + - number + - 'null' + description: The maximum number of points a user can be awarded in this points system + triggers: + type: array + description: Array of active triggers for this points system. + items: + $ref: '#/components/schemas/PointsTriggerResponse' + required: + - id + - name + - description + - badgeUrl + - maxPoints + - triggers + StreakRankingUser: + title: StreakRankingUser + type: object + description: A user with their streak length in the rankings. + properties: + userId: + type: string + description: The ID of the user. + example: user-123 + name: + type: + - string + - 'null' + description: The name of the user. May be null if no name is set. + example: Alice Johnson + streakLength: + type: integer + description: The user's streak length (active or longest depending on query parameter). + example: 15 + required: + - userId + - name + - streakLength + LeaderboardRanking: + title: LeaderboardRanking + type: object + description: A user's ranking in a leaderboard. + properties: + userId: + type: string + description: The ID of the user. + example: user-123 + userName: + type: + - string + - 'null' + description: The name of the user. May be null if no name is set. + example: Alice Johnson + rank: + type: integer + description: The user's rank in the leaderboard. + example: 1 + value: + type: integer + description: The user's value for this leaderboard (points, metric value, etc.). + example: 5000 + required: + - userId + - userName + - rank + - value + LeaderboardEvent: + title: LeaderboardEvent + type: object + description: A leaderboard event representing a change in a user's rank or value. + properties: + timestamp: + type: string + format: date-time + description: The timestamp when the event occurred. + example: '2025-01-15T10:30:00Z' + previousRank: + type: + - integer + - 'null' + description: The user's rank before this event, or null if they were not on the leaderboard. + example: 5 + rank: + type: + - integer + - 'null' + description: The user's rank after this event, or null if they are no longer on the leaderboard. + example: 3 + previousValue: + type: + - integer + - 'null' + description: The user's value before this event, or null if they were not on the leaderboard. + example: 1000 + value: + type: + - integer + - 'null' + description: The user's value after this event, or null if they are no longer on the leaderboard. + example: 3000 + required: + - time + - previousRank + - rank + - previousValue + - value + UserLeaderboardResponse: + title: UserLeaderboardResponse + type: object + description: A user's data for a specific leaderboard including rank, value, and history. + allOf: + - $ref: '#/components/schemas/LeaderboardResponse' + - type: object + properties: + rank: + type: + - integer + - 'null' + description: The user's current rank in this leaderboard. Null if the user is not on the leaderboard. + example: 2 + value: + type: + - integer + - 'null' + description: The user's current value in this leaderboard. Null if the user is not on the leaderboard. + example: 4500 + required: + - rank + - value + UserLeaderboardResponseWithHistory: + title: UserLeaderboardResponseWithHistory + type: object + description: A user's data for a specific leaderboard including rank, value, and history. + allOf: + - $ref: '#/components/schemas/UserLeaderboardResponse' + - type: object + properties: + history: + type: array + items: + $ref: '#/components/schemas/LeaderboardEvent' + description: An array of events showing the user's rank and value changes over time. + required: + - history + WebhookUserLeaderboardResponse: + title: WebhookUserLeaderboardResponse + type: object + description: A user's data for a specific leaderboard including rank, value, and history. + allOf: + - $ref: '#/components/schemas/UserLeaderboardResponse' + - type: object + properties: + previousRank: + type: + - integer + - 'null' + description: The user's rank before this event, or null if they were not on the leaderboard. + example: 5 + previousValue: + type: + - integer + - 'null' + description: The user's value before this event, or null if they were not on the leaderboard. + example: 1000 + required: + - previousRank + - previousValue + CreateStreakFreezesRequest: + title: CreateStreakFreezesRequest + type: object + description: Request body for creating streak freezes. + properties: + freezes: + type: array + items: + type: object + properties: + userId: + type: string + description: The ID of the user to create a freeze for. + example: user-123 + required: + - userId + description: Array of freezes to create. Maximum 1,000 freezes per request. + maxItems: 1000 + minItems: 1 + required: + - freezes + CreateStreakFreezesResponse: + title: CreateStreakFreezesResponse + type: object + description: Response containing any issues encountered while creating streak freezes. + properties: + issues: + type: array + items: + $ref: '#/components/schemas/BulkInsertIssue' + description: Array of issues encountered during freeze creation. + required: + - issues + CreatePointsBoostsRequest: + title: CreatePointsBoostsRequest + type: object + description: Request body for creating points boosts. + properties: + systemKey: + type: string + description: The key of the points system to create boosts for. + example: xp + boosts: + type: array + items: + type: object + properties: + userId: + type: string + description: The ID of the user to create a boost for. + example: user-123 + name: + type: string + description: The name of the boost. + maxLength: 255 + example: Double XP Weekend + start: + type: string + format: date + description: The start date of the boost (YYYY-MM-DD). + example: '2024-01-01' + end: + type: + - string + - 'null' + format: date + description: The end date of the boost (YYYY-MM-DD). If null, the boost has no end date. + example: '2024-01-03' + multiplier: + type: number + description: The points multiplier. Must be greater than 0, not equal to 1, and less than 100. + example: 2 + exclusiveMinimum: 0 + exclusiveMaximum: 100 + rounding: + type: string + enum: + - down + - up + - nearest + default: down + description: How to round the boosted points. Defaults to 'down'. + example: down + required: + - userId + - name + - start + - multiplier + description: Array of boosts to create. Maximum 1,000 boosts per request. + maxItems: 1000 + minItems: 1 + required: + - systemKey + - boosts + CreatedPointsBoost: + title: CreatedPointsBoost + type: object + description: A successfully created points boost returned from the create endpoint. + properties: + id: + type: string + format: uuid + description: The UUID of the created boost. + name: + type: string + description: The name of the boost. + status: + type: string + enum: ['active', 'scheduled', 'finished'] + description: The status of the boost. + start: + type: string + format: date + description: The start date (YYYY-MM-DD). + end: + type: + - string + - 'null' + format: date + description: The end date (YYYY-MM-DD) or null if no end date. + multiplier: + type: number + description: The points multiplier. + rounding: + type: string + enum: + - down + - up + - nearest + description: How boosted points are rounded. + userId: + type: string + description: The customer ID of the user the boost was created for. + required: + - id + - name + - status + - start + - end + - multiplier + - rounding + - userId + CreatePointsBoostsResponse: + title: CreatePointsBoostsResponse + type: object + description: Response containing created boosts and any issues encountered while creating points boosts. + properties: + created: + type: array + items: + $ref: '#/components/schemas/CreatedPointsBoost' + description: Array of successfully created boosts. + issues: + type: array + items: + $ref: '#/components/schemas/BulkInsertIssue' + description: Array of issues encountered during boost creation. + required: + - created + - issues + ArchivePointsBoostsResponse: + title: ArchivePointsBoostsResponse + type: object + description: Response containing the count of archived points boosts. + properties: + archivedCount: + type: integer + description: The number of boosts that were archived. + example: 3 + required: + - archivedCount + RestoreStreaksRequest: + title: RestoreStreaksRequest + type: object + description: Request body for restoring streaks for multiple users. + properties: + users: + type: array + items: + type: object + properties: + id: + type: string + description: The ID of the user to restore streaks for. + example: user-123 + required: + - id + description: Array of users to restore streaks for. Maximum 100 users per request. + maxItems: 100 + minItems: 1 + example: + - id: user-123 + - id: user-456 + required: + - users + RestoreStreaksResponse: + title: RestoreStreaksResponse + type: object + description: Response containing restored users and any issues encountered. + properties: + restoredUsers: + type: array + items: + type: string + description: Array of user IDs whose streaks were successfully restored. + example: + - user-123 + - user-456 + issues: + type: array + items: + $ref: '#/components/schemas/BulkInsertIssue' + description: Array of issues encountered during streak restoration. + required: + - restoredUsers + - issues + BulkInsertIssue: + title: BulkInsertIssue + type: object + description: An issue encountered while bulk inserting data. + properties: + userId: + type: string + description: The ID of the user the issue relates to. + example: user-123 + level: + type: string + enum: + - error + - warning + description: The severity level of the issue. + example: warning + reason: + type: string + description: A human-readable description of the issue. + example: Would exceed maximum freeze limit + required: + - userId + - level + - reason + WrappedMetric: + title: WrappedMetric + type: object + description: A user's metric data for a wrapped period. + properties: + name: + type: string + description: The name of the metric. + example: Words Written + units: + type: + - string + - 'null' + description: The units of the metric. + example: words + currentTotal: + type: number + description: The user's current total for the metric. + example: 15000 + changeThisPeriod: + type: number + description: The change in the metric value during the period. + example: 2500 + percentChange: + type: number + description: The percentage change in the metric value during the period. + example: 20 + percentileThisPeriod: + type: number + description: The user's percentile rank for this metric during the period. Only included for weekly, monthly, and yearly aggregation periods. + example: 85 + byAttribute: + type: object + additionalProperties: + type: object + additionalProperties: + type: object + properties: + name: + type: string + description: The name of the metric. + units: + type: + - string + - 'null' + description: The units of the metric. + currentTotal: + type: number + description: The current total for this attribute value. + changeThisPeriod: + type: number + description: The change during the period for this attribute value. + percentChange: + type: number + description: The percentage change for this attribute value. + percentileThisPeriod: + type: number + description: The user's percentile rank for this attribute value during the period. + description: Metric data broken down by attribute key and value. + required: + - name + - currentTotal + - changeThisPeriod + - percentChange + - byAttribute + WrappedPoints: + title: WrappedPoints + type: object + description: A user's points data for a wrapped period. + properties: + name: + type: string + description: The name of the points system. + example: Experience Points + description: + type: + - string + - 'null' + description: The description of the points system. + example: Points earned through activity + currentTotal: + type: number + description: The user's current total points. + example: 5000 + changeThisPeriod: + type: number + description: The change in points during the period. + example: 500 + percentChange: + type: number + description: The percentage change in points during the period. + example: 11.1 + percentileThisPeriod: + type: number + description: The user's percentile rank for this points system during the period. Only included for weekly, monthly, and yearly aggregation periods. + example: 88 + required: + - name + - currentTotal + - changeThisPeriod + - percentChange + WrappedStreak: + title: WrappedStreak + type: object + description: The user's longest streak during the wrapped period. + properties: + length: + type: integer + description: The length of the streak. + example: 45 + frequency: + $ref: '#/components/schemas/StreakFrequency' + description: The frequency of the streak. + periodStart: + type: + - string + - 'null' + format: date + description: The start date of the streak period. + example: '2024-02-01' + periodEnd: + type: + - string + - 'null' + format: date + description: The end date of the streak period. + example: '2024-03-17' + started: + type: + - string + - 'null' + format: date + description: The date the streak started. + example: '2024-02-01' + required: + - length + - frequency + - periodStart + - periodEnd + - started + WrappedActivityPeriod: + title: WrappedActivityPeriod + type: object + description: Activity data for a specific period (day, week, month, or year). + properties: + metrics: + type: object + additionalProperties: + $ref: '#/components/schemas/WrappedMetric' + description: The user's metrics during this period, keyed by metric key. + points: + type: object + additionalProperties: + $ref: '#/components/schemas/WrappedPoints' + description: The user's points during this period, keyed by points system key. + achievements: + type: array + items: + $ref: '#/components/schemas/UserAchievementResponse' + description: Achievements completed during this period. + leaderboards: + type: object + additionalProperties: + $ref: '#/components/schemas/UserLeaderboardResponse' + description: The user's best leaderboard rankings during this period, keyed by leaderboard key. + required: + - metrics + - points + - achievements + - leaderboards + WrappedMostActiveDay: + title: WrappedMostActiveDay + type: object + description: The user's most active day during the year. + allOf: + - $ref: '#/components/schemas/WrappedActivityPeriod' + - type: object + properties: + date: + type: string + format: date + description: The date of the most active day in YYYY-MM-DD format. + example: '2024-03-15' + required: + - date + WrappedMostActiveWeek: + title: WrappedMostActiveWeek + type: object + description: The user's most active week during the year. + allOf: + - $ref: '#/components/schemas/WrappedActivityPeriod' + - type: object + properties: + start: + type: string + format: date + description: The start date of the most active week in YYYY-MM-DD format. + example: '2024-03-11' + end: + type: string + format: date + description: The end date of the most active week in YYYY-MM-DD format. + example: '2024-03-17' + required: + - start + - end + WrappedMostActiveMonth: + title: WrappedMostActiveMonth + type: object + description: The user's most active month during the year. + allOf: + - $ref: '#/components/schemas/WrappedActivityPeriod' + - type: object + properties: + month: + type: integer + minimum: 0 + maximum: 11 + description: The month number (0-11, where 0 is January). + example: 2 + required: + - month + WrappedEntireYear: + title: WrappedEntireYear + type: object + description: The user's activity data for the entire year. + allOf: + - $ref: '#/components/schemas/WrappedActivityPeriod' + - type: object + properties: + longestStreak: + $ref: '#/components/schemas/WrappedStreak' + description: The user's longest streak during the year. + required: + - longestStreak + WrappedActivity: + title: WrappedActivity + type: object + description: The user's activity summary for the wrapped year. + properties: + daysActive: + type: integer + description: The number of days the user was active during the year. + example: 156 + weeksActive: + type: integer + description: The number of weeks the user was active during the year. + example: 42 + monthsActive: + type: integer + description: The number of months the user was active during the year. + example: 11 + mostActiveDay: + $ref: '#/components/schemas/WrappedMostActiveDay' + description: Data about the user's most active day. + mostActiveWeek: + $ref: '#/components/schemas/WrappedMostActiveWeek' + description: Data about the user's most active week. + mostActiveMonth: + $ref: '#/components/schemas/WrappedMostActiveMonth' + description: Data about the user's most active month. + entireYear: + $ref: '#/components/schemas/WrappedEntireYear' + description: Data about the user's activity for the entire year. + required: + - daysActive + - weeksActive + - monthsActive + - mostActiveDay + - mostActiveWeek + - mostActiveMonth + - entireYear + WrappedResponse: + title: WrappedResponse + type: object + description: A user's year-in-review wrapped data including activity summaries, metrics, points, achievements, streaks, and leaderboard rankings. + properties: + user: + $ref: '#/components/schemas/User' + description: The user's profile information. + activity: + $ref: '#/components/schemas/WrappedActivity' + description: The user's activity data for the wrapped year. + required: + - user + - activity + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: X-API-KEY +servers: + - x-fern-server-name: Application API + url: https://api.trophy.so/v1 + description: Application API + - x-fern-server-name: Admin API + url: https://admin.trophy.so/v1 + description: Admin API diff --git a/es/api-reference/rate-limiting.mdx b/es/api-reference/rate-limiting.mdx new file mode 100644 index 0000000..5eeaa39 --- /dev/null +++ b/es/api-reference/rate-limiting.mdx @@ -0,0 +1,26 @@ +--- +title: Rate Limiting +description: Learn about the Trophy API and request rate limiting. +"og:description": Learn about the Trophy API and request rate limiting. +icon: circle-gauge +--- + +import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; + +Trophy operates a tiered rate limiting model for each API endpoint. All rate limits are scoped at the organisation level. Exceeding rate limits will result in your application receiving a `429 Too Many Requests` response. + +## Rate Limit Tiers + +| Tier | Rate Limit | +| ---- | ---------- | +| | 10 req/s | +| | 100 req/s | + + + If you feel you need a higher rate limit, then please [contact + us](mailto:support@trophy.so) and we'll be happy to discuss your needs. + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/experimentation/engagement.mdx b/es/experimentation/engagement.mdx new file mode 100644 index 0000000..c0d3adc --- /dev/null +++ b/es/experimentation/engagement.mdx @@ -0,0 +1,56 @@ +--- +title: Engagement +description: Learn how to measure the impact of gamification on engagement +"og:description": Learn how to measure the impact of gamification on engagement +icon: heart +--- + +## What Is Engagement? + +User engagement in Trophy refers to the average level of activity that your users show when using your product. + +As Trophy tracks user interactions through [Metrics](/platform/metrics), it can give you insights into how active your users are against each, and in aggregate. + +User engagement in Trophy is the average total metric event value per daily active user. The formula is: + +``` +sum of all metric event values in a day / number of users active that day +``` + +## Engagement Analytics + +Trophy dashboards for each metric show engagement charts for that metric, and the main Trophy dashboard presents user engagement in aggregate. + + + + + +Trophy also displays charts that show user engagement within users' first days after signing up for your product. + + + + + +We refer to this as 'early engagement' and it is user engagement as measured by Trophy only for users within the time frame set in the 'New User Activation Window' setting on the [integration page](https://app.trophy.so/integration/configure). + + + + + +This is a useful chart for understanding the engagement of users within their very first days with your product and to understand where blockers to initial user activation may be occurring. + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/experimentation/overview.mdx b/es/experimentation/overview.mdx new file mode 100644 index 0000000..50c9027 --- /dev/null +++ b/es/experimentation/overview.mdx @@ -0,0 +1,70 @@ +--- +title: Overview +description: Learn how to use Trophy to experiment with the gamification experience and increase retention and user engagement. +"og:description": Learn how to use Trophy to experiment with the gamification experience and increase retention and user engagement. +--- + +import ControlFlagBlock from "/snippets/control-flag-block.mdx"; + +Trophy provides a core set of primitives to help you ship gamification experiences faster. But gamification is not something you can set and forget. + +A key part of running a gamified platform is experimentation — tweaking and optimizing the user experience to ensure that users keep coming back. + +Usually, you'd have to build these tools yourself, but with Trophy you get a simple experimentation stack out-of-the-box. + + + We have plans to extend Trophy's experimentation capabilities in the future to + support more granular A/B testing of your gamification features. + + +## Control Ratio + +Trophy has a built-in capability to automatically A/B test gamification features using a control ratio. + +Found on the [integration page](https://app.trophy.so/integration/configure) of the Trophy dashboard, the control ratio adjusts the percentage of users that are assigned to the 'control' group versus the 'experimental' group. + + + + + +Trophy returns the `control` attribute as `true` or `false` in most APIs that relate to user data allowing you to conditionally show gamification features to users who are in the experimental group. + +If you plan to measure the overall impact of gamification features, make sure to only show those features to users with `control` set to `false`. + + + + + Trophy also doesn't send any [Emails](/platform/emails) or [Push + Notifications](/platform/push-notifications) to users who are in the control + group. + + +Analytics dashboards then compare retention and user engagement between users in the control and experimental groups allowing you to measure the impact of the features you build with Trophy and adjust mechanics where necessary. + + + + + +## Retention & Engagement + +Both retention and user engagement are important metrics to use when measuring the impact of changes on the user experience. The following sections outline what each means and how it relates to features you build with Trophy. + + + + Learn how to measure the impact of gamification on retention + + + Learn how to measure the impact of gamification on user engagement + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/experimentation/retention.mdx b/es/experimentation/retention.mdx new file mode 100644 index 0000000..9676b68 --- /dev/null +++ b/es/experimentation/retention.mdx @@ -0,0 +1,38 @@ +--- +title: Retention +description: Learn how to measure the impact of gamification on retention +"og:description": Learn how to measure the impact of gamification on retention +icon: refresh-cw +--- + +## What Is Retention? + +User retention is the percentage of users who are still using your product after a certain . + +Common time frames include 7-day, 14-day and 30-day retention with shorter time frames providing information on initial user experience and longer time frames giving insight into long-term product-market fit. + +## Retention Analytics + +Trophy includes a retention chart for each metric on its metric dashboard and an aggregate user retention chart on the main dashboard. + + + + + +The [integration page](https://app.trophy.so/integration/configure) allows you to control the time frame over which you want to measure retention through the 'New User Activation Window' setting. + + + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/getting-started/introduction.mdx b/es/getting-started/introduction.mdx new file mode 100644 index 0000000..8ac325c --- /dev/null +++ b/es/getting-started/introduction.mdx @@ -0,0 +1,45 @@ +--- +title: Welcome to Trophy +description: Trophy documentation for gamification APIs, quickstart guides and tutorials. +--- + +Trophy is a developer-friendly toolkit for building gamified product experiences in any mobile or web app. + +It makes building features like [Achievements](/platform/achievements), [Streaks](/platform/streaks), [Points](/platform/points) and [Leaderboards](/platform/leaderboards) simple with just a few lines of code, and can send gamified [Emails](/platform/emails) and [Push Notifications](/platform/push-notifications) to increase your retention and engagement. + + + + Integrate Trophy into your backend in under 10 minutes. + + + Use Trophy to build practical solutions to common gamification use cases. + + + Learn the key concepts behind building gamification experiences with Trophy. + + + Explore the Trophy API and see what's possible. + + + +## Why Trophy + + + + + +If your goal is for your users to create a regular habit of using your app—to learn something, publish content, or even exercise—gamification could be for you. + +It's been proven by the likes of Duolingo and others to predictably boost retention and increase platform engagement. + +But building gamification at scale can be tricky. You have a global userbase, all in different timezones, all with different usage patterns that need to be measured and optimized in realtime. Plus, you want to run experiments to test different approaches and see how they impact retention, but this requires constant code changes, which holds up work on your core product. + +Trophy takes away the legwork involved in building features like achievements, streaks, points systems and leaderboards while providing reliable, scalable APIs for shipping gamification experiences faster than building in-house. Plus, it provides a web platform to easily configure, measure and experiment with the product experience without constant code changes. + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/getting-started/quickstart.mdx b/es/getting-started/quickstart.mdx new file mode 100644 index 0000000..5060ac2 --- /dev/null +++ b/es/getting-started/quickstart.mdx @@ -0,0 +1,127 @@ +--- +title: Quick Start +description: Get up and running with Trophy in under 10 minutes +"og:description": Follow this quick start guide to integrate Trophy into your backend and add gamified features like achievements and streaks into your app. +--- + +import SdkInstallCommand from "/snippets/sdk-install-command.mdx"; +import AddEnvVarBlock from "/snippets/add-env-var-block.mdx"; +import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; +import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; + +Here you'll integrate your backend web application with Trophy and start building your first gamified feature. + + + + First, [create a new account](https://app.trophy.so/sign-up?utm_source=docs&utm_medium=quickstart) if you don't already have one and head into the [Trophy dashboard](https://app.trophy.so). + + Head through onboarding to get your account set up. + + + + We have SDK libraries available in most major programming languages but if you don't see yours listed, let us know and we'll make one! + + + + Alternatively, you can directly call the API using any server-side HTTP library. + + + + Add your API key that you created during onboarding (or [create a new one](https://app.trophy.so/integration)) as an environment variable in your backend application: + + + + Make sure to pass this API key to the Trophy SDK or to your API client to authenticate. + + + + All gamification features are driven by user interactions. In Trophy, you use [Metrics](/platform/metrics) to define and model those interactions and [Events](/platform/events) to track them. + + Here you'll create your first metric to get started. In the Trophy dashboard, head into the [metrics page](https://app.trophy.so/metrics) and hit the _New Metric_ button: + + + + + + Give the metric a name and hit _Save_. + + + + Once you've created your metric, head to the configure tab and copy it's unique API reference key. + + + + + + To track an event against this metric when a user interacts with your product, call the [metric change event API](/api-reference/endpoints/metrics/send-a-metric-change-event), passing along details of the user that made the interaction. In this example the metric key would be `flashcards-flipped`: + + + + By making this call, you're telling Trophy that a specific user made an interaction with your product. As a result, Trophy will process any gamification features like achievements or streaks that you've configured against the metric automatically. + + + + With a metric integrated into your backend, you're ready to start adding gamification features to your product. + + Follow the links below to learn more about each feature you can build with Trophy: + + + + Reward users for continued progress or taking specific actions. + + + Motivate users to build regular usage habits. + + + Build sophisticated points systems to reward and retain users. + + + + + } + href="/platform/leaderboards"> + Create friendly competitions to increase user engagement. + + + Deliver personalized lifecycle emails to users at the perfect moment. + + + Drive automated notification flows using personalized gamification data. + + + + Or, explore our [API reference](/api-reference/introduction) to get familiar with what Trophy can do. + + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/guides/gamified-fitness-platform.mdx b/es/guides/gamified-fitness-platform.mdx new file mode 100644 index 0000000..05be787 --- /dev/null +++ b/es/guides/gamified-fitness-platform.mdx @@ -0,0 +1,1227 @@ +--- +title: How to Build a Gamified Fitness Tracker +description: Learn how to build a Strava-like fitness app with Next.js, featuring multi-sport tracking, city-based leaderboards, and normalized XP progression. +subtitle: Build a high-retention fitness app with streaks and leaderboards. +--- + +In this tutorial, we'll build **TrophyFitness**, a consumer fitness application that tracks running, cycling, and swimming. We'll implement a complete gamification loop including weekly leaderboards, habit-forming streaks, and a leveled progression system. + +If you want to skip straight to the code, check out the [example repository](https://github.com/trophyso/example-fitness-platform) or the [live demo](https://fitness.examples.trophy.so). + + + + + +## Table of Contents + +- [Tech Stack](#tech-stack) +- [Prerequisites](#prerequisites) +- [Setup & Installation](#setup--installation) +- [Designing the Data Model](#designing-the-data-model) +- [How Trophy Works](#how-trophy-works) +- [Setting Up Trophy](#setting-up-trophy) +- [Server Actions](#server-actions) +- [The Leveling System](#the-leveling-system) +- [Building the Dashboard](#building-the-dashboard) +- [Logging Workouts](#logging-workouts) +- [Implementing Leaderboards](#implementing-leaderboards) +- [Building the Achievements Page](#building-the-achievements-page) +- [Building the Profile Page](#building-the-profile-page) +- [The Result](#the-result) + +## Tech Stack + +- **Framework:** [Next.js 15](https://nextjs.org) (App Router) +- **UI:** [Shadcn/UI](https://ui.shadcn.com) + TailwindCSS +- **Icons:** [Lucide React](https://lucide.dev) +- **Gamification:** [Trophy](https://trophy.so) + +## Prerequisites + +- A Trophy account (sign up [here](https://app.trophy.so/sign-up)). +- Node.js 18+ installed. + +## Setup & Installation + +First, clone the starter repository or create a new Next.js app: + +```bash +npx create-next-app@latest trophy-fitness +``` + +Install the required dependencies: + +```bash +npm install @trophyso/node lucide-react clsx tailwind-merge +``` + +Configure your environment variables in `.env.local`: + +```bash +TROPHY_API_KEY=your_api_key_here +``` + +## Designing the Data Model + +For a multi-sport fitness app, we need to normalize efforts. A 10km cycle is not the same as a 10km run. We'll use three distinct metrics to track raw data, and a unified XP system for progression. + +### 1. The Metrics + +We will track distance as the primary value. + +- `distance_run` (km) - with `pace` attribute (walk/run). +- `distance_cycled` (km) +- `distance_swum` (m) - with `style` attribute (freestyle/breaststroke). + +### 2. The Attributes + +To enable local leaderboards, we'll tag every user with a `city` attribute. + +## How Trophy Works + +Before diving into the code, let's understand how Trophy powers our gamification layer. In Trophy, [Metrics](/platform/metrics) represent different interactions users can make and drive features like [Achievements](/platform/achievements), [Streaks](/platform/streaks), and [Emails](/platform/emails). + +```mermaid +flowchart TD + A@{ shape: rounded, label: "Events" } + B@{ shape: rounded, label: "Metrics" } + C@{ shape: rounded, label: "Achievements" } + D@{ shape: rounded, label: "Streaks" } + E@{ shape: rounded, label: "Leaderboards" } + F@{ shape: rounded, label: "Points" } + G@{ shape: rounded, label: "Emails" } + A-.->B + B-->C + B-->D + B-->E + B-->F + B-->G +``` + +When events are recorded for a specific user, any achievements linked to the specified metric will be unlocked if the requirements are met, streaks will be automatically calculated, leaderboards will update, and any configured emails will be scheduled. + +This is what makes building gamified experiences with Trophy so powerful—it does all the work behind the scenes. + +## Setting Up Trophy + +Here's how we'll configure Trophy for our fitness app: + + + + Head into the Trophy [metrics page](https://app.trophy.so/metrics) and create three metrics: + + - `distance_run` — Total kilometers run + - `distance_cycled` — Total kilometers cycled + - `distance_swum` — Total meters swum + + These keys are what we'll reference in our code when sending events. + + + + + + + + + While still on the [metrics page](https://app.trophy.so/metrics), set up + the **pace** and **style** event attributes: + + + + + + Then, go to the [user attributes page](https://app.trophy.so/users/attributes) + and create the **city** user atribute: + + + + + + + + + Head into the Trophy [achievements page](https://app.trophy.so/achievements) + and create milestone achievements for each sport. For example: + + **Running:** + + - First 5K (5km total) + - Half Marathon Hero (21.1km total) + - Marathon Master (42.2km total) + + **Cycling:** + + - Century Rider (100km total) + - Tour Stage (200km total) + + **Swimming:** + - Pool Regular (1000m total) + - Open Water Ready (5000m total) + + Link each achievement to the appropriate metric. + + + + + + + + Head to the [leaderboards page](https://app.trophy.so/leaderboards) and set up + weekly leaderboards to drive competition. Each leaderboard should be + configured with **Repetition Unit: Days** and **Repetition Interval: 7** to + repeat weekly. + + + + + + **Global leaderboards:** + + - `weekly-distance-run` + - `weekly-distance-cycled` + - `weekly-distance-swum` + + **City-based leaderboards** (breakdown by user attribute **city**): + + - `weekly-distance-run-cities` + - `weekly-distance-cycled-cities` + - `weekly-distance-swum-cities` + + + + + + + + Head to the [points page](https://app.trophy.so/points) and create a points + system called `xp` that awards points based on activity: + + - Running: 10 XP per km + - Cycling: 3 XP per km + - Swimming: 10 XP per 100m + + This normalized approach ensures fair progression across sports. + + + + + + Then configure **[Points Levels](/platform/points#points-levels)** on the same `xp` system so Trophy can track each user's tier. Use the keys and thresholds below so the code in this guide lines up with your dashboard: + + | Name | Key | Points threshold | + | ------- | --------- | ---------------- | + | Rookie | `rookie` | 0 | + | Active | `active` | 100 | + | Mover | `mover` | 500 | + | Athlete | `athlete` | 2500 | + | Pro | `pro` | 10000 | + + + + Head to the [streaks page](https://app.trophy.so/streaks) and configure a + daily streak linked to any of the distance metrics. Users will maintain their + streak by logging at least one activity per day. + + + + Head to the [emails page](https://app.trophy.so/emails) and add and activate templates for these automated engagement emails: + + - **Achievement unlocked** — Celebrate new badges + - **Streak at risk** — Remind users before they lose their streak + - **Weekly recap** — Summary of progress and leaderboard position + + Each email type has its own section—add a template under each and activate it to start sending. Configure your branding in the [branding page](https://app.trophy.so/branding) for professional emails. + + + +## Server Actions + +We'll create a `src/app/actions.ts` file to handle all interactions with the Trophy API. This keeps our API keys secure and allows us to leverage Next.js Server Actions. + +```tsx src/app/actions.ts [expandable] +"use server"; + +import { TrophyApiClient, TrophyApi } from "@trophyso/node"; +import { revalidatePath } from "next/cache"; + +const trophy = new TrophyApiClient({ + apiKey: process.env.TROPHY_API_KEY as string, +}); + +export async function identifyUser(userId: string, name?: string, tz?: string) { + try { + const user = await trophy.users.identify(userId, { name, tz }); + return { success: true, user }; + } catch (error) { + return { success: false, error: "Failed to identify user" }; + } +} + +export async function updateUserCity(userId: string, city: string) { + try { + await trophy.users.update(userId, { attributes: { city } }); + revalidatePath("/leaderboards"); + revalidatePath("/profile"); + return { success: true }; + } catch (error) { + return { success: false, error: "Failed to update city" }; + } +} + +export async function getUserStats(userId: string) { + try { + // Fetch all user data in parallel + const [streak, achievements, metrics, pointsResponse, pointsLevels] = + await Promise.all([ + trophy.users.streak(userId).catch(() => null), + trophy.users + .achievements(userId, { includeIncomplete: "true" }) + .catch(() => []), + trophy.users.allMetrics(userId).catch(() => []), + trophy.users.points(userId, "xp").catch(() => null), + trophy.points.levels("xp").catch(() => []), + ]); + + return { + streak, + achievements, + points: pointsResponse, + pointsLevels, + metrics, + }; + } catch (error) { + console.error("Failed to fetch user stats:", error); + return null; + } +} + +export async function logActivity(params: { + type: "run" | "cycle" | "swim"; + distance: number; + userId: string; + city?: string; + pace?: string; + style?: string; +}) { + const { type, distance, userId, city, pace, style } = params; + + let metricKey = ""; + const eventAttributes: Record = {}; + + switch (type) { + case "run": + metricKey = "distance_run"; + if (pace) eventAttributes.pace = pace; + break; + case "cycle": + metricKey = "distance_cycled"; + break; + case "swim": + metricKey = "distance_swum"; + if (style) eventAttributes.style = style; + break; + } + + try { + // Log the event + const response = await trophy.metrics.event(metricKey, { + user: { + id: userId, + ...(city ? { attributes: { city } } : {}), + }, + value: distance, + ...(Object.keys(eventAttributes).length > 0 + ? { attributes: eventAttributes } + : {}), + }); + + revalidatePath("/"); + revalidatePath("/leaderboards"); + revalidatePath("/profile"); + + return { success: true, data: response }; + } catch (error) { + console.error("Failed to log activity:", error); + return { success: false, error: "Failed to log activity" }; + } +} + +export async function getLeaderboard(leaderboardKey: string, city?: string) { + try { + const response = await trophy.leaderboards.get(leaderboardKey, { + userAttributes: city ? `city:${city}` : undefined, + }); + return response.rankings || []; + } catch (error) { + return []; + } +} + +export async function getRecentActivities(userId: string) { + // Fetch daily summaries for the last 30 days + const endDate = new Date().toISOString().split("T")[0]; + const startDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) + .toISOString() + .split("T")[0]; + + const metrics = [ + { key: "distance_run", type: "run", unit: "km" }, + { key: "distance_cycled", type: "cycle", unit: "km" }, + { key: "distance_swum", type: "swim", unit: "m" }, + ]; + + try { + const summaries = await Promise.all( + metrics.map(async (metric) => { + try { + const data = await trophy.users.metricEventSummary( + userId, + metric.key, + { + aggregation: "daily", + startDate, + endDate, + }, + ); + return data + .filter((item) => item.change > 0) + .map((item) => ({ + id: `${metric.key}-${item.date}`, + type: metric.type, + value: item.change, + unit: metric.unit, + date: item.date, + })); + } catch { + return []; + } + }), + ); + + return summaries + .flat() + .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) + .slice(0, 5); + } catch { + return []; + } +} + +// Helper to get User ID from cookies +export async function getUserIdFromCookies() { + const { cookies } = await import("next/headers"); + const cookieStore = await cookies(); + return cookieStore.get("trophy-fitness-user-id")?.value ?? null; +} +``` + +## The Leveling System + +Trophy stores each user's tier on the `xp` points system when you configure [Points Levels](/platform/points#points-levels). The [user points](/api-reference/endpoints/users/get-a-users-points) payload includes a `level` object. The [list levels API](/api-reference/endpoints/points/get-points-levels) returns every tier and threshold so you can show progress toward the next one. + +Add a small helper next to your app constants in `src/lib/constants.ts`: + +```tsx src/lib/constants.ts +export type ActivityType = "run" | "cycle" | "swim"; + +/** Shape of a level from Trophy (`GET /points/{key}/levels`). */ +export type TrophyPointsLevel = { + id: string; + key: string; + name: string; + description: string | null; + badgeUrl: string | null; + points: number; +}; + +/** + * Derive progress from Trophy totals + levels. Prefer `currentLevel` from the user + * points API when present; otherwise infer from `total` and threshold order. + */ +export function getLevelProgress( + total: number, + levels: TrophyPointsLevel[], + currentLevel: TrophyPointsLevel | null | undefined, +) { + const sorted = [...levels].sort((a, b) => a.points - b.points); + if (sorted.length === 0) { + return { + currentLevel: null as TrophyPointsLevel | null, + tierNumber: null as number | null, + nextLevel: null as TrophyPointsLevel | null, + progressToNextLevel: 0, + xpToNext: null as number | null, + }; + } + + let current = currentLevel ?? null; + if (!current) { + for (let i = sorted.length - 1; i >= 0; i--) { + if (total >= sorted[i].points) { + current = sorted[i]; + break; + } + } + } + + const idx = current != null ? sorted.findIndex((l) => l.key === current.key) : -1; + const tierNumber = idx >= 0 ? idx + 1 : null; + const nextLevel = + idx >= 0 && idx < sorted.length - 1 ? sorted[idx + 1] : null; + + let progressToNextLevel = 100; + let xpToNext: number | null = null; + + if (nextLevel != null && current != null) { + const span = nextLevel.points - current.points; + const into = total - current.points; + progressToNextLevel = + span > 0 ? Math.min(100, Math.max(0, (into / span) * 100)) : 100; + xpToNext = Math.max(0, nextLevel.points - total); + } else if (nextLevel != null && current == null) { + const span = nextLevel.points; + progressToNextLevel = + span > 0 ? Math.min(100, Math.max(0, (total / span) * 100)) : 0; + xpToNext = Math.max(0, nextLevel.points - total); + } + + return { + currentLevel: current, + tierNumber, + nextLevel, + progressToNextLevel, + xpToNext, + }; +} +``` + +## Building the Dashboard + +The dashboard aggregates all user stats. We fetch data server-side and derive progress bars from thresholds returned by the API. + +```tsx src/app/page.tsx [expandable] +import { + getUserStats, + getUserIdFromCookies, + getRecentActivities, +} from "./actions"; +import { getLevelProgress } from "@/lib/constants"; +import { + Zap, + Flame, + Footprints, + Bike, + Waves, + Trophy, + TrendingUp, +} from "lucide-react"; +import { Card, CardContent } from "@/components/ui/card"; +import { Progress } from "@/components/ui/progress"; +import { Button } from "@/components/ui/button"; +import { LogActivityDialog } from "@/components/log-activity-dialog"; + +export default async function Dashboard() { + const userId = await getUserIdFromCookies(); + const [stats, recentActivities] = await Promise.all([ + getUserStats(userId ?? ""), + getRecentActivities(userId ?? ""), + ]); + + const streakLength = stats?.streak?.length ?? 0; + const totalXP = stats?.points?.total ?? 0; + const levelProgress = getLevelProgress( + totalXP, + stats?.pointsLevels ?? [], + stats?.points?.level, + ); + + // Helper to get total for a metric key + const getMetricTotal = (key: string) => + stats?.metrics?.find((m) => m.key === key)?.current ?? 0; + + // Find the next badge to earn + const nextAchievement = stats?.achievements?.find((a) => !a.achievedAt); + + return ( +
+ {/* Level & Streak Header */} +
+
+
+
+ +
+
+
+ {levelProgress.tierNumber != null + ? `Level ${levelProgress.tierNumber}` + : "Level"} +
+
+ {levelProgress.currentLevel?.name ?? "XP"} +
+
+
+ +
+ {totalXP} XP + {levelProgress.nextLevel != null && levelProgress.xpToNext != null && ( + + {levelProgress.xpToNext} XP to {levelProgress.nextLevel.name} + + )} +
+
+ +
+ 0 ? "text-orange-500" : "text-muted-foreground"}`} + /> + + {streakLength} + + + day streak + +
+
+ + {/* Stats Grid */} +
+ + + +
+ {getMetricTotal("distance_run").toFixed(1)} +
+
km run
+
+
+ {/* Repeat for Cycle and Swim... */} +
+ + {/* Next Badge Teaser */} + {nextAchievement && ( + + +
+ +
+
+
+ Next Badge +
+

{nextAchievement.name}

+

+ {nextAchievement.description} +

+
+ + + +
+
+ )} +
+ ); +} +``` + +Note the `LogActivityDialog` wrapper around the button—we'll build that next. + +## Logging Workouts + +The Log Workout button opens a dialog where users select their activity type, enter distance, and submit. This triggers the `logActivity` server action which sends the event to Trophy. + +First, install the required shadcn/ui components: + +```bash +npx shadcn@latest add dialog tabs input label select +``` + +Then create the dialog component: + +```tsx components/log-activity-dialog.tsx [expandable] +"use client"; + +import { useState, useTransition } from "react"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, + DialogDescription, +} from "@/components/ui/dialog"; +import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Footprints, Bike, Waves, Loader2, Zap } from "lucide-react"; +import { toast } from "sonner"; +import { logActivity } from "@/app/actions"; +import { ActivityType } from "@/lib/constants"; +import { getUserCity } from "@/lib/city"; +import { useUser } from "@/components/user-provider"; + +export function LogActivityDialog({ children }: { children: React.ReactNode }) { + const { userId } = useUser(); + const [open, setOpen] = useState(false); + const [isPending, startTransition] = useTransition(); + const [activeTab, setActiveTab] = useState("run"); + const [distance, setDistance] = useState(""); + const [pace, setPace] = useState("run"); + const [style, setStyle] = useState("freestyle"); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!userId) { + toast.error("User not initialized"); + return; + } + + const distNum = parseFloat(distance); + if (!distNum || distNum <= 0) { + toast.error("Please enter a valid distance"); + return; + } + + const city = getUserCity(); + + startTransition(async () => { + const result = await logActivity({ + userId, + type: activeTab, + distance: distNum, + city, + pace: activeTab === "run" ? pace : undefined, + style: activeTab === "swim" ? style : undefined, + }); + + if (result.success) { + toast.success("Workout Saved!", { + description: `Logged ${distNum} ${activeTab === "swim" ? "m" : "km"} ${activeTab}.`, + }); + setOpen(false); + setDistance(""); + } else { + toast.error("Failed to save workout"); + } + }); + }; + + return ( + + {children} + + + Log Workout + + Record your workout to earn XP and climb the leaderboard. + + + + setActiveTab(v as ActivityType)}> + + Run + Cycle + Swim + + +
+
+ + setDistance(e.target.value)} + required + /> +
+ + {activeTab === "run" && ( +
+ + +
+ )} + + {activeTab === "swim" && ( +
+ + +
+ )} + + +
+
+
+
+ ); +} +``` + +### User Context + +The dialog needs access to the current user ID. Create a context provider: + +```tsx components/user-provider.tsx [expandable] +"use client"; + +import { createContext, useContext, useEffect, useState, ReactNode } from "react"; +import { getUserId, getUserName } from "@/lib/user"; +import { identifyUser } from "@/app/actions"; + +interface UserContextType { + userId: string | null; + userName: string | null; + isLoading: boolean; +} + +const UserContext = createContext({ + userId: null, + userName: null, + isLoading: true, +}); + +export function useUser() { + return useContext(UserContext); +} + +export function UserProvider({ children }: { children: ReactNode }) { + const [userId, setUserId] = useState(null); + const [userName, setUserName] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const initUser = async () => { + const id = getUserId(); + const name = getUserName(); + const tz = Intl.DateTimeFormat().resolvedOptions().timeZone; + + setUserId(id); + setUserName(name); + + await identifyUser(id, name ?? undefined, tz); + setIsLoading(false); + }; + initUser(); + }, []); + + return ( + + {children} + + ); +} +``` + +Wrap your app with the provider in `layout.tsx`: + +```tsx app/layout.tsx +import { UserProvider } from "@/components/user-provider"; + +export default function RootLayout({ children }) { + return ( + + + + {children} + + + + ); +} +``` + +### Helper Utilities + +The user context relies on helper functions for managing user identity in localStorage: + +```tsx lib/user.ts [expandable] +const USER_ID_KEY = "trophy-fitness-user-id"; +const USER_NAME_KEY = "trophy-fitness-user-name"; + +const ADJECTIVES = ["Swift", "Mighty", "Blazing", "Iron", "Golden", "Thunder", "Lightning"]; +const NOUNS = ["Runner", "Cyclist", "Swimmer", "Athlete", "Champion", "Tiger", "Eagle"]; + +function generateRandomName(): string { + const adj = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)]; + const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)]; + return `${adj}${noun}${Math.floor(Math.random() * 100)}`; +} + +export function getUserId(): string { + let userId = localStorage.getItem(USER_ID_KEY); + if (!userId) { + userId = crypto.randomUUID(); + localStorage.setItem(USER_ID_KEY, userId); + localStorage.setItem(USER_NAME_KEY, generateRandomName()); + } + // Sync to cookie for server-side access + document.cookie = `${USER_ID_KEY}=${userId}; path=/; max-age=31536000; SameSite=Lax`; + return userId; +} + +export function getUserName(): string | null { + return localStorage.getItem(USER_NAME_KEY); +} +``` + +For city-based leaderboards, we infer the user's city from their timezone: + +```tsx lib/city.ts [expandable] +const TIMEZONE_TO_CITY: Record = { + "America/New_York": "New York", + "America/Los_Angeles": "Los Angeles", + "Europe/London": "London", + "Europe/Paris": "Paris", + "Europe/Berlin": "Berlin", + "Asia/Tokyo": "Tokyo", + "Australia/Sydney": "Sydney", + // ... add more as needed +}; + +const STORAGE_KEY = "trophy-fitness-city"; + +export function getUserCity(): string { + const stored = localStorage.getItem(STORAGE_KEY); + if (stored) return stored; + + const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; + return TIMEZONE_TO_CITY[timezone] || "London"; +} + +export function setStoredCity(city: string): void { + localStorage.setItem(STORAGE_KEY, city); +} +``` + +## Implementing Leaderboards + +We'll build a tabbed interface that allows users to switch between activities (Run/Cycle/Swim) and scopes (Global vs. Local City). + +```tsx src/app/leaderboards/page.tsx [expandable] +"use client"; + +import { useState, useEffect, useMemo } from "react"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { getLeaderboard } from "@/app/actions"; +import { Trophy, Globe, MapPin } from "lucide-react"; +import { getUserCity } from "@/lib/city"; + +export default function LeaderboardsPage() { + const [scope, setScope] = useState<"global" | "city">("global"); + const [activeTab, setActiveTab] = useState("run"); + const [data, setData] = useState([]); + + // Get user's city from localStorage + const city = useMemo(() => getUserCity(), []); + + useEffect(() => { + // Determine the leaderboard key based on tab and scope + const baseKey = `weekly-distance-${activeTab}`; + const key = scope === "city" ? `${baseKey}-cities` : baseKey; + const cityParam = scope === "city" ? city : undefined; + + getLeaderboard(key, cityParam).then(setData); + }, [scope, activeTab, city]); + + return ( +
+
+

+ Leaderboards +

+ + {/* Scope Toggle */} +
+ + +
+
+ + + + Run + Cycle + Swim + + + + {data.map((entry, index) => ( +
+
{index + 1}
+
+ {entry.userName || "Anonymous"} +
+
+ {entry.value} {activeTab === "swim" ? "m" : "km"} +
+
+ ))} +
+
+
+ ); +} +``` + +## Building the Achievements Page + +A dedicated space to show off badges is essential for long-term retention. We'll use the `stats.achievements` data to render a grid of badges, visually distinguishing between earned (colorful) and locked (grayscale) states. + +```tsx src/app/achievements/page.tsx [expandable] +import { getUserStats, getUserIdFromCookies } from "@/app/actions"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Card, CardContent } from "@/components/ui/card"; +import { Award, Lock } from "lucide-react"; + +export default async function AchievementsPage() { + const userId = await getUserIdFromCookies(); + const stats = await getUserStats(userId ?? ""); + + // Helper to split achievements + const earned = stats?.achievements?.filter((a) => a.achievedAt) ?? []; + const locked = stats?.achievements?.filter((a) => !a.achievedAt) ?? []; + + const AchievementCard = ({ achievement, isEarned }) => ( + + +
+ {isEarned ? ( + + ) : ( + + )} +
+
{achievement.name}
+
+ {achievement.description} +
+
+
+ ); + + return ( +
+

+ Achievements +

+ + + + All + Earned + Locked + + + + {[...earned, ...locked].map((a) => ( + + ))} + + {/* Repeat grid for "earned" and "locked" tabs... */} + +
+ ); +} +``` + +## Building the Profile Page + +Finally, the profile page brings it all together. It shows the user's "Lifetime Stats," their current XP progression, and allows them to update their settings (like their city). + +```tsx src/app/profile/page.tsx [expandable] +import { getUserStats, getUserIdFromCookies } from "@/app/actions"; +import { getLevelProgress } from "@/lib/constants"; +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { Card, CardContent } from "@/components/ui/card"; +import { Progress } from "@/components/ui/progress"; +import { CitySetting } from "@/components/city-setting"; + +export default async function ProfilePage() { + const userId = await getUserIdFromCookies(); + const stats = await getUserStats(userId ?? ""); + const levelProgress = getLevelProgress( + stats?.points?.total ?? 0, + stats?.pointsLevels ?? [], + stats?.points?.level, + ); + + // Helper to safely get metric totals + const getTotal = (key: string) => + stats?.metrics?.find((m) => m.key === key)?.current ?? 0; + + return ( +
+ {/* Header */} +
+ + + ME + + +

Athlete Profile

+
+ {levelProgress.currentLevel?.name ?? "Athlete"} +
+
+ + {/* Progress Card */} + + +
+ + Experience Points + + + {stats?.points?.total ?? 0} XP + +
+ +
+
+ + {/* Lifetime Stats */} +
+ + +
+ {getTotal("distance_run").toFixed(1)} +
+
km run
+
+
+ {/* Repeat for other sports... */} +
+ + {/* Settings Component (Client Component) */} + + + + + +
+ ); +} +``` + +## The Result + +You now have a fully functional fitness gamification loop! Users can log workouts across multiple sports, progress through points levels, earn badges, and compete on leaderboards. + + + + + +### What You've Built + +- **Multi-sport tracking** with normalized XP across running, cycling, and swimming +- **Weekly leaderboards** with global and city-based competition +- **Leveled progression** from Rookie to Pro +- **Achievement system** with milestone badges +- **Daily streaks** to drive retention + +### Next Steps + +- **Connect fitness wearables** — Integrate with Strava, Apple Health, or Google Fit to auto-log workouts +- **Add social features** — Let users follow friends, compare stats, and share achievements +- **Build a mobile experience** — Convert to a PWA or wrap with Capacitor for app store distribution +- **Add push notifications** — Alert users when their streak is at risk or they've been passed on the leaderboard diff --git a/es/guides/gamified-study-platform.mdx b/es/guides/gamified-study-platform.mdx new file mode 100644 index 0000000..cd25818 --- /dev/null +++ b/es/guides/gamified-study-platform.mdx @@ -0,0 +1,2314 @@ +--- +title: How to Build a Gamified Study Platform +description: Follow along as we build out a gamified study platform using Trophy with achievements, a daily streak, XP and leaderboards. +subtitle: Follow along as we build out a gamified study platform using Trophy. +noindex: true +--- + +import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; +import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; + +In this tutorial we'll build an example study platform using Trophy for gamification. If you want to just skip to the end then feel free to check out the [example repository](https://github.com/trophyso/example-study-platform) or the [live demo](https://study.examples.trophy.so). + + + + + +## Table of Contents + +- [Tech Stack](#tech-stack) +- [Pre-requisites](#pre-requisites) +- [Setup & Installation](#setup-%26-installation) +- [Building the Flashcards Feature](#building-the-flashcards-feature) +- [Adding Basic Gamification](#adding-basic-gamification) +- [Adding an XP System](#adding-an-xp-system) +- [Adding Leaderboards](#adding-leaderboards) +- [Adding an Energy System](#adding-an-energy-system) +- [The Result](#the-result) + +## Tech Stack + +- [NextJS 15](https://nextjs.org/docs) (React 19) +- [Shadcn/Ui](https://ui.shadcn.com) +- [Lucide](https://lucide.dev/icons) for iconography +- [Motion](https://motion.dev/) for animations +- [HTML5 Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) for sound effects +- [Trophy](https://trophy.so) for gamification + +## Pre-requisites + +- A Trophy account (don't worry we'll set this up as we go along...) + +## Setup & Installation + + + Want to skip the setup? Head straight to [the fun + part](#setting-up-flashcard-data). + + +First we need to create a new NextJS project: + +```bash +npx create-next-app@latest +``` + +Feel free to configure this new project however you like but for the purposes of this tutorial we'll pretty much stick with the defaults: + +```bash +What is your project named? my-app +Would you like to use TypeScript? Yes +Would you like to use ESLint? Yes +Would you like to use Tailwind CSS? Yes +Would you like your code inside a `src/` directory? Yes +Would you like to use App Router? (recommended) Yes +Would you like to use Turbopack for `next dev`? Yes +Would you like to customize the import alias (`@/*` by default)? No +``` + +Next, we'll initialize a new install of everyone's favourite UI library, shadcn/ui: + +```bash +npx shadcn@latest init +``` + +I ran into a warning with React 19, which [looks to be a common issue](https://ui.shadcn.com/docs/react-19) when initializing with `npm`: + +```bash +It looks like you are using React 19. +Some packages may fail to install due to peer dependency issues in npm (see https://ui.shadcn.com/react-19). + +? How would you like to proceed? › - Use arrow-keys. Return to submit. +❯ Use --force + Use --legacy-peer-deps +``` + +For the purposes of this tutorial I chose `--force` but you should choose whichever setting you feel suits your requirements. + +## Building the Flashcards Feature + +### Setting Up Flashcard Data + +For the purposes of this tutorial, we're going to be using some simple types with an in-memory data store. In a production application you'd probably want to consider storing this information in a database. + +Here we'll have a very simple type that stores information about each flashcard where we'll use the `front` property to store questions that the student wants to learn the answer to, and the `back` property to store the answers to each question: + +```ts src/types/flashcard.ts +export interface IFlashcard { + id: string; + front: string; + back: string; +} +``` + +Then to get us started we'll store a few flashcards in memory centered around learning capital cities: + +```ts src/data.ts [expandable] +import { IFlashcard } from "./types/flashcard"; + +export const flashcards: IFlashcard[] = [ + { + id: "1", + front: "What is the capital of France?", + back: "Paris", + }, + { + id: "2", + front: "What is the capital of Germany?", + back: "Berlin", + }, + { + id: "3", + front: "What is the capital of Italy?", + back: "Rome", + }, + { + id: "4", + front: "What is the capital of Spain?", + back: "Madrid", + }, + { + id: "5", + front: "What is the capital of Portugal?", + back: "Lisbon", + }, + { + id: "6", + front: "What is the capital of Greece?", + back: "Athens", + }, + { + id: "7", + front: "What is the capital of Turkey?", + back: "Ankara", + }, + { + id: "8", + front: "What is the capital of Poland?", + back: "Warsaw", + }, + { + id: "9", + front: "What is the capital of Romania?", + back: "Bucharest", + }, + { + id: "10", + front: "What is the capital of Bulgaria?", + back: "Sofia", + }, + { + id: "11", + front: "What is the capital of Hungary?", + back: "Budapest", + }, + { + id: "12", + front: "What is the capital of Czechia?", + back: "Prague", + }, + { + id: "13", + front: "What is the capital of Slovakia?", + back: "Bratislava", + }, + { + id: "14", + front: "What is the capital of Croatia?", + back: "Zagreb", + }, + { + id: "15", + front: "What is the capital of Serbia?", + back: "Belgrade", + }, + { + id: "16", + front: "What is the capital of Montenegro?", + back: "Podgorica", + }, + { + id: "17", + front: "What is the capital of North Macedonia?", + back: "Skopje", + }, + { + id: "18", + front: "What is the capital of Kosovo?", + back: "Pristina", + }, + { + id: "19", + front: "What is the capital of Albania?", + back: "Tirana", + }, + { + id: "20", + front: "What is the capital of Bosnia and Herzegovina?", + back: "Sarajevo", + }, +]; +``` + +### Basic Flashcard Layout + +With some basic data set up, we need to add a way for users to flick through their flashcards. + +For this we'll use the `carousel` and `card` components from shadcn/ui so we need to add these to our project: + +```bash +npx shadcn@latest add carousel card +``` + +Then, we'll create a new `` component that combines these into a working solution, specifying that we can pass along any list of `IFlashcard` objects as props + +```tsx src/app/flashcards.tsx [expandable] +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselPrevious, + CarouselNext, +} from "@/components/ui/carousel"; +import { Card, CardContent } from "@/components/ui/card"; +import { IFlashcard } from "@/types/flashcard"; + +interface Props { + flashcards: IFlashcard[]; +} + +export default function Flashcards({ flashcards }: Props) { + return ( + + + {flashcards.map((flashcard) => ( + +
+ + + + {flashcard.front} + + + +
+
+ ))} +
+ + +
+ ); +} +``` + +Then we'll update our `page.tsx` file to display our `` component, passing in our example flashcard data: + +```tsx src/app/page.tsx +import { flashcards } from "@/data"; +import Flashcards from "./flashcards"; + +export default function Home() { + return ( +
+ +
+ ); +} +``` + +At the end of this step, you should have a working flashcard UI that allows you to flick through each flashcard in our cities data set. + + + + + +### Flipping Flashcards + +Now this is great, but it's not much use as a study app right now as there's no way to see if you got the answer right! We need to add a way to flip flashcards over and check our answer... + +To make this simpler, we'll first create a `` component that will be responsible for all the logic for each flashcard: + +```tsx src/app/flashcard.tsx [expandable] +import { Card, CardContent } from "@/components/ui/card"; +import { CarouselItem } from "@/components/ui/carousel"; +import { IFlashcard } from "@/types/flashcard"; + +interface Props { + flashcard: IFlashcard; +} + +export default function Flashcard({ flashcard }: Props) { + return ( + +
+ + + + {flashcard.front} + + + +
+
+ ); +} +``` + +Then we'll simplify our `` component to instead just render out a list of the individual `` components: + +```tsx src/app/flashcards.tsx [expandable] +import { + Carousel, + CarouselContent, + CarouselPrevious, + CarouselNext, +} from "@/components/ui/carousel"; +import { IFlashcard } from "@/types/flashcard"; +import Flashcard from "./flashcard"; + +interface Props { + flashcards: IFlashcard[]; +} + +export default function Flashcards({ flashcards }: Props) { + return ( + + + {flashcards.map((flashcard) => ( + + ))} + + + + + ); +} +``` + +Now we're ready to add interactivity to each flashcard. Here's what we'll do: + +- First, we'll add a `side` state variable that will hold the current side of the flashcard that's showing. +- Next, we'll add an `onClick()` callback to the `` component that will update the `side` state to `back` when clicked if the front of the card is currently showing. +- Finally, we'll conditional render the text in the `` based on the value of the `side` state variable. + +Here's the finished file: + +```tsx src/app/flashcard.tsx [expandable] {10,12-16,21,24} +import { Card, CardContent } from "@/components/ui/card"; +import { CarouselItem } from "@/components/ui/carousel"; +import { IFlashcard } from "@/types/flashcard"; + +interface Props { + flashcard: IFlashcard; +} + +export default function Flashcard({ flashcard }: Props) { + const [side, setSide] = useState<"front" | "back">("front"); + + const handleCardClick = () => { + if (side === "front") { + setSide("back"); + } + }; + + return ( + +
+ + + + {side === "front" ? flashcard.front : flashcard.back} + + + +
+
+ ); +} +``` + +Then, we'll use [Motion](https://motion.dev) to add a neat flip animation to the card when we click on it. For this we first need to install the package into our project: + +```bash +npm install motion +``` + +If you think about it, when you flip a flashcard, you tend to do it in the Y-axis. So here we'll use a `` with a light spring animation in the y-axis to create the effect: + +```tsx src/app/flashcard.tsx [expandable] {7-8,26-37} +"use client"; + +import { Card, CardContent } from "@/components/ui/card"; +import { CarouselItem } from "@/components/ui/carousel"; +import { IFlashcard } from "@/types/flashcard"; +import { useState } from "react"; +import { motion } from "motion/react"; +import styles from "./flashcard.module.css"; + +interface Props { + flashcard: IFlashcard; +} + +export default function Flashcard({ flashcard }: Props) { + const [side, setSide] = useState<"front" | "back">("front"); + + const handleCardClick = () => { + if (side === "front") { + setSide("back"); + } + }; + + return ( + +
+ + + + + {side === "front" ? flashcard.front : flashcard.back} + + + + +
+
+ ); +} +``` + +You'll notice we also added a couple of styles here. These do a couple of things: + +- Ensure that when a `` is flipping, the 'back face' isn't visible during the animation with `backface-visibility: hidden;` +- As the `` component is a child of the ``, usually it would appear flat when it's parent rotates in 3D. Adding `transform-style: preserve-3d;` to the `` ensures it keeps it's 3D effect when it's parent animates. + +```css src/app/flashcard.module.css +.backface-hidden { + backface-visibility: hidden; + -webkit-backface-visibility: hidden; +} + +.card { + transform-style: preserve-3d; + -webkit-transform-style: preserve-3d; +} +``` + +### A Flippin' Bug! + +Sweet! Our project is now starting to feel like a real study tool! However the keen eyed (or maybe not so keen...) will notice there's one major bug here. When we flip a card over, the answer on the back appears in reverse 😢... + + + + + +I you think about it, when you write a flashcard, you actually write the answer on the back in the opposite direction to the question on the front. + +And as we're using `motion` to literally flip over our card in the Y-axis, we need to make sure we write our answers backwards as well. + +First, we'll add a little CSS snippet to handle writing text backwards: + +```css src/app/flascard.module.css {11-14} +.backface-hidden { + backface-visibility: hidden; + -webkit-backface-visibility: hidden; +} + +.card { + transform-style: preserve-3d; + -webkit-transform-style: preserve-3d; +} + +.flipped_text { + transform: scaleX(-1); + transform-origin: center; +} +``` + +Then we'll conditionally add this style to our card text based on which side of the card is showing: + +```tsx src/app/flashcard.tsx + + {side === "front" ? flashcard.front : flashcard.back} + +``` + +Ok awesome. Now when we flip over a card the answer on the back should read in the right direction: + + + + + +### Tracking Progress + +The next step is to add some UI to show the user how many flashcards they've looked at, and how many in the set they have left. We'll use a simple progress bar to achieve this. + +Before we can start tracking this level of information, we need to set up tracking for a new state variable that holds the index of the flashcard the user is currently looking at. We'll use the [carousel api](https://ui.shadcn.com/docs/components/carousel#api) to hook into the functionality here and keep our state variable up to date: + +```tsx src/app/flashcards.tsx [expandable] {1,8,19-34,37} +"use client"; + +import { + Carousel, + CarouselContent, + CarouselPrevious, + CarouselNext, + type CarouselApi, +} from "@/components/ui/carousel"; +import { IFlashcard } from "@/types/flashcard"; +import Flashcard from "./flashcard"; +import { useEffect, useState } from "react"; + +interface Props { + flashcards: IFlashcard[]; +} + +export default function Flashcards({ flashcards }: Props) { + const [flashIndex, setFlashIndex] = useState(0); + const [api, setApi] = useState(); + + useEffect(() => { + if (!api) { + return; + } + + // Initialize the flash index + setFlashIndex(api.selectedScrollSnap() + 1); + + // Update the flash index when the carousel is scrolled + api.on("select", () => { + setFlashIndex(api.selectedScrollSnap() + 1); + }); + }, [api]); + + return ( + + + {flashcards.map((flashcard) => ( + + ))} + + + + + ); +} +``` + +Then we need to add the `progress` component from shadcn/ui to our project: + +```bash +npx shadcn@latest add progress +``` + +Finally we can add a progress bar above the carousel: + +```tsx [expandable] {13,38-39,49} +"use client"; + +import { + Carousel, + CarouselContent, + CarouselPrevious, + CarouselNext, + type CarouselApi, +} from "@/components/ui/carousel"; +import { IFlashcard } from "@/types/flashcard"; +import Flashcard from "./flashcard"; +import { useEffect, useState } from "react"; +import { Progress } from "@/components/ui/progress"; + +interface Props { + flashcards: IFlashcard[]; +} + +export default function Flashcards({ flashcards }: Props) { + const [flashIndex, setFlashIndex] = useState(0); + const [api, setApi] = useState(); + + useEffect(() => { + if (!api) { + return; + } + + // Initialize the flash index + setFlashIndex(api.selectedScrollSnap() + 1); + + // Update the flash index when the carousel is scrolled + api.on("select", () => { + setFlashIndex(api.selectedScrollSnap() + 1); + }); + }, [api]); + + return ( +
+ + + + {flashcards.map((flashcard) => ( + + ))} + + + + +
+ ); +} +``` + +Sweet! Now things are really starting to come together. + + + + + +Now the real fun begins... + +## Adding Basic Gamification + +In this section we'll be adding the following gamification elements to the flashcard project: + +- Achievements for completing: + - 10 flashcards + - 50 flashcards + - 100 flashcards + - 250 flashcards +- Daily streak for completing at least one flashcard a day. +- Automated emails for: + - Achievement unlocked + - Weekly progress summaries + +This might sound like a lot, but would you be surprised if I told you could build all this in less than 10 lines of code? + +Yup, you can. Trophy makes it super easy to build these features with a few lines of integration code. + +So, let's get started... + +### How Trophy Works + +First off, we need a new [Trophy account](https://app.trophy.so/sign-up). Then we can start to piece together the various parts of our gamification experience. + +In Trophy, [Metrics](/platform/metrics) represent different interactions users can make and can drive features like [Achievements](/platform/achievements), [Streaks](/platform/streaks) and [Emails](/platform/emails). + +```mermaid +flowchart TD + A@{ shape: rounded, label: "Events" } + B@{ shape: rounded, label: "Metrics" } + C@{ shape: rounded, label: "Achievements" } + D@{ shape: rounded, label: "Streaks" } + E@{ shape: rounded, label: "Points" } + F@{ shape: rounded, label: "Leaderboards" } + A-.->B + B-->C + B-->D + B-->E + B-->F +``` + +In Trophy we track user interactions by sending [Events](/platform/events) from our code to Trophy against a specific metric. + +When events are recorded for a specific user, any achievements linked to the specified metric will be unlocked if the requirements are met, daily streaks will be automatically calculated and kept up to date, points are awarded, and leaderboards are updated. + +This is what makes building gamified experiences with Trophy so easy, is does all the work for you behind the scenes. + +Recording an event against a metric for a specific user makes use of Trophy's [metric event API](/api-reference/endpoints/metrics/send-a-metric-change-event), which in practice looks like this: + + + +That's how in just a few lines of code we can power a whole suite of gamification features. However, before we can start sending events, we need to set up our new Trophy account. + +### Setting Up Trophy + +Here's how we'll setup our Trophy account for our study app: + +- First, we'll set up a _Flashcards Flipped_ metric +- Next, we'll setup achievements linked to this metric +- Then, we'll configure a daily streak linked to this metric +- Finally, we'll configure automated email sequences for this metric + +Let's get into it... + + + + Head into the Trophy [metrics page](https://app.trophy.so/metrics) and create a new metric, making sure to specify `flashcards-flipped` as the metric `key`. This is what we'll use to reference the metric in our code when sending events. + + + + + + + + + Next, create one achievement for each of the following milestones: + - 10 flashcards (Elementary) + - 50 flashcards (Novice) + - 100 flashcards (Scholar) + - 250 flashcards (Expert) + +{" "} + + + Free free to download and use this zip of badges (ready made for this example + app): + [flashcard_badges.zip](../assets/guides/example-apps/example-study-app/flashcard_badges.zip) + + + + + + + + + + Next, head into the [streaks page](https://app.trophy.so/streaks) and set up a daily streak. + + + + + + + + + Head into the [emails page](https://app.trophy.so/emails) and add and activate templates for the two types of emails we want. + - Achievement unlocked emails + - Recap emails (weekly) + + Each email type has its own section—add a template under each and activate it to start sending. + +Trophy also gives us a preview of the emails which is nice. Plus, feel free to configure a nice logo and brand colors in the [branding page](https://app.trophy.so/branding). These settings are automatically used in all emails sent by Trophy. + +{" "} + + + + + +{" "} + + + Before you can start sending emails, you'll need to [configure your sending + address](/platform/emails#sending-emails) with Trophy. But this can be done + later on if preferred. + + + + + +### Integrating Trophy SDK + +First we need to grab our API key from the Trophy [integration page](https://app.trophy.so/integration) and add this as a **server-side only** environment variable. + + + Make sure you **don't** expose your API key in client-side code + + +```bash .env.local +TROPHY_API_KEY='*******' +``` + +Next, we'll install the Trophy SDK: + +```bash +npm install @trophyso/node +``` + +Then whenever a user views a flashcard, we simply send an event to Trophy with details of the user that performed the action (which we'll mock), and the number of flashcards they viewed (1 in this case). + +In NextJS we'll do this with a server action that makes the call to Trophy to send the event, and we'll call this action when the user moves to the next flashcard in the carousel. + +First, create the server action: + +```tsx src/app/actions.ts [expandable] +"use server"; + +import { TrophyApiClient } from "@trophyso/node"; +import { EventResponse } from "@trophyso/node/api"; + +// Set up Trophy SDK with API key +const trophy = new TrophyApiClient({ + apiKey: process.env.TROPHY_API_KEY as string, +}); + +/** + * Track a flashcard viewed event in Trophy + * @returns The event response from Trophy + */ +export async function viewFlashcard(): Promise { + try { + return await trophy.metrics.event("flashcards-flipped", { + user: { + // Mock email + email: "user@example.com", + + // Mock timezone + tz: "Europe/London", + + // Mock user ID + id: "18", + }, + + // Event represents a single user viewing 1 flashcard + value: 1, + }); + } catch (error) { + console.error(error); + return null; + } +} +``` + +Then call it when the user views the next flashcard: + +```tsx src/app/flashcards.tsx [expandable] {14,36-37} +"use client"; + +import { + Carousel, + CarouselContent, + CarouselPrevious, + CarouselNext, + type CarouselApi, +} from "@/components/ui/carousel"; +import { IFlashcard } from "@/types/flashcard"; +import Flashcard from "./flashcard"; +import { useEffect, useState } from "react"; +import { Progress } from "@/components/ui/progress"; +import { viewFlashcard } from "./actions"; + +interface Props { + flashcards: IFlashcard[]; +} + +export default function Flashcards({ flashcards }: Props) { + const [flashIndex, setFlashIndex] = useState(0); + const [api, setApi] = useState(); + + useEffect(() => { + if (!api) { + return; + } + + // Initialize the flash index + setFlashIndex(api.selectedScrollSnap() + 1); + + api.on("select", () => { + // Update the flash index when the carousel is scrolled + setFlashIndex(api.selectedScrollSnap() + 1); + + // Track the flashcard viewed event + viewFlashcard(); + }); + }, [api]); + + return ( +
+ + + + {flashcards.map((flashcard) => ( + + ))} + + + + +
+ ); +} +``` + +We can validate this is working by checking the Trophy [dashboard](https://app.trophy.so) which should show our first user tracked in the _Top Users_ table: + + + + + +Next, we'll add some UI to show off our gamification features in practice. + +### Adding Gamification UX + +The response to the API call that we make to track events in Trophy helpfully gives us back any changes to the users progress as a result of that event: + + + +This includes: + +- The `achievements` array which is a list of newly unlocked achievements as a result of the event. +- The `currentStreak` object which is the users most up to date streak data after the event has taken place. + +This makes it really easy for us to react to changes in the users progress and do whatever we want. In this example, each time Trophy tells us a user has unlocked a new achievement, or extended their streak we'll: + +- Show a pop-up toast +- Play a sound effect + +#### Achievement Unlocked Toasts + +First, we'll show a toast when users unlock new achievements. For this we need to add the `sonner` component from shadcn/ui to our project: + +```bash +npx shadcn@latest add sonner +``` + +Then we'll create the `` UI component we need to display toasts and a `toast()` utility function to trigger them: + +```tsx src/lib/toast.tsx [expandable] +"use client"; + +import React from "react"; +import { toast as sonnerToast } from "sonner"; +import Image from "next/image"; + +interface ToastProps { + id: string | number; + title: string; + description?: string; + image?: { + src: string; + alt: string; + }; +} + +/** + * A fully custom toast that still maintains the animations and interactions. + * @param toast - The toast to display. + * @returns The toast component. + */ +export function toast(toast: Omit) { + return sonnerToast.custom((id) => ( + + )); +} + +/** + * A fully custom toast that still maintains the animations and interactions. + * @param props - The toast to display. + * @returns The toast component. + */ +export function Toast(props: ToastProps) { + const { title, description, image } = props; + + return ( +
+ {image && ( +
+ {image.alt} +
+ )} +
+
+

{title}

+ {description && ( +

{description}

+ )} +
+
+
+ ); +} +``` + +Next, we need to update the `page.tsx` file with the main `` component. This is the component from `sonner` which is responsible for displaying our toasts on the screen when we trigger them: + +```tsx src/app/page.tsx {3,9} +import { flashcards } from "@/data"; +import Flashcards from "./flashcards"; +import { Toaster } from "@/components/ui/sonner"; + +export default function Home() { + return ( +
+ + +
+ ); +} +``` + +Next, to make sure NextJS shows our badges, we need to configure Trophy's image host as a trusted domain. If you use custom badge URLs on your own domain or CDN, add those host names here too, or use a plain `` and skip remote patterns. + +```ts next.config.ts {4-11} +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "lookmizerpsrsczvtlwh.supabase.co", + }, + ], + }, +}; + +export default nextConfig; +``` + +And finally, we update our `` component to read the response from Trophy and display toasts for any unlocked achievements: + +```tsx src/app/flashcards.tsx [expandable] {15,33,38-56} +"use client"; + +import { + Carousel, + CarouselContent, + CarouselPrevious, + CarouselNext, + type CarouselApi, +} from "@/components/ui/carousel"; +import { IFlashcard } from "@/types/flashcard"; +import Flashcard from "./flashcard"; +import { useEffect, useState } from "react"; +import { Progress } from "@/components/ui/progress"; +import { viewFlashcard } from "./actions"; +import { toast } from "@/lib/toast"; + +interface Props { + flashcards: IFlashcard[]; +} + +export default function Flashcards({ flashcards }: Props) { + const [flashIndex, setFlashIndex] = useState(0); + const [api, setApi] = useState(); + + useEffect(() => { + if (!api) { + return; + } + + // Initialize the flash index + setFlashIndex(api.selectedScrollSnap() + 1); + + api.on("select", async () => { + // Update flashIndex when the carousel is scrolled + setFlashIndex(api.selectedScrollSnap() + 1); + + // Track the flashcard viewed event + const response = await viewFlashcard(); + + if (!response) { + return; + } + + // Show toasts if the user has unlocked any new achievements + response.achievements.forEach((achievement) => { + toast({ + title: achievement.name as string, + description: achievement.description, + image: { + src: achievement.badgeUrl as string, + alt: achievement.name as string, + }, + }); + }); + }); + }, [api]); + + return ( +
+ + + + {flashcards.map((flashcard) => ( + + ))} + + + + +
+ ); +} +``` + +To see this in action, all we need to do is unlock our first achievement by viewing 10 flashcards: + + + + + +#### Streak Extended Toasts + +We'll use the same methods to show similar toasts when a user extends their streak. As we've set up a daily streak in Trophy, this will trigger the first time a user views a flashcard each day. + + + If we wanted to experiment with a different streak cadence, like a weekly + streak, then none of this code changes - the great thing about Trophy is + everything can be configured from within the dashboard and doesn't require any + code changes. + + +All we need to do is read the `currentStreak.extended` property from Trophy and handle showing another toast with a new `if` statement: + +```tsx src/app/flashcards.tsx [expandable] {56-63} +"use client"; + +import { + Carousel, + CarouselContent, + CarouselPrevious, + CarouselNext, + type CarouselApi, +} from "@/components/ui/carousel"; +import { IFlashcard } from "@/types/flashcard"; +import Flashcard from "./flashcard"; +import { useEffect, useState } from "react"; +import { Progress } from "@/components/ui/progress"; +import { viewFlashcard } from "./actions"; +import { toast } from "@/lib/toast"; + +interface Props { + flashcards: IFlashcard[]; +} + +export default function Flashcards({ flashcards }: Props) { + const [flashIndex, setFlashIndex] = useState(0); + const [api, setApi] = useState(); + + useEffect(() => { + if (!api) { + return; + } + + // Initialize the flash index + setFlashIndex(api.selectedScrollSnap() + 1); + + api.on("select", async () => { + // Update flashIndex when the carousel is scrolled + setFlashIndex(api.selectedScrollSnap() + 1); + + // Track the flashcard viewed event + const response = await viewFlashcard(); + + if (!response) { + return; + } + + // Show toasts if the user has unlocked any new achievements + response.achievements.forEach((achievement) => { + toast({ + title: achievement.name as string, + description: achievement.description, + image: { + src: achievement.badgeUrl as string, + alt: achievement.name as string, + }, + }); + }); + + // Show toast if user has extended their streak + if (response.currentStreak?.extended) { + toast({ + title: "You're on a roll!", + description: `Keep going to keep your ${response.currentStreak.length} day streak!`, + }); + } + }); + }, [api]); + + return ( +
+ + + + {flashcards.map((flashcard) => ( + + ))} + + + + +
+ ); +} +``` + +Now the first time a user views a flashcard each day, they'll see one of our streak extended toasts: + + + + + +#### Sound Effects + +Gamification is first and foremost about increasing user retention. The best way to do this is to make the features we build as engaging as possible. A great way to do this that's often overlooked is with sound effects. + +Here we'll add two distinct sound effects for each case where we show a toast to further engage the user. This helps distinguish the different toasts and helps users build up an expectation of what each sound means. + +For this we'll make use of the [HTML5 Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) and use sounds from [freesounds.org](https://freesounds.org). + + + The sounds used in this example are in the repository in the `public/sounds` + directory. Feel free to use them or pick others you like! + + +To load the sound files into our application, we'll create a ref for each one, then we simply call the `play()` method on each when we want to actually play the sound: + +```tsx src/app/flashcards.tsx [expandable] {25-31,53-57,73-77} +"use client"; + +import { + Carousel, + CarouselContent, + CarouselPrevious, + CarouselNext, + type CarouselApi, +} from "@/components/ui/carousel"; +import { IFlashcard } from "@/types/flashcard"; +import Flashcard from "./flashcard"; +import { useEffect, useState, useRef } from "react"; +import { Progress } from "@/components/ui/progress"; +import { viewFlashcard } from "./actions"; +import { toast } from "@/lib/toast"; + +interface Props { + flashcards: IFlashcard[]; +} + +export default function Flashcards({ flashcards }: Props) { + const [flashIndex, setFlashIndex] = useState(0); + const [api, setApi] = useState(); + + const achievementSound = useRef(null); + const streakSound = useRef(null); + + useEffect(() => { + achievementSound.current = new Audio("/sounds/achievement_unlocked.mp3"); + streakSound.current = new Audio("/sounds/streak_extended.mp3"); + }, []); + + useEffect(() => { + if (!api) { + return; + } + + // Initialize the flash index + setFlashIndex(api.selectedScrollSnap() + 1); + + api.on("select", async () => { + // Update flashIndex when the carousel is scrolled + setFlashIndex(api.selectedScrollSnap() + 1); + + // Track the flashcard viewed event + const response = await viewFlashcard(); + + if (!response) { + return; + } + + if (response.achievements?.length) { + // Play the achievement sound only once for all new achievements + if (achievementSound.current) { + achievementSound.current.currentTime = 0; + achievementSound.current.play(); + } + + // Show toasts if the user has unlocked any new achievements + response.achievements.forEach((achievement) => { + toast({ + title: achievement.name as string, + description: achievement.description, + image: { + src: achievement.badgeUrl as string, + alt: achievement.name as string, + }, + }); + }); + } + + if (response.currentStreak?.extended) { + // Play the streak sound + if (streakSound.current) { + streakSound.current.currentTime = 0; + streakSound.current.play(); + } + + // Show toast if the user has extended their streak + toast({ + title: "You're on a roll!", + description: `Keep going to keep your ${response.currentStreak.length} day streak!`, + }); + } + }); + }, [api]); + + return ( +
+ + + + {flashcards.map((flashcard) => ( + + ))} + + + + +
+ ); +} +``` + +#### Displaying Study Journey + +The last piece of UI we'll add will be a dialog to display the user's study progress, including their streak and any achievements they've unlocked so far. + +First, we'll add a couple of new server actions to fetch the users streak and achievements from Trophy. As we're using a daily streak here, we'll fetch the last 14 days of streak data from Trophy to give us enough to work with in our UI: + +```tsx src/app/actions.ts [expandable] {6-7,10-11,24,33,45-58,60-73} +"use server"; + +import { TrophyApiClient } from "@trophyso/node"; +import { + EventResponse, + MultiStageAchievementResponse, + StreakResponse, +} from "@trophyso/node/api"; + +const USER_ID = "39"; +const FLASHCARDS_VIEWED_METRIC_KEY = "flashcards-flipped"; + +// Set up Trophy SDK with API key +const trophy = new TrophyApiClient({ + apiKey: process.env.TROPHY_API_KEY as string, +}); + +/** + * Track a flashcard viewed event in Trophy + * @returns The event response from Trophy + */ +export async function viewFlashcard(): Promise { + try { + return await trophy.metrics.event(FLASHCARDS_VIEWED_METRIC_KEY, { + user: { + // Mock email + email: "user@example.com", + + // Mock timezone + tz: "Europe/London", + + // Mock user ID + id: USER_ID, + }, + + // Event represents a single user viewing 1 flashcard + value: 1, + }); + } catch (error) { + console.error(error); + return null; + } +} + +/** + * Get the achievements for a user + * @returns The achievements for the user + */ +export async function getAchievements(): Promise< + MultiStageAchievementResponse[] | null +> { + try { + return await trophy.users.allachievements(USER_ID); + } catch (error) { + console.error(error); + return null; + } +} + +/** + * Get the streak for a user + * @returns The streak for the user + */ +export async function getStreak(): Promise { + try { + return await trophy.users.streak(USER_ID, { + historyPeriods: 14, + }); + } catch (error) { + console.error(error); + return null; + } +} +``` + +Then we'll call these actions on the server when the page is requested: + +```tsx src/app/page.tsx {7-8} +import { flashcards } from "@/data"; +import Flashcards from "./flashcards"; +import { Toaster } from "@/components/ui/sonner"; +import { getAchievements, getStreak } from "./actions"; + +export default async function Home() { + const achievements = await getAchievements(); + const streak = await getStreak(); + + return ( +
+ + +
+ ); +} +``` + +Then we'll create a new `` component that takes in the achievements and streak data as props and renders it nicely in a dialog. Before adding this we need to add the shadcn/ui `dialog` component to our project, and while we're at it, we'll add the `seperator` component too: + +```bash +npx shadcn@latest add dialog separator +``` + +We'll also need `dayjs` to help us with displaying dates nicely as well: + +```bash +npm i dayjs +``` + +Now we have everything we need to build our study journey component: + +```tsx src/app/study-journey.tsx [expandable] +import { Separator } from "@/components/ui/separator"; +import { + MultiStageAchievementResponse, + StreakResponse, +} from "@trophyso/node/api"; +import { Flame, GraduationCap } from "lucide-react"; +import Image from "next/image"; +import dayjs from "dayjs"; +import { + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, +} from "@/components/ui/dialog"; + +interface Props { + achievements: MultiStageAchievementResponse[] | null; + streak: StreakResponse | null; +} + +export default function StudyJourney({ achievements, streak }: Props) { + const sundayOffset = 7 - ((new Date().getDay() + 6) % 7); + + const adjustedStreakHistory = + streak?.streakHistory?.slice( + sundayOffset - 1, + streak.streakHistory.length, + ) || Array(14).fill(null); + + return ( +
+ + +
+ +
+
+ + {/* Heading */} + + Your study journey + + Keep studying to extend your streak and earn new badges + + + + {/* Streak */} +
+
+
+ + + + +
+ + {streak?.length || 0} + +
+
+
+

+ {streak && streak.length > 0 + ? `Your study streak` + : `No study streak`} +

+

+ {streak && streak.length > 0 + ? `${streak.length} day${ + streak.length > 1 ? "s" : "" + } in a row` + : `Start a streak`} +

+
+
+
+
+ {["M", "T", "W", "T", "F", "S", "S"].map((day, i) => ( +
+ {day} +
+ ))} +
+
+ {adjustedStreakHistory.map((day, i) => { + if (day === null) { + return ( +
+ +
+ ); + } + + return ( +
0 ? "bg-primary" : "bg-primary/10" + } flex items-center justify-center`} + > + 0 ? "text-white" : "text-primary/30" + }`} + /> +
+ ); + })} +
+
+
+ + + + {/* Achievements */} + {achievements && achievements.length > 0 ? ( +
+
+

Your badges

+
+
+ {achievements?.map((achievement) => ( +
+ {achievement.name +

{achievement.name}

+

+ {dayjs(achievement.achievedAt).format("MMM D, YYYY")} +

+
+ ))} +
+
+ ) : ( +
+

No badges yet

+

+ Keep studying to unlock more badges! +

+
+ )} +
+
+
+ ); +} +``` + +Finally, we simply add this component to our page and we should be good to go: + +```tsx src/app/page.tsx {4,13} +import { flashcards } from "@/data"; +import Flashcards from "./flashcards"; +import { Toaster } from "@/components/ui/sonner"; +import StudyJourney from "./study-journey"; +import { getAchievements, getStreak } from "./actions"; + +export default async function Home() { + const achievements = await getAchievements(); + const streak = await getStreak(); + + return ( +
+ + + +
+ ); +} +``` + + + + + +## Adding an XP System + +Now let's take our study platform to the next level by adding an XP (points) system. This will give users a sense of progression and reward them for their study activity. + +### Setting Up Points in Trophy + +First, head to the Trophy [points page](https://app.trophy.so/points) and create a new points system. We'll call ours "XP" with the key `points`. + + + + + +Configure your points system to award XP for viewing flashcards. You can also set up bonus XP for streak milestones. + +### Integrating Points API + +Add new server actions to fetch points data: + +```tsx src/app/actions.ts [expandable] +const POINTS_SYSTEM_KEY = "points"; + +/** + * Get the points system configuration + */ +export async function getPointsSystem(): Promise { + try { + return await trophy.points.system(POINTS_SYSTEM_KEY); + } catch (error) { + console.error(error); + return null; + } +} + +/** + * Get the points for a user + */ +export async function getUserPoints( + userId: string, +): Promise { + try { + return await trophy.users.points(userId, POINTS_SYSTEM_KEY, { + awards: 5, + }); + } catch (error) { + console.error(error); + return null; + } +} + +/** + * Get the points history summary for a user + */ +export async function getPointsSummary( + userId: string, +): Promise { + try { + const now = dayjs(); + return await trophy.users.pointsEventSummary(userId, POINTS_SYSTEM_KEY, { + aggregation: "daily", + startDate: now.subtract(6, "day").toISOString().split("T")[0], + endDate: now.toISOString().split("T")[0], + }); + } catch (error) { + console.error(error); + return null; + } +} +``` + +### Creating a Points Context + +To manage points state across the app, create a React context: + +```tsx src/contexts/UserPointsContext.tsx [expandable] +"use client"; + +import { + createContext, + useContext, + useEffect, + useState, + ReactNode, + useCallback, + useRef, +} from "react"; +import { GetUserPointsResponse } from "@trophyso/node/api"; +import { getUserPoints } from "@/app/actions"; +import { getUserId } from "@/lib/user"; + +interface UserPointsContextType { + points: GetUserPointsResponse | null; + lastPoints: GetUserPointsResponse | null; + loading: boolean; + error: string | null; + refetch: () => Promise; +} + +const UserPointsContext = createContext( + undefined, +); + +export function UserPointsProvider({ children }: { children: ReactNode }) { + const [points, setPoints] = useState(null); + const [lastPoints, setLastPoints] = useState( + null, + ); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const timeoutRef = useRef(null); + + const fetchPoints = async () => { + const userId = getUserId(); + setLoading(true); + setError(null); + + try { + setLastPoints(points); + const pointsData = await getUserPoints(userId); + setPoints(pointsData); + } catch (err) { + setError(err instanceof Error ? err.message : "Failed to fetch points"); + } finally { + setLoading(false); + } + }; + + const refetch = useCallback(async () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + timeoutRef.current = setTimeout(() => { + fetchPoints(); + }, 300); + }, []); + + useEffect(() => { + fetchPoints(); + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + }; + }, []); + + return ( + + {children} + + ); +} + +export function useUserPoints() { + const context = useContext(UserPointsContext); + if (context === undefined) { + throw new Error("useUserPoints must be used within a UserPointsProvider"); + } + return context; +} +``` + +### Building the Points Center UI + +Create a points display component that shows the user's XP with a satisfying number animation: + +```tsx src/app/user-center/points-center/points-center.tsx [expandable] +import { + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, +} from "@/components/ui/dialog"; +import { useUserPoints } from "@/contexts/UserPointsContext"; +import { useEffect, useState } from "react"; +import { Sparkle } from "lucide-react"; +import { NumberTicker } from "@/components/magicui/number-ticker"; + +export default function PointsCenter() { + const [open, setOpen] = useState(false); + const { points, lastPoints, loading } = useUserPoints(); + const [isSpinning, setIsSpinning] = useState(false); + + // Trigger spinning animation when points change + useEffect(() => { + if (points?.total !== lastPoints?.total && points?.total !== undefined) { + setIsSpinning(true); + const timer = setTimeout(() => setIsSpinning(false), 1000); + return () => clearTimeout(timer); + } + }, [points?.total, lastPoints?.total]); + + return ( + + +
+
+ + +
+
+
+ + + +
+ + Your XP +
+
+ Keep studying to earn more XP +
+ {/* Add points history chart, recent awards, etc. */} +
+
+ ); +} +``` + +### Updating Points on Flashcard View + +Finally, re-fetch points whenever a user views a flashcard to show real-time XP updates: + +```tsx src/app/flashcards.tsx {4,8,45} +import { useUserPoints } from "@/contexts/UserPointsContext"; + +export default function Flashcards({ flashcards }: Props) { + const { refetch: refetchPoints } = useUserPoints(); + + // ... existing code ... + + api.on("select", async () => { + setFlashIndex(api.selectedScrollSnap() + 1); + + const response = await viewFlashcard(); + + if (!response) return; + + // Refetch points to update the XP display + refetchPoints(); + + // ... handle achievements and streaks ... + }); +} +``` + +Now when users view flashcards, they'll see their XP update in real-time with a satisfying animation! + + + + + +## Adding Leaderboards + +Nothing drives engagement like friendly competition. Let's add a leaderboard to show how users stack up against each other. + +### Setting Up Leaderboards in Trophy + +Head to the Trophy [leaderboards page](https://app.trophy.so/leaderboards) and create a new leaderboard. We'll create a daily leaderboard with the key `daily-champions` that ranks users by flashcards viewed. + + + + + +### Leaderboard API Integration + +Add server actions for fetching leaderboard data: + +```tsx src/app/actions.ts [expandable] +const LEADERBOARD_KEY = "daily-champions"; + +/** + * Get the leaderboard rankings + */ +export async function getLeaderboard( + limit?: number, + offset?: number, + userId?: string, + runDate?: string, +): Promise { + try { + return await trophy.leaderboards.get(LEADERBOARD_KEY, { + limit: limit || 10, + offset: offset || 0, + userId: userId || undefined, + run: runDate || undefined, + }); + } catch (error) { + console.error("Get leaderboard error:", error); + return null; + } +} + +/** + * Get a specific user's leaderboard position and history + */ +export async function getUserLeaderboard( + userId: string, + run?: string, +): Promise { + try { + return await trophy.users.leaderboard(userId, LEADERBOARD_KEY, { + run, + }); + } catch (error) { + console.error("Get user leaderboard error:", error); + return null; + } +} +``` + +### Building the Leaderboard UI + +Create a leaderboard component that displays rankings with pagination: + +```tsx src/app/leaderboard-center/leaderboard-center.tsx [expandable] +import { + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { useState, useEffect } from "react"; +import { getLeaderboard } from "@/app/actions"; +import { LeaderboardResponseWithRankings } from "@trophyso/node/api"; + +export default function LeaderboardCenter() { + const [open, setOpen] = useState(false); + const [leaderboard, setLeaderboard] = + useState(null); + + useEffect(() => { + if (open) { + getLeaderboard(10, 0).then(setLeaderboard); + } + }, [open]); + + return ( + + + + + + + Daily Champions + +
+ {leaderboard?.rankings?.map((entry, index) => ( +
+
+ #{index + 1} + {entry.userId} +
+ {entry.score} cards +
+ ))} +
+
+
+ ); +} +``` + +The leaderboard automatically updates as users study, creating a dynamic competitive environment that encourages daily engagement. + +## Adding an Energy System + +Energy (or "lives") systems are a proven retention mechanic that encourages users to return throughout the day. Let's add one to our study platform. + +### Setting Up Energy in Trophy + +Trophy's points system is flexible enough to handle energy mechanics. Create a new points system with the key `energy` and configure it with: + +- A maximum cap (e.g., 5 energy) +- Automatic regeneration rules +- Costs per action (e.g., -1 energy per flashcard session) + + + + + +### Energy API Integration + +Add server actions for energy management: + +```tsx src/app/actions.ts [expandable] +const ENERGY_SYSTEM_KEY = "energy"; + +/** + * Get the energy system configuration + */ +export async function getEnergySystem(): Promise { + try { + return await trophy.points.system(ENERGY_SYSTEM_KEY); + } catch (error) { + console.error("Get energy system error:", error); + return null; + } +} + +/** + * Get the current energy for a user + */ +export async function getUserEnergy( + userId: string, +): Promise { + try { + return await trophy.users.points(userId, ENERGY_SYSTEM_KEY, { + awards: 5, + }); + } catch (error) { + console.error("Get user energy error:", error); + return null; + } +} +``` + +### Creating an Energy Context + +Similar to points, create a context for managing energy state: + +```tsx src/contexts/UserEnergyContext.tsx [expandable] +"use client"; + +import { + createContext, + useContext, + useEffect, + useState, + ReactNode, + useCallback, +} from "react"; +import { GetUserPointsResponse } from "@trophyso/node/api"; +import { getUserEnergy } from "@/app/actions"; +import { getUserId } from "@/lib/user"; + +interface UserEnergyContextType { + energy: GetUserPointsResponse | null; + loading: boolean; + refetch: () => Promise; +} + +const UserEnergyContext = createContext( + undefined, +); + +export function UserEnergyProvider({ children }: { children: ReactNode }) { + const [energy, setEnergy] = useState(null); + const [loading, setLoading] = useState(false); + + const fetchEnergy = async () => { + const userId = getUserId(); + setLoading(true); + try { + const energyData = await getUserEnergy(userId); + setEnergy(energyData); + } finally { + setLoading(false); + } + }; + + const refetch = useCallback(async () => { + await fetchEnergy(); + }, []); + + useEffect(() => { + fetchEnergy(); + }, []); + + return ( + + {children} + + ); +} + +export function useUserEnergy() { + const context = useContext(UserEnergyContext); + if (context === undefined) { + throw new Error("useUserEnergy must be used within a UserEnergyProvider"); + } + return context; +} +``` + +### Building the Energy UI + +Create an energy display that shows remaining energy with visual indicators: + +```tsx src/app/energy-center/energy-center.tsx [expandable] +import { useUserEnergy } from "@/contexts/UserEnergyContext"; +import { Zap } from "lucide-react"; + +export default function EnergyCenter() { + const { energy, loading } = useUserEnergy(); + const maxEnergy = 5; + const currentEnergy = energy?.total || 0; + + return ( +
+ {Array.from({ length: maxEnergy }).map((_, i) => ( + + ))} +
+ ); +} +``` + +With energy in place, users have a reason to return multiple times throughout the day as their energy regenerates, boosting your daily active users. + +## The Result + +Congrats! If you're reading this you made it to the end of the tutorial and built yourself a fully-functioning study platform. Of course there's loads more we could do to this, so here's a few ideas: + +- Persist flashcards to a database +- Create multiple flashcard sets for other topics +- Add authentication +- Allow users to create their own flashcard sets + + + If you had fun or think you learned something along the way then give the repo + a star on [GitHub](https://github.com/trophyso/example-study-platform)! + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/guides/how-to-build-a-leaderboards-feature.mdx b/es/guides/how-to-build-a-leaderboards-feature.mdx new file mode 100644 index 0000000..3bf8686 --- /dev/null +++ b/es/guides/how-to-build-a-leaderboards-feature.mdx @@ -0,0 +1,238 @@ +--- +title: How To Build A Leaderboards Feature +description: Learn how to use Trophy to add a leaderboards feature to your web or mobile app. +subtitle: Learn how to use Trophy to add a leaderboards feature to your web or mobile app. +noindex: true +--- + +import SDKInstallCommand from "/snippets/sdk-install-command.mdx"; +import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; +import LeaderboardRankingsRequest from "/snippets/leaderboard-rankings-request.mdx"; +import LeaderboardRankingsResponse from "/snippets/leaderboard-rankings-response.mdx"; +import UserLeaderboardRankingsRequest from "/snippets/user-leaderboard-rankings-request.mdx"; +import UserLeaderboardRankingsResponse from "/snippets/user-leaderboard-rankings-response.mdx"; + +The guide outlines the full process of adding a leaderboards feature to your web or mobile app using Trophy. + +For illustration purposes we'll use the example of a study platform that uses a daily leaderboard to create friendly competition around viewing flashcards. + + + 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). + + +## Pre-requisites + +- A [Trophy](https://app.trophy.so/sign-up) account +- About 10 minutes + +## Trophy Setup + +In Trophy, [Metrics](/platform/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 a metric that best represents the interaction you want to build leaderboards around. + +In the Trophy dashboard, head to the [metrics page](https://app.trophy.so/metrics) and create a metric. + + + + + +Once you've created your metric, head to the [leaderboards page](https://app.trophy.so/leaderboards) and create the leaderboards you want. You can find all the details on the types of leaderboards and the different use cases in the [leaderboards docs](/platform/leaderboards). + + + + + +For the purposes of this guide we've set up a daily leaderboard that tracks the total XP a user earns by viewing flashcards. + + + For a full guide on adding an XP feature to your web or mobile app, check out + our [full guide](/guides/how-to-build-an-xp-feature). + + +In Trophy you track user interactions by sending [Events](/platform/events) from your code to Trophy APIs against a specific metric. + +When events are recorded for a specific user, Trophy automatically updates their ranking in each leaderboard they are a part of. + +This is what makes building gamified experiences with Trophy so easy, it does all the work for you behind the scenes. + +## Installing Trophy SDK + +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: + + + +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 +TROPHY_API_KEY='*******' +``` + + + Make sure you **don't** expose your API key in client-side code. + + +## Tracking User Interactions + +To track an event (user interaction) against your metric, use the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event). + + + +The response to this API call is the complete set of changes to any features you've built with Trophy, including any changes to their ranking in any leaderboards they are a part of. + +{/* vale off */} + +```json Response [expandable] +{ + "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", + "eventId": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "total": 750, + ..., + "leaderboards": { + "daily_champions": { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123535", + "key": "daily_champions", + "name": "Daily Champions", + "description": null, + "rankBy": "metric", + "runUnit": null, + "runInterval": 0, + "maxParticipants": 100, + "metricName": "Flashcards Flipped", + "metricKey": "flashcards-flipped", + "threshold": 10, + "start": "2025-01-01", + "end": null, + "previousRank": 50, + "rank": 12 + } + } +} +``` + +{/* vale on */} + +Validate this is working by checking the Trophy [dashboard](https://app.trophy.so). + + + + + +## Displaying Leaderboards + +You have a number of options for displaying leaderboards in your application. Here we'll look at the most common options. + +### Pop-up Notifications + +We can use the response of the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event) to show pop-up notifications (or 'toasts') when users move up the rankings. + +Here's an example of this in action: + +```ts Leaderboard Rank Up Pop-up +// Sends event to Trophy +const response = await viewFlashcard(); + +if (!response) { + return; +} + +const leaderboard = response.leaderboards["daily_champions"]; + +if (!leaderboard) { + return; +} + +// Show toasts if the user moved up the leaderboard +if (leaderboard.rank > leaderboard.previousRank) { + toast({ + title: "You're on the move!, + description: `You moved up ${leaderboard.previousRank - leaderboard.rank} places!, + }); +} +``` + + + 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. + + +### Displaying Leaderboard Rankings + +To fetch a leaderboard and its most up to date rankings, use the [leaderboard API](/api-reference/endpoints/leaderboards/get-leaderboard). + + + + + You can also use the `run` parameter with the date of the specific past 'run' + of a leaderboard you want to fetch data for. + + +Here's an example of the data returned from the leaderboard API: + + + +### Displaying User Rank History + +Use the [user leaderboard API](/api-reference/endpoints/users/get-a-users-leaderboard) to fetch data on how a specific user's rank has changed over time in a particular leaderboard. + + + +Here's an example of the data returned from the user leaderboard rankings API which includes the users current rank in the `rank` attribute and an array of previous ranks in the `history` attribute: + + + +## Analytics + +The [leaderboards page](https://app.trophy.so/achievements) in Trophy shows how many users are actively participating in a leaderboard, as well as a measure of competitiveness based on how many rank changes are occurring. + +The analytics page also shows a distribution of users scores to help identify clusters of users within rankings. + + + + + +Additionally the leaderboard rankings page shows current and past rankings of a leaderboard: + + + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/guides/how-to-build-a-streaks-feature.mdx b/es/guides/how-to-build-a-streaks-feature.mdx new file mode 100644 index 0000000..ff0ce46 --- /dev/null +++ b/es/guides/how-to-build-a-streaks-feature.mdx @@ -0,0 +1,209 @@ +--- +title: How To Build A Streaks Feature +description: Learn how to use Trophy to add a streaks feature to your web or mobile app. +subtitle: Learn how to use Trophy to add a streaks feature to your web or mobile app. +noindex: true +--- + +import SDKInstallCommand from "/snippets/sdk-install-command.mdx"; +import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; +import StreakRequestBlock from "/snippets/streak-request-block.mdx"; +import StreakResponseBlock from "/snippets/streak-response-block.mdx"; + +The guide outlines the full process of adding a streaks feature to your web or mobile app using Trophy. + +For illustration purposes we'll use the example of a study platform that uses a daily streak to incentivize and reward users for viewing flashcards. + + + 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). + + +## Pre-requisites + +- A [Trophy](https://app.trophy.so/sign-up) account +- About 10 minutes + +## Trophy Setup + +In Trophy, [Metrics](/platform/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 a metric that best represents the interaction you want to drive streaks from. + +In the Trophy dashboard, head to the [metrics page](https://app.trophy.so/metrics) and create a metric. + + + + + +Once you've created your metric, head to the [streaks page](https://app.trophy.so/streaks/configure) and select the streak frequency you want (daily, weekly or monthly). + +Then add your metric to the streak conditions and set the threshold users must meet (e.g. at least 1 to extend their streak each period). + +You can also add multiple metrics and choose whether users must meet all of them or just one—see the [streak conditions docs](/platform/streaks#streak-conditions) for details. + +In Trophy you track user interactions by sending [Events](/platform/events) from your code to Trophy APIs against a specific metric. + +When events are recorded for a specific user, Trophy will automatically check whether they've met their streak conditions for the current period and update the user's streak accordingly. + +This is what makes building gamified experiences with Trophy so easy, it does all the work for you behind the scenes. + +## Installing Trophy SDK + +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: + + + +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 +TROPHY_API_KEY='*******' +``` + + + Make sure you **don't** expose your API key in client-side code. + + +## Tracking User Interactions + +To track an event (user interaction) against your metric, use the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event). + + + + + By including the user's timezone in the `tz` attribute, Trophy will + automatically track streaks according to the user's local clock and handle + awkward edge cases where users change time zones. + + +The response to this API call is the complete set of changes to any features you've built with Trophy, including the latest data on the users streak. + +{/* vale off */} + +```json Response [expandable] +{ + "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", + "eventId": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "total": 750, + ..., + "currentStreak": { + "length": 1, + "frequency": "daily", + "started": "2025-04-02", + "periodStart": "2025-03-31", + "periodEnd": "2025-04-05", + "expires": "2025-04-12" + }, + ... +} +``` + +{/* vale on */} + +Validate this is working by checking the Trophy [dashboard](https://app.trophy.so). + +## Displaying Streaks + +You have a number of options for displaying streaks in your application. Here we'll look at the most common options. + +### Pop-up Notifications + +We can use the response of the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event) to show pop-up notifications (or 'toasts') when user extends their streak. + +Here's an example of this in action: + +```ts Streak Extended Pop-up +// Sends event to Trophy +const response = await viewFlashcard(); + +if (!response) { + return; +} + +// Show toast if user has extended their streak +if (response.currentStreak?.extended) { + toast({ + title: "You're on a roll!", + description: `Keep going to keep your ${response.currentStreak.length} day streak!`, + }); +} +``` + + + + + + + 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. + + +### Displaying User Streaks + +To fetch data on a user's streak use the [user streak API](/api-reference/endpoints/users/get-a-users-streak). + + + +This API not only returns data on the user's current streak like `length` and `expires`, but it can also return historical streak data which can be used to display any kind of streak UI you like through the `historyPeriods` parameter. + + + +Here's an example of a git-style streak calendar built using the data in the response above: + + + + + + + Check this [example + repo](https://github.com/trophyso/example-study-platform/blob/demo/src/app/user-center/study-center/default-view.tsx) + for a React component that drives this UI using data from Trophy. + + +## Analytics + +The [streaks page](https://app.trophy.so/streaks) in Trophy shows data on active streaks, the average length of streaks and longest streaks. + + + + + +## Streak Freezes + +Trophy also supports streak freezes which can help prevent users from losing their streak if they accidentally miss a period. + +Learn more about streak freezes in the dedicated [streak freezes docs](/platform/streaks#streak-freezes). + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/guides/how-to-build-an-achievements-feature.mdx b/es/guides/how-to-build-an-achievements-feature.mdx new file mode 100644 index 0000000..6abbd59 --- /dev/null +++ b/es/guides/how-to-build-an-achievements-feature.mdx @@ -0,0 +1,252 @@ +--- +title: How To Build An Achievements Feature +description: Learn how to use Trophy to add an achievements feature to your web or mobile app. +subtitle: Learn how to use Trophy to add an achievements feature to your web or mobile app. +noindex: true +--- + +import SDKInstallCommand from "/snippets/sdk-install-command.mdx"; +import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; +import UserAchievementsRequestBlock from "/snippets/user-achievements-request-block.mdx"; +import AllAchievementsRequestBlock from "/snippets/all-achievements-request-block.mdx"; + +The guide outlines the full process of adding an achievements feature to your web or mobile app using Trophy. + +For illustration purposes we'll use the example of a study platform that uses achievements to incentivize and reward users for viewing flashcards. + + + 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). + + +## Pre-requisites + +- A [Trophy](https://app.trophy.so/sign-up) account +- About 10 minutes + +## Trophy Setup + +In Trophy, [Metrics](/platform/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 a metric that best represents the interaction you want to build achievements around. + +In the Trophy dashboard, head to the [metrics page](https://app.trophy.so/metrics) and create a metric. + + + + + +Once you've created your metric, head to the [achievements page](https://app.trophy.so/achievements) and create the achievements you want. You can find all the details on the types of achievements and the different use cases in the [achievements docs](/platform/achievements). + +For the purposes of this guide we've set up a couple of achievements based on an increasing number of flashcards flipped: + +- 10 flashcards +- 50 flashcards +- 100 flashcards +- 250 flashcards +- 1,000 flashcards + +We've also set up a few achievements related to [Streaks](/platform/streaks), but we won't go into detail on these in this guide. + + + For a full guide on adding a streaks feature to your web or mobile app, check + out our [full guide](/guides/how-to-build-a-streaks-feature). + + +In Trophy you track user interactions by sending [Events](/platform/events) from your code to Trophy APIs against a specific metric. + +When events are recorded for a specific user, any achievements linked to the specified metric will be **completed automatically** if the requirements are met. + +This is what makes building gamified experiences with Trophy so easy, it does all the work for you behind the scenes. + +## Installing Trophy SDK + +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: + + + +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 +TROPHY_API_KEY='*******' +``` + + + Make sure you **don't** expose your API key in client-side code. + + +## Tracking User Interactions + +To track an event (user interaction) against your metric, use the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event). + + + +The response to this API call is the complete set of changes to any features you've built with Trophy, including any achievements that were unlocked as a result of the event. + +{/* vale off */} + +```json Response [expandable] +{ + "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", + "eventId": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "total": 750, + "achievements": [ + { + "id": "5100fe51-6bce-6j44-b0hs-bddc4e123682", + "trigger": "metric", + "metricId": "5100fe51-6bce-6j44-b0hs-bddc4e123682", + "metricName": "Flashcards Flipped", + "metricValue": 500, + "name": "500 Flashcards Flipped", + "description": "Write 500 words in the app.", + "achievedAt": "2020-01-01T00:00:00Z" + } + ], + ... +} +``` + +{/* vale on */} + +Validate this is working by checking the Trophy [dashboard](https://app.trophy.so). + + + + + +## Displaying Achievements + +You have a number of options for displaying achievements in your application. Here we'll look at the most common options. + +### Pop-up Notifications + +We can use the response of the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event) to show pop-up notifications (or 'toasts') when users complete achievements. + +Here's an example of this in action: + +```ts Achievement Completed Pop-up +// Sends event to Trophy +const response = await viewFlashcard(); + +if (!response) { + return; +} + +// Show toasts if the user has unlocked any new achievements +response.achievements.forEach((achievement) => { + toast({ + title: achievement.name, + description: achievement.description, + image: { + src: achievement.badgeUrl, + alt: achievement.name, + }, + }); +}); +``` + + + + + + + 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. + + +### Displaying User Achievements + +To fetch all achievements a user has completed, use the [user achievements API](/api-reference/endpoints/users/get-a-users-completed-achievements). + + + + + You can also fetch incomplete achievements at the same time by passing + `includeIncomplete` as `'true'`. + + + + + + +### Displaying All Achievements + +If instead you want to display all achievements you've set up in Trophy as part of a globally accessible UI that isn't linked to a particular user, you can use the [all achievements API](/api-reference/endpoints/achievements/all-achievements). + + + +### Achievement Completion Stats + +Both the user achievements API and the all achievements API include completion +statistics like `completions` (the number of users that have completed an +achievement) and `rarity` (the percentage of users that have completed an +achievement). + + + + + +## Analytics + +The [achievements page](https://app.trophy.so/achievements) in Trophy shows how many users have completed each achievement you've set up. + + + + + +Additionally the analytics page on any metric in Trophy includes a chart that shows the progress of users through your achievements. + + + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/guides/how-to-build-an-energy-feature.mdx b/es/guides/how-to-build-an-energy-feature.mdx new file mode 100644 index 0000000..2132070 --- /dev/null +++ b/es/guides/how-to-build-an-energy-feature.mdx @@ -0,0 +1,217 @@ +--- +title: How To Build An Energy Feature +description: Learn how to use Trophy to add an energy feature to your web or mobile app. +subtitle: Learn how to use Trophy to add an energy feature to your web or mobile app. +noindex: true +--- + +import SDKInstallCommand from "/snippets/sdk-install-command.mdx"; +import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; +import UserPointsRequest from "/snippets/user-points-request-block.mdx"; +import UserEnergyResponse from "/snippets/user-energy-response-block.mdx"; +import UserPointsEventSummaryRequest from "/snippets/user-points-summary-request-block.mdx"; + +The guide outlines the full process of adding an energy feature to your web or mobile app using Trophy. + +For illustration purposes we'll use the example of a study platform that uses energy to meter the rate at which users can view flashcards. + + + 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). + + +## Pre-requisites + +- A [Trophy](https://app.trophy.so/sign-up) account +- About 10 minutes + +## Trophy Setup + +In Trophy, [Metrics](/platform/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 grant and consume energy from. + +In the Trophy dashboard, head to the [metrics page](https://app.trophy.so/metrics) and create a metric. + + + + + +Once you've created your metric, head to the [points page](https://app.trophy.so/points) and create a new points system called 'Energy'. + + + + + +Once created, you'll be taken to the configure page for the energy system where you can create [points triggers](/platform/points#types-of-triggers) for each of the ways you want to grant or consume energy. + + + + + +Use 'time' triggers to grant users with new energy on an hourly or daily basis, and use [other types](/platform/points#types-of-triggers) of triggers with negative values to consume energy from the different user interactions you want. + +In Trophy you track user interactions by sending [Events](/platform/events) from your code to Trophy APIs against a specific metric. + +When events are recorded for a specific user, Trophy will automatically check if any of the triggers set up against your energy system should be triggered, and process them accordingly. + +Trophy also takes care of automatically granting new energy to users over time in accordance with any 'time' triggers you've set up. + +This is what makes building gamified experiences with Trophy so easy, it does all the work for you behind the scenes. + +## Installing Trophy SDK + +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: + + + +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 +TROPHY_API_KEY='*******' +``` + + + Make sure you **don't** expose your API key in client-side code. + + +## Tracking User Interactions + +To track an event (user interaction) against your metric, use the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event). + + + +The response to this API call is the complete set of changes to any features you've built with Trophy, including any changes in energy as a result of the event, and from what triggers energy was consumed. + +{/* vale off */} + +```json Response [expandable] +{ + "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", + "eventId": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "total": 750, + ..., + "points": { + "energy": { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "name": "Energy", + "description": null, + "badgeUrl": null, + "total": 9, + "added": -1, + "awards": [ + { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "awarded": -1, + "date": "2021-01-01T00:00:00Z", + "total": 9, + "trigger": { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "type": "metric", + "metricName": "Flashcards Flipped", + "metricThreshold": 1, + "points": -1 + } + } + ] + } + }, + ... +} +``` + +{/* vale on */} + +Validate this is working by checking the Trophy [dashboard](https://app.trophy.so). + +## Metering Usage + +To prevent users from taking actions in your product based on energy, use the [user points API](/api-reference/endpoints/users/get-a-users-points) to fetch their current total energy. + + + +This returns data on the total energy the user has, allowing you to use the `total` property to control what actions a user can perform: + + + +Here's an example where a user is only allowed to view a flashcard if `total > 0` + +```ts +const energy = await trophy.users.points("user-id", "energy"); + +if (!energy) { + return; +} + +if (energy.total > 0) { + showNextFlashcard(); +} +``` + +You can then modify your trigger setup in Trophy and control the rate at which users can interact with your product right from the Trophy dashboard without needing to make any code changes. + +## Displaying Energy + +To fetch a users energy use the [user points API](/api-reference/endpoints/users/get-a-users-points). + + + +This API returns data on the user's total energy but can be configured to also return between 1 and 100 of the user's most recent energy changes by using the `awards` [query parameter](/api-reference/endpoints/users/get-a-users-points#parameter-awards). + + + +The [user points summary API](/api-reference/endpoints/users/get-a-users-points-summary) can also be used to drive chart-based UI, like showing users their energy usage over time. + + + +Here's an example of a UI that shows users their current energy, a chart showing their usage over time, and a list of their most recent changes in energy. + + + + + +## Analytics + +In Trophy your [energy system page](https://app.trophy.so/points), includes analytics charts that shows data on total energy awarded/consumed and a breakdown of exactly what triggers cause the most frequent changes in energy. + + + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/guides/how-to-build-an-xp-feature.mdx b/es/guides/how-to-build-an-xp-feature.mdx new file mode 100644 index 0000000..e34e73c --- /dev/null +++ b/es/guides/how-to-build-an-xp-feature.mdx @@ -0,0 +1,263 @@ +--- +title: How To Build An XP Feature +description: Learn how to use Trophy to add an XP feature to your web or mobile app. +subtitle: Learn how to use Trophy to add an XP feature to your web or mobile app. +noindex: true +--- + +import SDKInstallCommand from "/snippets/sdk-install-command.mdx"; +import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; +import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; +import UserPointsRequest from "/snippets/user-points-request-block.mdx"; +import UserPointsResponse from "/snippets/user-points-response-block.mdx"; +import UserPointsEventSummaryRequest from "/snippets/user-points-summary-request-block.mdx"; +import UserPointsEventSummaryResponse from "/snippets/user-points-summary-response-block.mdx"; + +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. + + + 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). + + +## Pre-requisites + +- A [Trophy](https://app.trophy.so/sign-up) account +- About 10 minutes + +## Trophy Setup + +In Trophy, [Metrics](/platform/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. + + + + + +Once you've created your metric, head to the [points page](https://app.trophy.so/points) and create a new points system called 'XP'. + + + + + +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. + + + + + +In Trophy you track user interactions by sending [Events](/platform/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. + + + XP can also be awarded to users based on achievements, streaks and other + triggers. See the dedicated [points docs](/platform/points#points-triggers) + for more information. + + +## Installing Trophy SDK + +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: + + + +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 +TROPHY_API_KEY='*******' +``` + + + Make sure you **don't** expose your API key in client-side code. + + +## Tracking User Interactions + +To track an event (user interaction) against your metric, use the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event). + + + +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. + + + If you use [Points Levels](/platform/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. + + +{/* vale off */} + +```json Response [expandable] +{ + "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 + } + } + ] + } + }, + ... +} +``` + +{/* vale on */} + +Validate this is working by checking the Trophy [dashboard](https://app.trophy.so). + +## Displaying XP + +You have a number of options for displaying XP in your application. Here we'll look at the most common options. + +### Pop-up Notifications + +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 +// 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()}`, + }); +} +``` + + + 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. + + +### Displaying User XP + +To fetch a users XP use the [user points API](/api-reference/endpoints/users/get-a-users-points). + + + +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](/api-reference/endpoints/users/get-a-users-points#parameter-awards). + + + +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. + + + + + +### User XP Chart + +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. + + + +Use the `aggregation`, `start_date` and `end_date` query parameters to control the data returned. Here's an example of XP data aggregated daily: + + + +And here's an example of the types of charts you can build with this data: + + + + + +## Analytics + +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. + + + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/platform/achievements.mdx b/es/platform/achievements.mdx new file mode 100644 index 0000000..ec0e08e --- /dev/null +++ b/es/platform/achievements.mdx @@ -0,0 +1,399 @@ +--- +title: Achievements +description: Learn how to use Achievements in a gamified product experience with Trophy. +"og:description": Use metric, API, streak and composite achievements to reward users for continued progress and to encourage them to discover new parts of your app. +icon: trophy +--- + +import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; +import AllAchievementsResponseBlock from "/snippets/all-achievements-response-block.mdx"; + +## What Are Achievements? + +Achievements are rewards that users can unlock as they use your platform. They can be used to reward users for making continued progress along core user journeys, or to motivate users to explore more nascent features. + +Achievements work best when designed to incentivize users to take actions that +are likely to lead to increased retention. + + + Use Trophy's [metric analytics](/platform/metrics#metric-analytics) to compare + the retention of each user interaction, then configure achievements around + these interactions to maximize retention impact. + + +Here we'll have a look of the types of achievements you can build with Trophy, the different ways to use them, and how to integrate them into your platform. + +Watch Charlie run walk through using achievements in a NextJS application: + + + + + +## Achievement Types + +Trophy offers four types of achievements, [Metric](#metric-achievements), [API](#api-achievements), [Streak](#streak-achievements) and [Composite achievements](#composite-achievements), detailed below. + +### Metric Achievements + +Metric achievements are tied to [Metrics](/platform/metrics) and are best used when you want to incentivize users to take the same action over and over again. + +Let's take the example of a study platform that uses Trophy to encourage users to view more flashcards with metric achievements as follows: + +- 1,000 flashcards +- 2,500 flashcards +- 5,000 flashcards +- 10,000 flashcards +- 25,000 flashcards +- 50,000 flashcards + +In this case you would create a metric called _Flashcards Flipped_ and create achievements against the metric for each milestone. + +Since these achievements are directly tied to the _Flashcards Flipped_ metric, Trophy will automatically track when users unlock these achievements as they [increment the metric](/platform/events#tracking-metric-events). + +When achievements are unlocked, Trophy includes information about the unlocked achievements in the [Event API](/api-reference/endpoints/metrics/send-a-metric-change-event) response, and automatically triggers [Achievement Emails](/platform/emails#achievement-emails) if configured. + + + +### API Achievements + +API achievements can only be completed once and are useful for rewarding users for taking specific actions. + +Common examples include: + +- A user completing their profile after signing up +- A user linking their social account to a platform +- A user sharing their product experience on social media + +API achievements serve as an easy way to reward users for completing any action that you think is important for retention. + +Just like metric achievements, API achievements can also trigger automated [Achievement Emails](/platform/emails#achievement-emails) if configured. + +### Streak Achievements + +Streak achievements are directly tied to a user's [Streak](/platform/streaks) and are automatically unlocked when users reach a particular streak length. + +You can create as many streak achievements as you like for increasing lengths of streak, for example 7 days, 30 days and 365 days to motivate users to use your app more and more. + +Just like metric and API achievements, you can add a custom name and assign a badge to streak achievements. + +### Composite Achievements + +Composite achievements unlock automatically when a user has completed all of the prerequisite achievements you select. They're ideal for creating tiered or mastery-level rewards that recognize users who have accomplished a specific set of milestones. + +For example, you could create a "Study Master" composite achievement that unlocks when a user has completed: +- 1,000 flashcards +- 7-day streak +- Complete onboarding + +Composite achievements are useful for creating progression pathways and encouraging users to engage across different areas of your app. Just like other achievement types, they can trigger [Achievement Emails](/platform/emails#achievement-emails) when unlocked. + +## Creating Achievements + +To create new achievements, head to the [achievements page](https://app.trophy.so/achievements) in the Trophy dashboard and hit the **New Achievement** button: + + + + + + + + Enter a name for the achievement. This will be returned from APIs and made available for use in emails and other areas of Trophy where appropriate. + + + + Enter a short description of the achievement. This will be returned from APIs + and made available for use in emails and other areas of Trophy where + appropriate. + + + + You can add a badge by uploading an image or by entering a custom image URL. + The URL is returned in API responses and used in emails and other areas of + Trophy where appropriate. + + + + Choose how you want this achievement to be unlocked. + +- Choosing **Metric** will mean the achievement will be automatically unlocked when the user's metric total reaches the achievement trigger value. + +- Choosing **Streak** will mean the achievement will be automatically unlocked when the user's streak length reaches the achievement trigger value. + +- Choosing **API Call** will mean the achievement will only be unlocked when explicitly marked as completed by your code through a request to the [complete achievement API](/api-reference/endpoints/achievements/mark-an-achievement-as-completed). + +- Choosing **Composite** will mean the achievement will be automatically unlocked when the user has completed all of the prerequisite achievements you select. + + + + + Once you've chosen the trigger type for the achievement, you need to set up the trigger settings. + +- If you chose the **Metric** trigger, you'll need to choose the metric and the user's total value that should unlock the achievement when reached. + +- If you chose the **Streak** trigger, you'll need to set the streak length that should unlock the achievement. + +- If you chose the **API Call** trigger, you'll need to choose a unique reference `key` you'll use to complete the achievement via the [API](/api-reference/endpoints/achievements/mark-an-achievement-as-completed). + +- If you chose the **Composite** trigger, you'll need to select the prerequisite achievements that must all be completed for this achievement to unlock. The composite achievement will automatically unlock when a user completes the final prerequisite. + + + + +You can assign attribute filters to an achievement to further restrict who can unlock them and when. + +- To limit a **Metric** achievement to only apply to events with specific [custom event attributes](/platform/events#custom-event-attributes), select an attribute and enter a value in the **Event Attribute** section. + +- To limit any type of achievement to only apply to a user with one or more specific [custom user attributes](/platform/users#custom-user-attributes), add attributes and the desired values in the **User Attributes** section. + + + + + Save the new achievement. + + + +## Managing Achievements + +Trophy has built in tools to help you test and control which achievements can be unlocked, by who and when, without affecting production. + +### Achievement Statuses + +Here's an overview of the different achievement statuses and what they mean. + +**Inactive** + +All achievements are created as inactive. Inactive achievements can't be completed and aren't returned in any achievement APIs. Users won't see them until you make them active. + +**Active** + +When you make an achievement active, it makes it 'live'. Users can complete it and it will be returned from all achievement APIs. + +**Locked** + +When you lock an achievement, users who haven't unlocked it yet won't be able to unlock it anymore, but users who have already unlocked it won't be affected. + +Locked achievements are only returned in APIs for users who have already achieved them. + +**Archived** + +Archived achievements can't be completed and aren't returned in any achievement APIs. + + + Once you archive an achievement it disappears from Trophy so be sure to only + archive achievements that you no longer need. + + +Archived achievements can be restored by [contacting support](#get-support). + +### Achievement Workflow + +Achievements can be moved through different statuses according to the following workflow: + +```mermaid +flowchart LR + A@{ shape: rounded, label: "Inactive" } + B@{ shape: rounded, label: "Active" } + C@{ shape: rounded, label: "Active" } + D@{ shape: rounded, label: "Locked" } + E@{ shape: rounded, label: "Archived" } + F@{ shape: rounded, label: "Locked" } + G@{ shape: rounded, label: "Active" } + H@{ shape: rounded, label: "Archived" } + A-->B + C-->D + D-->E + C-->E + F-->G + F-->H + style A fill:#FDFDEA,stroke:#F7F0D5 + style B fill:#F2FAF7,stroke:#E2F6E6 + style C fill:#F2FAF7,stroke:#E2F6E6 + style D fill:#eee,stroke:#ddd + style E fill:#ddd,stroke:#ccc + style F fill:#eee,stroke:#ddd + style G fill:#F2FAF7,stroke:#E2F6E6 + style H fill:#ddd,stroke:#ccc + +``` + +## Completing Achievements + +If you're using metric achievements, there's no need to explicitly _complete_ achievements. Once you've set up [metric tracking](/platform/events#tracking-metric-events) in your code, all achievements linked to the metric will be automatically tracked. + +Similarly, if you're using streak achievements, all achievements related to the user's streak will automatically be unlocked when a user reaches the respective streak length. + +Composite achievements are also completed automatically. They unlock as soon as a user has completed all of their prerequisite achievements. No additional API calls are needed. + +However if you're using any API achievements, you will have to mark them as completed for each user as appropriate. To do this, you can use the [Complete Achievement API](/api-reference/endpoints/achievements/mark-an-achievement-as-completed) using the `key` of the achievement you want to complete. + +This will return back a response that contains details of the achievement that was completed that can be used in any post-completion workflows, like showing an in-app notification. + +{/* vale off */} + +```json Response +{ + "completionId": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "achievement": { + "id": "5100fe51-6bce-6j44-b0hs-bddc4e123682", + "trigger": "api", + "name": "Finish onboarding", + "description": "Complete the onboarding process.", + "badgeUrl": "https://example.com/badge.png", + "key": "finish-onboarding", + "achievedAt": "2021-01-01T00:00:00Z" + } +} +``` + +{/* vale on */} + +## Backdating Achievements + +By default, whenever you move an achievement to 'Active' [status](#managing-achievements), Trophy will check if any existing users meet the requirements of the achievement and complete it for them behind the scenes. + +This means when you release new achievements into production, or edit an existing live achievement, backdating will happen automatically. + + + When achievements are completed in this way, users don't receive any + notifications this has happened. This is to prevent changes to your + achievements in Trophy resulting in users getting lots of notifications. + + +You can check how many users have completed achievements at any time on the [achievements page](https://app.trophy.so/achievements) in the Trophy dashboard. The _Users_ column in the achievements can update during backdating. + + + + + +## Using Badges + + + + + +You can assign a badge to any achievement by uploading an image—Trophy serves it and returns the URL—or by providing your own image URL. Relevant API responses include that value in `badgeUrl` for use as the `src` on `` tags. + +```json Response {8} +{ + "completionId": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "achievement": { + "id": "5100fe51-6bce-6j44-b0hs-bddc4e123682", + "trigger": "api", + "name": "Finish onboarding", + "description": "Complete the onboarding process.", + "badgeUrl": "https://example.com/badge.png", + "key": "finish-onboarding", + "achievedAt": "2021-01-01T00:00:00Z" + } +} +``` + +## Displaying Achievements + +Trophy has a number of APIs that support displaying achievements within your applications. Here we'll look at the different ways to use them and the types of UI's you can build. + + + Check out our [full guide](/guides/how-to-build-an-achievements-feature) on + adding an achievements feature to your app for more details. + + +### All Achievements + +To display a high-level overview of all achievements users can complete, use the [all achievements endpoint](/api-reference/endpoints/achievements/all-achievements). Use this data to build UI that gives users an idea of the progression pathways within your application. + + + + + +The all achievements endpoint returns a list of all achievements within your Trophy account. Each achievement returned also includes `completions` (the number of users who have compeleted the achievement) and `rarity` (the percentage of users who have completed the achievement) as follows: + + + +### User Achievements + +If instead you're building user-specific UI elements, then use the [user achievements endpoint](/api-reference/endpoints/users/get-a-users-completed-achievements) to return achievements a specific user has completed. + + + You can also include achievements that a user has yet to complete by including + the query parameter `includeIncomplete=true`. + + + + + + +## Achievement Analytics + +If you have achievements set up for any of your [Metrics](/platform/metrics), then the metric analytics page displays a chart that shows you the current progress of all Users as follows: + + + + + +## Frequently Asked Questions + + + + Use metric achievements for rewarding users for taking the same action over and over again, and to incentivise them to do it more. + + Use streak achievements for rewarding users for keeping their streak. + + Use API achievements when you want to reward users for taking specific actions that they only need to take once. + + Use composite achievements when you want to create tiered or mastery-level rewards that unlock when users complete a specific set of prerequisite achievements. + + + + + Achievements, like all gamification, offer the best retention when tightly aligned to the user's core reason for using your platform. + + Use Trophy's [metric analytics](/platform/metrics#metric-analytics) to compare the retention of each user interaction, then configure achievements around these interactions to maximize retention impact. + + + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/platform/emails.mdx b/es/platform/emails.mdx new file mode 100644 index 0000000..66eaeee --- /dev/null +++ b/es/platform/emails.mdx @@ -0,0 +1,843 @@ +--- +title: Emails +description: Learn how to use emails in a gamified product experience with Trophy. +"og:description": Use automated email flows to extend your gamification experience outside your app and remind users to keep coming back. +icon: mail +--- + +import { PlanBadge } from "/snippets/plan-badge.jsx"; + +Trophy can send automated emails to users based on key triggers without requiring any code. Here we'll look at what these triggers are, and how they can form part of your product's gamification experience. + +## Types Of Emails + +Trophy supports 4 types of emails, each of which is designed to suit a common scenario in building gamification experiences. + +All emails are optional, but all four can be used simultaneously and can be controlled from the [Emails](https://app.trophy.so/emails/configure) page in the Trophy dashboard. + + + + + +- **Achievement emails** are sent to users each time they unlock an [Achievement](/platform/achievements). +- **Recap emails** are sent to users on a pre-defined frequency to summarize progress. Recap Emails can be configured to be sent daily, weekly, monthly or yearly depending on your use case. +- **Reactivation emails** are sent to users after they become inactive with the goal of bringing them back to your app. +- **Streak emails** are automatically sent to users reminding them to extend their [Streak](/platform/streaks). + +## Sending Emails + +To start sending emails with Trophy, you'll need to verify your domain and add and activate email templates for the types you want to send. + + + Users can control which email notifications they receive through Trophy's + [user preferences API](/platform/users#notification-preferences). This allows + you to build a preference center in your application where users can opt in or + out of specific email types. + + +### Domain Verification + +Trophy supports sending emails from your own domain out-of-the-box. There are two ways to set this up, Single Sender Verification and DNS Verification. + +All domain settings can be found in the [Domains](https://app.trophy.so/emails/domains) page in the Trophy dashboard. + + + If you're looking to get set up quickly we recommend starting with Single + Sender Verification then setting up DNS Verification when you're ready to move + to production. + + + + + Quickly verify a single email address + + + Recommended for production use + + + +#### Sender Verification (Basic) + +During onboarding you'll be prompted to set up Sender Verification. This is a super simple way to quickly verify a single email address that you want to use with Trophy without needing to change any code or DNS settings. + +You'll be prompted to enter the email address, from name and reply-to email that you'd like Trophy to use when sending emails: + + + + + +Trophy will send a verification email to that address. Simply click the link in the email to let Trophy know what you own the address and you'll be good to go. + + + The email you receive will come from our email provider, Postmark. So keep an + eye out for 'Postmark Support' in your inbox! + + +Sender Verification is great for getting started but is limited in that you won't benefit from your existing email reputation and can only send emails from one address. + +Also, if you want to change the address in the future, you'll have to go through Sender Verification again which you can do in the [settings screen](https://app.trophy.so/integration?tab=domains). + +#### DNS Verification (Advanced) + + + This feature is available on the [Starter](/account/billing#starter-plan) + plan. + + +For production use we recommend DNS Verification. Once set up, this allows you to configure Trophy to send emails from any address on your domain. So if you want to change the address in future, you won't need to verify again. + +Completing DNS verification also gives you the full benefits of any existing domain reputation and is the best way to make sure your emails avoid the spam folder. + +This does however require adding a couple of new entries into your DNS to allow Trophy to verify you own the domain. + +Follow the steps below to set up DNS verification: + + + + Within the Emails page, you'll find the [Domains](https://app.trophy.so/emails/domains) tab which is where you'll configure everything related to the email domain Trophy will use to send emails on your behalf. + + + + Head down to the DNS Verification section and enter the domain you want to set up with Trophy. + + You can also also configure a custom Return Path. This is where Trophy will forward notifications of bounced emails to help you investigate deliverability issues: + + + + + + + + + + Once you've entered your email domain, Trophy will provide you with details for two new DNS records that you'll need to add to your DNS provider. + + + + + + - First is the **DKIM** record which is used by inbox providers like Google to verify the authenticity of emails. Enter the name and value provided as a new **TXT** record in your DNS provider. + - Second is the **Return Path** record. This is used to forward notifications of email bounces to your domain. Enter the name and value provided as a new **CNAME** record in your DNS provider. + + Here are the pages for the most common DNS providers on how to set up new records: + + + + [Add a TXT record](https://www.godaddy.com/en-uk/help/add-a-txt-record-19232) + [Add a CNAME record](https://www.godaddy.com/en-uk/help/add-a-cname-record-19236) + + + [Add DNS records](https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-dns-records/#create-dns-records) + + + [Add a CNAME record](https://www.namecheap.com/support/knowledgebase/article.aspx/9646/2237/how-to-create-a-cname-record-for-your-domain/) + [Add a TXT record](https://www.namecheap.com/support/knowledgebase/article.aspx/317/2237how-do-i-add-txtspfdkimdmarc-records-for-my-domain/#:~:text=this%20guide%20.-,DKIM%20records,-DKIM%20(DomainKeys%20Identified)) + + + + Note: If you're using a proxy like Cloudflare, make sure to turn it off for these records or Trophy won't be able to properly verify them. + + + + + + Once you've added the two records into your DNS settings, hit the refresh icon and Trophy will attempt to verify the records for you. + + Usually Trophy can verify records within a few minutes but bear in mind that DNS changes can take up to a few hours to fully propogate. + + If you see the following alert, the records may not have propogated yet, or there may be an issue with your setup. + + + + + + Once you're sure everything's set up correctly in your DNS provider, hit refresh and Trophy will attempt to verify your DNS records once again. + + As soon as you see the following message, you know everything's verified: + + + + + + + + + + Once your DNS records are verified, it's time to choose the email address you want to send emails from. Note that now you've fully verified your domain, you can change this address whenever you like without impacting deliverability. + + Simply enter the email address you want Trophy to send from, the name to show to users in the inbox when they receive emails and a support email address they can use to contact you if needed: + + + + + + + + + +### Email Triggers + +On the [email configuration](https://app.trophy.so/emails/configure) page, each email type has its own section. Add templates under the relevant type and activate one to start sending. + +#### Recap Emails + +**Recap** emails send weekly or monthly progress reports to users. You can change the frequency of these emails on the [integration](https://app.trophy.so/integration) page using the **Aggregation Period** setting. + + + + + +#### Reactivation Emails + +**Reactivation** emails send win-back messages to users after they become inactive. These emails are sent according to the following timeline: + +- After 3 days of inactivity +- After 5 days of inactivity +- After 7 days of inactivity +- After 14 days of inactivity +- After 30 days of inactivity + + + + + +#### Streak Emails + +**Streak** emails remind users to extend their streak before it expires. These emails are sent according to the streak frequency you configured on the [streaks page](https://app.trophy.so/streaks): + +- If your app uses a **daily streak**, this email will send four hours before the end of the day (in each user's timezone). +- For **weekly streaks**, it will send on Friday morning. +- For **monthly streaks**, it will send on the 25th of the month. + + + + + +#### Achievement Emails + +**Achievement** emails congratulate users when they complete an [achievement](/platform/achievements) you've configured in Trophy. These emails send between 5-9PM on the day the achievement is completed, or the next day at 5PM if it is past 9PM when the user completes the achievement. + + + + + +#### Limiting Emails to Specific Types of Users + +Each email template can be limited to users with specific [custom user attribute](/platform/users#custom-user-attributes) values. + +For each attribute you'd like to restrict the email to, click the plus icon next to the **User Filters** header for the email template, then select the attribute and enter the desired value. Only users that have **all** specified attribute values will receive emails from this template. + + + + + +#### Time Zones + +If you specify a timezone for each user through [User Identification](/platform/users#param-tz), Trophy will use that local time zone to schedule emails according to the logic specified above. If you do not provide time zones for the users you identify, Trophy will default to Eastern Time. + +## Designing Emails + +Trophy has a fully-featured block-based email builder that allows you to design templates, controlling all email copy and subject lines. On the [emails](https://app.trophy.so/emails/configure) page, add templates under each [email type](#email-triggers) section you want to use. + + + + + +### Default Templates + +By default, Trophy provides a template for each [email type](#types-of-emails) as a good starting point. The default templates can't be changed, but you can duplicate and customize these as you wish. + + + You can also create blank templates if you just want to start from scratch. + + +### Creating A New Template + +To create a new email template, follow the steps below. + + + + Within the Emails page, you'll find the + [Configure](https://app.trophy.so/emails/configure) tab, where each email type section contains its templates. + + + Click the *Add Template* button within the section for the email type you want to create a template for. + + + + + + + + Use the block-based editor to design your email template. + + + + + + + + +### Block Types + +Trophy's email builder supports a number of different block types that serve different purposes. All the basic components you'd expect to find in an email editor like paragraphs, headers, and images are called [Basic Blocks](#basic-blocks). There is also a set of more powerful components like charts and streaks that leverage Trophy's user activity data and gamification features. These are called [Smart Blocks](#smart-blocks). + + + + + +#### Basic Blocks + +Here's the full list of un-opinionated basic blocks that Trophy supports: + +- **Paragraph** - Good for short or longer text snippets. +- **H1, H2, H3** - Useful for headings of varying sizes. +- **Button** - Creates a call to action for users to take in emails. +- **Card** - Can nest other blocks, focusing attention. +- **Divider** - Good for separating content into logical sections. +- **Emoji** - Include any supported emoji, controlling size. +- **Image** - Upload any image and Trophy will render it in emails. +- **Spacer** - Good for giving blocks room to stand out. +- **Columns** - Useful for creating up to 3-column layouts with any content. +- **Logo** - Will render your organization's logo, set on the [Branding](https://app.trophy.so/branding) page. + +#### Conditional Blocks + +Trophy also has a powerful conditional rendering system powered by the _Conditional_ block type. + +By nesting any other block inside a conditional block and setting up the conditions logic, you can create almost any email design that will show different blocks in emails based on the evaluation of conditions at send time. + +If you're familiar with the `if/else` logical operators then this will feel very familiar to you. Otherwise here's a quick diagram to explain how it works. + +```mermaid +flowchart LR + B@{ shape: diamond, label: "First condition true?" } + C@{ shape: rounded, label: "Show first block" } + D@{ shape: diamond, label: "Second condition true?" } + E@{ shape: rounded, label: "Show second block" } + F@{ shape: rounded, label: "Show default block" } + + B-- Yes --->C + B-- No --->D + D-- Yes --->E + D-- No --->F +``` + +One common use case for the conditional block is to conditionally show a user their streak (one of Trophy's built-in [Smart Blocks](#smart-blocks)) and some streak-related motivational text only if they have an active streak. If the recipient isn't on a streak at the time the email is sent, then a simple message is displayed instead. + + + + + +This creates a powerful framework to design emails based on highly relevant and personalized user data and can be used to create a wide-range of emails for common gamification use cases. + +#### Smart Blocks + +Smart blocks are powerful components designed to support common gamification use cases and integrate with all of Trophy's features including metrics, achievements and streaks. + +You can find all smart blocks in the _Recommended Blocks_ section, and they are always recognizable by the icon. + +Watch Charlie walk through smart blocks and how they work: + + + + + +Read more about the use case of each smart block: + + + + The achievement progress chart block displays a chart with bars for up to five achievements for a particular metric, highlighting any that the recipient has unlocked. + + Choose the metric to display achievements for using the metric selector. + + + + + + + +{" "} + + + The achievements unlocked block displays the achievement the recipient has unlocked. This is only useful in the context of achievement unlocked emails. If the recipient hasn't unlocked any achievements, then this block won't show in emails. + +Where achievements have badges, these will be automatically shown, as well as the name of the achievement. + + + + + + +{" "} + + + The progress chart block shows the recipient's progress against a particular metric over the last 3 [aggregation periods](#aggregation-period). + + + + + + + + The streak block displays the recipient's current streak according to your account's streak settings. + + For daily streaks, a calendar block is shown displaying the last month's streak history. Weekly and monthly streaks display the last 7 periods of streak history. + + + + + + + + +### Email Variables + +Trophy provides an expansive set of email variables that can be used to insert highly relevant and personalized copy into the body of emails and subject lines. + +Variables bring context from your Trophy account, the recipient's progress data, and email specific settings to your email templates. + +Email variables can be inserted by typing `@` in any block that supports rich text, like headers, paragraphs, and buttons, and searching for your chosen variable. + +This will open up the variable editor window where you can configure variables as in the demo below. + +You can also use variables in email subject lines. + + + + + +See below for a full list of all email variables supported by the email builder, broken down by category. + + + +Variables related to the recipient of the email. + +- **Name**: The recipient's name, if set + +Additionally all [custom user attributes](/platform/users#custom-user-attributes) are available as email variables. + + + + Variables related to the recipients tracked event data againt metrics. + +Each Trophy metric supports the following email variables: + +- **Metric Name**: The name of the metric +- **Metric Units**: The metric's units +- **Current Total**: The sum of all the recipients event values to date +- **Change This Period**: Absolute change in the recipients current total in the current aggregation period +- **Change This Period (%)**: Percent change in the recipients current total in the current aggregation period +- **Percentile (All Time)**: The recipients current total compared to all other users for all time +- **Percentile (This Period)**: The recipients current total compared to all other users in the current aggregion period + +Additionally, the **Highest** and **Lowest** dynamic aliases support the same set of variables. When using these aliases, Trophy will pick the metric where the recipient has either the highest or lowest current total at send time. + +When using metric variables, you also have the option of filtering the data that any of the above variables reference through a [custom event attribute](/platform/events#custom-event-attributes). + + + + + +Similarly to metrics, each points system has the following variables that can be used to add dynamic data to email copy: + +- **Current Total**: The recipients total points +- **Change This Period**: Absolute change in the recipients total points in the current aggregation period +- **Change This Period (%)**: Percent change in the recipients total points in the current aggregation period +- **Percentile (All Time)**: The recipients total points compared to all other users for all time +- **Percentile (This Period)**: The recipients total points compared to all other users in the current aggregion period + +. + + + + + These variables are only relevant for [achievement emails](#achievement-emails). + + +When configuring a template for use with achievement emails, the following variables are available: + +- **Is Final Achievement**: If the achievement triggering the email is part of a series of metric achievements, this is true when the achievement is the final achievement in the series +- **Percent To Next Achievement**: If the achievement triggering the email is part of a series of metric achievements, this is the percentage until the user unlocks the next achievement in the series +- **Achievement Name**: The name of the achievement that triggered the email +- **Achievement Description**: The description of the achievement that triggered the email + +. + + + + Variables related to the recipient's streak. + +- **Has Active Streak**: True if the recipient has an active streak at the time the email is sent, false otherwise +- **Streak Length**: The length of the recipient's current streak +- **Streak Extended**: True if the recipient's streak was extended, false otherwise +- **Days Since Last Extended**: The number of days since the user last extended their streak + +. + + + + + These variables are only relevant for [reactivation emails](#reactivation-emails). + + +- **Message Number**: The number of the message in the reactivation sequence (1-5) + +. + + + + + These variables are only relevant for [recap emails](#recap-emails). + + +- **Current Period Date Range**: The start and end dates of the current aggregation period + +. + + + + +#### Advanced Usage + +There are a couple of additional options to consider when using emails variables. + +**Prefix & Suffix** + +You can provide a prefix and suffix for text variables like the recipients name, or the name of a particular metric. + + + + + +If the value of the variable fails to render when the email is sent, then Trophy won't display the variable or the prefix or suffix to keep emails clean. + +**Singular & Plural** + +When using numeric variables like the recipients current points total or metric total, you can also provide a singular and plural suffix to accompany it. + + + + + +### Text Variations + +Variations can be used to add randomness to text within emails sent by Trophy. This prevents emails from getting boring and helps improve open and click rates. + + + + + +Any block that supports text entry inculding H1, H2, H3 and paragraph support variations. + +You can also use variations to add randomness to email subject lines. + +To create a variation click the _Add Variation_ button on any block that supports them. + + + + + + + Variations do not support A/B testing yet, but this is on our roadmap so stay + tuned... + + +### Using The Editor + +The email template editor is a blank canvas for designing emails that look great in the inbox. Using pre-configured [Blocks](#block-types) makes it really easy to create email templates that suit common gamification use cases. Here we'll walk through how to best use the editor to create awesome looking emails. + +#### Adding Blocks + +To add a new block an email template hit the key, this will open the block selection dialog where you can choose the block you want to add. + + + + + +Or if you want to add a block immediately after another block, use the block menu. + + + + + +#### Arranging Blocks + +Blocks can be dragged up and down using the block menu. + + + + + +#### Text Formatting + +The email editor offers rich text formatting including bold, italics, [hyperlinks](https://www.youtube.com/watch?v=dQw4w9WgXcQ) and `code` formatting. + + + + + +### Font Style + +On the [email configure page](https://app.trophy.so/emails/configure), you'll find a setting to change the font style used in all emails. + + + + + +Other email styles are pulled from your Trophy account's [Branding](https://app.trophy.so/branding) settings. + +## Handling Unsubscribes + +All emails that Trophy sends include an unsubscribe link and message. This is important for maintaining compliance with regulations and it's not possible to hide it. + + + The message will read as follows: _You are receiving this email because you + are subscribed to progress report and achievement emails. If you no longer + wish to receive these emails, you can unsubscribe._ + + +Any recipient that clicks this link will be taken to a branded page that allows them to control which emails notifications they receive. + + + As an alternative to the unsubscribe link, you can also build a preference + center in your application using Trophy's [user preferences + API](/platform/users#notification-preferences). This gives users more granular + control over which types of emails they receive. + + +## Email Analytics + +Trophy has built-in analytics for all Emails that it sends. This includes: + +- Recipients (The total number of people that recieved the email) +- Open rate (The percentage of recipients that opened the email) +- Click rate (The percentage of recipients that clicked on at least one link in the email) +- Retention rate (The percentage of users that opened the email that came back and used your platform after at least 2 days) + + + + + +## Frequently Asked Questions + + + + Yes! Sending emails from your own domain is supported out-of-the-box. Follow the steps in the section on [sending emails](#sending-emails) to get started. + + +{" "} + + + No, we don't limit how many emails you can send. However, we do monitor usage + to prevent abuse. + + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/platform/events.mdx b/es/platform/events.mdx new file mode 100644 index 0000000..792a440 --- /dev/null +++ b/es/platform/events.mdx @@ -0,0 +1,221 @@ +--- +title: Events +description: Events are data objects that represent individual user interactions against metrics in Trophy. +"og:description": Events are data objects that represent individual user interactions against metrics in Trophy. +icon: radio +--- + +import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; +import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; +import IdempotentEventTracking from "/snippets/idempotent-event-tracking.mdx"; + +## What are Events? + +Events represent individual user interactions against [Metrics](/platform/metrics) in Trophy. One event corresponds to a single interaction made by a single user. + +When you [integrate metrics](#tracking-metric-events) into your platform, you're setting up your platform to continuously stream events to your Trophy metrics for each user interaction. These interactions then drive all the gamification features you set up around these metrics. + +## Key Attributes + +### Event Value + +The `value` of an event is the numerical amount that will be added to the user's total metric count as a result of the user interaction it relates to. + + +The value of an event can be positive or negative, and can be a whole number or a decimal. + + +## Custom Event Attributes + + + This feature is available on the [Pro](/account/billing#pro-plan) plan + + + + + + +You can specify a number of custom event attributes to help you track additional information relevant to your use case against events you send to Trophy. + +For example, a language learning app might have a 'Questions Completed' metric and use a custom event attribute to store wether the answer to each question was correct. + +Similarly a fitness app might use an 'Exercises Completed' metric and use a custom event attribute to store the weight that was used in each exercise and another custom event attribute to store how long the exercise lasted. + +Using custom event attributes in this way allows you to enrich events in Trophy with additional context relevant to your use case and use it to power even more engaging gamification features. + +### Creating Attributes + +To create a new custom event attribute, head to the metrics page in the Trophy dashboard and hit the _Add Event Attribute_ button. + +Give the attribute a name and a unique key, you'll use the key when referencing the attribute in API calls. + + + + + +### Setting Attributes + +To set the value of a custom attribute on an event, pass its value in the `attributes` object in your metric tracking code. + + + Trophy will only set values of attributes that have first been created + in the dashboard. We do this to help you keep a clean set of attributes and + prevent accidental overwrites. + +If you receive an error similar to the following then you might have miss-spelled the attribute key in the request, or you need to create the attribute first in the Trophy dashboard: + +```json +{ + "error": "Invalid attribute keys: device. Please ensure all attribute keys match those set up at https://app.trophy.so/metrics." +} +``` + + + +Here's an example of an event payload where the values of two attributes, `device` and `duration`, are set: + +```json {7-10} +{ + "user": { + "id": "18", + "tz": "Europe/London" + }, + "value": 25, + "attributes": { + "device": "ios", + "duration": "120" + } +} +``` + +### Using Attributes + +Custom event attributes can be used to power more advanced triggers for achievements and points and can be used in email templates to customize copy and to control the data shown in charts. + +#### Advanced Feature Triggers + +Custom event attributes can be used to set up achievements or points triggers that only track events with specific attribute values. Follow the links to the relevant pages below to learn more. + + + + Configure achievements that can only be unlocked by events with certain + attribute values. + + + Set up points triggers to only award points from events with specific + attribute values. + + + +#### Email Customization + +If you use any Trophy [Emails](/platform/emails), event attributes can be used to customize the data shown in certain email blocks. + +Firstly, when using metric-based variables in email copy you can use event attributes to further control what data the variable refrences. + +For example here's a case where we use an email variable to tell users what their total number of workouts they've done on different gym equipment is using a metric 'Workouts' and an attribute 'Equipment': + + + + + +Secondly, here's an example where we add a chart to an email that shows users how many workouts they've done on a bike over time: + + + + + +There's a huge number of possibilities here, so get creative! + +## Tracking Metric Events + +Each metric has a unique `key` which you can use to reference and track events against it in your code. You can find the `key` in the metric settings page. + +To start tracking user interactions as events against your Trophy metrics, use the [Metrics API](/api-reference/endpoints/metrics/send-a-metric-change-event) or one of our type-safe [Client SDKs](/api-reference/client-libraries) supported in most major programming languages. + +Here's an example where a fictional study platform is using a metric to track the number of flashcards flipped by each student. Each time an student interacts, the platform sends an event to Trophy telling it how many flashcards they viewed: + + + +Any [Achievements](/platform/achievements), [Streaks](/platform/streaks), [Points](/platform/points) or [Leaderboards](/platform/leaderboards) that have been set up against this metric will be automatically processed, and the response will contain any updates to the user's progress that are a direct result of the event occurring: + + + +In this example the response includes the following: + +- The user's newly unlocked achievements as a result of the event +- The user's latest streak as result of the event +- The user's latest points for each points system that changed as a result of the event +- The user's latest leaderboard data for each leaderboard that changed as a result of the event + +With a little bit of custom code, this response data can be used to drive any in-app experience you wish including: + +- Triggering in-app notifications +- Sound effects +- Animations + +Watch Charlie integrate metric tracking into a simple NextJS application using the Trophy [NodeJS SDK](/api-reference/client-libraries): + + + + + +### Idempotent Events + +Trophy supports enforcing uniqueness on events so that users cannot increase a metric by taking the same exact action over and over. + +For example, a language learning app could specify that users can only increase the `lessons-completed` metric by 1 for each unique lesson completed, so if they complete the same lesson twice only the first counts. + + + +This helps keep your codebase free of logic that checks if users have completed actions before, and can instead trust Trophy to uphold the constraints you need. + +To use idempotent events, use the `Idempotency-Key` header in the [metric event API](/api-reference/endpoints/metrics/send-a-metric-change-event). + +[Learn more about idempotency](/api-reference/idempotency). + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/platform/leaderboards.mdx b/es/platform/leaderboards.mdx new file mode 100644 index 0000000..4b38191 --- /dev/null +++ b/es/platform/leaderboards.mdx @@ -0,0 +1,353 @@ +--- +title: Leaderboards +description: Learn how to use Leaderboards in a gamified product experience with Trophy. +"og:description": Create daily, weekly or monthly competitions that rank users based on metrics, points or streaks and increase user engagement. +icon: /icons/leaderboard.svg +--- + +import LeaderboardSingleAttributeRequest from "/snippets/leaderboard-rankings-request-single-attribute.mdx"; +import LeaderboardMultiAttributeRequest from "/snippets/leaderboard-rankings-request-multiple-attributes.mdx"; + +## What are Leaderboards? + +Leaderboards are social competitions between users of your application. Use leaderboards to increase engagement and foster social interaction. + + + + + +## Types of Leaderboards + +In this section we outline the different types of leaderboards supported in Trophy and when to use each one. + +### Perpetual Leaderboards + +Perpetual leaderboards never reset. Once started they continually track and rank users progress over time forever, or until the configured [end date](#end-dates). + +Use perpetual leaderboards when you want to create all-time rankings of user activity. + +### Repeating Leaderboards + +Repeating leaderboards can be configured to reset after any arbitrary number of days, months or years. + +In Trophy each instance of a repeating leaderboard is called a **'run'**. For example, a monthly leaderboard would have 12 runs in a year, but a daily leaderboard would have `n` runs in a month where `n` is the number of days in a given month. + +Trophy tracks the rankings in each run of a repeating leaderboard individually and provides [APIs](/api-reference/endpoints/leaderboards/get-leaderboard) to fetch ranking data on historical runs. + + + We recommend using repeating leaderboards over perpetual where possible as + repeating leaderboards give new users an equal chance to compete with existing + users, helping to prevent leaderboards from becoming stale. + + +#### Handling Time Zones + +If you have tracked users' [time zones](/platform/users#param-tz) with Trophy, these will be used to ensure that each user has an equal chance of winning no matter where they are in the world. + +In practice this means leaderboards are finalized and winners chosen about 12 hours after they naturally finish in UTC to allow users in all time zones to make their final push. + +#### Tips for Weekly Leaderboards + +To create a weekly leaderboard, set up a [repeating leaderboard](#repeating-leaderboards) on a 7 day schedule and set the start date to be the next occurring first day of the week. + +While you wait for the start date to come around, the leaderboard will be in `scheduled` status and will automatically go live on the start date. + +## Ranking Logic + +Leaderboards in Trophy are configurable to rank participants in a number of different ways to support common use cases. + +### Ranking Methods + +The ranking method of a leaderboard determines on what dimension participants will be ordered. + + + + + +#### Metric Rankings + +Metric leaderboards are linked to an existing Trophy [Metric](/platform/metrics) and rank users based on their total metric value. + +Use metric leaderboards if you only want to rank users based on a single interaction. + +#### Points Rankings + +Points leaderboards are linked to an existing Trophy [Points System](/platform/points) and automatically rank users according to their total points. + +Use a points leaderboard if you want to rank users based on a combination of metrics, achievements or other Trophy features. + +#### Streak Rankings + +Streak leaderboards rank users based on their current streak length. + + + Streak leaderboards can only be [perpetual](#perpetual-leaderboards). + + +### Ranking Breakdowns + +If you have a large user base, it's best practice to split up leaderboard participants into smaller, more socially-connected groups. This often leads to higher engagement than when using global leaderboards. + +Leaderboards in Trophy can be configured to group users into smaller groups according to a specific [custom user attribute](#custom-user-attributes). + + + When using leaderboard breakdowns, [participant limits](#participant-limits) + apply at the group level, not overall. + + +To set up a leaderboard breakdown head to the leaderboard configuration page and create or select your user attribute in the 'Breakdown Attribute' field. + +Trophy will automatically start grouping users into smaller leaderboards based on the values of your chosen attribute for each of your users. + + + + + +To fetch rankings for a particular group of users with a specific attribute value, use the [leaderboard rankings API](/api-reference/endpoints/leaderboards/get-leaderboard), specifying the attribute value in the [`userAttributes` parameter](/api-reference/endpoints/leaderboards/get-leaderboard#parameter-user-attributes) as follows: + + + +If you wish to fetch rankings for a particular group of users with a specific combination of user attributes, create a new attribute to track the combination and use that as your breakdown attribute as follows: + + + +## Start & End Dates + +Use start and end dates to control the window within which leaderboards are actively ranking users. + + + + + +### Start Dates + +Leaderboards in Trophy can be set to start at a future date of your choice. This is often useful to allow some time for last minute changes or adjustments before leaderboards start ranking users. + +Leaderboards with a start date in the future are scheduled and automatically go live on the start date you choose. + +### End Dates + +Leaderboards in Trophy can have end dates. If you set an end date on a leaderboard then after that date it will enter `finished` status and rankings will be finalized and winners chosen. + + + Due to differences in [time zones](#handling-time-zones), leaderboards can be + finalized up to 12 hours after the end date in UTC to allow all users to reach + the end date according to their local clock. + + +## Participant Limits + +Leaderboards in Trophy have a maximum number of participants of **1,000**. However a leaderboard can be configured to have any arbitrary number of participants to support use cases like _Top 100_ or similar. + + + + + +If a leaderboard already has a number of participants that matches its configured maximum, new users will have to surpass the score of the lowest rank to join the leaderboard. + + +We chose to limit leaderboard size to help guide customers on best practice. + +Traditionally, leaderboards with lots of participants fail to engage users beyond the top 1%, and have a **negative** impact on users in the bottom half, particularly new users. To avoid this, keep your leaderboards small by breaking them down into smaller leaderboards using [breakdown attributes](#ranking-breakdowns). + +For more background on the negative effects on global leaderboards, read this [blog post](https://www.trophy.so/blog/leaderboard-only-motivating-top-one-percent). + + + +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. + +## Creating Leaderboards + +To create a leaderboard, head to the [leaderboards page](https://app.trophy.so/leaderboards) in the Trophy dashboard and hit the _New Leaderboard_ button. + + + + + + + + Choose a name for the leaderboard. + + + + Enter a unique reference key for the leaderboard. This is what you'll use to reference the leaderboard in your application code. + + + + Choose one of the [methods](#ranking-method) that the leaderboard will rank users by: + + - **Metric**: Ranks users by total value of a chosen metric + - **Points**: Ranks users by total points in a chosen points system + - **Streak**: Ranks users by current streak length + + + + + Choose the maximum number of participants the leaderboard should support. The + current upper limit supported by Trophy is 1,000. Read [this + section](#participant-limits) to learn more about how we chose this limit. + + + + Hit save and head to the configure page to set up [start & end dates](#start-and-end-dates), [repeating leaderboard schedules](#repeating-leaderboards) and more. + + + + +## Managing Leaderboards + +Leaderboards in Trophy have a number of statuses to help you control when users and how users can join them. + +### Leaderboard Statuses + +Leaderboards can have one of the following statuses: + +- `Inactive` +- `Scheduled` +- `Active` +- `Finished` +- `Archived` + +All new leaderboards are created as `Inactive`. While inactive, any properties or settings of the leaderboard can be changed, they won't be visible to users and users can't join them. + +Once you're ready for users to start participating, you can make it `Active`. This means Trophy will start tracking users' activity and entering them into leaderboards. + +Leaderboards that have been configured with a [start date](#start-dates) in the future can't be made active, they can only be `Scheduled`. Once the start date is past, Trophy will automatically make them `Active` and start accepting participants. + +If a leaderboard has an [end date](#end-dates), then once it has past Trophy will automatically move it to `Finished` status and stop monitoring user activity. Once a leaderboard has finished it won't be visible to users but you can still query APIs to get rankings for historical runs. + +If you decide you no longer need a leaderboard, you can move it to `Archived` status. + + + Once a leaderboard is archived, it can only be restored by contacting support. + + +## Displaying Leaderboards + + + Check out our [full guide](/guides/how-to-build-a-leaderboards-feature) on + adding leaderboards to your app for more details. + + +## Leaderboard Analytics + +Trophy has built-in analytics to help you understand how users are engaging with your leaderboards. + + + + + +### Total Unique Participants + +This chart shows how many unique users have participated in any run of a leaderboard over time. This is useful to understand how many of your users actually take part in leaderboards and how [participant limits](#participant-limits) are affecting this. + + + + + +### Active Users + +This chart show the number of users who have changed rank at least once in a given leaderboard. This is useful to get a sense of how competitive the average user is in a particular leaderboard. + + + + + +### Rank Changes + +This chart shows the total number of rank changes in a particular leaderboard over time. This is useful to understand how competitive users are across the board. + + + + + +### Score Distribution + +This chart is a histogram of users' scores in a particular leaderboard. This is useful to get a sense of how bunched up or spread out users are, and what sections of the rankings are the most competitive. + + + + + +## Frequently Asked Questions + + + + We limit leaderboards to 1,000 participants. + + Read more about this in the [dedicated section](#participant-limits) of this page. + + + +{" "} + + + Trophy supports running repeating leaderboards on any arbitrary number of + days. So a weekly leaderboard would just be a leaderboard that repeats every 7 + days. Read [this section](#tips-for-weekly-leaderboards) for more tips of + creating weekly leaderboards. + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/platform/metrics.mdx b/es/platform/metrics.mdx new file mode 100644 index 0000000..55a7c03 --- /dev/null +++ b/es/platform/metrics.mdx @@ -0,0 +1,112 @@ +--- +title: Metrics +description: Learn how to model user interactions using Trophy Metrics. Model any user interaction and trigger automated gamification flows with just a few lines of code. +"og:description": Metrics are flexible data primitives that can be used to model any user interaction and drive a wide range of gamified features. +icon: box +--- + +## What are Metrics? + +All gamification features are centered around user interactions. At Trophy, we use the term **Metrics** to refer to the data objects that model those interactions within your web or mobile app. + +Metrics are un-opinionated, meaning they can be used to model absolutely any user interaction you can think of. Examples could include: + +- Followers, likes or posts +- Tasks completed +- Miles ran +- Lessons completed +- Words written +- ... + +Metrics are the building blocks of the infrastructure that powers Trophy's gamification features. Each user interaction that relates to a metric is stored as an [Event](/platform/events). + +Events are stored and processed in chronological order. At any particular point in time, the combined state of a user's event history reflects their overall progress on your platform. + +## Key Attributes + +Here we describe the key attributes that allow you to create metrics to best fit your use case. + +### Units + +You can easily assign units to metrics in the dashboard for either: + +- Arbitrary numbers (tasks, posts, messages etc.) +- Currencies ($, £, €) + +## Creating Metrics + +To create a Metric, head over to the [Metrics](https://app.trophy.so/metrics) page within Trophy and hit the **New Metric** button: + + + + + + + + Choose a name for the metric. + + + + Save the new metric. + + + + Configure additional settings like metric units or currency if applicable on the metric configure page. + + + +## Metric Analytics + +Trophy has a built-in analytics dashboard for each metric you create. It shows you: + +- The total value of all tracked events against the metric +- The number of users actively making progress against the metric +- The metric's [Achievement Completion Chart](#achievement-completion-chart) + + + + + +### Achievement Completion Chart + +The achievement completion chart shows the current state of the userbase in terms of the number of users who have completed each achievement that you've configured against this metric. + +[Learn more about achievements](/platform/achievements). + + + + + +## Frequently Asked Questions + + + + As many as you want! + + + + No, metrics are purely server-side and only support the business logic around achievements, streaks and other workflows like sending emails. + + They don't control any in-app UI, but our APIs provide all the data you need to build whatever UI you want. + + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/platform/overview.mdx b/es/platform/overview.mdx new file mode 100644 index 0000000..0d282a5 --- /dev/null +++ b/es/platform/overview.mdx @@ -0,0 +1,74 @@ +--- +title: Overview +description: Learn the key concepts behind Trophy's gamification system. +"og:description": Learn the key concepts behind Trophy's gamification system including Metrics, Events, Achievements and Streaks. +--- + +Trophy is built around a set of key concepts that all gamification experiences have in common, while still offering enough customization for you to build any kind of gamified experience you need. + +## Platform Concepts + +Trophy's platform concepts are scalable building blocks that support all of Trophy's features and are the main things for developers to get comfortable with. + + + + Model how users interact with your application. + + + Track user interactions against metrics. + + + Tell Trophy about the people using your application. + + + +## Gamification Concepts + +Trophy's gamification concepts are flexible primitives built on the infrastructure concepts that support a wide range of common gamification use cases. + + + + Encourage users to make continued progress or to take specific actions. + + + Motivate users to build regular usage habits. + + + Reward users with sophisticated points systems. + + + + + } + href="/platform/leaderboards" + > + Create friendly competitions to increase user engagement. + + + Deliver personalized lifecycle emails to users to increase retention. + + + Drive automated notification flows using personalized gamification data + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/platform/points.mdx b/es/platform/points.mdx new file mode 100644 index 0000000..fffab11 --- /dev/null +++ b/es/platform/points.mdx @@ -0,0 +1,504 @@ +--- +title: Points +description: Learn how to build points-based systems using Trophy +"og:description": Use points systems to reward users for in-app actions. +icon: sparkle +--- + +import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; +import MetricChangeEventPointsLevelExcerpt from "/snippets/metric-change-event-points-level-excerpt-block.mdx"; +import PointsHistogramSummaryResponse from "/snippets/points-histogram-summary-response-block.mdx"; +import PointsLevelSummaryResponse from "/snippets/points-level-summary-response-block.mdx"; +import PointsLevelsListResponse from "/snippets/points-levels-list-response-block.mdx"; +import PointsSystemResponse from "/snippets/points-system-response-block.mdx"; +import UserPointsResponse from "/snippets/user-points-response-block.mdx"; +import UserPointsEventSummaryResponse from "/snippets/user-points-summary-response-block.mdx"; +import WebhookPointsLevelChangedPayload from "/snippets/webhook-points-level-changed-payload-block.mdx"; + +## What is a Points System? + +Points systems are used to create counters that track users' interactions with [Metrics](/platform/metrics), [Achievements](/platform/achievements) and [Streaks](/platform/streaks). You can then build features like 'XP' and 'Energy' around these counters within your product. + +## Use Cases + +### Rewards + +Points systems can be used to create features like 'XP' or 'Gems' that reward users for a number of interactions at different rates. + +In this way points can be used to weight the value of certain interactions differently to others to reward users for taking the actions you consider most closely correlated to retention. + +### Metering + +Points systems can also be used to create features like 'Energy' that meter usage of your product in a way that gives you control over promoting and restricting user activity. + +This allows you to control the rate at which users can use your product with a flexible mechanic that sits outside your codebase. + +## Creating Points Systems + +Trophy let's you set up multiple points systems for different use cases within your application. + + + + + +To create a points system, head to the [points page](https://app.trophy.so/points) and follow the steps below. + + + + Give the new points system a name, and a unique key. The key is what you'll + use to reference the points system in APIs and in email templates if + relevant. + + + You can also give the points system a description which is returned from + APIs to be displayed in your application. + + + If you want to limit the number of points that each user can have in your new system, set a value in the 'max points' field. + + + Any [points triggers](#points-triggers) that you configure against this points system will respect the maximum set. + + + + You can assign a badge or logo by uploading an image or by entering a + custom image URL. APIs return a `src`-friendly URL for display in your + application. + + + +## Points Triggers + +In Trophy, points are awarded to or deducted from users through triggers. These define the different mechanics that make up your points system. + +You can add as many triggers as you like to each points system you set up, allowing you to create different logic for how points are awarded or deducted for different points systems. + +### Types of Triggers + +There are multiple types of triggers in Trophy that can be used to award or deduct points in different ways. + +#### Metric Triggers + +Points can be awarded or deducted continually as users increment [Metrics](/platform/metrics). You can choose to award or deduct any arbitrary number of points at any arbitrary metric threshold, for example "award 10 points for every 3 tasks completed". + +#### Streak Triggers + +Points can be awarded or deducted for reaching any arbitrary length of a [Streak](/platform/streaks), for example "award 50 points for every 7 days streak". + +#### Achievement Triggers + +Points can be awarded or deducted when users unlock specific [Achievements](/platform/achievements), for example "award 100 points when users completed the `profile-completed` achievement". + +#### Time-based Triggers + +Points can be awarded or deducted at repeating time intervals, every hour or every day. For example "award 10 points every 3 hours". + +#### User Identification Triggers + +Points can be awarded when users are first identified in Trophy, useful for granting an initial amount of points when they sign up to your product. + +### Creating Triggers + +To create a new points trigger, head to the points system that you want to create a trigger for and follow the steps below. + + + All new points triggers are created as 'Inactive' to allow testing and + balancing before deployment to production. + + + + + + + + + Choose how you want points to be awarded or deducted as described by the available [trigger types](#types-of-triggers). + + + + Once you've chosen the points trigger type, you need to set up the trigger settings. + +- If you chose the **Metric** trigger, you'll need to choose the metric and the threshold amount at which to award or deduct points. + +- If you chose the **Streak** trigger, you'll need to set the streak length that should award or deduct points. + +- If you chose the **Achievement** trigger, you'll need to choose the achievement that should award or deduct points when completed. + +- If you chose the **Time** trigger, you'll need to choose the time unit you want to award or deduct points on (hours or days) and the number of those time units that you want to award or deduct points after. + +- If you chose the **First User Identification** trigger, you won't need to add any additional configuration. + + + + + Once your trigger is configured, set the number of points to award or deduct + when fired. + + + + You can assign attribute filters to a points trigger to further restrict when they apply. + +- To limit a **Metric trigger** to only apply to events with specific [custom event attributes](/platform/events#custom-event-attributes), select an attribute and enter a value in the **Event Attribute** section. + +- To limit any type of trigger to only apply to a user with one or more specific [custom user attributes](/platform/users#custom-user-attributes), add attributes and the desired values in the **User Attributes** section. + + + + + Save the new points trigger. + + + Once you're happy that your new trigger will behave as expected, change its status to active to make it live. + + + +## Balancing Points + +Running an effective points system requires finding the optimal pace at which users earn points. Too fast, and users will get points fatigue, rendering them useless. Too slow, and users may get bored and churn. + +Trophy's preview tool can model different scenarios to help you determine how frequently users should earn points in each of your points systems. + + + + + +## Points Boosts + +Points boosts are multipliers that you can use to increase the number of points awarded to users during a specific time period. + +This section explains how boosts work and how they can be used to increase your application's retention and engagement. + +### Boost Targeting + +There are a few different ways you can use boosts in Trophy. + +- **Global boosts** multiply points earned by all users during the boost window, or can be scoped to a specific user cohort by using [custom user attributes](/platform/users#custom-user-attributes). +- **User-specific boosts** only multiply points earned by a single user within the boost window. + +Typically, global boosts are used to increase platform-wide engagement during key calendar events like Black Friday/Cyber Monday in e-commerce, New Year in fitness and exam season in ed-tech platforms. + +Conversely, user-specific boosts are commonly used to provide additional incentives for users to take actions that are correlated with higher retention. + +### Creating Boosts + + +User-specific boosts can only be created programmatically through the [Admin API](/admin-api/endpoints/points/create-boosts). + + +To create a global points boost in the Trophy dashboard, follow the steps below. + + + + + + + + Go to the [points page](https://app.trophy.so/points) and click on the points system you want to create a boost for. Then navigate to the Boosts tab and click the *New Boost* button. + + + Choose a name for your boost that clearly describes what it's for. For example "2X Boost - All Users - Christmas 2026". + + + Set the numeric value that the boost will multiply points earned by. If you choose a decimal multiplier, you'll have the option to choose the [rounding behavior](#boost-rounding) the boost should use. + + + Choose a start and (optionally) an end date for your boost. If you choose a future start date, Trophy will schedule the boost to go active on your chosen date. Similarly if you set an end date, Trophy will take care of stopping your boost for you. + + + + + This feature requires [custom user attributes](/platform/users#custom-user-attributes) which are available on the [Pro](/account/billing#pro-plan) plan. + + + If you want your boost to only affect a specific user cohort, set user attribute conditions that match those of your target cohort. Trophy will take care of only applying the boost to points earned by users with matching user attribute values. + + + + + + + Click *Save Boost* to save your new boost to your account. The boost will be in the inactive state by default. To activate it, click the dropdown to the right of the boost name on the Boosts page and click Activate. + + + +### Boost Multipliers + +Every boost has a single multiplier value that increases the total points earned by users affected by that boost. + +Boost multipliers must be positive numbers, but can be decimals to support scenarios where percentage boosts like '50% more points' may be required. + +### Boost Stacking + +Points boosts in Trophy stack through [multiplication](#boost-multipliers). This is to support allowing users to benefit from multiple boosts simultaneously. + +To demonstrate stacking consider a 2X, 1.5X and a 3X boost all active within the same time period. The resulting boost multiplier for a user that qualifies for all three boosts would be as follows: + +```txt Boost Stacking Example +Overall Multiplier (3 boosts) = 2X * 1.5X * 3X = 9X +``` + +### Boost Rounding + +Trophy supports [floating point metric event values](/platform/events#event-value), but takes care of rounding points to integers automatically. + +However, when using decimal [boost multipliers](#boost-multipliers), there may be some scenarios where this default rounding is not enough to always produce integer points values. + +Therefore Trophy offers three rounding modes to provide additional control on rounding behavior specifically when boosts are involved in points calculations. + +- **Round Down**: By default, Trophy will round points down to the nearest integer when boost multiplication results in a decimal. +- **Round Up**: Trophy rounds decimal points up to the nearest integer after boost multiplication. +- **Round Nearest**: Trophy will round points to the nearest integer, up or down, based on the final value after boost multiplication. For example `1.2` is rounded to `1` and `1.7` is rounded to `2`. + + +In scenarios where points are used to mimic platform credit mechanics, it's recommended to use the default rounding down behavior to protect against slippage. + + +Simply choose your preferred rounding mode when [creating boosts](/platform/points#creating-boosts) through the Trophy dashboard. + +## Points Levels + +Points levels are discrete milestones you define on a points system in Trophy. + +Each level has a threshold. When a user's total points in the system exceeds this threshold, Trophy assigns them that level automatically. You do not need to maintain level logic in your own codebase. + +Use levels for rank tiers, progression UI, reward tiers, or analytics. Trophy keeps each user's current level in sync whenever they earn or lose points in that system. + +### Configuring levels + +To set up levels for a points system, open it from the [points page](https://app.trophy.so/points) in the Trophy dashboard and use the levels tab to add or manage levels. + + + + + + + + From the points page, select the points system you want to add levels to. + + + From your points system page, open the levels tab. + + + Create each level with a stable key, a display name, optional description, badge image, and the points threshold required to reach that level. + + Thresholds should reflect how you want progression to feel as users accumulate points from your triggers. + + + Save your levels. Trophy will evaluate totals against these thresholds on every points change. + + + +### Displaying Levels + +Most teams combine three approaches: a static picture of every level (for progression screens), the user’s current level (for headers and profile), and immediate feedback when an action pushes them into a new level. + +#### Displaying All Levels + +Call the [get levels API](/api-reference/endpoints/points/get-points-levels) once per points system key (for example on app load or from your server when rendering a progression page). + +The response is an array of levels with `key`, `name`, `description`, optional `badgeUrl`, and the `points` threshold for each level. By binding your UI to the Trophy API response, you'll ensure that it automatically updates when you make changes to levels in the Trophy dashboard. + + + +#### Displaying User Level + +The [user points API](/api-reference/endpoints/users/get-a-users-points) returns the user’s `total`, their current `level` object, and recent `awards`. The `level` field contains the user's current level, or null if no levels exist or they have not reached the points threshold for any level yet. + +Pair `total` with the ordered level list from the previous step to draw a progress bar between the current threshold and the next. + + + +#### Level Change Notifications + +When you send a [metric change event](/api-reference/endpoints/metrics/send-a-metric-change-event), the response will include a `points` map keyed by your points system keys for each system that changed as a result of the event. + +The data contains a `level` key only when the user’s level changed because of this event. If their level stayed the same, that key is omitted, so you can safely treat a present `level` as a level change signal without extra bookkeeping. + + + +A typical pattern is to check the object for your system after a successful event, then trigger a toast, modal, or sound: + +```ts +const pts = response.points?.xp; // use your points system key + +if (pts?.level) { + // Level changed on this request — e.g. toast, modal, confetti if level up + notifyLevelChange(pts.level.name, pts.total); +} +``` + +The same `level` behavior applies when points change from an [achievement completion](/api-reference/endpoints/achievements/mark-an-achievement-as-completed). + +For server-driven notifications (email, push, CRM) that must not depend on the client seeing the HTTP response, subscribe to the [`points.level_changed`](/webhooks/events/points/points-level-changed) webhook instead. + +This webhook fires when a user’s level changes as a result of earning or losing points, and includes `previousLevel` and `newLevel`. + +#### Account-level analytics + +The [get level summary API](/api-reference/endpoints/points/get-points-level-summary) returns how many users are currently at each level which is useful for admin dashboards, funnel views, or balancing progression. + + + +## Displaying Points + +There are a few ways to use Trophy to fetch and display points in your app. + + + For working examples check out our guides on adding an [XP + feature](/guides/how-to-build-an-xp-feature) or an [energy + feature](/guides/how-to-build-an-energy-feature) to your web or mobile app. + + + + + + +### Triggering Transactional UI + +Firstly, any points awarded to or deducted from users as a result of a metric change event are returned in the response when using the [metric change event API](/api-reference/endpoints/metrics/send-a-metric-change-event). + +The response includes the user's new total points, how many points were awarded or deducted as a result of the event, and the details of the specific points triggers that fired. When [Points Levels](#points-levels) are enabled, each points object in that response may also include **`level`**: Trophy includes the user's **new** level **only when it changed** as a result of that event. The same optional `level` behavior applies when points change from an [achievement completion](/api-reference/endpoints/achievements/mark-an-achievement-as-completed). + +Example `POST /metrics/{key}/event` response (fields match the Trophy API spec; your keys under `points` and `leaderboards` depend on your account): + + + +This makes it really simple to read the response and trigger any of the following transactional UI in your application: + +- Displaying in-app notifications and pop-ups +- Playing sound effects + +### Displaying User's Points + +Trophy also has APIs that allow you fetch user's points data whenever you want. + +First, the [user points API](/api-reference/endpoints/users/get-a-users-points) returns the user's total points for a particular points system, their current **`level`** (or `null` if levels are not in use or they have not reached a level yet), and up to 100 of the most recent events that awarded points to or deducted points from them. + +You can use this API to display the user's total points anywhere in your platform as well as a 'Latest awards' section or similar. + +`GET /users/{id}/points/{key}` — example response body (Trophy API spec): + + + + + + + +Then, the [user points summary API](/api-reference/endpoints/users/get-a-users-points-summary) can be used to fetch historical points data for a particular user. + +Data can be aggregated daily, weekly or monthly between a start and end date. Use this API to display points progress charts to users over any time frame. + +Example `GET /users/{id}/points/{key}/event-summary` **200** response (Trophy API spec; shape matches your `aggregation`, `startDate`, and `endDate` query parameters): + + + + + + + +### Displaying Aggregate Data + +Additionally there are a number of APIs that can be used to fetch and display points data at the account level. + +First, the [points summary API](/api-reference/endpoints/points/get-points-summary) returns aggregate points system data across your entire user base. + +Use this data to display a histogram of points for a particular points system and show users how they compare to others on the platform. When you use [Points Levels](#points-levels), the [level summary API](/api-reference/endpoints/points/get-points-level-summary) complements this with a per-level user count. + +`GET /points/{key}/summary` — example **200** response (Trophy API spec): + + + +Finally, the [points system API](/api-reference/endpoints/points/get-points) returns the system metadata and triggers. Example **200** response (Trophy API spec): + + + + + The points summary API can also be filtered to only return data for users with specific + [custom user attributes](/platform/users#custom-user-attributes). + + + + + + +Use the [points system API](/api-reference/endpoints/points/get-points) response to show users how they can earn points on your platform. Any new triggers you add will automatically be returned from this API, reducing code changes in your platform and shifting operations to Trophy. + + + + + +## Points Analytics + +Trophy has built-in analytics to track points awards for each points system you configure across your users in real time including: + +- Total points earned by all users over time +- Most points earned by a single user +- Average points earned in the first 14 days (useful for understanding new user retention patterns and the impact of points) +- A breakdown on the most commonly awarded points triggers + + + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/platform/push-notifications.mdx b/es/platform/push-notifications.mdx new file mode 100644 index 0000000..5a89462 --- /dev/null +++ b/es/platform/push-notifications.mdx @@ -0,0 +1,272 @@ +--- +title: Push Notifications +description: Learn how to use push notifications in a gamified product experience with Trophy. +"og:description": Use automated push notification flows to extend your gamification experience outside your app and remind users to keep coming back. +icon: bell +--- + +import IdentifyUserWithDeviceTokensRequestBlock from "/snippets/identify-user-with-device-tokens-request-block.mdx"; +import MetricEventWithDeviceTokensRequestBlock from "/snippets/metric-event-with-device-tokens-request-block.mdx"; + +Trophy can send automated push notifications to users based on key triggers without requiring any code. Here we’ll look at what these triggers are, and how they can form part of your product’s gamification experience. + +## Types Of Push Notifications + +Trophy supports 4 types of push notifications, each of which is designed to suit a common scenario in building gamification experiences. + +All push notifications are optional, but all four can be used simultaneously and can be controlled from the [Push Notifications](https://app.trophy.so/push/configure) page in the Trophy dashboard. + + + + + +- **Achievement notifications** are sent to users each time they unlock an [Achievement](/platform/achievements). +- **Recap notifications** are sent to users on a pre-defined frequency to summarize progress. Recap notifications can be configured to be sent daily, weekly, monthly or yearly depending on your use case. +- **Reactivation notifications** are sent to users after they become inactive with the goal of bringing them back to your app. +- **Streak notifications** are automatically sent to users reminding them to extend their [Streak](/platform/streaks). + +## Supported Channels + +Trophy supports sending push notifications using 3 channels which can all be configured on the [Channels](https://app.trophy.so/push/channels) page of the Trophy dashboard. + +### Apple Push Notification Service (APNs) + +Trophy supports sending push notifications to users via APNs through a certificate-based connection. + +To send push notifications using APNs, you'll need to provide Trophy with the following credentials from your Apple Developer Account: + +- **APN Signing Key**: Trophy uses this to securely send notifications via APNs +- **Team ID**: The team ID of your Apple Developer Account +- **Key ID**: The ID of your APNs authentication key +- **Bundle ID**: Your app's unique bundle identifier + + + For more information on how to configure certificate-based connection to APNs + from a third-party service, read the [official setup + guide](https://developer.apple.com/documentation/usernotifications/establishing-a-certificate-based-connection-to-apns). + + +### Firebase Cloud Messaging (FCM) + +Trophy supports sending push notifications to users via FCM. To achieve this you'll need to provide Trophy with your Firebase Service Account JSON in the following format: + +{/* vale off */} + +```json FCM Service Account JSON +{ + "type": "service_account", + "project_id": "XXX", + "private_key_id": "XXX", + "private_key": "-----BEGIN PRIVATE KEY-----\n\n-----END PRIVATE KEY-----\n", + "client_email": "XXX@XXX.com", + "client_id": "XXX", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/XXX" +} +``` + +{/* vale on */} + + + For more information on Firebase service accounts and how to authenticate + external services to use FCM, read the [official service accounts + guide](https://firebase.google.com/support/guides/service-accounts). + + +### Expo Push Service + +Trophy supports sending push notifications to users via Expo Push Service. + +Expo Push Service saves you having to integreate with FCM and APNs individually and automatically directs notifications through the relevant service based on your users platform. + +To achieve this you must provide Trophy with a few key details from your Expo account: + +- **Expo Project Name**: The name of your Expo project +- **Expo Push Token**: Your authentication token for sending notifications via Expo (required if using [enhanced security](https://docs.expo.dev/push-notifications/sending-notifications/#additional-security)). + + + For more information on Expo Push Service and how to authenticate external + providers, see the [official + guide](https://docs.expo.dev/push-notifications/sending-notifications). + + +## Sending Push Notifications + +Follow the steps below to start sending push notifications using Trophy. + + + Users can control which push notifications they receive through Trophy's [user + preferences API](/platform/users#notification-preferences). This allows you to + build a preference center in your application where users can opt in or out of + specific notification types. + + + + + Trophy support sending push notifications via 3 channels, [Apple Push Notification Service (APNs)](#apple-push-notification-service-apns), [Firebase Cloud Messaging (FCM)](#firebase-cloud-messaging-fcm) or [Expo Push Service](#expo-push-service). + + Head into the [channels page](https://app.trophy.so/push/channels) of the Trophy dashboard and configure your preferred channel by following the dedicated guides above. + + + + + + + + + Trophy provides a default template for each [type of push notification](#types-of-push-notifications) that Trophy supports. + + On the [configure page](https://app.trophy.so/push/configure), each notification type has its own section. Add templates under the relevant type. + + Follow the guide on [designing push notifications](#designing-push-notifications) to learn more. + + + + + + + + To send notifications to users, Trophy needs to know the unique tokens assigned to their devices. You tell Trophy about devices by associating device tokens with each user you identify. + + Trophy will use the device tokens you associate with each user when communicating with supported [push notification channels](#supported-channels) to route notifications to the correct device. + + To associate a device token with a user in Trophy, either assign `deviceTokens` when [identifying users explicitly](https://docs.trophy.so/api-reference/endpoints/users/identify-a-user#body-device-tokens), or when [sending metric events](https://docs.trophy.so/api-reference/endpoints/metrics/send-a-metric-change-event#body-user-device-tokens). + + Here's how to associated device tokens when identifying users: + + + + And here's how to associate device tokens when sending metric events: + + + + + Each user can have multiple device tokens associated with them, to support users who use multiple devices. + + + + + The last step is to activate a template within each [type of push notification](#types-of-push-notifications) section that you want to send. + + On the [configure page](https://app.trophy.so/push/configure), activate one template in each type section you want to enable. Once activated, Trophy will start sending push notifications automatically. + + + Users can control which push notification types they receive through Trophy's [user preferences API](/platform/users#notification-preferences). When a user has disabled a notification type in their preferences, Trophy will respect that setting and won't send those notifications to that user. + + + + + + + + + + +## Designing Push Notifications + +By default, Trophy provides a template for each [type of push notification](#types-of-push-notifications) as a good starting point. The default templates can’t be changed, but you can duplicate and customize these as you wish. + + + You can also create blank templates if you just want to start from scratch. + + +### Template Structure + +Each push notification has a title and body, and Trophy's no-code push notification template editor allows you to fully customize both fields. + + + + + +### Using Variables + +Trophy provides an expansive set of variables that can be used to insert highly relevant and personalized data into your push notifications. + +Variables can be inserted by typing `@` in the title or body of any push notification, and searching for your chosen variable. + +This will open up the variable editor window where you can configure variables as in the demo below. You can also test different variable values and how your template behaves in different scenarios by using the preview sidebar. + + + + + +### Using Variations + +Variations can be used to add randomness to the title and body of push notifications sent by Trophy. This prevents notifications from getting boring and helps improve engagement rates. + +At send time, Trophy automatically picks one of your variations of the title and body at random and sends the notification to the user. + + + + + +### Using Conditions + +Trophy's push notification template builder supports using conditions to send different content to each recipient based on their unique context. + +All [template variables](#using-variables) can be used in conditions to control what content is used in the title and body of notifications sent by Trophy. + +At send time Trophy computes all conditions using each recipients unique context, delivering highly personalized and relevant notifications to each user. + + + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/platform/streaks.mdx b/es/platform/streaks.mdx new file mode 100644 index 0000000..3d69234 --- /dev/null +++ b/es/platform/streaks.mdx @@ -0,0 +1,225 @@ +--- +title: Streaks +description: Learn how to use streaks in a gamified product experience with Trophy. +"og:description": Use daily, weekly or monthly streaks to create habitual usage patterns and keep users coming back to your app. +icon: flame +--- + +import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; + +## What are Streaks? + +A streak is a period of consecutive days, weeks or months that a user has performed a key action on your platform. Streaks have been shown to meaningfully increase retention, particularly when the user action being tracked aligns with the core value of your product. + +## Streak Frequency + +Streaks in Trophy can be **daily, weekly or monthly**. This means that a user must meet their [streak conditions](#streak-conditions) at least once every calendar day, week or month to maintain their streak. + +If you've [configured time zones](/platform/users#param-tz) for your users, Trophy will automatically track each user's streak in their local time zone (including taking into account when users change time zones) and keep streaks in tact. + + +Trophy automatically computes streak data for every streak frequency, meaning you can switch at any time. + + +## Streak Conditions + +In Trophy you can set the thresholds that a user must meet in order to extend their streak based on your configured [Metrics](/platform/metrics). + +You can choose which metrics should be part of your streak, and for those that you chose, you can set a custom threshold that users must meet. + + + + + +When combining metrics in this way, you can choose between two evaluation modes: +- `ALL` means a user must meet every metric threshold to extend their streak +- `OR` means a user only has to meet one of your metric thresholds to extend their streak + +In this way you can design your streak logic to match your application use case. + +## Streak Freezes + +Streak freezes help users keep their streaks for longer by allowing them to miss periods without it resetting to zero. This helps keep streaks motivating even if users don't maintain a perfect usage habit. + + + + + +Streak freezes are optional in Trophy but can be configured on the [streaks page](https://app.trophy.so/streaks) of the Trophy dashboard. + +### Granting Initial Freezes + +You can configure any number of arbitrary freezes to grant to new users when you first identify them with Trophy. + + +Giving users too many freezes may decrease their perceived value, but granting too few freezes might result it a higher number of lost streaks. A good starting point is one freeze per user, but the trick is to experiment! + + +### Freeze Accumulation + +As users use up streak freezes, they'll need a continuous supply of new ones to keep them going. To facilitate this, Trophy can automatically grant streak freezes to users over time. You can choose an arbitrary number of days over which to grant an arbitrary number of freezes to each user. + +If you've [configured time zones](/platform/users#param-tz) for your users, Trophy will automatically consume freezes at midnight in the user's time zone when necessary to extend their streak, and if any new freezes are due to be granted to a user, they will be granted up to ten minutes later. + +### Maximum Freeze Count + +In Trophy you also configure the maximum number of freezes that each user can have, up to a limit of 100. [Freeze accumulation](#freeze-accumulation) will only ever grant freezes up to this limit. + +## Tracking Streaks + +Trophy automatically calculates streaks for all users based on the [metric events](/platform/events#tracking-metric-events) you report to Trophy. + +There's no extra work required to track streaks, and you can start using them right away. Just make sure that streaks are enabled in the Trophy dashboard. + + +For a full walk through on how to set up a streak feature using Trophy, check out our [official guide](/guides/how-to-build-a-streaks-feature). + + +## Managing Streaks + +This section outlines some of the operations you can perform to manage user's streaks in your application. + +### Restoring A Users Streak + +To restore a user's streak, head to the user details page and use the 'Restore Streak' action. Restoring a user's streak sets it to the length it was when they last lost it. + + +You can also use the [restore streak admin API](/admin-api/endpoints/streaks/restore-streaks) to restore streaks programmatically. + + + + + + +## Displaying Streaks + +Trophy exposes streak data in two ways, which can be used to build UI elements within your applications and display streaks to users. + + + Check out our [full guide](/guides/how-to-build-a-streaks-feature) on adding a + streaks feature to your app for more details. + + +### Metric Event Response + +When you [increment a metric](/platform/events#tracking-metric-events) for a user, the [metric API](/api-reference/endpoints/metrics/send-a-metric-change-event) response will include the user's current +streak. + + + +This can be used to transactionally trigger UI/UX elements including: + +- Showing in-app pop-ups +- Playing sound effects + +### User Streaks API + +The [user streaks API](/api-reference/endpoints/users/get-a-users-streak) returns the current streak for a single user, along with their recent streak history. Use the [`historyPeriods`](/api-reference/endpoints/users/get-a-users-streak#parameter-history-periods) query parameter to control how many periods to return. + +{/* vale off */} + +```json Response [expandable] +{ + "length": 1, + "frequency": "weekly", + "started": "2025-04-02", + "periodStart": "2025-03-31", + "periodEnd": "2025-04-05", + "expires": "2025-04-12", + "rank": 5, + "streakHistory": [ + { + "periodStart": "2025-03-02", + "periodEnd": "2025-03-08", + "length": 9 + }, + { + "periodStart": "2025-03-09", + "periodEnd": "2025-03-15", + "length": 0 + }, + { + "periodStart": "2025-03-16", + "periodEnd": "2025-03-22", + "length": 0 + }, + { + "periodStart": "2025-03-23", + "periodEnd": "2025-03-29", + "length": 1 + }, + { + "periodStart": "2025-03-30", + "periodEnd": "2025-04-05", + "length": 2 + }, + { + "periodStart": "2025-04-06", + "periodEnd": "2025-04-12", + "length": 3 + }, + { + "periodStart": "2025-04-13", + "periodEnd": "2025-04-19", + "length": 4 + } + ] +} +``` + +{/* vale on */} + +Use this data to display a user's streak history within your application. + + + + + +### List Multiple User's Streaks + +If you want to display streaks for multiple users at once, for example to support a friend streak or user group feature, then use the [list user streaks API](/api-reference/endpoints/streaks/get-streaks). + +```json [expandable] +[ + { + "userId": "user-123", + "streakLength": 15, + "extended": "2025-01-01T05:03:00Z" + }, + { + "userId": "user-456", + "streakLength": 12, + "extended": "2025-01-01T08:43:00Z" + }, + { + "userId": "user-789", + "streakLength": 0, + "extended": null + } +] +``` + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/platform/users.mdx b/es/platform/users.mdx new file mode 100644 index 0000000..37506a4 --- /dev/null +++ b/es/platform/users.mdx @@ -0,0 +1,435 @@ +--- +title: Users +description: Learn how to track interactions across your userbase using Trophy. +"og:description": Users are the people that use your platform. Learn how to tell Trophy about them. +icon: users +--- + +import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; +import IdentifyUserRequestBlock from "/snippets/identify-user-request-block.mdx"; +import UpdateUserNotificationPreferencesBlock from "/snippets/update-user-notification-preferences-block.mdx"; +import GetUserNotificationPreferencesBlock from "/snippets/get-user-notification-preferences-block.mdx"; + +## What are Users? + +Users are the individual people that use your product. You tell Trophy about your users through APIs and use the dashboard to design gamification experiences around them. + +Users must be individuals, they cannot be companies or organisations. To create organizational structures or user groupings, consider using a [custom user attribute](#custom-user-attributes). + +## Key Attributes + +Key attributes are properties of users controlled and managed by Trophy and are for things like `id` or `email`, some are required while others are optional. + +### Required Attributes + +Trophy only requires one key attribute, `id`. Every user you tell Trophy about must have an `id`, this is what identifies them as a unique person. + + + This is the ID of the user in **your** database. + + + + To make things simple, Trophy lets you use your own `id` that you already have + in your database instead of needing to manage another one just for Trophy. + + +### Optional Attributes + +Additionally, you can tell Trophy about any of the following optional key attributes and it will make them available to you as part of your gamification experience: + + + The user's full name. This is accessible in email templates and other areas + where you may want to address the user by name. + + + + The user's email address. This address will be used in any + [Emails](/platform/emails) that you set up as part of your gamification + experience with Trophy. + + + + The user's time zone. Must be specified as an [IANA timezone + identifier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). + Used for streaks, leaderboard rankings and sending emails to users in + accordance with their local clock. + +In JavaScript, you can fetch the users time zone using this snippet: + +```js Fetching timezone +// e.g. 'Europe/London' +const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; +``` + + + + + If you've configured any Trophy [Emails](/platform/emails), then they'll only + be sent to a user when this field is true. Note: If you don't provide an + `email`, attempting to set this field to true will result in an error. + + + + The list of device tokens to associate with the user. If you've configured any + Trophy [Push Notifications](/platform/push-notifications), then they'll only + be sent to a user when this field is provided. + + +## Custom User Attributes + + + This feature is available on the [Pro](/account/billing#pro-plan) plan + + + + + + +Custom user attributes are managed by you and can be anything you like based on your use case. + +For example a language learning app might use a custom user attribute for the language a user is learning, or a study platform might use one for the users favourite subject. + +Custom user attributes let you tell Trophy about this contextual information and use it to personalize gamification features. + +### Creating Attributes + +To create a new custom user attribute, head to the [attributes tab](https://app.trophy.so/users/attributes) of the users page in the Trophy dashboard and hit the _Add User Attribute_ button. + +Give the attribute a name and a unique key. The key is what you'll use to reference the attribute in API calls. + + + + + +### Setting Attributes + +Attributes can be assigned values for specific users using their unique key either inline, as users increment metrics through the [metric increment API](/api-reference/endpoints/metrics/send-a-metric-change-event), or explicitly through the [user identification](/api-reference/endpoints/users/identify-a-user), [create user](/api-reference/endpoints/users/create-a-user), or [update user](api-reference/endpoints/users/update-a-user) APIs. + + + Trophy will only set values of attributes that have first been created + in the dashboard. We do this to help you keep a clean set of attributes and + prevent accidental overwrites. + +If you receive an error similar to the following then you might have miss-spelled the attribute key in the request, or you need to create the attribute first in the Trophy dashboard: + +{/* vale off */} + +```json +{ + "error": "Invalid attribute keys: pln. Please ensure all attribute keys match those set up at https://app.trophy.so/users/attributes." +} +``` + +{/* vale on */} + + + +Across all APIs, the schema for setting attribute values is consistent. Here's an example of a request payload that sets the value of two custom user attributes `subject` and `plan`: + +```json {4-7} +{ + "name": "Joe Bloggs", + "email": "joe.bloggs@example.com", + "attributes": { + "subject": "physics", + "plan": "free" + } +} +``` + +### Using Attributes + +Use custom user attributes across Trophy to personalize gamification features , aggregate and filter data returned from APIs, and to segment and compare user cohorts in analytics. + +#### Feature Personalization + +Custom user attributes can be used to personalize achievements, customize the way points are earned by different users and more. Follow the links to the relevant pages below to learn more. + + + + Configure achievements that can only be unlocked by specific users. + + + Personalize how different users earn points. + + + Control which users receive gamified emails. + + + Personalize email copy and subject lines with custom user attributes. + + + +#### Data Aggregation and Filtering + +You can use custom user attributes to aggregate and filter data returned from some APIs to support a wide range of gamification use cases. In all cases, attributes are included in the `userAttributes` query parameter using a consistent schema: + +`?userAttributes=city:london,subscription-plan:pro` + +Here's a list of all APIs that support the `userAttributes` query parameter: + +- [Points Summary](/api-reference/endpoints/points/get-points-summary) + +#### Segmented Analytics + +Custom user attributes can be used to segment and compare retention and engagement charts between user groups. + +This is useful to understand which cohorts are making full use of your gamification features, and which are struggling to engage and therefore require some attention. + + + + + +## Identifying Users + +When you tell Trophy about a user in your platform, we call this **identification**. There are two ways you can identify users with Trophy, [inline](#inline-identification) and [explicit](#explicit-identification) identification. + + + We recommend getting started with inline identification if you're new to + Trophy. If you decide you need more control, try explicit identification. + + +### Inline Identification + +Inline identification is the easiest way of telling Trophy about your users as it doesn't require any specific user identification code. You simply tell Trophy about users as they go about normal use of your platform. + +In practice this means whenever you use the [Metric Event API](/api-reference/endpoints/metrics/send-a-metric-change-event), you pass along full details of the user who triggered the event. + +Here's an example where an API call is made to the metric events API, and the details of the user who made the interaction are passed along in the request body: + + + +In this case, if this is the user's first interaction (i.e. they are a new user) then a new record will be created for them in Trophy automatically. + +However if Trophy finds an existing record with the same `id`, then Trophy will update any details of the user that are passed through. + +In other words, inline identification is by means of an `UPSERT` operation. + +
+ +```mermaid +flowchart LR + A@{ shape: sm-circ } + B@{ shape: diamond, label: "Is new user?" } + C@{ shape: rounded, label: "Identify new user" } + D@{ shape: rounded, label: "Update existing user" } + E@{ shape: rounded, label: "Process event" } + F@{ shape: framed-circle } + A-->B + B-- Yes --->C + B-- No --->D + C-->E + D-->E + E-->F +``` + +
+ +Identifying users in this way has two key benefits: + +- All new users that sign up for your platform and increment a metric are automatically tracked in Trophy without any explicit identification code written by you +- Any changes to existing user properties like name or email address are automatically synced to Trophy the next time the user increments a metric + +In this way inline identification allows you to keep your entire userbase in constant sync with Trophy with the least amount of code required by you. + +This is why we recommend starting with inline identification first, then exploring explicit identification if you discover you need more control. + +### Explicit Identification + +Explicit identification is when you write code in your application that explicitly tells Trophy about users separate from any metric interactions. This is useful if you want complete control over how and when Trophy learns about your users. + +Scenarios where you might want to use explicit identification might be: + +- You want to tell Trophy about new users immediately on sign-up, before they increment a metric +- You track a lot of metrics in Trophy, and don't want to repeat inline identification code. +- You want to only tell Trophy about a specific cohort of users, of which you control the conditions around + +In this case, you can tell Trophy about new users using the [User Identification API](/api-reference/endpoints/users/identify-a-user). + + + + + Just like in inline identification, explicit identification also works by + means of an `UPSERT` operation meaning all user attributes are kept in sync + with Trophy automatically. + + +## Creating Users + +To explicitly create a new user in Trophy, use the [Create User API](/api-reference/endpoints/users/create-a-user). + +## Keeping Users Up To Date + +To tell Trophy about an update to a user in your platform you can use the [Update User API](/api-reference/endpoints/users/update-a-user). + +Any properties that you pass to Trophy will be updated to the new values you specify. + + + As [user identification](#identifying-users) keeps user records up to date by + default, it's usually not necessary to explicitly update users in Trophy every + time one of their properties changes in your application. However the user + update API is there if you really need it. + + +## Setting User Preferences + +Trophy has APIs to help you power a preference center that your users can use to control and customize how they interact with your gamification features. + +This section walks through all the preferences that you can expose to your users, and their function. + +### Notification Preferences + +You can use Trophy to send gamified [Emails](/platform/emails) and [Push Notifications](/platform/push-notifications) to your users. + +Trophy's [update preferences API](/api-reference/endpoints/users/update-user-preferences) allows you to build a preference center within your application that your users can interact with to control which notifications they receive, and through which channels. + + + + + +The API lets you update a user's notification preferences for different notification types: + +- **achievement_completed**: Notifications sent when a user completes an achievement +- **recap**: Periodic summary notifications about user progress +- **reactivation**: Notifications sent to re-engage inactive users +- **streak_reminder**: Reminders to help users maintain their streaks + +For each notification type, you can specify which channels the user should receive notifications on: `email`, `push`, or both. To disable a notification type entirely, pass an empty array. + +For example, the following code snippet updates a user's notification preferences in Trophy: + + + +To build a preference center UI in your application, you'll first need to fetch the user's current preferences using the [get preferences API](/api-reference/endpoints/users/get-user-preferences). + +This will return the user's current notification settings, which you can then display in your UI and allow users to modify. + + + +The API returns a response containing the user's current notification preferences for each notification type: + +```json Response {3} +{ + "notifications": { + "achievement_completed": ["email", "push"], + "recap": ["email"], + "reactivation": ["push"], + "streak_reminder": ["email", "push"] + } +} +``` + +You can use this response to: + +- **Populate form controls**: Pre-select checkboxes or toggles in your preference center UI based on the current values +- **Show current state**: Display which notifications are enabled and through which channels +- **Handle missing preferences**: If a notification type is not present in the response, it means the user hasn't set preferences for it yet, and you can default to your application's default settings + +Once you've fetched and displayed the preferences, users can make changes in your UI, and you can use the [update preferences API](/api-reference/endpoints/users/update-user-preferences) to save their selections back to Trophy. + +## Retrieving User Information + +To fetch the details of a user that you've already identified with Trophy, use the [Get User API](/api-reference/endpoints/users/get-a-single-user). + +This will return the full details of the user along with the `control` attribute that you can use to conditionally enroll users in any gamification features. Learn more about [Experimentation](/experimentation/overview). + +```json Response {3} +{ + "id": "user-id", + "control": false, + "created": "2021-01-01T00:00:00Z", + "email": "user@example.com", + "name": "User", + "subscribeToEmails": true, + "tz": "Europe/London", + "updated": "2021-01-01T00:00:00Z" +} +``` + +## User Analytics + +### Basic Analytics + +By default Trophy includes high-level user analytics including on the [Users page](https://app.trophy.so/users) including: + +- The total number of users that you've told Trophy about +- The number of users that are active on a daily basis +- The number of users that are active on a monthly basis + + + + + +On this page you can also search through every user that Trophy has recorded, which can be useful for debugging. + +### Top Users + +Additionally, on the [Dashboard](https://app.trophy.so), Trophy shows a _Top Users_ list with the set of users who have made the most progress against your platform's metrics. + +These are your most engaged users, so it's useful to know who they are! + +## Frequently Asked Questions + + + + As many as you want! Trophy only charges each month for _active_ users, which are the users that interact with your product at least once in a given month. + + This is great because it means if a user churns, you won't pay for them in subsequent months. + + You can view the total number of users you'll be charged for each month within Trophy on the sidebar: + + + + + + You can estimate your usage costs on our [Pricing page](https://trophy.so/pricing). + + + + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/snippets/add-env-var-block.mdx b/es/snippets/add-env-var-block.mdx new file mode 100644 index 0000000..32f67e7 --- /dev/null +++ b/es/snippets/add-env-var-block.mdx @@ -0,0 +1,5 @@ +{/* vale off */} + +```bash +TROPHY_API_KEY='*******' +``` diff --git a/es/snippets/all-achievements-request-block.mdx b/es/snippets/all-achievements-request-block.mdx new file mode 100644 index 0000000..c4faef6 --- /dev/null +++ b/es/snippets/all-achievements-request-block.mdx @@ -0,0 +1,36 @@ + +```bash cURL +curl --request GET \ + --url https://api.trophy.so/v1/achievements \ + --header 'X-API-KEY: ' +``` + +```typescript Node +trophy.achievements(); +``` + +```python Python +client.achievements() +``` + +```php PHP +$trophy->achievements(); +``` + +```java Java +UserAchievementsResponse response = client.achievements(); +``` + +```go Go +response, err := client.Achievements() +``` + +```csharp C# +await trophy.AchievementsAsync(); +``` + +```ruby Ruby +result = client.achievements() +``` + + diff --git a/es/snippets/all-achievements-response-block.mdx b/es/snippets/all-achievements-response-block.mdx new file mode 100644 index 0000000..bb4b9db --- /dev/null +++ b/es/snippets/all-achievements-response-block.mdx @@ -0,0 +1,48 @@ +```json Response [expandable] +[ + { + "id": "5100fe51-6bce-6j44-b0hs-bddc4e123682", + "trigger": "api", + "name": "Finish onboarding", + "description": "Complete the onboarding process.", + "badgeUrl": "https://example.com/badge.png", + "key": "finish-onboarding", + "completions": 8, + "rarity": 80 + }, + { + "id": "5100fe51-6bce-6j44-b0hs-bddc4e123683", + "trigger": "metric", + "name": "500 Flashcards Flipped", + "description": "View 500 flashcards in the app.", + "badgeUrl": "https://example.com/badge.png", + "metricId": "5100fe51-6bce-6j44-b0hs-bddc4e123683", + "metricName": "Flashcards Flipped", + "metricValue": 500, + "completions": 6, + "rarity": 60 + }, + { + "id": "5100fe51-6bce-6j44-b0hs-bddc4e123684", + "trigger": "streak", + "name": "10 days of exercise", + "description": "Exercise at least once a day for 10 days in a row.", + "badgeUrl": "https://example.com/badge.png", + "streakLength": 10, + "completions": 2, + "rarity": 20 + }, + { + "id": "5100fe51-6bce-6j44-b0hs-bddc4e123685", + "trigger": "achievement", + "name": "Study Master", + "description": "Complete all introductory milestones.", + "badgeUrl": "https://example.com/badge.png", + "achievementIds": [ + "5100fe51-6bce-6j44-b0hs-bddc4e123682", "5100fe51-6bce-6j44-b0hs-bddc4e123683", "5100fe51-6bce-6j44-b0hs-bddc4e123684" + ], + "completions": 1, + "rarity": 5 + } +] +``` diff --git a/es/snippets/control-flag-block.mdx b/es/snippets/control-flag-block.mdx new file mode 100644 index 0000000..8d61184 --- /dev/null +++ b/es/snippets/control-flag-block.mdx @@ -0,0 +1,15 @@ +```json The control attribute {6} +{ + "id": "user-id", + "email": "user@example.com", + "tz": "Europe/London", + "subscribedToEmails": true, + "control": true, + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z", + "attributes": { + "department": "engineering", + "role": "developer" + } +} +``` diff --git a/es/snippets/event-attributes-request-block.mdx b/es/snippets/event-attributes-request-block.mdx new file mode 100644 index 0000000..6fbb7a7 --- /dev/null +++ b/es/snippets/event-attributes-request-block.mdx @@ -0,0 +1,108 @@ + +```bash cURL +curl -X POST https://app.trophy.so/api/metrics/flashcards-flipped/event \ + -H "X-API-KEY: " \ + -H "Content-Type: application/json" \ + -d '{ + "user": { + "id": "18", + "email": "user@example.com", + "tz": "Europe/London" + }, + "value": 750, + "attributes": { + "device": "ios" + } +}' +``` + +```typescript Node +trophy.metrics.event("flashcards-flipped", { + user: { + id: "18", + email: "user@example.com", + tz: "Europe/London", + }, + value: 750, +}); +``` + +```python Python +client.metrics.event( + key="flashcards-flipped", + user=EventRequestUser( + id="18", + email="user@example.com", + tz="Europe/London", + ), + value=750.0, +) +``` + +```php PHP +$user = new EventRequestUser([ + 'id' => '18', + 'email' => 'user@example.com' +]); + +$request = new MetricsEventRequest([ + 'user' => $user, + 'value' => 750 +]); + +$trophy->metrics->event("flashcards-flipped", $request); +``` + +```java Java +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 +response, err := client.Metrics.Event( + "flashcards-flipped", + &api.MetricsEventRequest{ + User: &api.EventRequestUser{ + Id: "18", + Email: "user@example.com", + }, + Value: 750, + }, +) +``` + +```csharp C# +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 +result = client.metrics.event( + :key => 'flashcards-flipped', + :user => { + :id => '18', + :email => 'user@example.com' + }, + :value => 750 +) +``` + + diff --git a/es/snippets/get-user-notification-preferences-block.mdx b/es/snippets/get-user-notification-preferences-block.mdx new file mode 100644 index 0000000..3334dd6 --- /dev/null +++ b/es/snippets/get-user-notification-preferences-block.mdx @@ -0,0 +1,36 @@ + +```bash cURL +curl --request GET \ + --url https://api.trophy.so/v1/users/user-123/preferences \ + --header 'X-API-KEY: ' +``` + +```typescript Node +const response = await trophy.users.getPreferences("user-123"); +``` + +```python Python +response = client.users.get_preferences(id="user-123") +``` + +```php PHP +$response = $trophy->users()->getPreferences("user-123"); +``` + +```java Java +UserPreferencesResponse response = client.users().getPreferences("user-123"); +``` + +```go Go +response, err := client.Users.GetPreferences("user-123") +``` + +```csharp C# +var response = await trophy.Users.GetPreferencesAsync("user-123"); +``` + +```ruby Ruby +response = client.users.get_preferences(id: "user-123") +``` + + diff --git a/es/snippets/idempotent-event-tracking.mdx b/es/snippets/idempotent-event-tracking.mdx new file mode 100644 index 0000000..0ebed61 --- /dev/null +++ b/es/snippets/idempotent-event-tracking.mdx @@ -0,0 +1,115 @@ + +```bash cURL {3} +curl -X POST https://app.trophy.so/api/metrics/lessons-completed/event \ + -H "X-API-KEY: " \ + -H "Idempotency-Key: " \ + -H "Content-Type: application/json" \ + -d '{ + "user": { + "id": "18", + "email": "user@example.com", + "tz": "Europe/London" + }, + "value": 1 +}' +``` + +```typescript Node {9} +trophy.metrics.event("lessons-completed", + { + user: { + id: "18", + email: "user@example.com", + tz: "Europe/London", + }, + value: 1, + idempotencyKey: "lesson-123", + } +); +``` + +```python Python {9} +client.metrics.event( + key="lessons-completed", + user=EventRequestUser( + id="18", + email="user@example.com", + tz="Europe/London", + ), + value=1, + idempotencyKey="lesson-123" +) +``` + +```php PHP {9} +$user = new EventRequestUser([ + 'id' => '18', + 'email' => 'user@example.com' +]); + +$request = new MetricsEventRequest([ + 'user' => $user, + 'value' => 1, + 'idempotencyKey' => 'lesson-123' +]); + +$trophy->metrics->event("lessons-completed", $request); +``` + +```java Java {9} +MetricsEventRequest request = MetricsEventRequest.builder() + .user( + EventRequestUser.builder() + .id("18") + .email("user@example.com") + .build() + ) + .value(1) + .idempotencyKey("lesson-123") + .build(); + +EventResponse response = client.metrics().event("lessons-completed", request); +``` + +```go Go {10} +response, err := client.Metrics.Event( + "lessons-completed", + &api.MetricsEventRequest{ + User: &api.EventRequestUser{ + Id: "18", + Email: "user@example.com", + }, + Value: 1, + IdempotencyKey: "lesson-123" + }, +) +``` + +```csharp C# {9} +var user = new EventRequestUser { + Id = "18", + Email = "user@example.com" +}; + +var request = new MetricsEventRequest { + User = user, + Value = 1, + IdempotencyKey = "lesson-123" +}; + +await trophy.Metrics.EventAsync("lessons-completed", request); +``` + +```ruby Ruby {8} +result = client.metrics.event( + :key => 'lessons-completed', + :user => { + :id => '18', + :email => 'user@example.com' + }, + :value => 1, + :idempotencyKey => 'lesson-123' +) +``` + + diff --git a/es/snippets/identify-user-request-block.mdx b/es/snippets/identify-user-request-block.mdx new file mode 100644 index 0000000..552108f --- /dev/null +++ b/es/snippets/identify-user-request-block.mdx @@ -0,0 +1,14 @@ + +```bash cURL +curl --request PUT \ + --url https://app.trophy.so/api/users/{id} \ + --header 'Content-Type: application/json' \ + --header 'X-API-KEY: ' \ + --data '{ + "email": "user@example.com", + "name": "User", + "tz": "Europe/London", + "subscribeToEmails": true +}' +``` + diff --git a/es/snippets/identify-user-with-device-tokens-request-block.mdx b/es/snippets/identify-user-with-device-tokens-request-block.mdx new file mode 100644 index 0000000..e9d864d --- /dev/null +++ b/es/snippets/identify-user-with-device-tokens-request-block.mdx @@ -0,0 +1,98 @@ +{/* vale off */} + + +```bash cURL {10} +curl --request PUT \ + --url https://app.trophy.so/api/users/{id} \ + --header 'Content-Type: application/json' \ + --header 'X-API-KEY: ' \ + --data '{ + "email": "user@example.com", + "name": "User", + "tz": "Europe/London", + "subscribeToEmails": true, + "deviceTokens": ["token1", "token2"] +}' +``` + +```typescript Node {6} +trophy.users.identify("user-id", { + email: "user@example.com", + name: "User", + tz: "Europe/London", + subscribeToEmails: true, + deviceTokens: ["token1", "token2"], +}); +``` + +```python Python {7} +client.users.identify( + id="user-id", + email="user@example.com", + name="User", + tz="Europe/London", + subscribeToEmails=True, + deviceTokens=["token1", "token2"], +) +``` + +```php PHP {6} +$trophy->users->identify("user-id", [ + 'email' => 'user@example.com', + 'name' => 'User', + 'tz' => 'Europe/London', + 'subscribeToEmails' => true, + 'deviceTokens' => ['token1', 'token2'], +]); +``` + +```java Java {6} +UpdatedUser user = UpdatedUser.builder() + .email("user@example.com") + .name("User") + .tz("Europe/London") + .subscribeToEmails(true) + .deviceTokens(Arrays.asList("token1", "token2")) + .build(); + +client.users().identify("user-id", user); +``` + +```go Go {8} +response, err := client.Users.Identify( + "user-id", + &api.UpdatedUser{ + Email: "user@example.com", + Name: "User", + Tz: "Europe/London", + SubscribeToEmails: true, + DeviceTokens: []string{"token1", "token2"}, + }, +) +``` + +```csharp C# {6} +var user = new UpdatedUser { + Email = "user@example.com", + Name = "User", + Tz = "Europe/London", + SubscribeToEmails = true, + DeviceTokens = new[] { "token1", "token2" } +}; + +await trophy.Users.IdentifyAsync("user-id", user); +``` + +```ruby Ruby {7} +result = client.users.identify( + "user-id", + :email => "user@example.com", + :name => "User", + :tz => "Europe/London", + :subscribeToEmails => true, + :deviceTokens => ["token1", "token2"] +) +``` + + + diff --git a/es/snippets/leaderboard-rankings-request-multiple-attributes.mdx b/es/snippets/leaderboard-rankings-request-multiple-attributes.mdx new file mode 100644 index 0000000..dbb98d8 --- /dev/null +++ b/es/snippets/leaderboard-rankings-request-multiple-attributes.mdx @@ -0,0 +1,82 @@ + +```bash cURL +curl --request GET \ + --url https://api.trophy.so/v1/leaderboards/{key}?userAttributes=region_city:southeast_london \ + --header 'X-API-KEY: ' +``` + +```typescript Node +trophy.leaderboards.get("daily_champions", { + offset: 0, + limit: 10, + run: "2025-01-15", + userAttributes: "region_city:southeast_london" +}); +``` + +```python Python +client.leaderboards.get( + key="daily_champions", + offset=0, + limit=10, + run="2025-01-15", + user_attributes="region_city:southeast_london" +) +``` + +```php PHP +$request = new LeaderboardsGetRequest([ + 'offset' => 0, + 'limit' => 10, + 'run' => "2025-01-15", + 'user_attributes' => "region_city:southeast_london" +]); + +$trophy->leaderboards->get("daily_champions", $request); +``` + +```java Java +LeaderboardsGetRequest request = LeaderboardsGetRequest.builder() + .offset(0) + .limit(10) + .run("2025-01-15") + .userAttributes("region_city:southeast_london") + .build(); + +LeaderboardsGetResponse response = client.leaderboards().get("daily_champions", request); +``` + +```go Go +response, err := client.Leaderboards.Get( + "daily_champions", + &api.LeaderboardsGetRequest{ + Offset: 0, + Limit: 10, + Run: "2025-01-15", + UserAttributes: "region_city:southeast_london", + }, +) +``` + +```csharp C# +var request = new LeaderboardsGetRequest { + Offset = 0, + Limit = 10, + Run = "2025-01-15", + UserAttributes = "region_city:southeast_london" +}; + +await trophy.Leaderboards.GetAsync("daily_champions", request); +``` + +```ruby Ruby +result = client.Leaderboards.Get( + :key => "daily_champions", + :offset => 0, + :limit => 10, + :run => "2025-01-15", + :user_attributes => "region_city:southeast_london" +) +``` + + diff --git a/es/snippets/leaderboard-rankings-request-single-attribute.mdx b/es/snippets/leaderboard-rankings-request-single-attribute.mdx new file mode 100644 index 0000000..65aa2d5 --- /dev/null +++ b/es/snippets/leaderboard-rankings-request-single-attribute.mdx @@ -0,0 +1,82 @@ + +```bash cURL +curl --request GET \ + --url https://api.trophy.so/v1/leaderboards/{key}?userAttributes=city:london \ + --header 'X-API-KEY: ' +``` + +```typescript Node +trophy.leaderboards.get("daily_champions", { + offset: 0, + limit: 10, + run: "2025-01-15", + userAttributes: "city:london" +}); +``` + +```python Python +client.leaderboards.get( + key="daily_champions", + offset=0, + limit=10, + run="2025-01-15", + user_attributes="city:london" +) +``` + +```php PHP +$request = new LeaderboardsGetRequest([ + 'offset' => 0, + 'limit' => 10, + 'run' => "2025-01-15", + 'user_attributes' => "city:london" +]); + +$trophy->leaderboards->get("daily_champions", $request); +``` + +```java Java +LeaderboardsGetRequest request = LeaderboardsGetRequest.builder() + .offset(0) + .limit(10) + .run("2025-01-15") + .userAttributes("city:london") + .build(); + +LeaderboardsGetResponse response = client.leaderboards().get("daily_champions", request); +``` + +```go Go +response, err := client.Leaderboards.Get( + "daily_champions", + &api.LeaderboardsGetRequest{ + Offset: 0, + Limit: 10, + Run: "2025-01-15", + UserAttributes: "city:london", + }, +) +``` + +```csharp C# +var request = new LeaderboardsGetRequest { + Offset = 0, + Limit = 10, + Run = "2025-01-15", + UserAttributes = "city:london" +}; + +await trophy.Leaderboards.GetAsync("daily_champions", request); +``` + +```ruby Ruby +result = client.Leaderboards.Get( + :key => "daily_champions", + :offset => 0, + :limit => 10, + :run => "2025-01-15", + :user_attributes => "city:london" +) +``` + + diff --git a/es/snippets/leaderboard-rankings-request.mdx b/es/snippets/leaderboard-rankings-request.mdx new file mode 100644 index 0000000..8131fc6 --- /dev/null +++ b/es/snippets/leaderboard-rankings-request.mdx @@ -0,0 +1,47 @@ + +```bash cURL +curl --request GET \ + --url https://api.trophy.so/v1/leaderboards/{key} \ + --header 'X-API-KEY: ' +``` + +```typescript Node +trophy.leaderboards.get("daily_champions", { + offset: 0, + limit: 10, + run: "2025-01-15" +}); +``` + +```python Python +client.leaderboards.get( + key="daily_champions", + offset=0, + limit=10, + run="2025-01-15" +) +``` + +```php PHP +$trophy->leaderboards->get("daily_champions"); +``` + +```java Java +client.leaderboards().get("daily_champions"); +``` + +```go Go +response, err := client.Leaderboards.Get("daily_champions") +``` + +```csharp C# +trophy.Leaderboards.GetAsync("daily_champions"); +``` + +```ruby Ruby +result = client.Leaderboards.Get( + :key => "daily_champions" +) +``` + + diff --git a/es/snippets/leaderboard-rankings-response.mdx b/es/snippets/leaderboard-rankings-response.mdx new file mode 100644 index 0000000..a20ca77 --- /dev/null +++ b/es/snippets/leaderboard-rankings-response.mdx @@ -0,0 +1,41 @@ +{/* vale off */} + +```json Response [expandable] +{ + "id": "5100fe51-6bce-6j44-b0hs-bddc4e123682", + "name": "Weekly Flashcard Challenge", + "key": "weekly-challenge", + "rankBy": "metric", + "metricKey": "flashcards-flipped", + "metricName": "Flashcards Flipped", + "pointsSystemKey": null, + "pointsSystemName": null, + "description": "Compete weekly to see who views the most flashcards", + "status": "active", + "start": "2025-01-01", + "end": null, + "maxParticipants": 100, + "runUnit": "day", + "runInterval": 7, + "rankings": [ + { + "userId": "user-123", + "userName": "Alice Johnson", + "rank": 1, + "value": 5000 + }, + { + "userId": "user-456", + "userName": "Bob Smith", + "rank": 2, + "value": 4500 + }, + { + "userId": "user-789", + "userName": "Charlie Brown", + "rank": 3, + "value": 4200 + } + ] +} +``` \ No newline at end of file diff --git a/es/snippets/metric-change-event-points-level-excerpt-block.mdx b/es/snippets/metric-change-event-points-level-excerpt-block.mdx new file mode 100644 index 0000000..041be1c --- /dev/null +++ b/es/snippets/metric-change-event-points-level-excerpt-block.mdx @@ -0,0 +1,21 @@ +{/* vale off */} + +```json Response [expandable] +{ + ..., + "points": { + "xp": { + ..., + "level": { + "id": "1140fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "bronze", + "name": "Bronze", + "description": "Starting level", + "badgeUrl": null, + "points": 0 + }, + ... + } + } +} +``` diff --git a/es/snippets/metric-change-request-block.mdx b/es/snippets/metric-change-request-block.mdx new file mode 100644 index 0000000..af91699 --- /dev/null +++ b/es/snippets/metric-change-request-block.mdx @@ -0,0 +1,105 @@ + +```bash cURL +curl -X POST https://app.trophy.so/api/metrics/flashcards-flipped/event \ + -H "X-API-KEY: " \ + -H "Content-Type: application/json" \ + -d '{ + "user": { + "id": "18", + "email": "user@example.com", + "tz": "Europe/London" + }, + "value": 750 +}' +``` + +```typescript Node +trophy.metrics.event("flashcards-flipped", { + user: { + id: "18", + email: "user@example.com", + tz: "Europe/London", + }, + value: 750, +}); +``` + +```python Python +client.metrics.event( + key="flashcards-flipped", + user=EventRequestUser( + id="18", + email="user@example.com", + tz="Europe/London", + ), + value=750.0, +) +``` + +```php PHP +$user = new EventRequestUser([ + 'id' => '18', + 'email' => 'user@example.com' +]); + +$request = new MetricsEventRequest([ + 'user' => $user, + 'value' => 750 +]); + +$trophy->metrics->event("flashcards-flipped", $request); +``` + +```java Java +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 +response, err := client.Metrics.Event( + "flashcards-flipped", + &api.MetricsEventRequest{ + User: &api.EventRequestUser{ + Id: "18", + Email: "user@example.com", + }, + Value: 750, + }, +) +``` + +```csharp C# +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 +result = client.metrics.event( + :key => 'flashcards-flipped', + :user => { + :id => '18', + :email => 'user@example.com' + }, + :value => 750 +) +``` + + diff --git a/es/snippets/metric-change-response-block.mdx b/es/snippets/metric-change-response-block.mdx new file mode 100644 index 0000000..d787bcb --- /dev/null +++ b/es/snippets/metric-change-response-block.mdx @@ -0,0 +1,83 @@ +{/* vale off */} + +```json Response [expandable] +{ + "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", + "eventId": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "total": 750, + "achievements": [ + { + "id": "5100fe51-6bce-6j44-b0hs-bddc4e123682", + "trigger": "metric", + "metricId": "5100fe51-6bce-6j44-b0hs-bddc4e123682", + "metricName": "Flashcards Flipped", + "metricValue": 500, + "name": "500 Flashcards Flipped", + "description": "Write 500 words in the app.", + "achievedAt": "2020-01-01T00:00:00Z" + } + ], + "currentStreak": { + "length": 1, + "frequency": "daily", + "started": "2025-04-02", + "periodStart": "2025-03-31", + "periodEnd": "2025-04-05", + "expires": "2025-04-12" + }, + "points": { + "xp": { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "xp", + "name": "XP", + "description": null, + "badgeUrl": null, + "maxPoints": null, + "total": 10, + "level": { + "id": "1140fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "bronze", + "name": "Bronze", + "description": "Starting level", + "badgeUrl": null, + "points": 0 + }, + "added": 10, + "awards": [ + { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "awarded": 10, + "date": "2021-01-01T00:00:00Z", + "total": 10, + "trigger": { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "type": "metric", + "metricName": "Flashcards Flipped", + "metricThreshold": 100, + "points": 10 + } + } + ] + } + }, + "leaderboards": { + "daily_champions": { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123535", + "key": "daily_champions", + "name": "Daily Champions", + "description": null, + "rankBy": "metric", + "runUnit": null, + "runInterval": 0, + "maxParticipants": 100, + "metricName": "Flashcards Flipped", + "metricKey": "flashcards-flipped", + "threshold": 10, + "start": "2025-01-01", + "end": null, + "previousRank": 50, + "rank": 12 + } + } +} +``` diff --git a/es/snippets/metric-event-with-device-tokens-request-block.mdx b/es/snippets/metric-event-with-device-tokens-request-block.mdx new file mode 100644 index 0000000..a84b84c --- /dev/null +++ b/es/snippets/metric-event-with-device-tokens-request-block.mdx @@ -0,0 +1,116 @@ +{/* vale off */} + + +```bash cURL {10} +curl -X POST https://app.trophy.so/api/metrics/flashcards-flipped/event \ + -H "X-API-KEY: " \ + -H "Content-Type: application/json" \ + -d '{ + "user": { + "id": "18", + "email": "user@example.com", + "tz": "Europe/London", + "deviceTokens": ["token1", "token2"] + }, + "value": 750 +}' +``` + +```typescript Node {6} +trophy.metrics.event("flashcards-flipped", { + user: { + id: "18", + email: "user@example.com", + tz: "Europe/London", + deviceTokens: ["token1", "token2"], + }, + value: 750, +}); +``` + +```python Python {7} +client.metrics.event( + key="flashcards-flipped", + user=EventRequestUser( + id="18", + email="user@example.com", + tz="Europe/London", + deviceTokens=["token1", "token2"], + ), + value=750.0, +) +``` + +```php PHP {6} +$user = new EventRequestUser([ + 'id' => '18', + 'email' => 'user@example.com', + 'deviceTokens' => ['token1', 'token2'], +]); + +$request = new MetricsEventRequest([ + 'user' => $user, + 'value' => 750 +]); + +$trophy->metrics->event("flashcards-flipped", $request); +``` + +```java Java {6} +MetricsEventRequest request = MetricsEventRequest.builder() + .user( + EventRequestUser.builder() + .id("18") + .email("user@example.com") + .deviceTokens(Arrays.asList("token1", "token2")) + .build() + ) + .value(750) + .build(); + +EventResponse response = client.metrics().event("flashcards-flipped", request); +``` + +```go Go {8} +response, err := client.Metrics.Event( + "flashcards-flipped", + &api.MetricsEventRequest{ + User: &api.EventRequestUser{ + Id: "18", + Email: "user@example.com", + DeviceTokens: []string{"token1", "token2"}, + }, + Value: 750, + }, +) +``` + +```csharp C# {6} +var user = new EventRequestUser { + Id = "18", + Email = "user@example.com", + DeviceTokens = new[] { "token1", "token2" } +}; + +var request = new MetricsEventRequest { + User = user, + Value = 750 +}; + +await trophy.Metrics.EventAsync("flashcards-flipped", request); +``` + +```ruby Ruby {7} +result = client.metrics.event( + :key => 'flashcards-flipped', + :user => { + :id => '18', + :email => 'user@example.com', + :deviceTokens => ['token1', 'token2'] + }, + :value => 750 +) +``` + + + diff --git a/es/snippets/plan-badge.jsx b/es/snippets/plan-badge.jsx new file mode 100644 index 0000000..59612aa --- /dev/null +++ b/es/snippets/plan-badge.jsx @@ -0,0 +1,26 @@ +export const PlanBadge = ({ plan }) => { + return ( +
+ + {plan.charAt(0).toUpperCase() + plan.slice(1)} + +
+ ); +}; diff --git a/es/snippets/points-histogram-summary-response-block.mdx b/es/snippets/points-histogram-summary-response-block.mdx new file mode 100644 index 0000000..5c6943f --- /dev/null +++ b/es/snippets/points-histogram-summary-response-block.mdx @@ -0,0 +1,17 @@ +{/* vale off */} + +```json GET /points/{key}/summary — 200 response [expandable] +[ + { "from": 0, "to": 0, "users": 5012 }, + { "from": 1, "to": 100, "users": 1501 }, + { "from": 101, "to": 200, "users": 1007 }, + { "from": 201, "to": 300, "users": 584 }, + { "from": 301, "to": 400, "users": 201 }, + { "from": 401, "to": 500, "users": 102 }, + { "from": 501, "to": 600, "users": 25 }, + { "from": 601, "to": 700, "users": 0 }, + { "from": 701, "to": 800, "users": 0 }, + { "from": 801, "to": 900, "users": 0 }, + { "from": 901, "to": 1000, "users": 0 } +] +``` diff --git a/es/snippets/points-level-summary-response-block.mdx b/es/snippets/points-level-summary-response-block.mdx new file mode 100644 index 0000000..abbcc2f --- /dev/null +++ b/es/snippets/points-level-summary-response-block.mdx @@ -0,0 +1,39 @@ +{/* vale off */} + +```json Response [expandable] +[ + { + "level": { + "id": "1140fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "bronze", + "name": "Bronze", + "description": "Starting level", + "badgeUrl": "https://example.com/bronze.png", + "points": 0 + }, + "users": 5012 + }, + { + "level": { + "id": "2240fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "silver", + "name": "Silver", + "description": "Mid-tier level", + "badgeUrl": null, + "points": 50 + }, + "users": 1501 + }, + { + "level": { + "id": "3340fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "gold", + "name": "Gold", + "description": "Top level", + "badgeUrl": "https://example.com/gold.png", + "points": 200 + }, + "users": 102 + } +] +``` diff --git a/es/snippets/points-levels-list-response-block.mdx b/es/snippets/points-levels-list-response-block.mdx new file mode 100644 index 0000000..0b7319d --- /dev/null +++ b/es/snippets/points-levels-list-response-block.mdx @@ -0,0 +1,30 @@ +{/* vale off */} + +```json Response [expandable] +[ + { + "id": "1140fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "bronze", + "name": "Bronze", + "description": "Starting level", + "badgeUrl": "https://example.com/bronze.png", + "points": 0 + }, + { + "id": "2240fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "silver", + "name": "Silver", + "description": "Mid-tier level", + "badgeUrl": null, + "points": 50 + }, + { + "id": "3340fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "gold", + "name": "Gold", + "description": "Top level", + "badgeUrl": "https://example.com/gold.png", + "points": 200 + } +] +``` diff --git a/es/snippets/points-system-response-block.mdx b/es/snippets/points-system-response-block.mdx new file mode 100644 index 0000000..90eb952 --- /dev/null +++ b/es/snippets/points-system-response-block.mdx @@ -0,0 +1,49 @@ +{/* vale off */} + +```json GET /points/{key} — 200 response [expandable] +{ + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "name": "XP System", + "description": "Experience points for user engagement", + "badgeUrl": "https://example.com/badge.png", + "maxPoints": null, + "triggers": [ + { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "type": "metric", + "points": 10, + "status": "active", + "metricId": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "metricName": "words written", + "metricThreshold": 1000, + "userAttributes": [ + { "key": "plan-type", "value": "premium" }, + { "key": "region", "value": "us-east" } + ], + "eventAttribute": { "key": "source", "value": "mobile-app" }, + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z" + }, + { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123536", + "type": "streak", + "points": 10, + "status": "active", + "streakLengthThreshold": 7, + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z" + }, + { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123535", + "type": "achievement", + "points": 50, + "status": "active", + "achievementId": "0040fe51-6bce-4b44-b0ad-bddc4e123535", + "achievementName": "finish onboarding", + "userAttributes": [{ "key": "plan-type", "value": "premium" }], + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z" + } + ] +} +``` diff --git a/es/snippets/rate-limit-badge.jsx b/es/snippets/rate-limit-badge.jsx new file mode 100644 index 0000000..4ada2b3 --- /dev/null +++ b/es/snippets/rate-limit-badge.jsx @@ -0,0 +1,35 @@ +export const RateLimitBadge = ({ tier, noTooltip = false }) => { + const getColor = (tier) => { + switch (tier) { + case 1: + return "yellow"; + case 2: + return "green"; + default: + return "gray"; + } + }; + + const getLimit = (tier) => { + switch (tier) { + case 1: + return 10; + case 2: + return 100; + } + }; + + return ( + noTooltip ? ( + Tier {tier} + ) : ( + + Tier {tier} + + ) + ); +}; diff --git a/es/snippets/sdk-install-command.mdx b/es/snippets/sdk-install-command.mdx new file mode 100644 index 0000000..c7f7d38 --- /dev/null +++ b/es/snippets/sdk-install-command.mdx @@ -0,0 +1,45 @@ + +```bash Node +npm install @trophyso/node +``` + +```bash Ruby +gem install trophy_api_client +``` + +```bash Python +pip install trophy +``` + +```bash PHP +composer require trophyso/php +``` + +```bash Java (Gradle) +implementation 'so.trophy:trophy-java:1.0.0' +``` + +```bash Java (Maven) + + so.trophy + trophy-java + 1.0.0 + +``` + +```bash Go +go get github.com/trophy-so/trophy-go +``` + +```bash .NET (C#) +// .NET Core CLI +dotnet add package Trophy + +// Nuget Package Manager +nuget install Trophy + +// Visual Studio +Install-Package Trophy +``` + + diff --git a/es/snippets/snippet-intro.mdx b/es/snippets/snippet-intro.mdx new file mode 100644 index 0000000..c57e7c7 --- /dev/null +++ b/es/snippets/snippet-intro.mdx @@ -0,0 +1,4 @@ +One of the core principles of software development is DRY (Don't Repeat +Yourself). This is a principle that apply to documentation as +well. If you find yourself repeating the same content in multiple places, you +should consider creating a custom snippet to keep your content in sync. diff --git a/es/snippets/streak-request-block.mdx b/es/snippets/streak-request-block.mdx new file mode 100644 index 0000000..4876468 --- /dev/null +++ b/es/snippets/streak-request-block.mdx @@ -0,0 +1,61 @@ + +```bash cURL +curl --request GET \ + --url https://api.trophy.so/v1/users/{id}/streak \ + --header 'X-API-KEY: ' +``` + +```typescript Node +trophy.users.streak("user-id", { + historyPeriods: 14 +}); +``` + +```python Python +client.users.streak( + id="user-id", + history_periods=14, +) +``` + +```php PHP +$request = new UsersStreakRequest([ + 'history_periods' => 14 +]); + +$trophy->users->streak("user-id", $request); +``` + +```java Java +UsersStreakRequest request = UsersStreakRequest.builder() + .historyPeriods(14) + .build(); + +UsersStreakResponse response = client.users().streak("user-id", request); +``` + +```go Go +response, err := client.Users.Streak( + "user-id", + &api.UsersStreakRequest{ + HistoryPeriods: 14, + }, +) +``` + +```csharp C# +var request = new UsersStreakRequest { + HistoryPeriods = 14 +}; + +await trophy.Users.StreaksAsync("user-id", request); +``` + +```ruby Ruby +result = client.users.streak( + :id => 'user-id', + :history_periods => 14 +) +``` + + diff --git a/es/snippets/streak-response-block.mdx b/es/snippets/streak-response-block.mdx new file mode 100644 index 0000000..320798a --- /dev/null +++ b/es/snippets/streak-response-block.mdx @@ -0,0 +1,49 @@ +{/* vale off */} + +```json Response [expandable] +{ + "length": 1, + "frequency": "weekly", + "started": "2025-04-02", + "periodStart": "2025-03-31", + "periodEnd": "2025-04-05", + "expires": "2025-04-12", + "streakHistory": [ + { + "periodStart": "2025-03-30", + "periodEnd": "2025-04-05", + "length": 1 + }, + { + "periodStart": "2025-04-06", + "periodEnd": "2025-04-12", + "length": 2 + }, + { + "periodStart": "2025-04-13", + "periodEnd": "2025-04-19", + "length": 3 + }, + { + "periodStart": "2025-04-20", + "periodEnd": "2025-04-26", + "length": 0 + }, + { + "periodStart": "2025-04-27", + "periodEnd": "2025-05-03", + "length": 1 + }, + { + "periodStart": "2025-05-04", + "periodEnd": "2025-05-10", + "length": 2 + }, + { + "periodStart": "2025-05-11", + "periodEnd": "2025-05-17", + "length": 3 + } + ] +} +``` diff --git a/es/snippets/update-user-notification-preferences-block.mdx b/es/snippets/update-user-notification-preferences-block.mdx new file mode 100644 index 0000000..2c1ff38 --- /dev/null +++ b/es/snippets/update-user-notification-preferences-block.mdx @@ -0,0 +1,85 @@ +{/* vale off */} + + +```bash cURL +curl --request PATCH \ + --url https://api.trophy.so/v1/users/user-123/preferences \ + --header 'Content-Type: application/json' \ + --header 'X-API-KEY: ' \ + --data '{ + "notifications": { + "recap": ["email"], + "streak_reminder": [] + } +}' +``` + +```typescript Node +const response = await trophy.users.updatePreferences("user-123", { + notifications: { + recap: ["email"], + streak_reminder: [] + } +}); +``` + +```python Python +response = client.users.update_preferences( + id="user-123", + notifications={ + "recap": ["email"], + "streak_reminder": [] + } +) +``` + +```php PHP +$response = $trophy->users()->updatePreferences("user-123", [ + "notifications" => [ + "recap" => ["email"], + "streak_reminder" => [] + ] +]); +``` + +```java Java +UpdateUserPreferencesRequest request = UpdateUserPreferencesRequest.builder() + .notifications(NotificationPreferences.builder() + .recap(Arrays.asList("email")) + .streakReminder(Collections.emptyList()) + .build()) + .build(); +UserPreferencesResponse response = client.users().updatePreferences("user-123", request); +``` + +```go Go +response, err := client.Users.UpdatePreferences("user-123", &UpdateUserPreferencesRequest{ + Notifications: &NotificationPreferences{ + Recap: []string{"email"}, + StreakReminder: []string{}, + }, +}) +``` + +```csharp C# +var response = await trophy.Users.UpdatePreferencesAsync("user-123", new UpdateUserPreferencesRequest +{ + Notifications = new NotificationPreferences + { + Recap = new[] { "email" }, + StreakReminder = Array.Empty() + } +}); +``` + +```ruby Ruby +response = client.users.update_preferences( + id: "user-123", + notifications: { + recap: ["email"], + streak_reminder: [] + } +) +``` + + diff --git a/es/snippets/user-achievements-request-block.mdx b/es/snippets/user-achievements-request-block.mdx new file mode 100644 index 0000000..432cc2d --- /dev/null +++ b/es/snippets/user-achievements-request-block.mdx @@ -0,0 +1,38 @@ + +```bash cURL +curl --request GET \ + --url https://api.trophy.so/v1/users/{id}/achievements \ + --header 'X-API-KEY: ' +``` + +```typescript Node +trophy.users.achievements("user-id"); +``` + +```python Python +client.users.achievements("user-id") +``` + +```php PHP +$trophy->users->achievements("user-id"); +``` + +```java Java +UserAchievementsResponse response = client.users().achievements("user-id"); +``` + +```go Go +response, err := client.Users.Achievements("user-id") +``` + +```csharp C# +await trophy.Users.AchievementsAsync("user-id"); +``` + +```ruby Ruby +result = client.users.achievements( + :id => 'user-id' +) +``` + + diff --git a/es/snippets/user-energy-response-block.mdx b/es/snippets/user-energy-response-block.mdx new file mode 100644 index 0000000..fc9602d --- /dev/null +++ b/es/snippets/user-energy-response-block.mdx @@ -0,0 +1,39 @@ +{/* vale off */} + +```json Response [expandable] +{ + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "name": "Energy", + "description": null, + "badgeUrl": null, + "total": 10, + "awards": [ + { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "awarded": 1, + "date": "2021-01-01T00:00:00Z", + "total": 10, + "trigger": { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "type": "time", + "points": 1, + "timeUnit": "day", + "timeInterval": 1 + } + }, + { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "awarded": -1, + "date": "2021-01-01T00:00:00Z", + "total": 9, + "trigger": { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "type": "metric", + "points": -1, + "metricName": "Flashcards Flipped", + "metricThreshold": 1 + } + } + ] +} +``` diff --git a/es/snippets/user-leaderboard-rankings-request.mdx b/es/snippets/user-leaderboard-rankings-request.mdx new file mode 100644 index 0000000..6ca2c89 --- /dev/null +++ b/es/snippets/user-leaderboard-rankings-request.mdx @@ -0,0 +1,41 @@ + +```bash cURL +curl --request GET \ + --url https://api.trophy.so/v1/users/{id}/leaderboards/{key} \ + --header 'X-API-KEY: ' +``` + +```typescript Node +trophy.users.leaderboards("user-id", "daily_champions", { + run: "2025-01-15" +}); +``` + +```python Python +client.users.leaderboards("user-id", "daily_champions", run="2025-01-15") +``` + +```php PHP +$trophy->users->leaderboards("user-id", "daily_champions") +``` + +```java Java +client.users().leaderboards("user-id", "daily_champions") +``` + +```go Go +response, err := client.Users.Leaderboards("user-id", "daily_champions") +``` + +```csharp C# +trophy.Users.Leaderboards("user-id", "daily_champions"); +``` + +```ruby Ruby +result = client.Leaderboards.Get( + :id => "user-id", + :key => "daily_champions" +) +``` + + diff --git a/es/snippets/user-leaderboard-rankings-response.mdx b/es/snippets/user-leaderboard-rankings-response.mdx new file mode 100644 index 0000000..9765d92 --- /dev/null +++ b/es/snippets/user-leaderboard-rankings-response.mdx @@ -0,0 +1,45 @@ +{/* vale off */} + +```json Response [expandable] +{ + "id": "5100fe51-6bce-6j44-b0hs-bddc4e123682", + "name": "Weekly Word Count Challenge", + "key": "weekly-words", + "rankBy": "metric", + "metricKey": "flashcards-flipped", + "metricName": "Flashcards Flipped", + "pointsSystemKey": null, + "pointsSystemName": null, + "description": "Compete weekly to see who writes the most words", + "start": "2025-01-01", + "end": null, + "maxParticipants": 100, + "runUnit": "day", + "runInterval": 7, + "rank": 2, + "value": 4500, + "history": [ + { + "timestamp": "2025-01-15T10:30:00Z", + "previousRank": null, + "rank": 5, + "previousValue": null, + "value": 1000 + }, + { + "timestamp": "2025-01-15T14:15:00Z", + "previousRank": 5, + "rank": 3, + "previousValue": 1000, + "value": 3000 + }, + { + "timestamp": "2025-01-15T18:45:00Z", + "previousRank": 3, + "rank": 2, + "previousValue": 3000, + "value": 4500 + } + ] +} +``` \ No newline at end of file diff --git a/es/snippets/user-points-request-block.mdx b/es/snippets/user-points-request-block.mdx new file mode 100644 index 0000000..fceab27 --- /dev/null +++ b/es/snippets/user-points-request-block.mdx @@ -0,0 +1,45 @@ + +```bash cURL +curl --request GET \ + --url https://api.trophy.so/v1/users/{id}/points/{key} \ + --header 'X-API-KEY: ' +``` + +```typescript Node +trophy.users.points("user-id", "points-system-key"); +``` + +```python Python +client.users.points( + id="user-id", + key="points-system-key", +) +``` + +```php PHP +$trophy->users->points("user-id", "points-system-key"); +``` + +```java Java +client.users().points("user-id","points-system-key"); +``` + +```go Go +response, err := client.Users.Points( + "user-id", + "points-system-key" +) +``` + +```csharp C# +trophy.Users.PointsAsync("user-id", "points-system-key"); +``` + +```ruby Ruby +result = client.users.points( + :id => 'user-id', + :key => "points-system-key" +) +``` + + diff --git a/es/snippets/user-points-response-block.mdx b/es/snippets/user-points-response-block.mdx new file mode 100644 index 0000000..860c7d5 --- /dev/null +++ b/es/snippets/user-points-response-block.mdx @@ -0,0 +1,36 @@ +{/* vale off */} + +```json Response [expandable] +{ + "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 + } + } + ] +} +``` diff --git a/es/snippets/user-points-summary-request-block.mdx b/es/snippets/user-points-summary-request-block.mdx new file mode 100644 index 0000000..f987698 --- /dev/null +++ b/es/snippets/user-points-summary-request-block.mdx @@ -0,0 +1,52 @@ + +```bash cURL +curl --request GET \ + --url https://api.trophy.so/v1/users/{id}/points/{key}/event-summary \ + --header 'X-API-KEY: ' +``` + +```typescript Node +trophy.users.pointsEventSummary("user-id", "points-system-key", { + aggregation: "daily", + start_date: "2025-01-01", + end_date: "2025-01-07" +}); +``` + +```python Python +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 +$trophy->users->pointsEventSummary("user-id", "points-system-key"); +``` + +```java Java +client.users().pointsEventSummary("user-id","points-system-key"); +``` + +```go Go +response, err := client.Users.PointsEventSummary( + "user-id", + "points-system-key" +) +``` + +```csharp C# +trophy.Users.PointsEventSummaryAsync("user-id", "points-system-key"); +``` + +```ruby Ruby +result = client.users.pointsEventSummary( + :id => 'user-id', + :key => "points-system-key" +) +``` + + diff --git a/es/snippets/user-points-summary-response-block.mdx b/es/snippets/user-points-summary-response-block.mdx new file mode 100644 index 0000000..be8b33a --- /dev/null +++ b/es/snippets/user-points-summary-response-block.mdx @@ -0,0 +1,21 @@ +{/* vale off */} + +```json GET /users/{id}/points/{key}/event-summary — 200 response [expandable] +[ + { + "date": "2024-01-01", + "total": 100, + "change": 100 + }, + { + "date": "2024-01-02", + "total": 300, + "change": 200 + }, + { + "date": "2024-01-03", + "total": 600, + "change": 300 + } +] +``` diff --git a/es/snippets/webhook-points-level-changed-payload-block.mdx b/es/snippets/webhook-points-level-changed-payload-block.mdx new file mode 100644 index 0000000..c1d0702 --- /dev/null +++ b/es/snippets/webhook-points-level-changed-payload-block.mdx @@ -0,0 +1,44 @@ +{/* vale off */} + +```json Webhook points.level_changed — request body [expandable] +{ + "type": "points.level_changed", + "user": { + "id": "user-id", + "email": "user@example.com", + "tz": "Europe/London", + "subscribedToEmails": true, + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z", + "attributes": { + "department": "engineering", + "role": "developer" + } + }, + "points": { + "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "xp", + "name": "XP", + "description": "Experience points", + "badgeUrl": null, + "maxPoints": null, + "total": 100 + }, + "previousLevel": { + "id": "1140fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "bronze", + "name": "Bronze", + "description": "Starting level", + "badgeUrl": "https://example.com/bronze.png", + "points": 0 + }, + "newLevel": { + "id": "2240fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "silver", + "name": "Silver", + "description": "Mid-tier level", + "badgeUrl": null, + "points": 50 + } +} +``` diff --git a/es/webhooks/events/achievements/achievement-completed.mdx b/es/webhooks/events/achievements/achievement-completed.mdx new file mode 100644 index 0000000..674b47d --- /dev/null +++ b/es/webhooks/events/achievements/achievement-completed.mdx @@ -0,0 +1,8 @@ +--- +openapi: webhook achievement.completed +--- + + + This webhook doesn't fire for achievements that are + [backdated](/platform/achievements#backdating-achievements). + diff --git a/es/webhooks/events/leaderboards/leaderboard-changed.mdx b/es/webhooks/events/leaderboards/leaderboard-changed.mdx new file mode 100644 index 0000000..9e62056 --- /dev/null +++ b/es/webhooks/events/leaderboards/leaderboard-changed.mdx @@ -0,0 +1,3 @@ +--- +openapi: webhook leaderboard.changed +--- diff --git a/es/webhooks/events/leaderboards/leaderboard-finished.mdx b/es/webhooks/events/leaderboards/leaderboard-finished.mdx new file mode 100644 index 0000000..727719c --- /dev/null +++ b/es/webhooks/events/leaderboards/leaderboard-finished.mdx @@ -0,0 +1,15 @@ +--- +openapi: webhook leaderboard.finished +--- + + + Events fire between 13:00-13:59 UTC the day following the end of a leaderboard + run. This is because each leaderboard run is relative to each user's time + zone, and users in the most delayed time zones must be given the chance to + finish. + + + + Events only include the top 10 user rankings. To fetch more, use the [single + leaderboard API](/api-reference/endpoints/leaderboards/get-leaderboard). + diff --git a/es/webhooks/events/leaderboards/leaderboard-rank-changed.mdx b/es/webhooks/events/leaderboards/leaderboard-rank-changed.mdx new file mode 100644 index 0000000..0ab2e6a --- /dev/null +++ b/es/webhooks/events/leaderboards/leaderboard-rank-changed.mdx @@ -0,0 +1,3 @@ +--- +openapi: webhook leaderboard.rank_changed +--- diff --git a/es/webhooks/events/leaderboards/leaderboard-started.mdx b/es/webhooks/events/leaderboards/leaderboard-started.mdx new file mode 100644 index 0000000..c864559 --- /dev/null +++ b/es/webhooks/events/leaderboards/leaderboard-started.mdx @@ -0,0 +1,12 @@ +--- +openapi: webhook leaderboard.started +--- + + + Events fire at 10:00 UTC the day before the leaderboard's UTC start time. This + is because leaderboard runs are relative to each user's time zone, and the + start event must fire before users in the most advanced time zones begin + ranking. + + **Make sure to take into account users' time zones if scheduling notifications from this webhook event**. + diff --git a/es/webhooks/events/points/points-boost-finished.mdx b/es/webhooks/events/points/points-boost-finished.mdx new file mode 100644 index 0000000..28c119b --- /dev/null +++ b/es/webhooks/events/points/points-boost-finished.mdx @@ -0,0 +1,9 @@ +--- +openapi: webhook points.boost_finished +--- + + + For boosts that apply to all users or a user group, events fire between 13:00-13:59 UTC the day following the end of a boost. This is because each boost is relative to each user's time zone, and users in the most delayed time zones must be given the chance to experience the whole duration of the boost. + + For user-specific boosts (boosts that only apply to one user), events fire at the exact boost finish time in that user's time zone. + \ No newline at end of file diff --git a/es/webhooks/events/points/points-boost-started.mdx b/es/webhooks/events/points/points-boost-started.mdx new file mode 100644 index 0000000..f12cce6 --- /dev/null +++ b/es/webhooks/events/points/points-boost-started.mdx @@ -0,0 +1,12 @@ +--- +openapi: webhook points.boost_started +--- + + + For boosts that apply to all users or a user group, events fire at 10:00 UTC the day before the boost's UTC start time. This + is because boosts are relative to each user's time zone, and the start event must fire before users in the most advanced time zones would see the boost. + + For user-specific boosts (boosts that only apply to one user), events fire at the exact boost start time in that user's time zone. + + **Make sure to take into account users' time zones if scheduling notifications from this webhook event**. + diff --git a/es/webhooks/events/points/points-changed.mdx b/es/webhooks/events/points/points-changed.mdx new file mode 100644 index 0000000..c83305b --- /dev/null +++ b/es/webhooks/events/points/points-changed.mdx @@ -0,0 +1,8 @@ +--- +openapi: webhook points.changed +--- + + + Does not apply to [user identification trigger + type](/platform/points#types-of-triggers). + diff --git a/es/webhooks/events/points/points-level-changed.mdx b/es/webhooks/events/points/points-level-changed.mdx new file mode 100644 index 0000000..2c1ecdd --- /dev/null +++ b/es/webhooks/events/points/points-level-changed.mdx @@ -0,0 +1,7 @@ +--- +openapi: webhook points.level_changed +--- + + + This event fires when a user's **level** changes within a points system **because they earned or lost points**. It is separate from [`points.changed`](/webhooks/events/points/points-changed), which reflects points balance changes more broadly. Use `points.level_changed` when you care specifically about level transitions. + diff --git a/es/webhooks/events/streaks/streak-extended.mdx b/es/webhooks/events/streaks/streak-extended.mdx new file mode 100644 index 0000000..1635d2b --- /dev/null +++ b/es/webhooks/events/streaks/streak-extended.mdx @@ -0,0 +1,8 @@ +--- +openapi: webhook streak.extended +--- + + + Does not apply to [streak freeze](/platform/streaks#streak-freezes) + consumption. + diff --git a/es/webhooks/events/streaks/streak-freeze-consumed.mdx b/es/webhooks/events/streaks/streak-freeze-consumed.mdx new file mode 100644 index 0000000..fd55cc3 --- /dev/null +++ b/es/webhooks/events/streaks/streak-freeze-consumed.mdx @@ -0,0 +1,3 @@ +--- +openapi: webhook streak.freeze_consumed +--- diff --git a/es/webhooks/events/streaks/streak-freeze-earned.mdx b/es/webhooks/events/streaks/streak-freeze-earned.mdx new file mode 100644 index 0000000..0288189 --- /dev/null +++ b/es/webhooks/events/streaks/streak-freeze-earned.mdx @@ -0,0 +1,3 @@ +--- +openapi: webhook streak.freeze_earned +--- diff --git a/es/webhooks/events/streaks/streak-lost.mdx b/es/webhooks/events/streaks/streak-lost.mdx new file mode 100644 index 0000000..088f535 --- /dev/null +++ b/es/webhooks/events/streaks/streak-lost.mdx @@ -0,0 +1,3 @@ +--- +openapi: webhook streak.lost +--- diff --git a/es/webhooks/events/streaks/streak-started.mdx b/es/webhooks/events/streaks/streak-started.mdx new file mode 100644 index 0000000..77ac3b2 --- /dev/null +++ b/es/webhooks/events/streaks/streak-started.mdx @@ -0,0 +1,3 @@ +--- +openapi: webhook streak.started +--- diff --git a/es/webhooks/idempotency.mdx b/es/webhooks/idempotency.mdx new file mode 100644 index 0000000..1168888 --- /dev/null +++ b/es/webhooks/idempotency.mdx @@ -0,0 +1,57 @@ +--- +title: Idempotency +description: Learn how to ensure your webhook endpoints only process events once to prevent unintended side effects. +subtitle: Learn how to ensure your webhook endpoints only process events once to prevent unintended side effects. +icon: repeat +--- + +## What is Idempotency? + +In the context of webhooks, [idempotence](https://en.wikipedia.org/wiki/Idempotence) is the property of an endpoint to ensure it processes duplicate events exactly once. + +Most webhook emitters, including Trophy, operate on an "at least once" delivery guarantee. This means you may eventually receive the same webhook multiple times and your code needs to handle those scenarios without unintended consequences. + +For example, if your webhook handler is subscribed to [`leaderboard.finished`](/webhooks/events/leaderboards/leaderboard-finished) events and sends users gift cards if they place in the top 10, then your code needs to only send the gift cards once even if your code receives the same event from Trophy multiple times. + +## Implementing Webhook Idempotency + +Making your webhook handlers more robust is trivial in most cases. Let's take the gift card example above. + +After sending out a gift card to a user, your code could insert a record into your database that represents that the gift card has been sent to the user. + +```sql +-- Create a table to store gift card receipts +CREATE TABLE gift_card_receipts ( + id text PRIMARY KEY, + user_id text NOT NULL, + gift_card_id text NOT NULL +) + +-- Insert a row after sending gift card +INSERT INTO gift_card_receipts (id, user_id, gift_card_id) +VALUES ('webhook_event_id', 'user_123', 'gift_card_abc') + +-- Check before sending gift card +SELECT * FROM gift_card_receipts +WHERE user_id = 'user-id' +AND gift_card_id = 'gift-card-id' +LIMIT 1 +``` + +Then before sending gift cards add an `if` statement that checks if the gift card has already been sent to the user. + +```js +var receipt = await getGiftcardReceipt(); + +if (!receipt) { + sendGiftCard(); +} else { + // Duplicate event - do nothing +} +``` + +If your code receives a duplicate event, the gift card will not be sent again. + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/webhooks/introduction.mdx b/es/webhooks/introduction.mdx new file mode 100644 index 0000000..9a396e2 --- /dev/null +++ b/es/webhooks/introduction.mdx @@ -0,0 +1,53 @@ +--- +title: Introduction +description: Learn about webhooks in Trophy and how to use them to power custom gamification integrations. +subtitle: Learn about webhooks in Trophy and how to use them to power custom gamification integrations. +--- + + + This feature is available on the [Pro](/account/billing#pro-plan) plan + + +Trophy has support for a number of webhooks that fire based on key events such as `achievement.completed`, `leaderboard.finished` and `streak.lost`. + +These events allow you to trigger custom code and power extended gamification experiences using Trophy data. + +All Trophy webhooks are delivered over HTTP which allows customers to subscribe to receive data from Trophy via regular HTTP `POST` requests. + +Read more about getting started with webhooks below. + + + + Start receiving webhook events from Trophy in under 10 minutes. + + + Secure webhook endpoints using signature verification. + + + Learn about how Trophy handles event retries. + + + Handle duplicate events and prevent unintended side effects. + + + Learn how to monitor webhook delivery and diagnose issues. + + + +## Common Use Cases + +Common use cases include: + +- Triggering custom emails and push notification sequences +- Data warehousing +- Powering non-gamification related workflows like subscription updates and feature management. +- Integrating with IFTTT platforms + + + Want another webhook event? [Tell us](mailto:support@trophy.so) about your use + case and we'll build it!. + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/webhooks/observability.mdx b/es/webhooks/observability.mdx new file mode 100644 index 0000000..ad88a33 --- /dev/null +++ b/es/webhooks/observability.mdx @@ -0,0 +1,27 @@ +--- +title: Observability +description: Learn how to monitor and diagnose issues with webhook delivery. +subtitle: Learn how to monitor and diagnose issues with webhook delivery. +icon: activity +--- + +## Monitoring Webhook Delivery + +The Trophy dashboard has built-in analytics for all webhook events sent to your endpoints, including viewing the status of sent events and the payload. + + + + + +## Data Retention + +Trophy retains all webhook logs for **7 days**. If you would like to discuss increasing your data retention window, please [get in touch](mailto:support@trophy.so) and we'll happily discuss it with you. + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/webhooks/quickstart.mdx b/es/webhooks/quickstart.mdx new file mode 100644 index 0000000..21cdf92 --- /dev/null +++ b/es/webhooks/quickstart.mdx @@ -0,0 +1,237 @@ +--- +title: Quick Start +description: Start receiving webhook events from Trophy in under 10 minutes. +subtitle: Start receiving webhook events from Trophy in under 10 minutes. +icon: circle-play +--- + +## Getting Started + + + If you don't already have a Trophy account, follow the [Trophy quick start + guide](/getting-started/quickstart) first to get started. + + +In Trophy you can have multiple webhooks for handling one or more types of event. Follow the steps below to get started receiving webhook events from Trophy. + + + + + + + + To develop and test webhooks, it's useful to use a test service that can proxy events from Trophy and send them to your local development environment. + +You can choose to use any webhook development tool you like, but we recommend [Hookdeck Console](https://console.hookdeck.com). + +First up, head into your webhook testing platform of choice and create a new test endpoint. + +Then, set up this endpoint to proxy events it receives to your local development environment. + + + + + + + + Once you have your test endpoint, head to the [webhooks](https://app.trophy.so/integration/webhooks) page of the Trophy dashboard and create a new webhook. + + - Give your new webhook a name like 'Local Testing' or similar + - Paste your test endpoint URL in the 'Webhook URL' field + - Select the webhook events you want to subscribe this webhook to + - Save the new webhook + + + You can modify the events the webhook is subscribed to at any time after creation. + + + + + + + + + + A webhook handler is just a standard HTTP endpoint that is capable of receiving `POST` requests. + +Trophy will send requests to your handler, telling it what `type` of event it's sending. This allows you to construct a handler that can process multiple types of events. + + It's up to you how you choose to write your handler, but a common pattern is to use a `switch` statement. + + Here's an example of this pattern in NodeJS: + + ```js Handling Webhook Events + switch (payload.type) { + case "achievement.completed": + // Handle achievement completed events + break; + case "leaderboard.started": + // Handle leaderboard started events + break; + case "points.changed": + // Handle points changed events + break; + case "points.level_changed": + // Handle points level changed events + break; + default: + // Handle unrecognised event type + break; + } + ``` + + + Make sure your webhook handler is set up to return a `200` status code. If Trophy detects your handler returns a non-`2XX` status code, it will treat this as a failure and [retry the request](/webhooks/retries). + + + + + Once you've written and secured your handler, you're ready to send your first test event. + + Using a test user, perform interactions in your product of the type that you want to test webhooks for. For example if you want to test the `achievement.completed` webhook, trigger an achievement completion in Trophy. + + Trophy will fire the relevant events to your webhook URL and you should see these in your webhook testing tool. This will then forward the event to your local webhook handler, triggering your code to run. + + You can now iterate on your code until your happy you have the functionality you desire. + + + + To help you secure your webhook handler so that it only responds to events sent from Trophy and not malicious attackers, Trophy includes a webhook signature with every event. + +This signature is sent in the `X-Trophy-Signature` header and is a `base64` encoded hash of the request payload, hashed using a secure webhook secret provided by Trophy. + +To validate that events your webhook handler receives do actually come from Trophy, you need to create your own hash using your secure webhook secret and compare it to the signature in the `X-Trophy-Signature` header. + +Grab your secure webhook secret from the webhooks page in Trophy. + + + + + + + Make sure you store your webhook secret in a secure environment variable and + do not commit it to source control. + + +Once you have your webhook secret, you're ready to start validating events. Here's an example in NodeJS: + +```js Validating Webhook Events + // Extract X-Trophy-Signature header from the request + const hmacHeader = request.headers.get("X-Trophy-Signature"); + + // Create a hash based on the parsed body + const hash = crypto + .createHmac("sha256", process.env.TROPHY_WEBHOOK_SECRET as string) + .update(await request.text()) + .digest("base64"); + + // Compare the created hash with the value of the X-Trophy-Signature header + if (hash === hmacHeader) { + console.log("Webhook is originating from Trophy"); + // Request validated, continue processing + } else { + console.log("Signature is invalid, rejected"); + // Request is not from Trophy, reject with 4XX status + } +``` + + + If your handler detects a request that did not originate from Trophy, it's + important to reject the request as early as possible with a `4XX` status code. + + + + + Once your happy with your test webhook functionality, create a brand new webhook in Trophy with the same configuration as your test webhook, but this time with the URL of your production handler. + + + Each webhook in Trophy has a unique webhook secret so don't forget to add this as a new environment variable in your production deployment. + + + + + +## Full Webhook Example + +Here's a full working NextJS example webhook endpoint capable of securely receiving webhook events from Trophy. + +```ts Example NextJS Webhook Endpoint +import crypto from "crypto"; + +import { NextRequest, NextResponse } from "next/server"; + +interface WebhookPayload { + type: string; + [x: string]: unknown; +} + +export async function POST(request: NextRequest) { + // Extract 'X-Trophy-Signature' header from the request + const hmacHeader = request.headers.get("X-Trophy-Signature"); + + // Create a hash based on the parsed body + const hash = crypto + // copy this secret from the Webhooks page in Trophy + .createHmac("sha256", process.env.TROPHY_WEBHOOK_SECRET as string) + .update(await request.text()) + .digest("base64"); + + // Compare the created hash with the value of the X-Trophy-Signature header + if (hash === hmacHeader) { + console.log("Webhook is originating from Trophy"); + + handleEvent(request.body as unknown as WebhookPayload); + + return NextResponse.json({ message: "Webhook received" }, { status: 200 }); + } else { + console.log("Signature is invalid, rejected"); + + return NextResponse.json({ message: "Webhook rejected" }, { status: 403 }); + } +} + +function handleEvent(payload: WebhookPayload) { + switch (payload.type) { + case "points.changed": + console.log("Handling points changed event"); + break; + case "points.level_changed": + console.log("Handling points level changed event"); + break; + case "streak.extended": + console.log("Handling streak extended event"); + break; + default: + break; + } +} +``` + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/webhooks/retries.mdx b/es/webhooks/retries.mdx new file mode 100644 index 0000000..1f48a2b --- /dev/null +++ b/es/webhooks/retries.mdx @@ -0,0 +1,21 @@ +--- +title: Retries +description: Learn how Trophy handles webhook retries with configured endpoints. +subtitle: Learn how Trophy handles webhook retries with configured endpoints. +icon: rotate-ccw +--- + +Trophy will automatically retry any event that fails for one of the following reasons: + +- Timeout: If your webhook endpoint does not respond within 60 seconds. +- Network error: If your webhook endpoint is unreachable due to network issues. +- HTTP status code: If your webhook endpoint responds with a `non-2XX` HTTP status code. + + + Trophy will automatically retry events up to 3 times exponentially starting + with a 1 minute interval. + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/webhooks/security.mdx b/es/webhooks/security.mdx new file mode 100644 index 0000000..dab5c74 --- /dev/null +++ b/es/webhooks/security.mdx @@ -0,0 +1,67 @@ +--- +title: Security +description: Build secure webhook endpoints using webhook signature verification. +subtitle: Build secure webhook endpoints using webhook signature verification. +icon: shield-check +--- + +## Webhook Signatures + +To help you secure your webhook handlers so that they only respond to events sent from Trophy and not malicious attackers, Trophy includes a webhook signature with every event. + +This signature is sent in the `X-Trophy-Signature` header and is a `base64` encoded hash of the request payload, hashed using a secure webhook secret provided by Trophy. + +## Webhook Secrets + +Each webhook you set up in Trophy has a unique webhook secret which you can access from the [webhooks page](https://app.trophy.so/integration/webhooks) in the Trophy dashboard. + + + Make sure you store your webhook secret in a **secure environment variable** + and do not commit it to source control. + + +## Securing Webhook Handlers + +To validate that events your webhook handler receives do actually come from Trophy, you need to create your own hash using your secure [webhook secret](#webhook-secrets) and compare it to the [webhook signature](#webhook-signatures) in the `X-Trophy-Signature` header. + + + + + +Once you have your webhook secret, you're ready to start validating events. Here's an example in NodeJS: + +```js Validating Webhook Events + // Extract X-Trophy-Signature header from the request + const hmacHeader = request.headers.get("X-Trophy-Signature"); + + // Create a hash based on the parsed body + const hash = crypto + .createHmac("sha256", process.env.TROPHY_WEBHOOK_SECRET as string) + .update(await request.text()) + .digest("base64"); + + // Compare the created hash with the value of the X-Trophy-Signature header + if (hash === hmacHeader) { + console.log("Webhook is originating from Trophy"); + // Request validated, continue processing + } else { + console.log("Signature is invalid, rejected"); + // Request is not from Trophy, reject with 4XX status + } +``` + + + If your handler detects a request that did not originate from Trophy, it's + important to reject the request as early as possible with a `4XX` status code. + + +## Get Support + +Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/i18n.json b/i18n.json new file mode 100644 index 0000000..6b237f9 --- /dev/null +++ b/i18n.json @@ -0,0 +1,137 @@ +{ + "$schema": "https://lingo.dev/schema/i18n.json", + "version": "1.15", + "locale": { + "source": "en", + "targets": [ + "es" + ] + }, + "buckets": { + "mdx": { + "include": [ + "[locale]/account/billing.mdx", + "[locale]/account/branding.mdx", + "[locale]/account/members.mdx", + "[locale]/account/overview.mdx", + "[locale]/admin-api/endpoints/points/archive-a-boost.mdx", + "[locale]/admin-api/endpoints/points/archive-boosts-batch.mdx", + "[locale]/admin-api/endpoints/points/create-boosts.mdx", + "[locale]/admin-api/endpoints/streaks/grant-freezes.mdx", + "[locale]/admin-api/endpoints/streaks/restore-streaks.mdx", + "[locale]/admin-api/introduction.mdx", + "[locale]/api-reference/authentication.mdx", + "[locale]/api-reference/client-libraries.mdx", + "[locale]/api-reference/endpoints/achievements/all-achievements.mdx", + "[locale]/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx", + "[locale]/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx", + "[locale]/api-reference/endpoints/leaderboards/get-leaderboard.mdx", + "[locale]/api-reference/endpoints/metrics/send-a-metric-change-event.mdx", + "[locale]/api-reference/endpoints/points/get-points-boosts.mdx", + "[locale]/api-reference/endpoints/points/get-points-level-summary.mdx", + "[locale]/api-reference/endpoints/points/get-points-levels.mdx", + "[locale]/api-reference/endpoints/points/get-points-summary.mdx", + "[locale]/api-reference/endpoints/points/get-points.mdx", + "[locale]/api-reference/endpoints/streaks/get-streak-rankings.mdx", + "[locale]/api-reference/endpoints/streaks/get-streaks.mdx", + "[locale]/api-reference/endpoints/users/create-a-user.mdx", + "[locale]/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx", + "[locale]/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx", + "[locale]/api-reference/endpoints/users/get-a-single-user.mdx", + "[locale]/api-reference/endpoints/users/get-a-users-completed-achievements.mdx", + "[locale]/api-reference/endpoints/users/get-a-users-leaderboard.mdx", + "[locale]/api-reference/endpoints/users/get-a-users-points-boosts.mdx", + "[locale]/api-reference/endpoints/users/get-a-users-points-summary.mdx", + "[locale]/api-reference/endpoints/users/get-a-users-points.mdx", + "[locale]/api-reference/endpoints/users/get-a-users-streak.mdx", + "[locale]/api-reference/endpoints/users/get-a-users-wrapped.mdx", + "[locale]/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx", + "[locale]/api-reference/endpoints/users/get-user-preferences.mdx", + "[locale]/api-reference/endpoints/users/identify-a-user.mdx", + "[locale]/api-reference/endpoints/users/update-a-user.mdx", + "[locale]/api-reference/endpoints/users/update-user-preferences.mdx", + "[locale]/api-reference/idempotency.mdx", + "[locale]/api-reference/introduction.mdx", + "[locale]/api-reference/rate-limiting.mdx", + "[locale]/experimentation/engagement.mdx", + "[locale]/experimentation/overview.mdx", + "[locale]/experimentation/retention.mdx", + "[locale]/getting-started/introduction.mdx", + "[locale]/getting-started/quickstart.mdx", + "[locale]/guides/gamified-fitness-platform.mdx", + "[locale]/guides/gamified-study-platform.mdx", + "[locale]/guides/how-to-build-a-leaderboards-feature.mdx", + "[locale]/guides/how-to-build-a-streaks-feature.mdx", + "[locale]/guides/how-to-build-an-achievements-feature.mdx", + "[locale]/guides/how-to-build-an-energy-feature.mdx", + "[locale]/guides/how-to-build-an-xp-feature.mdx", + "[locale]/platform/achievements.mdx", + "[locale]/platform/emails.mdx", + "[locale]/platform/events.mdx", + "[locale]/platform/leaderboards.mdx", + "[locale]/platform/metrics.mdx", + "[locale]/platform/overview.mdx", + "[locale]/platform/points.mdx", + "[locale]/platform/push-notifications.mdx", + "[locale]/platform/streaks.mdx", + "[locale]/platform/users.mdx", + "[locale]/snippets/add-env-var-block.mdx", + "[locale]/snippets/all-achievements-request-block.mdx", + "[locale]/snippets/all-achievements-response-block.mdx", + "[locale]/snippets/control-flag-block.mdx", + "[locale]/snippets/event-attributes-request-block.mdx", + "[locale]/snippets/get-user-notification-preferences-block.mdx", + "[locale]/snippets/idempotent-event-tracking.mdx", + "[locale]/snippets/identify-user-request-block.mdx", + "[locale]/snippets/identify-user-with-device-tokens-request-block.mdx", + "[locale]/snippets/leaderboard-rankings-request-multiple-attributes.mdx", + "[locale]/snippets/leaderboard-rankings-request-single-attribute.mdx", + "[locale]/snippets/leaderboard-rankings-request.mdx", + "[locale]/snippets/leaderboard-rankings-response.mdx", + "[locale]/snippets/metric-change-event-points-level-excerpt-block.mdx", + "[locale]/snippets/metric-change-request-block.mdx", + "[locale]/snippets/metric-change-response-block.mdx", + "[locale]/snippets/metric-event-with-device-tokens-request-block.mdx", + "[locale]/snippets/points-histogram-summary-response-block.mdx", + "[locale]/snippets/points-level-summary-response-block.mdx", + "[locale]/snippets/points-levels-list-response-block.mdx", + "[locale]/snippets/points-system-response-block.mdx", + "[locale]/snippets/sdk-install-command.mdx", + "[locale]/snippets/snippet-intro.mdx", + "[locale]/snippets/streak-request-block.mdx", + "[locale]/snippets/streak-response-block.mdx", + "[locale]/snippets/update-user-notification-preferences-block.mdx", + "[locale]/snippets/user-achievements-request-block.mdx", + "[locale]/snippets/user-energy-response-block.mdx", + "[locale]/snippets/user-leaderboard-rankings-request.mdx", + "[locale]/snippets/user-leaderboard-rankings-response.mdx", + "[locale]/snippets/user-points-request-block.mdx", + "[locale]/snippets/user-points-response-block.mdx", + "[locale]/snippets/user-points-summary-request-block.mdx", + "[locale]/snippets/user-points-summary-response-block.mdx", + "[locale]/snippets/webhook-points-level-changed-payload-block.mdx", + "[locale]/webhooks/events/achievements/achievement-completed.mdx", + "[locale]/webhooks/events/leaderboards/leaderboard-changed.mdx", + "[locale]/webhooks/events/leaderboards/leaderboard-finished.mdx", + "[locale]/webhooks/events/leaderboards/leaderboard-rank-changed.mdx", + "[locale]/webhooks/events/leaderboards/leaderboard-started.mdx", + "[locale]/webhooks/events/points/points-boost-finished.mdx", + "[locale]/webhooks/events/points/points-boost-started.mdx", + "[locale]/webhooks/events/points/points-changed.mdx", + "[locale]/webhooks/events/points/points-level-changed.mdx", + "[locale]/webhooks/events/streaks/streak-extended.mdx", + "[locale]/webhooks/events/streaks/streak-freeze-consumed.mdx", + "[locale]/webhooks/events/streaks/streak-freeze-earned.mdx", + "[locale]/webhooks/events/streaks/streak-lost.mdx", + "[locale]/webhooks/events/streaks/streak-started.mdx", + "[locale]/webhooks/idempotency.mdx", + "[locale]/webhooks/introduction.mdx", + "[locale]/webhooks/observability.mdx", + "[locale]/webhooks/quickstart.mdx", + "[locale]/webhooks/retries.mdx", + "[locale]/webhooks/security.mdx" + ] + } + }, + "engineId": "${LINGO_ENGINE_ID}" +} diff --git a/lingo/brand-voice.md b/lingo/brand-voice.md new file mode 100644 index 0000000..f35de39 --- /dev/null +++ b/lingo/brand-voice.md @@ -0,0 +1,4 @@ +Write for a developer audience with a clear, concise, and practical tone. +Prioritize technical precision, active voice, and straightforward wording. +Match the source documentation's structure and intent, but avoid overly literal phrasing when it reduces clarity. +Keep terminology consistent across pages, avoid slang, and maintain a professional, helpful, and confident voice. diff --git a/lingo/glossary.csv b/lingo/glossary.csv new file mode 100644 index 0000000..85ec214 --- /dev/null +++ b/lingo/glossary.csv @@ -0,0 +1,16 @@ +source_locale,target_locale,source_text,target_text,type,hint +*,*,Trophy,Trophy,non_translatable,Brand name +*,*,Lingo.dev,Lingo.dev,non_translatable,Product name +en,*,API,API,non_translatable,Technical acronym +en,*,SDK,SDK,non_translatable,Technical acronym +en,*,CLI,CLI,non_translatable,Technical acronym +en,*,OAuth,OAuth,non_translatable,Protocol +en,*,JWT,JWT,non_translatable,Token format +en,*,JSON,JSON,non_translatable,Data format +en,*,MDX,MDX,non_translatable,File format +en,es,Leaderboard,Clasificación,custom_translation,Gamification feature noun +en,es,Leaderboards,Clasificaciones,custom_translation,Gamification feature noun plural +en,es,Streak,Racha,custom_translation,Gamification feature noun +en,es,Streaks,Rachas,custom_translation,Gamification feature noun plural +en,es,Achievement,Logro,custom_translation,Gamification feature noun +en,es,Achievements,Logros,custom_translation,Gamification feature noun plural diff --git a/lingo/instructions.global.txt b/lingo/instructions.global.txt new file mode 100644 index 0000000..04031ef --- /dev/null +++ b/lingo/instructions.global.txt @@ -0,0 +1,21 @@ +Instruction set for localization engine + +1) Preserve MDX and Markdown structure. +- Keep component tags, imports, links, frontmatter keys, and heading structure intact. + +2) Never translate code. +- Do not translate fenced code blocks, inline code, JSON keys, CLI flags, paths, endpoint paths, or query parameter names. + +3) Keep links and slugs stable. +- Do not alter URL targets or route slugs unless explicitly provided as localized paths. + +4) Localize user-facing prose. +- Translate titles, descriptions, paragraphs, callouts, captions, and alt text where present. + +5) Keep frontmatter valid. +- Preserve YAML syntax and key set. +- Translate frontmatter values for title/description/keywords where present. + +6) Preserve product vocabulary. +- Apply glossary entries first. +- Keep non-translatable technical and brand terms exactly as listed. diff --git a/package.json b/package.json index 3fb4f30..c07ce12 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,11 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "dev": "mint dev" + "dev": "mint dev", + "translate:generate": "node --env-file=.env scripts/generate-translations.mjs --target es", + "translate:docs-json": "node --env-file=.env scripts/translate-docs-json.mjs --target es", + "translate:verify": "npx lingo.dev@latest run --frozen", + "translate:validate": "node --env-file=.env scripts/validate-translations.mjs" }, "repository": { "type": "git", diff --git a/scripts/generate-translations.mjs b/scripts/generate-translations.mjs new file mode 100644 index 0000000..f08f399 --- /dev/null +++ b/scripts/generate-translations.mjs @@ -0,0 +1,85 @@ +#!/usr/bin/env node + +import fs from "node:fs"; +import { execSync } from "node:child_process"; + +const args = process.argv.slice(2); + +function getArg(name) { + const idx = args.indexOf(name); + if (idx === -1) return null; + return args[idx + 1] ?? null; +} + +const targetArg = getArg("--target"); +const pathFilter = getArg("--paths"); +const filters = pathFilter + ? pathFilter + .split(",") + .map((s) => s.trim()) + .filter(Boolean) + : []; + +const configPath = "i18n.json"; +const backupPath = "i18n.json.bak"; + +if (!fs.existsSync(configPath)) { + console.error("Missing i18n.json"); + process.exit(1); +} + +const originalRaw = fs.readFileSync(configPath, "utf8"); +const cfg = JSON.parse(originalRaw); + +if (!cfg.locale || !cfg.locale.source || !cfg.buckets) { + console.error("Invalid i18n.json: locale.source and buckets are required."); + process.exit(1); +} +const configuredTargets = Array.isArray(cfg.locale.targets) ? [...cfg.locale.targets] : []; + +const engineIdFromEnv = process.env.LINGO_ENGINE_ID; +if (engineIdFromEnv) { + cfg.engineId = engineIdFromEnv; +} +if (!cfg.engineId || cfg.engineId === "${LINGO_ENGINE_ID}") { + console.error("Missing LINGO_ENGINE_ID. Set it in your environment before running."); + process.exit(1); +} + +if (filters.length > 0 && cfg.buckets.mdx?.include) { + cfg.buckets.mdx.include = cfg.buckets.mdx.include.filter((pattern) => + filters.some((f) => pattern.includes(f)) + ); + if (cfg.buckets.mdx.include.length === 0) { + console.error("No files matched --paths filters."); + process.exit(1); + } +} + +fs.writeFileSync(backupPath, originalRaw); +const targets = targetArg + ? targetArg + .split(",") + .map((s) => s.trim()) + .filter(Boolean) + : configuredTargets; + +if (targets.length === 0) { + console.error("No target locales available. Set locale.targets in i18n.json or pass --target."); + process.exit(1); +} + +try { + for (const target of targets) { + cfg.locale.targets = [target]; + fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2) + "\n"); + execSync(`npx lingo.dev@latest run --target-locale ${target}`, { stdio: "inherit" }); + execSync(`node scripts/translate-docs-json.mjs --target ${target}`, { stdio: "inherit" }); + console.log(`Translation generation completed for target locale: ${target}`); + } +} finally { + if (fs.existsSync(backupPath)) { + fs.copyFileSync(backupPath, configPath); + fs.unlinkSync(backupPath); + } +} diff --git a/scripts/translate-docs-json.mjs b/scripts/translate-docs-json.mjs new file mode 100644 index 0000000..db09882 --- /dev/null +++ b/scripts/translate-docs-json.mjs @@ -0,0 +1,103 @@ +#!/usr/bin/env node + +import fs from "node:fs"; +import path from "node:path"; +import { execSync } from "node:child_process"; + +const args = process.argv.slice(2); +const targetIdx = args.indexOf("--target"); +const target = targetIdx >= 0 ? args[targetIdx + 1] : null; +if (!target) { + console.error("Missing --target for docs.json translation."); + process.exit(1); +} + +const engineId = process.env.LINGO_ENGINE_ID; +if (!engineId) { + console.error("Missing LINGO_ENGINE_ID. Set it before running."); + process.exit(1); +} + +const docsPath = "docs.json"; +const i18nPath = "i18n.json"; +const i18nBackup = "i18n.json.docsjson.bak"; +const tmpDir = path.join(".tmp", "docs-json-i18n"); + +const docs = JSON.parse(fs.readFileSync(docsPath, "utf8")); +const enLang = docs.navigation?.languages?.find((l) => l.language === "en"); +const targetLang = docs.navigation?.languages?.find((l) => l.language === target); + +if (!enLang || !targetLang) { + console.error(`Could not find both en and ${target} language entries in docs.json`); + process.exit(1); +} + +const enPayload = { + anchors: (enLang.anchors || []).map((a) => a.anchor), + navbar: { + dashboard: enLang.navbar?.links?.[0]?.label || "Dashboard", + cta: enLang.navbar?.primary?.label || "Start for free" + } +}; + +const targetPayload = { + anchors: (targetLang.anchors || []).map((a) => a.anchor), + navbar: { + dashboard: targetLang.navbar?.links?.[0]?.label || enPayload.navbar.dashboard, + cta: targetLang.navbar?.primary?.label || enPayload.navbar.cta + } +}; + +fs.mkdirSync(tmpDir, { recursive: true }); +const enPayloadPath = path.join(tmpDir, "en.json"); +const targetPayloadPath = path.join(tmpDir, `${target}.json`); +fs.writeFileSync(enPayloadPath, JSON.stringify(enPayload, null, 2) + "\n"); +fs.writeFileSync(targetPayloadPath, JSON.stringify(targetPayload, null, 2) + "\n"); + +const originalI18n = fs.readFileSync(i18nPath, "utf8"); +const cfg = JSON.parse(originalI18n); +cfg.locale.targets = [target]; +cfg.engineId = engineId; +cfg.buckets = { + json: { + include: [path.join(tmpDir, "[locale].json").replace(/\\/g, "/")] + } +}; + +fs.writeFileSync(i18nBackup, originalI18n); +fs.writeFileSync(i18nPath, JSON.stringify(cfg, null, 2) + "\n"); + +try { + execSync(`npx lingo.dev@latest run --target-locale ${target}`, { stdio: "inherit" }); + + const translated = JSON.parse(fs.readFileSync(targetPayloadPath, "utf8")); + const fallbackAnchors = targetLang.anchors || enLang.anchors || []; + targetLang.anchors = fallbackAnchors.map((a, idx) => ({ + anchor: translated.anchors?.[idx] || a.anchor, + href: a.href, + icon: a.icon + })); + + targetLang.navbar = { + links: [ + { + label: translated.navbar?.dashboard || targetLang.navbar?.links?.[0]?.label || enPayload.navbar.dashboard, + href: "https://app.trophy.so" + } + ], + primary: { + type: "button", + label: translated.navbar?.cta || targetLang.navbar?.primary?.label || enPayload.navbar.cta, + href: "https://app.trophy.so/sign-up?utm_source=docs&utm_medium=navbar-links" + } + }; + + fs.writeFileSync(docsPath, JSON.stringify(docs, null, 2) + "\n"); + console.log(`Updated docs.json localized navigation for ${target}.`); +} finally { + if (fs.existsSync(i18nBackup)) { + fs.copyFileSync(i18nBackup, i18nPath); + fs.unlinkSync(i18nBackup); + } + fs.rmSync(path.join(".tmp"), { recursive: true, force: true }); +} diff --git a/scripts/validate-translations.mjs b/scripts/validate-translations.mjs new file mode 100644 index 0000000..35d8230 --- /dev/null +++ b/scripts/validate-translations.mjs @@ -0,0 +1,96 @@ +#!/usr/bin/env node + +import fs from "node:fs"; +import path from "node:path"; + +const ROOT = process.cwd(); +const SOURCE_LOCALE = "en"; +const TARGET_LOCALE = "es"; + +function walkMdx(dir) { + if (!fs.existsSync(dir)) return []; + const entries = fs.readdirSync(dir, { withFileTypes: true }); + let files = []; + for (const entry of entries) { + const abs = path.join(dir, entry.name); + if (entry.isDirectory()) files = files.concat(walkMdx(abs)); + if (entry.isFile() && abs.endsWith(".mdx")) files.push(abs); + } + return files; +} + +function relNoLocale(file, locale) { + return path.relative(path.join(ROOT, locale), file).replace(/\\/g, "/"); +} + +function parseFrontmatter(content) { + if (!content.startsWith("---\n")) return null; + const endIdx = content.indexOf("\n---\n", 4); + if (endIdx === -1) return null; + const raw = content.slice(4, endIdx).split("\n"); + const out = {}; + for (const line of raw) { + const idx = line.indexOf(":"); + if (idx === -1) continue; + const key = line.slice(0, idx).trim(); + const val = line.slice(idx + 1).trim().replace(/^["']|["']$/g, ""); + out[key] = val; + } + return out; +} + +function fail(msg) { + console.error(`ERROR: ${msg}`); + process.exitCode = 1; +} + +const srcDir = path.join(ROOT, SOURCE_LOCALE); +const dstDir = path.join(ROOT, TARGET_LOCALE); +const srcFiles = walkMdx(srcDir); +const dstFiles = walkMdx(dstDir); + +const srcSet = new Set(srcFiles.map((f) => relNoLocale(f, SOURCE_LOCALE))); +const dstSet = new Set(dstFiles.map((f) => relNoLocale(f, TARGET_LOCALE))); + +for (const rel of srcSet) { + if (!dstSet.has(rel)) fail(`Missing translated file: ${TARGET_LOCALE}/${rel}`); +} +for (const rel of dstSet) { + if (!srcSet.has(rel)) fail(`Missing source file for translation: ${SOURCE_LOCALE}/${rel}`); +} + +for (const rel of srcSet) { + const srcPath = path.join(srcDir, rel); + const dstPath = path.join(dstDir, rel); + if (!fs.existsSync(dstPath)) continue; + + const srcContent = fs.readFileSync(srcPath, "utf8"); + const dstContent = fs.readFileSync(dstPath, "utf8"); + + const srcFm = parseFrontmatter(srcContent); + const dstFm = parseFrontmatter(dstContent); + + // Snippet-style MDX files may intentionally omit frontmatter. + if (!srcFm && !dstFm) continue; + if (!srcFm) fail(`Invalid source frontmatter: ${SOURCE_LOCALE}/${rel}`); + if (!dstFm) fail(`Invalid translated frontmatter: ${TARGET_LOCALE}/${rel}`); + if (!srcFm || !dstFm) continue; + + for (const key of Object.keys(srcFm)) { + if (!(key in dstFm)) fail(`Missing frontmatter key '${key}' in ${TARGET_LOCALE}/${rel}`); + } + + if ("title" in srcFm && (!dstFm.title || !dstFm.title.trim())) { + fail(`Missing translated title in ${TARGET_LOCALE}/${rel}`); + } + if ("description" in srcFm && (!dstFm.description || !dstFm.description.trim())) { + fail(`Missing translated description in ${TARGET_LOCALE}/${rel}`); + } +} + +if (process.exitCode) { + console.error("Translation validation failed."); + process.exit(process.exitCode); +} + +console.log("Translation parity/frontmatter checks passed."); From c86c1e2e221575979ad6a05d718d8184ff2fd85c Mon Sep 17 00:00:00 2001 From: Charlie Hopkins-Brinicombe Date: Mon, 30 Mar 2026 15:03:09 +0100 Subject: [PATCH 02/16] Add glossary tracking --- .cursor/rules/localization-workflow.mdc | 2 + README.md | 16 ++++-- lingo/glossary.csv | 28 ++++++++++ lingo/instructions.global.txt | 21 -------- package.json | 1 + scripts/validate-glossary-csv.mjs | 70 +++++++++++++++++++++++++ 6 files changed, 114 insertions(+), 24 deletions(-) delete mode 100644 lingo/instructions.global.txt create mode 100644 scripts/validate-glossary-csv.mjs diff --git a/.cursor/rules/localization-workflow.mdc b/.cursor/rules/localization-workflow.mdc index 7fc6443..7de7e9c 100644 --- a/.cursor/rules/localization-workflow.mdc +++ b/.cursor/rules/localization-workflow.mdc @@ -11,4 +11,6 @@ alwaysApply: true - Do not translate code snippets, inline code, URLs, route slugs, or API identifiers. - Treat `lingo/brand-voice.md` as the source of truth for brand voice text. - 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/README.md b/README.md index 35fd08c..2689377 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,8 @@ Notes: - `lingo/glossary.csv`: Terms that must stay fixed or use specific translations. - `lingo/brand-voice.md`: Single brand voice used for all locales. -- `lingo/instructions.global.txt`: Global structural rules (preserve MDX, never translate code, keep links stable). - `scripts/translate-docs-json.mjs`: Translates language-specific `docs.json` navigation labels directly in the source-of-truth `docs.json`. +- `scripts/validate-glossary-csv.mjs`: Validates glossary schema and duplicate canonical keys. #### Apply in Lingo.dev engine @@ -70,10 +70,10 @@ Notes: 2. In the Lingo.dev engine: - import/create glossary entries from `lingo/glossary.csv` - sync wildcard brand voice from `lingo/brand-voice.md` via Cursor chat + Lingo MCP (on demand) - - add instructions from `lingo/instructions.global.txt` (target locale `*` unless locale-specific) 3. Run: - `npx lingo.dev@latest run` to generate/update translations - `node scripts/translate-docs-json.mjs --target es` to translate language-specific labels in `docs.json` + - `npm run lingo:validate-glossary` to validate glossary rows before MCP sync - `npx lingo.dev@latest run --frozen` to enforce no pending translation deltas Brand voice sync workflow (manual via Cursor + MCP): @@ -81,6 +81,16 @@ Brand voice sync workflow (manual via Cursor + MCP): - In Cursor chat ask: "Sync `lingo/brand-voice.md` to Lingo brand voice for engine ``." - The agent will use MCP tools to find the engine brand voice id and update it. +Glossary sync workflow (manual via Cursor + MCP): +- Validate first: `npm run lingo:validate-glossary` +- Always run dry-run first, then apply +- Canonical key used for reconciliation: `sourceLocale|targetLocale|type|sourceText` +- Dry run prompt: + - `Dry-run glossary sync for engine using lingo/glossary.csv via Lingo MCP. Use canonical key sourceLocale|targetLocale|type|sourceText. Show counts and exact create/update/delete operations. Do not apply changes yet.` +- Apply prompt: + - `Apply glossary sync for engine using lingo/glossary.csv via Lingo MCP. Use canonical key sourceLocale|targetLocale|type|sourceText. Perform create/update and prune missing entries (delete remote items not in CSV).` +- Use prune only when you want remote glossary to match CSV exactly. + #### Add a new language 1. Add the locale to `docs.json`: @@ -94,7 +104,7 @@ Brand voice sync workflow (manual via Cursor + MCP): - Example: `en/platform/overview.mdx` -> `fr/platform/overview.mdx`. 4. Configure Lingo.dev engine controls for the new locale: - Add/update brand voice for that locale. - - Add locale-specific glossary entries or instructions if needed. + - Add locale-specific glossary entries if needed. 5. Generate translations: - One locale: `node scripts/generate-translations.mjs --target ` - Multiple locales: `node scripts/generate-translations.mjs --target es,fr,de` diff --git a/lingo/glossary.csv b/lingo/glossary.csv index 85ec214..52507ac 100644 --- a/lingo/glossary.csv +++ b/lingo/glossary.csv @@ -8,9 +8,37 @@ en,*,OAuth,OAuth,non_translatable,Protocol en,*,JWT,JWT,non_translatable,Token format en,*,JSON,JSON,non_translatable,Data format en,*,MDX,MDX,non_translatable,File format +en,*,HTTP,HTTP,non_translatable,Protocol acronym +en,*,URL,URL,non_translatable,Web address acronym +en,*,UTC,UTC,non_translatable,Timezone standard +en,*,DNS,DNS,non_translatable,Domain naming system acronym +en,*,FCM,FCM,non_translatable,Firebase Cloud Messaging acronym +en,*,DKIM,DKIM,non_translatable,Email authentication acronym +en,*,CNAME,CNAME,non_translatable,DNS record type acronym +en,*,MAU,MAU,non_translatable,Metric acronym +en,*,Python,Python,non_translatable,Programming language name +en,*,Ruby,Ruby,non_translatable,Programming language name +en,*,Go,Go,non_translatable,Programming language name +en,*,React,React,non_translatable,Framework/library name +en,*,Node.js,Node.js,non_translatable,Runtime name +en,*,TypeScript,TypeScript,non_translatable,Programming language name +en,*,JavaScript,JavaScript,non_translatable,Programming language name +en,*,Swift,Swift,non_translatable,Programming language name +en,*,Duolingo,Duolingo,non_translatable,Brand reference +en,*,Expo Push Service,Expo Push Service,non_translatable,Brand/service reference en,es,Leaderboard,Clasificación,custom_translation,Gamification feature noun en,es,Leaderboards,Clasificaciones,custom_translation,Gamification feature noun plural en,es,Streak,Racha,custom_translation,Gamification feature noun en,es,Streaks,Rachas,custom_translation,Gamification feature noun plural en,es,Achievement,Logro,custom_translation,Gamification feature noun en,es,Achievements,Logros,custom_translation,Gamification feature noun plural +en,es,Points,Puntos,custom_translation,Gamification feature noun plural +en,es,Metrics,Métricas,custom_translation,Product domain term plural +en,es,Push Notifications,Notificaciones push,custom_translation,Product domain term +en,es,Idempotency,Idempotencia,custom_translation,API behavior concept +en,es,Rate Limiting,Limitación de tasa,custom_translation,API behavior concept +en,es,Quickstart,Inicio rápido,custom_translation,Docs navigation term +en,es,Reactivation,Reactivación,custom_translation,Lifecycle/product term +en,es,Sender Verification,Verificación del remitente,custom_translation,Email product domain term +en,es,Wrapped,Resumen anual,custom_translation,Feature naming term +en,*,XP,XP,non_translatable,Gamification abbreviation diff --git a/lingo/instructions.global.txt b/lingo/instructions.global.txt deleted file mode 100644 index 04031ef..0000000 --- a/lingo/instructions.global.txt +++ /dev/null @@ -1,21 +0,0 @@ -Instruction set for localization engine - -1) Preserve MDX and Markdown structure. -- Keep component tags, imports, links, frontmatter keys, and heading structure intact. - -2) Never translate code. -- Do not translate fenced code blocks, inline code, JSON keys, CLI flags, paths, endpoint paths, or query parameter names. - -3) Keep links and slugs stable. -- Do not alter URL targets or route slugs unless explicitly provided as localized paths. - -4) Localize user-facing prose. -- Translate titles, descriptions, paragraphs, callouts, captions, and alt text where present. - -5) Keep frontmatter valid. -- Preserve YAML syntax and key set. -- Translate frontmatter values for title/description/keywords where present. - -6) Preserve product vocabulary. -- Apply glossary entries first. -- Keep non-translatable technical and brand terms exactly as listed. diff --git a/package.json b/package.json index c07ce12..f4e186d 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "dev": "mint dev", "translate:generate": "node --env-file=.env scripts/generate-translations.mjs --target es", "translate:docs-json": "node --env-file=.env scripts/translate-docs-json.mjs --target es", + "lingo:validate-glossary": "node scripts/validate-glossary-csv.mjs", "translate:verify": "npx lingo.dev@latest run --frozen", "translate:validate": "node --env-file=.env scripts/validate-translations.mjs" }, diff --git a/scripts/validate-glossary-csv.mjs b/scripts/validate-glossary-csv.mjs new file mode 100644 index 0000000..905d86d --- /dev/null +++ b/scripts/validate-glossary-csv.mjs @@ -0,0 +1,70 @@ +#!/usr/bin/env node + +import fs from "node:fs"; + +const path = "lingo/glossary.csv"; +const REQUIRED_HEADERS = [ + "source_locale", + "target_locale", + "source_text", + "target_text", + "type", + "hint" +]; +const ALLOWED_TYPES = new Set(["custom_translation", "non_translatable"]); + +if (!fs.existsSync(path)) { + console.error(`Missing ${path}`); + process.exit(1); +} + +const raw = fs.readFileSync(path, "utf8").trim(); +if (!raw) { + console.error("Glossary CSV is empty."); + process.exit(1); +} + +const lines = raw.split(/\r?\n/); +const headers = lines[0].split(",").map((v) => v.trim()); + +for (const [idx, header] of REQUIRED_HEADERS.entries()) { + if (headers[idx] !== header) { + console.error(`Invalid header at column ${idx + 1}: expected '${header}', got '${headers[idx] || ""}'`); + process.exit(1); + } +} + +const keySet = new Set(); +let rowCount = 0; + +for (let i = 1; i < lines.length; i++) { + const line = lines[i].trim(); + if (!line) continue; + rowCount += 1; + + const parts = line.split(",").map((v) => v.trim()); + if (parts.length < REQUIRED_HEADERS.length) { + console.error(`Row ${i + 1}: expected ${REQUIRED_HEADERS.length} columns, got ${parts.length}`); + process.exit(1); + } + + const [sourceLocale, targetLocale, sourceText, targetText, type] = parts; + if (!sourceLocale || !targetLocale || !sourceText || !targetText || !type) { + console.error(`Row ${i + 1}: source_locale,target_locale,source_text,target_text,type are required`); + process.exit(1); + } + + if (!ALLOWED_TYPES.has(type)) { + console.error(`Row ${i + 1}: invalid type '${type}'`); + process.exit(1); + } + + const key = `${sourceLocale}|${targetLocale}|${type}|${sourceText}`; + if (keySet.has(key)) { + console.error(`Row ${i + 1}: duplicate canonical key '${key}'`); + process.exit(1); + } + keySet.add(key); +} + +console.log(`Glossary CSV valid (${rowCount} entries, ${keySet.size} unique canonical keys).`); From 0156e26060c26b957ca611aa66950e1ec7631c08 Mon Sep 17 00:00:00 2001 From: Charlie Hopkins-Brinicombe Date: Mon, 30 Mar 2026 15:22:03 +0100 Subject: [PATCH 03/16] Generate initial translations --- .env.example | 2 +- .github/workflows/translate-on-main.yml | 4 +- .github/workflows/validate-translations.yml | 4 +- README.md | 4 +- es/account/billing.mdx | 120 +- es/account/branding.mdx | 26 +- es/account/members.mdx | 38 +- es/account/overview.mdx | 23 +- .../endpoints/points/archive-a-boost.mdx | 2 +- .../endpoints/points/archive-boosts-batch.mdx | 2 +- .../endpoints/points/create-boosts.mdx | 2 +- .../endpoints/streaks/grant-freezes.mdx | 2 +- .../endpoints/streaks/restore-streaks.mdx | 2 +- es/admin-api/introduction.mdx | 22 +- es/api-reference/authentication.mdx | 61 +- es/api-reference/client-libraries.mdx | 13 +- .../achievements/all-achievements.mdx | 2 +- .../mark-an-achievement-as-completed.mdx | 2 +- .../get-all-active-leaderboards.mdx | 2 +- .../leaderboards/get-leaderboard.mdx | 2 +- .../metrics/send-a-metric-change-event.mdx | 2 +- .../endpoints/points/get-points-boosts.mdx | 2 +- .../points/get-points-level-summary.mdx | 2 +- .../endpoints/points/get-points-levels.mdx | 2 +- .../endpoints/points/get-points-summary.mdx | 2 +- .../endpoints/points/get-points.mdx | 2 +- .../endpoints/streaks/get-streak-rankings.mdx | 2 +- .../endpoints/streaks/get-streaks.mdx | 2 +- .../endpoints/users/create-a-user.mdx | 2 +- ...single-metric-event-summary-for-a-user.mdx | 2 +- .../users/get-a-single-metric-for-a-user.mdx | 2 +- .../endpoints/users/get-a-single-user.mdx | 2 +- .../get-a-users-completed-achievements.mdx | 2 +- .../users/get-a-users-leaderboard.mdx | 2 +- .../users/get-a-users-points-boosts.mdx | 2 +- .../users/get-a-users-points-summary.mdx | 2 +- .../endpoints/users/get-a-users-points.mdx | 2 +- .../endpoints/users/get-a-users-streak.mdx | 2 +- .../endpoints/users/get-a-users-wrapped.mdx | 2 +- .../users/get-all-metrics-for-a-user.mdx | 2 +- .../endpoints/users/get-user-preferences.mdx | 2 +- .../endpoints/users/identify-a-user.mdx | 2 +- .../endpoints/users/update-a-user.mdx | 2 +- .../users/update-user-preferences.mdx | 2 +- es/api-reference/idempotency.mdx | 57 +- es/api-reference/introduction.mdx | 26 +- es/api-reference/rate-limiting.mdx | 23 +- es/experimentation/engagement.mdx | 26 +- es/experimentation/overview.mdx | 51 +- es/experimentation/retention.mdx | 22 +- es/getting-started/introduction.mdx | 39 +- es/getting-started/quickstart.mdx | 79 +- es/guides/gamified-fitness-platform.mdx | 261 +- es/guides/gamified-study-platform.mdx | 423 ++- .../how-to-build-a-leaderboards-feature.mdx | 110 +- es/guides/how-to-build-a-streaks-feature.mdx | 110 +- .../how-to-build-an-achievements-feature.mdx | 110 +- es/guides/how-to-build-an-energy-feature.mdx | 90 +- es/guides/how-to-build-an-xp-feature.mdx | 108 +- es/platform/achievements.mdx | 264 +- es/platform/emails.mdx | 473 ++-- es/platform/events.mdx | 132 +- es/platform/leaderboards.mdx | 227 +- es/platform/metrics.mdx | 90 +- es/platform/overview.mdx | 57 +- es/platform/points.mdx | 361 +-- es/platform/push-notifications.mdx | 157 +- es/platform/streaks.mdx | 107 +- es/platform/users.mdx | 286 +- .../all-achievements-request-block.mdx | 1 + .../event-attributes-request-block.mdx | 1 + ...et-user-notification-preferences-block.mdx | 1 + es/snippets/idempotent-event-tracking.mdx | 1 + es/snippets/identify-user-request-block.mdx | 2 + ...-user-with-device-tokens-request-block.mdx | 2 +- ...d-rankings-request-multiple-attributes.mdx | 1 + ...oard-rankings-request-single-attribute.mdx | 1 + es/snippets/leaderboard-rankings-request.mdx | 1 + es/snippets/metric-change-request-block.mdx | 1 + ...event-with-device-tokens-request-block.mdx | 2 +- es/snippets/sdk-install-command.mdx | 1 + es/snippets/snippet-intro.mdx | 5 +- es/snippets/streak-request-block.mdx | 1 + ...te-user-notification-preferences-block.mdx | 1 + .../user-achievements-request-block.mdx | 1 + .../user-leaderboard-rankings-request.mdx | 1 + es/snippets/user-points-request-block.mdx | 1 + .../user-points-summary-request-block.mdx | 1 + .../achievements/achievement-completed.mdx | 4 +- .../leaderboards/leaderboard-finished.mdx | 8 +- .../leaderboards/leaderboard-started.mdx | 10 +- .../events/points/points-boost-finished.mdx | 4 +- .../events/points/points-boost-started.mdx | 8 +- es/webhooks/events/points/points-changed.mdx | 4 +- .../events/points/points-level-changed.mdx | 2 +- .../events/streaks/streak-extended.mdx | 5 +- .../events/streaks/streak-freeze-consumed.mdx | 2 +- .../events/streaks/streak-freeze-earned.mdx | 2 +- es/webhooks/events/streaks/streak-lost.mdx | 2 +- es/webhooks/idempotency.mdx | 30 +- es/webhooks/introduction.mdx | 58 +- es/webhooks/observability.mdx | 18 +- es/webhooks/quickstart.mdx | 94 +- es/webhooks/retries.mdx | 24 +- es/webhooks/security.mdx | 36 +- i18n.lock | 2504 +++++++++++++++++ package.json | 4 +- scripts/generate-translations.mjs | 7 +- 108 files changed, 4731 insertions(+), 2193 deletions(-) create mode 100644 i18n.lock diff --git a/.env.example b/.env.example index a38fe1a..9a8a86f 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,7 @@ # Copy this file to .env and replace placeholders with real values. # Lingo.dev API key (used by CLI and CI workflows) -LINGODOTDEV_API_KEY=ldv_your_api_key_here +LINGO_API_KEY=lingo_sk_your_api_key_here # Lingo.dev localization engine id LINGO_ENGINE_ID=eng_your_engine_id_here diff --git a/.github/workflows/translate-on-main.yml b/.github/workflows/translate-on-main.yml index 34b424f..581a67c 100644 --- a/.github/workflows/translate-on-main.yml +++ b/.github/workflows/translate-on-main.yml @@ -37,7 +37,7 @@ jobs: - name: Run Lingo translation env: - LINGO_API_KEY: ${{ secrets.LINGODOTDEV_API_KEY }} + LINGO_API_KEY: ${{ secrets.LINGO_API_KEY }} run: | locales=$(node -e 'const fs=require("fs");const j=JSON.parse(fs.readFileSync("i18n.json","utf8"));console.log((j.locale?.targets||[]).join(" "));') if [ -z "$locales" ]; then @@ -52,7 +52,7 @@ jobs: - name: Translate docs.json language-specific navigation env: LINGO_ENGINE_ID: ${{ secrets.LINGO_ENGINE_ID }} - LINGO_API_KEY: ${{ secrets.LINGODOTDEV_API_KEY }} + LINGO_API_KEY: ${{ secrets.LINGO_API_KEY }} run: | locales=$(node -e 'const fs=require("fs");const j=JSON.parse(fs.readFileSync("i18n.json","utf8"));console.log((j.locale?.targets||[]).join(" "));') for locale in $locales; do diff --git a/.github/workflows/validate-translations.yml b/.github/workflows/validate-translations.yml index 7b7f1fb..befbef5 100644 --- a/.github/workflows/validate-translations.yml +++ b/.github/workflows/validate-translations.yml @@ -48,11 +48,11 @@ jobs: - name: Translation freshness gate env: - LINGO_API_KEY: ${{ secrets.LINGODOTDEV_API_KEY }} + LINGO_API_KEY: ${{ secrets.LINGO_API_KEY }} LINGO_ENGINE_ID: ${{ secrets.LINGO_ENGINE_ID }} run: | if [ -z "${LINGO_API_KEY}" ] || [ -z "${LINGO_ENGINE_ID}" ]; then - echo "LINGODOTDEV_API_KEY or LINGO_ENGINE_ID is not set, skipping frozen translation gate." + echo "LINGO_API_KEY or LINGO_ENGINE_ID is not set, skipping frozen translation gate." else npx lingo.dev@latest run --frozen fi diff --git a/README.md b/README.md index 2689377..e45400a 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ This repository uses Lingo.dev for static-content localization of docs pages and "lingo": { "url": "https://mcp.lingo.dev/account", "headers": { - "x-api-key": "${env:LINGODOTDEV_API_KEY}" + "x-api-key": "${env:LINGO_API_KEY}" } } } @@ -46,7 +46,7 @@ This repository uses Lingo.dev for static-content localization of docs pages and ``` 3. Ensure the API key is available to the Cursor app process as an environment variable: - - `LINGODOTDEV_API_KEY` + - `LINGO_API_KEY` 4. Restart Cursor fully after updating `mcp.json` or environment variables. 5. Verify by asking Cursor chat to list engines from the Lingo MCP server. diff --git a/es/account/billing.mdx b/es/account/billing.mdx index 1e9e263..a0b2f49 100644 --- a/es/account/billing.mdx +++ b/es/account/billing.mdx @@ -1,111 +1,113 @@ --- -title: Billing -description: Learn how Trophy controls costs by charging based on monthly active users. -"og:description": Learn how Trophy controls costs by charging based on monthly active users. +title: Facturación +description: Aprende cómo Trophy controla los costos cobrando según los usuarios + activos mensuales. +og:description: Aprende cómo Trophy controla los costos cobrando según los + usuarios activos mensuales. icon: gauge --- import { PlanBadge } from "/snippets/plan-badge.jsx"; -## Usage-based Billing +## Facturación basada en uso -Trophy follows a usage-based pricing model where customers only pay for the units of usage they consume. For Trophy, a unit of usage corresponds to a single [Monthly Active User](##monthly-active-users-maus) (MAU). +Trophy sigue un modelo de precios basado en uso donde los clientes solo pagan por las unidades de uso que consumen. Para Trophy, una unidad de uso corresponde a un único [Usuario Activo Mensual](##monthly-active-users-maus) (MAU). - See our [pricing page](https://trophy.so/pricing) to get an estimate of your - costs based on your expected usage. + Consulta nuestra [página de precios](https://trophy.so/pricing) para obtener una estimación de tus + costos según tu uso esperado. -## Monthly Active Users (MAUs) +## Usuarios Activos Mensuales (MAU) -Trophy defines an MAU as a single user that sends at least one [metric event](/platform/events) to Trophy in a given month. +Trophy define un MAU como un único usuario que envía al menos un [evento métrico](/platform/events) a Trophy en un mes determinado. - Bear in mind **you never pay for churned users**. If a user signs up for your - product in a given month but doesn't return, you only pay for that user once - and never again. + Ten en cuenta que **nunca pagas por usuarios que abandonan**. Si un usuario se registra en tu + producto en un mes determinado pero no regresa, solo pagas por ese usuario una vez + y nunca más. -## Free Tier +## Nivel gratuito -The free tier allows teams to test and evaluate Trophy up to **100 MAUs** without accruing usage charges. +El nivel gratuito permite a los equipos probar y evaluar Trophy hasta **100 MAU** sin generar cargos por uso. - Once you exceed the free plan, your account will continue to function and - we'll reach out to you directly with a friendly reminder to upgrade. + Una vez que superes el plan gratuito, tu cuenta seguirá funcionando y + nos pondremos en contacto contigo directamente con un recordatorio amigable para actualizar. -## Paid Plans +## Planes de pago -Trophy has two paid plans, [Starter](#starter-plan) and [Pro](#pro-plan). +Trophy tiene dos planes de pago, [Starter](#starter-plan) y [Pro](#pro-plan). -### Starter Plan +### Plan Starter -The starter plan is for customers who have graduated from the testing and evaluation phase and are using Trophy in small-scale production deployments. +El plan starter es para clientes que han superado la fase de prueba y evaluación y están usando Trophy en implementaciones de producción a pequeña escala. -Unlike the [Free Tier](#free-tier), the starter plan has no limits on MAUs. +A diferencia del [Nivel gratuito](#free-tier), el plan starter no tiene límites en MAU. - Beyond ~`16,000` MAUs it's usually more cost-efficient to upgrade to the [Pro - Plan](#pro-plan). + Más allá de ~`16,000` MAU, generalmente es más rentable actualizar al [Plan + Pro](#pro-plan). -### Pro Plan +### Plan Pro -The pro plan is for customers who are using Trophy in larger scale deployments or who require more advanced [features](#features). +El plan pro está diseñado para clientes que utilizan Trophy en implementaciones de mayor escala o que requieren [funciones](#features) más avanzadas. -### Plan Allowances +### Límites del Plan -Here's a comparison of the allowances of each paid plan: +Aquí hay una comparación de los límites de cada plan de pago: -| Item | Starter | Pro | -| --------------------------- | ------- | -------- | -| Base price | `$99` | `$299` | -| Included MAUs | `1,000` | `10,000` | -| Included Emails | `5,000` | `50,000` | -| Included Push Notifications | `5,000` | `50,000` | +| Elemento | Starter | Pro | +| ------------------------------- | ------- | -------- | +| Precio base | `$99` | `$299` | +| MAU incluidos | `1,000` | `10,000` | +| Emails incluidos | `5,000` | `50,000` | +| Notificaciones push incluidas | `5,000` | `50,000` | -### Overages +### Excedentes -Overages are charged on paid plans above the included [allowances](#plan-allowances) at the following rates: +Los excedentes se cobran en los planes de pago por encima de los [límites](#plan-allowances) incluidos a las siguientes tarifas: -| Item | Starter | Pro | -| ------------------------ | -------- | -------- | -| 1 MAU | `$0.015` | `$0.015` | -| 1,000 Emails | `$2.50` | `$2.00` | -| 1,000 Push Notifications | `$1.50` | `$1.00` | +| Elemento | Starter | Pro | +| ---------------------------- | -------- | -------- | +| 1 MAU | `$0.015` | `$0.015` | +| 1.000 Emails | `$2.50` | `$2.00` | +| 1.000 Notificaciones push | `$1.50` | `$1.00` | -Volume discounts are available as part of [custom contracts](#custom-contracts). +Los descuentos por volumen están disponibles como parte de [contratos personalizados](#custom-contracts). -## Features +## Funciones -Here's a list of all Trophy features and the plan they become available on: +Aquí hay una lista de todas las funciones de Trophy y el plan en el que están disponibles: -- [Achievements](/platform/achievements) -- [Streaks](/platform/streaks) -- [Points](/platform/points) -- [Leaderboards](/platform/leaderboards) +- [Logros](/platform/achievements) +- [Rachas](/platform/streaks) +- [Puntos](/platform/points) +- [Clasificaciones](/platform/leaderboards) - [Emails](/platform/emails) -- [Push Notifications](/platform/push-notifications) +- [Notificaciones push](/platform/push-notifications) -- [DNS Verification](/platform/emails#dns-verification-advanced) +- [Verificación DNS](/platform/emails#dns-verification-advanced) - [Webhooks](/webhooks) -- [Custom Attributes](/platform/users#custom-user-attributes) +- [Atributos Personalizados](/platform/users#custom-user-attributes) -## Custom Contracts +## Contratos Personalizados -If you have more than 100K MAUs and would like to discuss custom contracts including volume discounts to suit your business needs then please [get in touch](mailto:hello@trophy.so) and we'll be happy to help. +Si tiene más de 100K MAU y desea discutir contratos personalizados que incluyan descuentos por volumen adaptados a las necesidades de su negocio, [contáctenos](mailto:hello@trophy.so) y estaremos encantados de ayudarle. -## Viewing Your Usage +## Ver su Uso -You can view your usage for the current billing period on the [billing page](https://app.trophy.so/billing) of the Trophy dashboard and view all past invoices in your billing portal. +Puede ver su uso para el período de facturación actual en la [página de facturación](https://app.trophy.so/billing) del panel de Trophy y ver todas las facturas anteriores en su portal de facturación. -## Frequently Asked Questions +## Preguntas frecuentes - - We charge all customers on the 1st of the month for usage in arrears. + + Cobramos a todos los clientes el día 1 de cada mes por el uso del mes anterior. -## Get Support +## Obtener soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/account/branding.mdx b/es/account/branding.mdx index 1d1ced1..b86c577 100644 --- a/es/account/branding.mdx +++ b/es/account/branding.mdx @@ -1,24 +1,26 @@ --- -title: Branding -description: Configure your logo and brand colors used across features built with Trophy. -"og:description": Learn how Trophy tracks usage according to the number of users that use your product. +title: Marca +description: Configure su logotipo y los colores de marca utilizados en las + funcionalidades creadas con Trophy. +og:description: Descubra cómo Trophy realiza el seguimiento del uso según el + número de usuarios que utilizan su producto. icon: palette --- -## Configure Branding +## Configurar Marca -The [branding page](https://app.trophy.so/branding) in the Trophy dashboard allows you to configure the following account-level settings. These setting will be used in any communications you configure Trophy to send to users, like [Emails](/platform/emails). +La [página de marca](https://app.trophy.so/branding) en el panel de Trophy le permite configurar los siguientes ajustes a nivel de cuenta. Estos ajustes se utilizarán en cualquier comunicación que configure Trophy para enviar a los usuarios, como [Correos electrónicos](/platform/emails). -- **Logo**: Upload your brand’s logo to be displayed in the header of Trophy emails. We recommend using a horizontal logo with a transparent background or a plain background color with rounded corners. -- **Font**: The default font used in Trophy emails. -- **Brand Color**: Choose a primary color for your brand. Trophy will use this color for buttons and other elements in your emails. -- **App Name**: Enter the name of your app or service. Trophy will use this name in various places in your emails. -- **App URL**: Enter a default URL to use when not overridden on a per-email basis. Usually this is the URL of your app or service's sign in page. +- **Logotipo**: Cargue el logotipo de su marca para mostrarlo en el encabezado de los correos electrónicos de Trophy. Recomendamos usar un logotipo horizontal con fondo transparente o un color de fondo liso con esquinas redondeadas. +- **Fuente**: La fuente predeterminada utilizada en los correos electrónicos de Trophy. +- **Color de Marca**: Elija un color principal para su marca. Trophy utilizará este color para botones y otros elementos en sus correos electrónicos. +- **Nombre de la Aplicación**: Introduzca el nombre de su aplicación o servicio. Trophy utilizará este nombre en varios lugares de sus correos electrónicos. +- **URL de la Aplicación**: Introduzca una URL predeterminada para usar cuando no se anule por correo electrónico. Normalmente, esta es la URL de la página de inicio de sesión de su aplicación o servicio. -## Get Support +## Obtener Soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Desea ponerse en contacto con el equipo de Trophy? Comuníquese con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarle! diff --git a/es/account/members.mdx b/es/account/members.mdx index ab75c53..3d13e24 100644 --- a/es/account/members.mdx +++ b/es/account/members.mdx @@ -1,15 +1,17 @@ --- -title: Members -description: Give team members access to Trophy and manage your account settings. -"og:description": Give team members access to Trophy and manage your account settings. +title: Miembros +description: Otorga acceso a Trophy a los miembros del equipo y administra la + configuración de tu cuenta. +og:description: Otorga acceso a Trophy a los miembros del equipo y administra la + configuración de tu cuenta. icon: user-plus --- -## Invite Your team +## Invita a tu equipo -Trophy supports up to 5 team members per organization. However if you feel you need more [just ask](mailto:support@trophy.so) and we'll be happy to give you more room. +Trophy admite hasta 5 miembros de equipo por organización. Sin embargo, si consideras que necesitas más, [simplemente solicítalo](mailto:support@trophy.so) y estaremos encantados de darte más espacio. -To invite a team member to your Trophy organization, open up the organization management dialog and head into the _Members_ tab: +Para invitar a un miembro del equipo a tu organización de Trophy, abre el diálogo de administración de la organización y dirígete a la pestaña _Miembros_: -Hit _Invite_ and add the email address of the team member(s) you want to invite. If you're an account admin, you can add other admins, otherwise you'll only be able to add other members. +Haz clic en _Invitar_ y añade la dirección de correo electrónico de los miembros del equipo que deseas invitar. Si eres administrador de la cuenta, puedes añadir otros administradores; de lo contrario, solo podrás añadir otros miembros. -Once you're happy, hit _Send invitations_ and each team member will receive an email invitation to Trophy. +Una vez que estés satisfecho, haz clic en _Enviar invitaciones_ y cada miembro del equipo recibirá una invitación por correo electrónico a Trophy. -## Manage User Roles +## Administra los roles de usuario -Only organization admins can manage user roles +Solo los administradores de la organización pueden administrar los roles de usuario -Every team member in your Trophy organization has one of two roles: +Cada miembro del equipo en tu organización de Trophy tiene uno de dos roles: -- Admin: Can perform all account operations, including organization management and adding new admins. -- Member: Can perform all account operations, and add new members. +- Administrador: Puede realizar todas las operaciones de la cuenta, incluida la administración de la organización y la adición de nuevos administradores. +- Miembro: Puede realizar todas las operaciones de la cuenta y añadir nuevos miembros. -By default, the person who first set up Trophy will become the first admin. However you can change the role of any user at any time. +Por defecto, la persona que configuró Trophy por primera vez se convertirá en el primer administrador. Sin embargo, puedes cambiar el rol de cualquier usuario en cualquier momento. -To manage roles, open up the organization management dialog and head into the _Members_ tab: +Para administrar roles, abre el diálogo de administración de la organización y dirígete a la pestaña _Miembros_: -Here you can promote any existing member to an admin, or demote any existing admin to a member. +Aquí puedes promover a cualquier miembro existente a administrador, o degradar a cualquier administrador existente a miembro. -## Get Support +## Obtener Soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/account/overview.mdx b/es/account/overview.mdx index 4b42ea2..d74f373 100644 --- a/es/account/overview.mdx +++ b/es/account/overview.mdx @@ -1,23 +1,22 @@ --- -title: Overview -description: Learn how to manage your Trophy account. +title: Resumen +description: Aprende a gestionar tu cuenta de Trophy. --- -From inviting team members to understanding billing, this section of the documentation is dedicated to managing your Trophy account. +Desde invitar a miembros del equipo hasta comprender la facturación, esta sección de la documentación está dedicada a la gestión de tu cuenta de Trophy. - - Give team members access to Trophy and manage your account settings. + + Otorga acceso a Trophy a los miembros del equipo y gestiona la configuración de tu cuenta. - - Configure your logo and brand colors used across features built with Trophy. + + Configura tu logotipo y los colores de marca utilizados en las funciones creadas con Trophy. - - Learn how Trophy tracks usage according to the number of users that use your - product. + + Descubre cómo Trophy realiza el seguimiento del uso según el número de usuarios que utilizan tu producto. -## Get Support +## Obtener soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Escríbenos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/admin-api/endpoints/points/archive-a-boost.mdx b/es/admin-api/endpoints/points/archive-a-boost.mdx index e80a6fd..821a4ae 100644 --- a/es/admin-api/endpoints/points/archive-a-boost.mdx +++ b/es/admin-api/endpoints/points/archive-a-boost.mdx @@ -4,6 +4,6 @@ openapi: delete /points/boosts/{id} import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de velocidad** diff --git a/es/admin-api/endpoints/points/archive-boosts-batch.mdx b/es/admin-api/endpoints/points/archive-boosts-batch.mdx index 1a54ad0..6084a25 100644 --- a/es/admin-api/endpoints/points/archive-boosts-batch.mdx +++ b/es/admin-api/endpoints/points/archive-boosts-batch.mdx @@ -4,6 +4,6 @@ openapi: delete /points/boosts import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de tasa** diff --git a/es/admin-api/endpoints/points/create-boosts.mdx b/es/admin-api/endpoints/points/create-boosts.mdx index 34f2532..d3b4edf 100644 --- a/es/admin-api/endpoints/points/create-boosts.mdx +++ b/es/admin-api/endpoints/points/create-boosts.mdx @@ -4,6 +4,6 @@ openapi: post /points/boosts import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Tasa** diff --git a/es/admin-api/endpoints/streaks/grant-freezes.mdx b/es/admin-api/endpoints/streaks/grant-freezes.mdx index e16879d..e1da976 100644 --- a/es/admin-api/endpoints/streaks/grant-freezes.mdx +++ b/es/admin-api/endpoints/streaks/grant-freezes.mdx @@ -4,6 +4,6 @@ openapi: post /streaks/freezes import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Tasa** diff --git a/es/admin-api/endpoints/streaks/restore-streaks.mdx b/es/admin-api/endpoints/streaks/restore-streaks.mdx index b60cf8c..02c74cf 100644 --- a/es/admin-api/endpoints/streaks/restore-streaks.mdx +++ b/es/admin-api/endpoints/streaks/restore-streaks.mdx @@ -4,6 +4,6 @@ openapi: post /streaks/restore import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de velocidad** diff --git a/es/admin-api/introduction.mdx b/es/admin-api/introduction.mdx index 0a47a0c..fff2107 100644 --- a/es/admin-api/introduction.mdx +++ b/es/admin-api/introduction.mdx @@ -1,24 +1,26 @@ --- -title: Introduction -description: Learn about the Trophy Admin API and how to use it to drive custom workflows using Trophy data. -subtitle: Learn about the Trophy Admin API and how to use it to drive custom workflows using Trophy data. +title: Introducción +description: Aprende sobre la API de administración de Trophy y cómo utilizarla + para impulsar flujos de trabajo personalizados utilizando datos de Trophy. +subtitle: Aprende sobre la API de administración de Trophy y cómo utilizarla + para impulsar flujos de trabajo personalizados utilizando datos de Trophy. --- -The Trophy Admin API is a set of endpoints for building custom gamification workflows. +La API de administración de Trophy es un conjunto de endpoints para crear flujos de trabajo de gamificación personalizados. -While the [Application API](/api-reference/introduction) models the data and interactions required to deliver a gamification experience in-app, the Admin API models administrative actions that are only performed by your team or business logic outside of your application. +Mientras que la [API de aplicación](/api-reference/introduction) modela los datos y las interacciones necesarias para ofrecer una experiencia de gamificación dentro de la aplicación, la API de administración modela las acciones administrativas que solo realiza tu equipo o la lógica de negocio fuera de tu aplicación. -The Admin API is accessible through **the same SDKs** as the Application API or, for those who manage their own HTTP clients, is available at the following base URL. +La API de administración es accesible a través de **los mismos SDKs** que la API de aplicación o, para quienes administran sus propios clientes HTTP, está disponible en la siguiente URL base. ```bash Base URL https://admin.trophy.so/v1 ``` - Have a use case you think you need an admin API for? [Tell us about - it](mailto:support@trophy.so) and we'll build it! + ¿Tienes un caso de uso para el cual crees que necesitas una API de administración? [Cuéntanos sobre + ello](mailto:support@trophy.so) y lo construiremos. -## Get Support +## Obtener soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/api-reference/authentication.mdx b/es/api-reference/authentication.mdx index 7187fba..e6faba7 100644 --- a/es/api-reference/authentication.mdx +++ b/es/api-reference/authentication.mdx @@ -1,50 +1,53 @@ --- -title: Authentication -description: Integrate securely with the API using account-level API keys transmitted in the `X-API-KEY` header. -"og:description": Integrate securely with the Trophy API using account-level API keys transmitted in the `X-API-KEY` header. +title: Autenticación +description: Integre de forma segura con la API utilizando claves API a nivel de + cuenta transmitidas en el encabezado + `X-API-KEY`. +og:description: Integre de forma segura con la API de Trophy utilizando claves + API a nivel de cuenta transmitidas en el encabezado + `X-API-KEY`. icon: key --- -## API Keys +## Claves API -Every account can create a maximum of 3 API keys from the [Integration Page](https://app.trophy.so/integration). If you've already signed up, you'll have created one during onboarding. +Cada cuenta puede crear un máximo de 3 claves API desde la [Página de Integración](https://app.trophy.so/integration). Si ya se ha registrado, habrá creado una durante la incorporación inicial. -Trophy keeps track of and displays when each API key in your account was created and when it was last used so you can easily keep track of usage. +Trophy registra y muestra cuándo se creó cada clave API en su cuenta y cuándo se usó por última vez para que pueda hacer un seguimiento fácil del uso. -### Anatomy of an API key +### Estructura de una clave API -Each API key is made up of 2 parts separated by a period: +Cada clave API se compone de 2 partes separadas por un punto: ```bash {prefix}•{body} ``` -- The _prefix_ is the first 8 characters. It's readable and will always stay the same so it's easily recognisable. -- The _body_ is the secret part, and is only shown to you once when you create the key. +- El _prefijo_ son los primeros 8 caracteres. Es legible y siempre permanecerá igual, por lo que es fácilmente reconocible. +- El _cuerpo_ es la parte secreta y solo se le muestra una vez cuando crea la clave. - When using the API, both parts of your API key must be sent in the `X-API-KEY` - header. + Al usar la API, ambas partes de su clave API deben enviarse en el encabezado `X-API-KEY`. -### Authenticating Requests +### Autenticación de solicitudes -When making requests to the API, make sure to include **both** parts of your API key in the `X-API-KEY` header as in this example: +Al realizar solicitudes a la API, asegúrese de incluir **ambas** partes de su clave API en el encabezado `X-API-KEY` como en este ejemplo: ```bash curl https://app.trophy.so/api/users//metrics/ \ -H "X-API-KEY: ********.***********************" ``` -If you do not pass an API key, or your API key is invalid, you'll receive a `401` response code. +Si no pasa una clave API o su clave API no es válida, recibirá un código de respuesta `401`. -## Managing API keys +## Gestión de claves API -There are a few different operations you can perform on API keys from within your Trophy dashboard to manage your integration. +Hay varias operaciones diferentes que puede realizar en las claves API desde su panel de Trophy para gestionar su integración. -### Rotating keys +### Rotación de claves -API keys can be rotated if you want to change them for any reason. At the point of rotation, the original API key will no longer function and any requests still using it will begin receiving `401` responses immediately. +Las claves de API se pueden rotar si deseas cambiarlas por cualquier motivo. En el momento de la rotación, la clave de API original dejará de funcionar y cualquier solicitud que todavía la utilice comenzará a recibir respuestas `401` inmediatamente. - Note that when rotating keys, both the prefix and the body will change. + Ten en cuenta que al rotar las claves, tanto el prefijo como el cuerpo cambiarán. -### Revoking keys +### Revocación de claves -API keys can also be revoked entirely at which point they become _Inactive_. At the point of revocation, the API key will no longer function and any requests still using it will begin receiving `401` responses immediately. +Las claves de API también se pueden revocar por completo, momento en el que pasan a estar _Inactivas_. En el momento de la revocación, la clave de API dejará de funcionar y cualquier solicitud que todavía la utilice comenzará a recibir respuestas `401` inmediatamente. -Once revoked you can re-activate the API key at any time. +Una vez revocada, puedes reactivar la clave de API en cualquier momento. - Neither the prefix or the body of the key change when revoked or re-activated. + Ni el prefijo ni el cuerpo de la clave cambian cuando se revoca o se reactiva. -### Deleting API keys +### Eliminación de claves de API -If you're 100% sure you no longer need an API key, they can be deleted. +Si estás 100% seguro de que ya no necesitas una clave de API, se pueden eliminar. -Once API keys are deleted, they cannot be recovered. +Una vez que las claves de API se eliminan, no se pueden recuperar. -## Get Support +## Obtener soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/api-reference/client-libraries.mdx b/es/api-reference/client-libraries.mdx index f025f29..e984292 100644 --- a/es/api-reference/client-libraries.mdx +++ b/es/api-reference/client-libraries.mdx @@ -1,11 +1,12 @@ --- -title: Client Libraries -description: Use Trophy's type-safe SDKs to integrate with the API. -"og:description": Use Trophy's type-safe SDKs to integrate with the API and start building gamified product experiences. +title: Bibliotecas Cliente +description: Utiliza los SDKs con seguridad de tipos de Trophy para integrar con la API. +og:description: Utiliza los SDKs con seguridad de tipos de Trophy para integrar + con la API y comienza a crear experiencias de producto gamificadas. icon: code-xml --- -Trophy currently offers the following SDKs for interacting with the Trophy API. +Trophy ofrece actualmente los siguientes SDKs para interactuar con la API de Trophy. -## Get Support +## Obtener Soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/api-reference/endpoints/achievements/all-achievements.mdx b/es/api-reference/endpoints/achievements/all-achievements.mdx index f90d566..2e5ffaf 100644 --- a/es/api-reference/endpoints/achievements/all-achievements.mdx +++ b/es/api-reference/endpoints/achievements/all-achievements.mdx @@ -4,6 +4,6 @@ openapi: get /achievements import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx b/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx index 4aa9860..5162a9d 100644 --- a/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx +++ b/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx @@ -4,6 +4,6 @@ openapi: post /achievements/{key}/complete import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx b/es/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx index 7decf5d..7a894e9 100644 --- a/es/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx +++ b/es/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx @@ -4,6 +4,6 @@ openapi: get /leaderboards import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/leaderboards/get-leaderboard.mdx b/es/api-reference/endpoints/leaderboards/get-leaderboard.mdx index 12fae63..6fd1cb0 100644 --- a/es/api-reference/endpoints/leaderboards/get-leaderboard.mdx +++ b/es/api-reference/endpoints/leaderboards/get-leaderboard.mdx @@ -4,6 +4,6 @@ openapi: get /leaderboards/{key} import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/metrics/send-a-metric-change-event.mdx b/es/api-reference/endpoints/metrics/send-a-metric-change-event.mdx index 1cbbbe1..0a66f0d 100644 --- a/es/api-reference/endpoints/metrics/send-a-metric-change-event.mdx +++ b/es/api-reference/endpoints/metrics/send-a-metric-change-event.mdx @@ -4,6 +4,6 @@ openapi: post /metrics/{key}/event import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/points/get-points-boosts.mdx b/es/api-reference/endpoints/points/get-points-boosts.mdx index 1e06e1f..4732fa7 100644 --- a/es/api-reference/endpoints/points/get-points-boosts.mdx +++ b/es/api-reference/endpoints/points/get-points-boosts.mdx @@ -4,6 +4,6 @@ openapi: get /points/{key}/boosts import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de velocidad** \ No newline at end of file diff --git a/es/api-reference/endpoints/points/get-points-level-summary.mdx b/es/api-reference/endpoints/points/get-points-level-summary.mdx index 3d464be..5e4b7a3 100644 --- a/es/api-reference/endpoints/points/get-points-level-summary.mdx +++ b/es/api-reference/endpoints/points/get-points-level-summary.mdx @@ -4,6 +4,6 @@ openapi: get /points/{key}/level-summary import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Tasa** diff --git a/es/api-reference/endpoints/points/get-points-levels.mdx b/es/api-reference/endpoints/points/get-points-levels.mdx index c86f5eb..fb9de3a 100644 --- a/es/api-reference/endpoints/points/get-points-levels.mdx +++ b/es/api-reference/endpoints/points/get-points-levels.mdx @@ -4,6 +4,6 @@ openapi: get /points/{key}/levels import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de tasa** diff --git a/es/api-reference/endpoints/points/get-points-summary.mdx b/es/api-reference/endpoints/points/get-points-summary.mdx index 9c2b1c7..bae543a 100644 --- a/es/api-reference/endpoints/points/get-points-summary.mdx +++ b/es/api-reference/endpoints/points/get-points-summary.mdx @@ -4,6 +4,6 @@ openapi: get /points/{key}/summary import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/points/get-points.mdx b/es/api-reference/endpoints/points/get-points.mdx index c6a7a9b..078c817 100644 --- a/es/api-reference/endpoints/points/get-points.mdx +++ b/es/api-reference/endpoints/points/get-points.mdx @@ -4,6 +4,6 @@ openapi: get /points/{key} import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Velocidad** \ No newline at end of file diff --git a/es/api-reference/endpoints/streaks/get-streak-rankings.mdx b/es/api-reference/endpoints/streaks/get-streak-rankings.mdx index a127442..172d6f2 100644 --- a/es/api-reference/endpoints/streaks/get-streak-rankings.mdx +++ b/es/api-reference/endpoints/streaks/get-streak-rankings.mdx @@ -4,6 +4,6 @@ openapi: get /streaks/rankings import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/streaks/get-streaks.mdx b/es/api-reference/endpoints/streaks/get-streaks.mdx index a27e533..93d0ddd 100644 --- a/es/api-reference/endpoints/streaks/get-streaks.mdx +++ b/es/api-reference/endpoints/streaks/get-streaks.mdx @@ -4,6 +4,6 @@ openapi: get /streaks import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/create-a-user.mdx b/es/api-reference/endpoints/users/create-a-user.mdx index ec6082d..99d05db 100644 --- a/es/api-reference/endpoints/users/create-a-user.mdx +++ b/es/api-reference/endpoints/users/create-a-user.mdx @@ -4,6 +4,6 @@ openapi: post /users import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx b/es/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx index a8a4ec6..e6c36f4 100644 --- a/es/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx +++ b/es/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx @@ -4,6 +4,6 @@ openapi: get /users/{id}/metrics/{key}/event-summary import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx b/es/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx index b25d138..743ee5e 100644 --- a/es/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx +++ b/es/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx @@ -4,6 +4,6 @@ openapi: get /users/{id}/metrics/{key} import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-single-user.mdx b/es/api-reference/endpoints/users/get-a-single-user.mdx index c0f0d59..7c10ecd 100644 --- a/es/api-reference/endpoints/users/get-a-single-user.mdx +++ b/es/api-reference/endpoints/users/get-a-single-user.mdx @@ -4,6 +4,6 @@ openapi: get /users/{id} import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de frecuencia** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-users-completed-achievements.mdx b/es/api-reference/endpoints/users/get-a-users-completed-achievements.mdx index fa1bfe7..ccb06a6 100644 --- a/es/api-reference/endpoints/users/get-a-users-completed-achievements.mdx +++ b/es/api-reference/endpoints/users/get-a-users-completed-achievements.mdx @@ -4,6 +4,6 @@ openapi: get /users/{id}/achievements import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de velocidad** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-users-leaderboard.mdx b/es/api-reference/endpoints/users/get-a-users-leaderboard.mdx index d12ec33..4e5aece 100644 --- a/es/api-reference/endpoints/users/get-a-users-leaderboard.mdx +++ b/es/api-reference/endpoints/users/get-a-users-leaderboard.mdx @@ -4,6 +4,6 @@ openapi: get /users/{id}/leaderboards/{key} import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-users-points-boosts.mdx b/es/api-reference/endpoints/users/get-a-users-points-boosts.mdx index 1c91874..bc6fa25 100644 --- a/es/api-reference/endpoints/users/get-a-users-points-boosts.mdx +++ b/es/api-reference/endpoints/users/get-a-users-points-boosts.mdx @@ -4,6 +4,6 @@ openapi: get /users/{id}/points/{key}/boosts import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-users-points-summary.mdx b/es/api-reference/endpoints/users/get-a-users-points-summary.mdx index bc6bec2..1ce9c86 100644 --- a/es/api-reference/endpoints/users/get-a-users-points-summary.mdx +++ b/es/api-reference/endpoints/users/get-a-users-points-summary.mdx @@ -4,6 +4,6 @@ openapi: get /users/{id}/points/{key}/event-summary import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de velocidad** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-users-points.mdx b/es/api-reference/endpoints/users/get-a-users-points.mdx index 4df1d5d..a32f78c 100644 --- a/es/api-reference/endpoints/users/get-a-users-points.mdx +++ b/es/api-reference/endpoints/users/get-a-users-points.mdx @@ -4,6 +4,6 @@ openapi: get /users/{id}/points/{key} import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-users-streak.mdx b/es/api-reference/endpoints/users/get-a-users-streak.mdx index 64fb237..a357cef 100644 --- a/es/api-reference/endpoints/users/get-a-users-streak.mdx +++ b/es/api-reference/endpoints/users/get-a-users-streak.mdx @@ -4,6 +4,6 @@ openapi: get /users/{id}/streak import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-a-users-wrapped.mdx b/es/api-reference/endpoints/users/get-a-users-wrapped.mdx index e30ddaf..ca199f3 100644 --- a/es/api-reference/endpoints/users/get-a-users-wrapped.mdx +++ b/es/api-reference/endpoints/users/get-a-users-wrapped.mdx @@ -4,6 +4,6 @@ openapi: get /users/{id}/wrapped import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Velocidad** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx b/es/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx index f26742f..50bf0a8 100644 --- a/es/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx +++ b/es/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx @@ -4,6 +4,6 @@ openapi: get /users/{id}/metrics import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de velocidad** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/get-user-preferences.mdx b/es/api-reference/endpoints/users/get-user-preferences.mdx index 398c9c6..4769c50 100644 --- a/es/api-reference/endpoints/users/get-user-preferences.mdx +++ b/es/api-reference/endpoints/users/get-user-preferences.mdx @@ -4,6 +4,6 @@ openapi: get /users/{id}/preferences import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Frecuencia** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/identify-a-user.mdx b/es/api-reference/endpoints/users/identify-a-user.mdx index 7e7d2e3..f957af7 100644 --- a/es/api-reference/endpoints/users/identify-a-user.mdx +++ b/es/api-reference/endpoints/users/identify-a-user.mdx @@ -4,6 +4,6 @@ openapi: put /users/{id} import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de Tasa** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/update-a-user.mdx b/es/api-reference/endpoints/users/update-a-user.mdx index 3ee1fff..838bc08 100644 --- a/es/api-reference/endpoints/users/update-a-user.mdx +++ b/es/api-reference/endpoints/users/update-a-user.mdx @@ -4,6 +4,6 @@ openapi: patch /users/{id} import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de velocidad** \ No newline at end of file diff --git a/es/api-reference/endpoints/users/update-user-preferences.mdx b/es/api-reference/endpoints/users/update-user-preferences.mdx index 1346b04..1b03440 100644 --- a/es/api-reference/endpoints/users/update-user-preferences.mdx +++ b/es/api-reference/endpoints/users/update-user-preferences.mdx @@ -4,6 +4,6 @@ openapi: patch /users/{id}/preferences import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -**Rate Limits** +**Límites de velocidad** \ No newline at end of file diff --git a/es/api-reference/idempotency.mdx b/es/api-reference/idempotency.mdx index b37981e..f1a6459 100644 --- a/es/api-reference/idempotency.mdx +++ b/es/api-reference/idempotency.mdx @@ -1,50 +1,53 @@ --- -title: Idempotency -description: Prevent unintended side effects when retrying requests with idempotency controls built into the Trophy API and SDKs. -"og:description": Prevent unintended side effects when retrying requests with idempotency built into the Trophy API and SDKs using the `Idempotency-Key` request header. +title: Idempotencia +description: Previene efectos secundarios no deseados al reintentar solicitudes + con controles de idempotencia integrados en la API y SDKs de Trophy. +og:description: Previene efectos secundarios no deseados al reintentar + solicitudes con idempotencia integrada en la API y SDKs de Trophy usando el + encabezado de solicitud `Idempotency-Key`. icon: repeat --- import IdempotentEventTracking from "/snippets/idempotent-event-tracking.mdx"; -## What is Idempotency? +## ¿Qué es la Idempotencia? -When describing APIs, [idempotence](https://en.wikipedia.org/wiki/Idempotence) is a property of a particular operation whereby subsequent invocations after the first have no additional effect on the state of the system. +Al describir APIs, la [idempotencia](https://en.wikipedia.org/wiki/Idempotence) es una propiedad de una operación particular mediante la cual las invocaciones subsecuentes después de la primera no tienen efecto adicional sobre el estado del sistema. -Trophy's [event tracking API](/api-reference/endpoints/metrics/send-a-metric-change-event) can be used to enforce idempotency preventing client retries from having unintended side effects such as overcounting user interactions, or awarding points to users multiple times for the same action. +La [API de seguimiento de eventos](/api-reference/endpoints/metrics/send-a-metric-change-event) de Trophy puede utilizarse para garantizar la idempotencia, evitando que los reintentos del cliente tengan efectos secundarios no deseados como el conteo excesivo de interacciones de usuario o la concesión de Puntos a los usuarios múltiples veces por la misma acción. -This is particularly important where rewards are tied to user actions to prevent users from 'gaming the system'. +Esto es particularmente importante cuando las recompensas están vinculadas a acciones de usuario para prevenir que los usuarios 'manipulen el sistema'. -## Sending Idempotent Requests +## Envío de Solicitudes Idempotentes -To ensure idempotency is respected when sending events to Trophy, include an `Idempotency-Key` header when using the [event tracking API](/api-reference/endpoints/metrics/send-a-metric-change-event). Additionally, all [client SDKs](/api-reference/client-libraries) support idempotency with built-in type safety. +Para garantizar que se respete la idempotencia al enviar eventos a Trophy, incluye un encabezado `Idempotency-Key` al usar la [API de seguimiento de eventos](/api-reference/endpoints/metrics/send-a-metric-change-event). Además, todos los [SDKs de cliente](/api-reference/client-libraries) soportan idempotencia con seguridad de tipos integrada. -You can choose what to use as your idempotency key, but it should reflect the level of 'uniqueness' that you want Trophy to respect. +Puedes elegir qué usar como tu clave de idempotencia, pero debe reflejar el nivel de 'unicidad' que deseas que Trophy respete. - For example, if you use Trophy to reward users for completing lessons, and - want to make sure each user can only redeem rewards once for each lesson, use - the unique ID of the lesson as your idempotency key. + Por ejemplo, si usas Trophy para recompensar a los usuarios por completar lecciones, y + quieres asegurarte de que cada usuario solo pueda canjear recompensas una vez por cada lección, usa + el ID único de la lección como tu clave de idempotencia. -Here's an example of what using an idempotency key looks like: +Aquí hay un ejemplo de cómo se ve el uso de una clave de idempotencia: -## How Idempotency Works +## Cómo Funciona la Idempotencia -When Trophy detects an idempotency key has been sent with an event, it will first check if the user the event relates to has used it before. It will then proceed to take one of the following actions: +Cuando Trophy detecta que se ha enviado una clave de idempotencia con un evento, primero verificará si el usuario al que se refiere el evento la ha utilizado antes. Luego procederá a tomar una de las siguientes acciones: -- If the user has used the idempotency key before, Trophy will not process the event, returning a `202 Accepted` response. The response will reflect the current state of the system, but will not increase the users metric total, complete any achievements, award any points, extend the streak, etc. -- If instead Trophy detects the user hasn't used the idempotency key before, it will process the event as usual, returning a `201 Created` response. Finally Trophy will store the idempotency key for lookup during any subsequent requests. +- Si el usuario ha utilizado la clave de idempotencia anteriormente, Trophy no procesará el evento y devolverá una respuesta `202 Accepted`. La respuesta reflejará el estado actual del sistema, pero no incrementará el total de métricas del usuario, no completará logros, no otorgará puntos, no extenderá la racha, etc. +- Si por el contrario Trophy detecta que el usuario no ha utilizado la clave de idempotencia antes, procesará el evento de forma habitual y devolverá una respuesta `201 Created`. Finalmente, Trophy almacenará la clave de idempotencia para su consulta en solicitudes posteriores. - All Trophy [metrics](/platform/metrics) manage idempotency in isolation. - Trophy will accept a user using the same idempotency key for events against - different metrics as separate isolated requests. + Todas las [métricas](/platform/metrics) de Trophy gestionan la idempotencia de forma aislada. + Trophy aceptará que un usuario utilice la misma clave de idempotencia para eventos en + métricas diferentes como solicitudes aisladas independientes. -Additionally, when using an idempotency key the response will contain two properties to help clients manage replayed requests effectively: +Además, al utilizar una clave de idempotencia, la respuesta contendrá dos propiedades para ayudar a los clientes a gestionar las solicitudes repetidas de manera efectiva: {/* vale off */} @@ -59,11 +62,11 @@ Additionally, when using an idempotency key the response will contain two proper {/* vale on */} - By default Trophy uses an infinite time window for detecting duplicate events. - If you feel you need different behavior, please [get in - touch](mailto:support@trophy.so) and we'll happily set that up for you. + Por defecto, Trophy utiliza una ventana de tiempo infinita para detectar eventos duplicados. + Si necesita un comportamiento diferente, por favor [póngase en + contacto](mailto:support@trophy.so) y con gusto lo configuraremos para usted. -## Get Support +## Obtener Soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Desea ponerse en contacto con el equipo de Trophy? Comuníquese con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudar! diff --git a/es/api-reference/introduction.mdx b/es/api-reference/introduction.mdx index 7a9fcc3..5c01bd5 100644 --- a/es/api-reference/introduction.mdx +++ b/es/api-reference/introduction.mdx @@ -1,35 +1,35 @@ --- -title: Introduction -description: Learn about the Trophy API and get started building your integration. -subtitle: Learn about the Trophy API and get started building your integration. +title: Introducción +description: Aprende sobre la API de Trophy y comienza a construir tu integración. +subtitle: Aprende sobre la API de Trophy y comienza a construir tu integración. --- -The Trophy Application API is a set of endpoints that provide simple interfaces for building gamification experiences and covers the full range of actions that an end user of your application can perform. +La API de aplicación de Trophy es un conjunto de endpoints que proporcionan interfaces simples para crear experiencias de gamificación y cubre toda la gama de acciones que un usuario final de tu aplicación puede realizar. -It does not include global administrative functions or actions that should only be performed by your internal systems. If you are looking for admin functionality, please refer to the [Admin API](/admin-api/introduction). +No incluye funciones administrativas globales ni acciones que solo deben ser realizadas por tus sistemas internos. Si buscas funcionalidad de administración, consulta la [API de Administración](/admin-api/introduction). -The Application API is accessible through [SDKs](/api-reference/client-libraries) available in most major programming languages or, for those who wish manage their own HTTP clients, is available at the following base URL. +La API de Aplicación es accesible a través de [SDKs](/api-reference/client-libraries) disponibles en la mayoría de los principales lenguajes de programación o, para quienes deseen gestionar sus propios clientes HTTP, está disponible en la siguiente URL base. ```bash Base URL https://api.trophy.so/v1 ``` - - Securely integrate your applications with the Trophy API using API keys. + + Integra de forma segura tus aplicaciones con la API de Trophy utilizando claves API. - Learn about the Trophy API and request rate limiting. + Aprende sobre la API de Trophy y la limitación de tasa de solicitudes. - Use Trophy’s type-safe SDKs to integrate with the API. + Utiliza los SDKs con tipado seguro de Trophy para integrar con la API. -## Get Support +## Obtener soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres contactar con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/api-reference/rate-limiting.mdx b/es/api-reference/rate-limiting.mdx index 5eeaa39..8eac3a8 100644 --- a/es/api-reference/rate-limiting.mdx +++ b/es/api-reference/rate-limiting.mdx @@ -1,26 +1,25 @@ --- -title: Rate Limiting -description: Learn about the Trophy API and request rate limiting. -"og:description": Learn about the Trophy API and request rate limiting. +title: Limitación de tasa +description: Aprende sobre la API de Trophy y la limitación de tasa de solicitudes. +og:description: Aprende sobre la API de Trophy y la limitación de tasa de solicitudes. icon: circle-gauge --- import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; -Trophy operates a tiered rate limiting model for each API endpoint. All rate limits are scoped at the organisation level. Exceeding rate limits will result in your application receiving a `429 Too Many Requests` response. +Trophy opera un modelo de limitación de tasa por niveles para cada endpoint de la API. Todos los límites de tasa están definidos a nivel de organización. Superar los límites de tasa resultará en que tu aplicación reciba una respuesta `429 Too Many Requests`. -## Rate Limit Tiers +## Niveles de Limitación de Tasa -| Tier | Rate Limit | +| Nivel | Límite de Tasa | | ---- | ---------- | -| | 10 req/s | -| | 100 req/s | +| | 10 sol/s | +| | 100 sol/s | - If you feel you need a higher rate limit, then please [contact - us](mailto:support@trophy.so) and we'll be happy to discuss your needs. + Si consideras que necesitas un límite de tasa mayor, [contáctanos](mailto:support@trophy.so) y estaremos encantados de discutir tus necesidades. -## Get Support +## Obtener Soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/experimentation/engagement.mdx b/es/experimentation/engagement.mdx index c0d3adc..3bf0ddc 100644 --- a/es/experimentation/engagement.mdx +++ b/es/experimentation/engagement.mdx @@ -1,25 +1,25 @@ --- title: Engagement -description: Learn how to measure the impact of gamification on engagement -"og:description": Learn how to measure the impact of gamification on engagement +description: Aprende a medir el impacto de la gamificación en el engagement +og:description: Aprende a medir el impacto de la gamificación en el engagement icon: heart --- -## What Is Engagement? +## ¿Qué es el Engagement? -User engagement in Trophy refers to the average level of activity that your users show when using your product. +El engagement de usuarios en Trophy se refiere al nivel promedio de actividad que muestran tus usuarios al utilizar tu producto. -As Trophy tracks user interactions through [Metrics](/platform/metrics), it can give you insights into how active your users are against each, and in aggregate. +Como Trophy rastrea las interacciones de usuarios a través de [Métricas](/platform/metrics), puede brindarte información sobre qué tan activos están tus usuarios con respecto a cada una y en conjunto. -User engagement in Trophy is the average total metric event value per daily active user. The formula is: +El engagement de usuarios en Trophy es el valor promedio total de eventos de métrica por usuario activo diario. La fórmula es: ``` sum of all metric event values in a day / number of users active that day ``` -## Engagement Analytics +## Analíticas de Engagement -Trophy dashboards for each metric show engagement charts for that metric, and the main Trophy dashboard presents user engagement in aggregate. +Los dashboards de Trophy para cada métrica muestran gráficos de engagement para esa métrica, y el dashboard principal de Trophy presenta el engagement de usuarios de forma agregada. -Trophy also displays charts that show user engagement within users' first days after signing up for your product. +Trophy también muestra gráficos que presentan el engagement de usuarios durante sus primeros días después de registrarse en tu producto. -We refer to this as 'early engagement' and it is user engagement as measured by Trophy only for users within the time frame set in the 'New User Activation Window' setting on the [integration page](https://app.trophy.so/integration/configure). +Nos referimos a esto como 'engagement temprano' y es el engagement de usuarios medido por Trophy solo para usuarios dentro del marco temporal establecido en la configuración 'Ventana de Activación de Nuevos Usuarios' en la [página de integración](https://app.trophy.so/integration/configure). -This is a useful chart for understanding the engagement of users within their very first days with your product and to understand where blockers to initial user activation may be occurring. +Este es un gráfico útil para comprender el engagement de usuarios durante sus primeros días con tu producto y para identificar dónde pueden estar ocurriendo obstáculos para la activación inicial del usuario. -## Get Support +## Obtén Soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/experimentation/overview.mdx b/es/experimentation/overview.mdx index 50c9027..d048a6d 100644 --- a/es/experimentation/overview.mdx +++ b/es/experimentation/overview.mdx @@ -1,27 +1,29 @@ --- -title: Overview -description: Learn how to use Trophy to experiment with the gamification experience and increase retention and user engagement. -"og:description": Learn how to use Trophy to experiment with the gamification experience and increase retention and user engagement. +title: Descripción general +description: Aprende a usar Trophy para experimentar con la experiencia de + gamificación y aumentar la retención y el engagement de los usuarios. +og:description: Aprende a usar Trophy para experimentar con la experiencia de + gamificación y aumentar la retención y el engagement de los usuarios. --- import ControlFlagBlock from "/snippets/control-flag-block.mdx"; -Trophy provides a core set of primitives to help you ship gamification experiences faster. But gamification is not something you can set and forget. +Trophy proporciona un conjunto básico de primitivas para ayudarte a implementar experiencias de gamificación más rápido. Pero la gamificación no es algo que puedas configurar y olvidar. -A key part of running a gamified platform is experimentation — tweaking and optimizing the user experience to ensure that users keep coming back. +Una parte clave de administrar una plataforma gamificada es la experimentación: ajustar y optimizar la experiencia del usuario para asegurar que los usuarios sigan regresando. -Usually, you'd have to build these tools yourself, but with Trophy you get a simple experimentation stack out-of-the-box. +Normalmente, tendrías que construir estas herramientas tú mismo, pero con Trophy obtienes un stack de experimentación simple listo para usar. - We have plans to extend Trophy's experimentation capabilities in the future to - support more granular A/B testing of your gamification features. + Tenemos planes de ampliar las capacidades de experimentación de Trophy en el futuro para + admitir pruebas A/B más granulares de tus funciones de gamificación. -## Control Ratio +## Ratio de control -Trophy has a built-in capability to automatically A/B test gamification features using a control ratio. +Trophy tiene una capacidad integrada para realizar pruebas A/B automáticas de las funciones de gamificación mediante un ratio de control. -Found on the [integration page](https://app.trophy.so/integration/configure) of the Trophy dashboard, the control ratio adjusts the percentage of users that are assigned to the 'control' group versus the 'experimental' group. +Ubicado en la [página de integración](https://app.trophy.so/integration/configure) del panel de Trophy, el ratio de control ajusta el porcentaje de usuarios que se asignan al grupo de 'control' frente al grupo 'experimental'. -Trophy returns the `control` attribute as `true` or `false` in most APIs that relate to user data allowing you to conditionally show gamification features to users who are in the experimental group. +Trophy devuelve el atributo `control` como `true` o `false` en la mayoría de las APIs relacionadas con datos de usuario, lo que te permite mostrar condicionalmente funciones de gamificación a usuarios que están en el grupo experimental. -If you plan to measure the overall impact of gamification features, make sure to only show those features to users with `control` set to `false`. +Si planeas medir el impacto general de las funciones de gamificación, asegúrate de mostrar esas funciones solo a usuarios con `control` establecido en `false`. - Trophy also doesn't send any [Emails](/platform/emails) or [Push - Notifications](/platform/push-notifications) to users who are in the control - group. + Trophy tampoco envía [Correos electrónicos](/platform/emails) ni [Notificaciones push](/platform/push-notifications) a usuarios que están en el + grupo de control. -Analytics dashboards then compare retention and user engagement between users in the control and experimental groups allowing you to measure the impact of the features you build with Trophy and adjust mechanics where necessary. +Los paneles de análisis comparan la retención y la interacción de usuarios entre los grupos de control y experimentales, lo que te permite medir el impacto de las funcionalidades que construyes con Trophy y ajustar las mecánicas cuando sea necesario. -## Retention & Engagement +## Retención e Interacción -Both retention and user engagement are important metrics to use when measuring the impact of changes on the user experience. The following sections outline what each means and how it relates to features you build with Trophy. +Tanto la retención como la interacción de usuarios son métricas importantes para medir el impacto de los cambios en la experiencia del usuario. Las siguientes secciones explican qué significa cada una y cómo se relacionan con las funcionalidades que construyes con Trophy. - - Learn how to measure the impact of gamification on retention + + Aprende a medir el impacto de la gamificación en la retención - - Learn how to measure the impact of gamification on user engagement + + Aprende a medir el impacto de la gamificación en la interacción de usuarios -## Get Support +## Obtener Soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres contactar al equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/experimentation/retention.mdx b/es/experimentation/retention.mdx index 9676b68..d20599b 100644 --- a/es/experimentation/retention.mdx +++ b/es/experimentation/retention.mdx @@ -1,19 +1,19 @@ --- -title: Retention -description: Learn how to measure the impact of gamification on retention -"og:description": Learn how to measure the impact of gamification on retention +title: Retención +description: Aprende a medir el impacto de la gamificación en la retención +og:description: Aprende a medir el impacto de la gamificación en la retención icon: refresh-cw --- -## What Is Retention? +## ¿Qué es la retención? -User retention is the percentage of users who are still using your product after a certain . +La retención de usuarios es el porcentaje de usuarios que siguen utilizando tu producto después de un cierto periodo. -Common time frames include 7-day, 14-day and 30-day retention with shorter time frames providing information on initial user experience and longer time frames giving insight into long-term product-market fit. +Los marcos temporales comunes incluyen retención a 7, 14 y 30 días, donde los periodos más cortos proporcionan información sobre la experiencia inicial del usuario y los periodos más largos ofrecen información sobre el ajuste producto-mercado a largo plazo. -## Retention Analytics +## Analítica de retención -Trophy includes a retention chart for each metric on its metric dashboard and an aggregate user retention chart on the main dashboard. +Trophy incluye un gráfico de retención para cada métrica en su panel de métricas y un gráfico agregado de retención de usuarios en el panel principal. -The [integration page](https://app.trophy.so/integration/configure) allows you to control the time frame over which you want to measure retention through the 'New User Activation Window' setting. +La [página de integración](https://app.trophy.so/integration/configure) te permite controlar el marco temporal en el que deseas medir la retención mediante la configuración 'Ventana de activación de nuevos usuarios'. -## Get Support +## Obtener soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres contactar con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/getting-started/introduction.mdx b/es/getting-started/introduction.mdx index 8ac325c..0a0472c 100644 --- a/es/getting-started/introduction.mdx +++ b/es/getting-started/introduction.mdx @@ -1,45 +1,46 @@ --- -title: Welcome to Trophy -description: Trophy documentation for gamification APIs, quickstart guides and tutorials. +title: Bienvenido a Trophy +description: Documentación de Trophy para APIs de gamificación, guías de inicio + rápido y tutoriales. --- -Trophy is a developer-friendly toolkit for building gamified product experiences in any mobile or web app. +Trophy es un conjunto de herramientas amigable para desarrolladores que permite crear experiencias de producto gamificadas en cualquier aplicación móvil o web. -It makes building features like [Achievements](/platform/achievements), [Streaks](/platform/streaks), [Points](/platform/points) and [Leaderboards](/platform/leaderboards) simple with just a few lines of code, and can send gamified [Emails](/platform/emails) and [Push Notifications](/platform/push-notifications) to increase your retention and engagement. +Facilita la creación de funciones como [Logros](/platform/achievements), [Rachas](/platform/streaks), [Puntos](/platform/points) y [Clasificaciones](/platform/leaderboards) con solo unas pocas líneas de código, y puede enviar [Emails](/platform/emails) y [Notificaciones push](/platform/push-notifications) gamificados para aumentar tu retención y engagement. - Integrate Trophy into your backend in under 10 minutes. + Integra Trophy en tu backend en menos de 10 minutos. - - Use Trophy to build practical solutions to common gamification use cases. + + Usa Trophy para crear soluciones prácticas a casos de uso comunes de gamificación. - - Learn the key concepts behind building gamification experiences with Trophy. + + Aprende los conceptos clave detrás de la creación de experiencias de gamificación con Trophy. - - Explore the Trophy API and see what's possible. + + Explora la API de Trophy y descubre lo que es posible. -## Why Trophy +## Por qué Trophy -If your goal is for your users to create a regular habit of using your app—to learn something, publish content, or even exercise—gamification could be for you. +Si tu objetivo es que tus usuarios creen el hábito regular de usar tu aplicación—para aprender algo, publicar contenido o incluso hacer ejercicio—la gamificación podría ser para ti. -It's been proven by the likes of Duolingo and others to predictably boost retention and increase platform engagement. +Ha sido comprobado por empresas como Duolingo y otras que aumenta la retención de manera predecible e incrementa el engagement en la plataforma. -But building gamification at scale can be tricky. You have a global userbase, all in different timezones, all with different usage patterns that need to be measured and optimized in realtime. Plus, you want to run experiments to test different approaches and see how they impact retention, but this requires constant code changes, which holds up work on your core product. +Pero construir gamificación a escala puede ser complejo. Tienes una base de usuarios global, todos en diferentes zonas horarias, todos con diferentes patrones de uso que necesitan medirse y optimizarse en tiempo real. Además, quieres ejecutar experimentos para probar diferentes enfoques y ver cómo impactan la retención, pero esto requiere cambios constantes en el código, lo que retrasa el trabajo en tu producto principal. -Trophy takes away the legwork involved in building features like achievements, streaks, points systems and leaderboards while providing reliable, scalable APIs for shipping gamification experiences faster than building in-house. Plus, it provides a web platform to easily configure, measure and experiment with the product experience without constant code changes. +Trophy elimina el trabajo pesado involucrado en crear funciones como logros, rachas, sistemas de puntos y clasificaciones, al tiempo que proporciona APIs confiables y escalables para lanzar experiencias de gamificación más rápido que construyéndolas internamente. Además, proporciona una plataforma web para configurar, medir y experimentar fácilmente con la experiencia del producto sin cambios constantes en el código. -## Get Support +## Obtén soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/getting-started/quickstart.mdx b/es/getting-started/quickstart.mdx index 5060ac2..bb14c9e 100644 --- a/es/getting-started/quickstart.mdx +++ b/es/getting-started/quickstart.mdx @@ -1,7 +1,8 @@ --- -title: Quick Start -description: Get up and running with Trophy in under 10 minutes -"og:description": Follow this quick start guide to integrate Trophy into your backend and add gamified features like achievements and streaks into your app. +title: Inicio rápido +description: Ponte en marcha con Trophy en menos de 10 minutos +og:description: Sigue esta guía de inicio rápido para integrar Trophy en tu + backend y añadir funciones gamificadas como logros y rachas a tu aplicación. --- import SdkInstallCommand from "/snippets/sdk-install-command.mdx"; @@ -9,35 +10,35 @@ import AddEnvVarBlock from "/snippets/add-env-var-block.mdx"; import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; -Here you'll integrate your backend web application with Trophy and start building your first gamified feature. +Aquí integrarás tu aplicación web backend con Trophy y comenzarás a construir tu primera función gamificada. - - First, [create a new account](https://app.trophy.so/sign-up?utm_source=docs&utm_medium=quickstart) if you don't already have one and head into the [Trophy dashboard](https://app.trophy.so). + + Primero, [crea una nueva cuenta](https://app.trophy.so/sign-up?utm_source=docs&utm_medium=quickstart) si aún no tienes una y dirígete al [panel de Trophy](https://app.trophy.so). - Head through onboarding to get your account set up. + Completa el proceso de incorporación para configurar tu cuenta. - - We have SDK libraries available in most major programming languages but if you don't see yours listed, let us know and we'll make one! + + Tenemos bibliotecas SDK disponibles en la mayoría de los principales lenguajes de programación, pero si no ves el tuyo en la lista, ¡háznoslo saber y crearemos uno! - Alternatively, you can directly call the API using any server-side HTTP library. + Alternativamente, puedes llamar directamente a la API utilizando cualquier biblioteca HTTP del lado del servidor. - - Add your API key that you created during onboarding (or [create a new one](https://app.trophy.so/integration)) as an environment variable in your backend application: + + Añade tu clave API que creaste durante la incorporación (o [crea una nueva](https://app.trophy.so/integration)) como una variable de entorno en tu aplicación backend: - Make sure to pass this API key to the Trophy SDK or to your API client to authenticate. + Asegúrate de pasar esta clave API al SDK de Trophy o a tu cliente API para autenticarte. - - All gamification features are driven by user interactions. In Trophy, you use [Metrics](/platform/metrics) to define and model those interactions and [Events](/platform/events) to track them. + + Todas las funciones de gamificación están impulsadas por las interacciones del usuario. En Trophy, utilizas [Métricas](/platform/metrics) para definir y modelar esas interacciones y [Eventos](/platform/events) para rastrearlas. - Here you'll create your first metric to get started. In the Trophy dashboard, head into the [metrics page](https://app.trophy.so/metrics) and hit the _New Metric_ button: + Aquí crearás tu primera métrica para comenzar. En el panel de Trophy, dirígete a la [página de métricas](https://app.trophy.so/metrics) y pulsa el botón _Nueva Métrica_: - Give the metric a name and hit _Save_. + Asigna un nombre a la métrica y pulsa _Guardar_. - - Once you've created your metric, head to the configure tab and copy it's unique API reference key. + + Una vez que hayas creado tu métrica, ve a la pestaña de configuración y copia su clave de referencia única de la API. - To track an event against this metric when a user interacts with your product, call the [metric change event API](/api-reference/endpoints/metrics/send-a-metric-change-event), passing along details of the user that made the interaction. In this example the metric key would be `flashcards-flipped`: + Para registrar un evento contra esta métrica cuando un usuario interactúa con tu producto, llama a la [API de evento de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event), pasando los detalles del usuario que realizó la interacción. En este ejemplo, la clave de métrica sería `flashcards-flipped`: - By making this call, you're telling Trophy that a specific user made an interaction with your product. As a result, Trophy will process any gamification features like achievements or streaks that you've configured against the metric automatically. + Al realizar esta llamada, le estás indicando a Trophy que un usuario específico realizó una interacción con tu producto. Como resultado, Trophy procesará automáticamente cualquier función de gamificación como logros o rachas que hayas configurado contra la métrica. - - With a metric integrated into your backend, you're ready to start adding gamification features to your product. + + Con una métrica integrada en tu backend, estás listo para comenzar a agregar funciones de gamificación a tu producto. - Follow the links below to learn more about each feature you can build with Trophy: + Sigue los enlaces a continuación para obtener más información sobre cada función que puedes construir con Trophy: - - Reward users for continued progress or taking specific actions. + + Recompensa a los usuarios por el progreso continuo o por realizar acciones específicas. - - Motivate users to build regular usage habits. + + Motiva a los usuarios a desarrollar hábitos de uso regulares. - - Build sophisticated points systems to reward and retain users. + + Construye sistemas de puntos sofisticados para recompensar y retener usuarios. } href="/platform/leaderboards"> - Create friendly competitions to increase user engagement. + Crea competiciones amistosas para aumentar la participación de los usuarios. - - Deliver personalized lifecycle emails to users at the perfect moment. + + Envía correos electrónicos personalizados del ciclo de vida a los usuarios en el momento perfecto. - - Drive automated notification flows using personalized gamification data. + + Impulsa flujos de notificación automatizados utilizando datos de gamificación personalizados. - Or, explore our [API reference](/api-reference/introduction) to get familiar with what Trophy can do. + O explora nuestra [referencia de API](/api-reference/introduction) para familiarizarte con lo que Trophy puede hacer. -## Get Support +## Obtener Soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/guides/gamified-fitness-platform.mdx b/es/guides/gamified-fitness-platform.mdx index 05be787..f33e95b 100644 --- a/es/guides/gamified-fitness-platform.mdx +++ b/es/guides/gamified-fitness-platform.mdx @@ -1,12 +1,15 @@ --- -title: How to Build a Gamified Fitness Tracker -description: Learn how to build a Strava-like fitness app with Next.js, featuring multi-sport tracking, city-based leaderboards, and normalized XP progression. -subtitle: Build a high-retention fitness app with streaks and leaderboards. +title: Cómo construir un rastreador de fitness gamificado +description: Aprende a construir una aplicación de fitness similar a Strava con + Next.js, que incluye seguimiento multideportivo, clasificaciones por ciudad y + progresión de XP normalizada. +subtitle: Construye una aplicación de fitness de alta retención con rachas y + clasificaciones. --- -In this tutorial, we'll build **TrophyFitness**, a consumer fitness application that tracks running, cycling, and swimming. We'll implement a complete gamification loop including weekly leaderboards, habit-forming streaks, and a leveled progression system. +En este tutorial, construiremos **TrophyFitness**, una aplicación de fitness para consumidores que rastrea carrera, ciclismo y natación. Implementaremos un bucle completo de gamificación que incluye clasificaciones semanales, rachas para formar hábitos y un sistema de progresión por niveles. -If you want to skip straight to the code, check out the [example repository](https://github.com/trophyso/example-fitness-platform) or the [live demo](https://fitness.examples.trophy.so). +Si quieres ir directo al código, consulta el [repositorio de ejemplo](https://github.com/trophyso/example-fitness-platform) o la [demostración en vivo](https://fitness.examples.trophy.so). -## Table of Contents +## Tabla de contenidos -- [Tech Stack](#tech-stack) -- [Prerequisites](#prerequisites) -- [Setup & Installation](#setup--installation) -- [Designing the Data Model](#designing-the-data-model) -- [How Trophy Works](#how-trophy-works) -- [Setting Up Trophy](#setting-up-trophy) +- [Stack tecnológico](#tech-stack) +- [Requisitos previos](#prerequisites) +- [Configuración e instalación](#setup--installation) +- [Diseño del modelo de datos](#designing-the-data-model) +- [Cómo funciona Trophy](#how-trophy-works) +- [Configuración de Trophy](#setting-up-trophy) - [Server Actions](#server-actions) -- [The Leveling System](#the-leveling-system) -- [Building the Dashboard](#building-the-dashboard) -- [Logging Workouts](#logging-workouts) -- [Implementing Leaderboards](#implementing-leaderboards) -- [Building the Achievements Page](#building-the-achievements-page) -- [Building the Profile Page](#building-the-profile-page) -- [The Result](#the-result) +- [El sistema de niveles](#the-leveling-system) +- [Construcción del panel de control](#building-the-dashboard) +- [Registro de entrenamientos](#logging-workouts) +- [Implementación de clasificaciones](#implementing-leaderboards) +- [Construcción de la página de logros](#building-the-achievements-page) +- [Construcción de la página de perfil](#building-the-profile-page) +- [El resultado](#the-result) -## Tech Stack +## Stack tecnológico - **Framework:** [Next.js 15](https://nextjs.org) (App Router) - **UI:** [Shadcn/UI](https://ui.shadcn.com) + TailwindCSS -- **Icons:** [Lucide React](https://lucide.dev) -- **Gamification:** [Trophy](https://trophy.so) +- **Iconos:** [Lucide React](https://lucide.dev) +- **Gamificación:** [Trophy](https://trophy.so) -## Prerequisites +## Requisitos previos -- A Trophy account (sign up [here](https://app.trophy.so/sign-up)). -- Node.js 18+ installed. +- Una cuenta de Trophy (regístrate [aquí](https://app.trophy.so/sign-up)). +- Node.js 18+ instalado. -## Setup & Installation +## Configuración e instalación -First, clone the starter repository or create a new Next.js app: +Primero, clona el repositorio inicial o crea una nueva aplicación Next.js: ```bash npx create-next-app@latest trophy-fitness ``` -Install the required dependencies: +Instala las dependencias requeridas: ```bash npm install @trophyso/node lucide-react clsx tailwind-merge ``` -Configure your environment variables in `.env.local`: +Configura tus variables de entorno en `.env.local`: ```bash TROPHY_API_KEY=your_api_key_here ``` -## Designing the Data Model +## Diseño del modelo de datos -For a multi-sport fitness app, we need to normalize efforts. A 10km cycle is not the same as a 10km run. We'll use three distinct metrics to track raw data, and a unified XP system for progression. +Para una aplicación de fitness multideportivo, necesitamos normalizar los esfuerzos. Un ciclo de 10 km no es lo mismo que una carrera de 10 km. Usaremos tres métricas distintas para rastrear datos sin procesar, y un sistema unificado de XP para la progresión. -### 1. The Metrics +### 1. Las Métricas -We will track distance as the primary value. +Rastrearemos la distancia como valor principal. -- `distance_run` (km) - with `pace` attribute (walk/run). +- `distance_run` (km) - con atributo `pace` (caminar/correr). - `distance_cycled` (km) -- `distance_swum` (m) - with `style` attribute (freestyle/breaststroke). +- `distance_swum` (m) - con atributo `style` (estilo libre/braza). -### 2. The Attributes +### 2. Los Atributos -To enable local leaderboards, we'll tag every user with a `city` attribute. +Para habilitar clasificaciones locales, etiquetaremos cada usuario con un atributo `city`. -## How Trophy Works +## Cómo Funciona Trophy -Before diving into the code, let's understand how Trophy powers our gamification layer. In Trophy, [Metrics](/platform/metrics) represent different interactions users can make and drive features like [Achievements](/platform/achievements), [Streaks](/platform/streaks), and [Emails](/platform/emails). +Antes de profundizar en el código, entendamos cómo Trophy impulsa nuestra capa de gamificación. En Trophy, las [Métricas](/platform/metrics) representan diferentes interacciones que los usuarios pueden realizar e impulsan funciones como [Logros](/platform/achievements), [Rachas](/platform/streaks) y [Correos electrónicos](/platform/emails). ```mermaid flowchart TD @@ -105,23 +108,23 @@ flowchart TD B-->G ``` -When events are recorded for a specific user, any achievements linked to the specified metric will be unlocked if the requirements are met, streaks will be automatically calculated, leaderboards will update, and any configured emails will be scheduled. +Cuando se registran eventos para un usuario específico, cualquier logro vinculado a la métrica especificada se desbloqueará si se cumplen los requisitos, las rachas se calcularán automáticamente, las clasificaciones se actualizarán y cualquier correo electrónico configurado se programará. -This is what makes building gamified experiences with Trophy so powerful—it does all the work behind the scenes. +Esto es lo que hace tan poderosa la construcción de experiencias gamificadas con Trophy: hace todo el trabajo entre bastidores. -## Setting Up Trophy +## Configuración de Trophy -Here's how we'll configure Trophy for our fitness app: +Así es como configuraremos Trophy para nuestra aplicación de fitness: - - Head into the Trophy [metrics page](https://app.trophy.so/metrics) and create three metrics: + + Dirígete a la [página de métricas](https://app.trophy.so/metrics) de Trophy y crea tres métricas: - - `distance_run` — Total kilometers run - - `distance_cycled` — Total kilometers cycled - - `distance_swum` — Total meters swum + - `distance_run` — Kilómetros totales corridos + - `distance_cycled` — Kilómetros totales en bicicleta + - `distance_swum` — Metros totales nadados - These keys are what we'll reference in our code when sending events. + Estas claves son las que referenciaremos en nuestro código al enviar eventos. - - While still on the [metrics page](https://app.trophy.so/metrics), set up - the **pace** and **style** event attributes: + + Mientras sigues en la [página de métricas](https://app.trophy.so/metrics), configura + los atributos de evento **pace** y **style**: - Then, go to the [user attributes page](https://app.trophy.so/users/attributes) - and create the **city** user atribute: + Luego, ve a la [página de atributos de usuario](https://app.trophy.so/users/attributes) + y crea el atributo de usuario **city**: - - Head into the Trophy [achievements page](https://app.trophy.so/achievements) - and create milestone achievements for each sport. For example: + + Dirígete a la [página de logros](https://app.trophy.so/achievements) de Trophy + y crea logros por hitos para cada deporte. Por ejemplo: **Running:** - - First 5K (5km total) - - Half Marathon Hero (21.1km total) - - Marathon Master (42.2km total) + - Primer 5K (5km en total) + - Héroe de Media Maratón (21.1km en total) + - Maestro de Maratón (42.2km en total) **Cycling:** - - Century Rider (100km total) - - Tour Stage (200km total) + - Ciclista del Centenario (100km en total) + - Etapa del Tour (200km en total) **Swimming:** - - Pool Regular (1000m total) - - Open Water Ready (5000m total) + - Habitual de Piscina (1000m en total) + - Listo para Aguas Abiertas (5000m en total) - Link each achievement to the appropriate metric. + Vincula cada logro a la métrica correspondiente. - - Head to the [leaderboards page](https://app.trophy.so/leaderboards) and set up - weekly leaderboards to drive competition. Each leaderboard should be - configured with **Repetition Unit: Days** and **Repetition Interval: 7** to - repeat weekly. + + Dirígete a la [página de clasificaciones](https://app.trophy.so/leaderboards) y configura + clasificaciones semanales para impulsar la competencia. Cada clasificación debe + configurarse con **Unidad de Repetición: Days** e **Intervalo de Repetición: 7** para + repetirse semanalmente. - **Global leaderboards:** + **Clasificaciones globales:** - `weekly-distance-run` - `weekly-distance-cycled` - `weekly-distance-swum` - **City-based leaderboards** (breakdown by user attribute **city**): + **Clasificaciones por ciudad** (desglosadas por atributo de usuario **city**): - `weekly-distance-run-cities` - `weekly-distance-cycled-cities` @@ -229,15 +232,15 @@ Here's how we'll configure Trophy for our fitness app: - - Head to the [points page](https://app.trophy.so/points) and create a points - system called `xp` that awards points based on activity: + + Dirígete a la [página de puntos](https://app.trophy.so/points) y crea un sistema de puntos + llamado `xp` que otorgue puntos según la actividad: - - Running: 10 XP per km - - Cycling: 3 XP per km - - Swimming: 10 XP per 100m + - Running: 10 XP por km + - Cycling: 3 XP por km + - Swimming: 10 XP por cada 100m - This normalized approach ensures fair progression across sports. + Este enfoque normalizado garantiza una progresión equitativa entre deportes. - Then configure **[Points Levels](/platform/points#points-levels)** on the same `xp` system so Trophy can track each user's tier. Use the keys and thresholds below so the code in this guide lines up with your dashboard: + Luego configura **[Niveles de Puntos](/platform/points#points-levels)** en el mismo sistema `xp` para que Trophy pueda rastrear el nivel de cada usuario. Utiliza las claves y umbrales a continuación para que el código de esta guía se alinee con tu panel de control: - | Name | Key | Points threshold | + | Nombre | Clave | Umbral de puntos | | ------- | --------- | ---------------- | | Rookie | `rookie` | 0 | | Active | `active` | 100 | @@ -259,26 +262,26 @@ Here's how we'll configure Trophy for our fitness app: | Pro | `pro` | 10000 | - - Head to the [streaks page](https://app.trophy.so/streaks) and configure a - daily streak linked to any of the distance metrics. Users will maintain their - streak by logging at least one activity per day. + + Dirígete a la [página de rachas](https://app.trophy.so/streaks) y configura una + racha diaria vinculada a cualquiera de las métricas de distancia. Los usuarios mantendrán su + racha registrando al menos una actividad por día. - - Head to the [emails page](https://app.trophy.so/emails) and add and activate templates for these automated engagement emails: + + Dirígete a la [página de correos electrónicos](https://app.trophy.so/emails) y añade y activa plantillas para estos correos electrónicos de engagement automatizados: - - **Achievement unlocked** — Celebrate new badges - - **Streak at risk** — Remind users before they lose their streak - - **Weekly recap** — Summary of progress and leaderboard position + - **Logro desbloqueado** — Celebra nuevas insignias + - **Racha en riesgo** — Recuerda a los usuarios antes de que pierdan su racha + - **Resumen semanal** — Resumen del progreso y posición en la clasificación - Each email type has its own section—add a template under each and activate it to start sending. Configure your branding in the [branding page](https://app.trophy.so/branding) for professional emails. + Cada tipo de correo electrónico tiene su propia sección—añade una plantilla en cada una y actívala para comenzar a enviar. Configura tu branding en la [página de branding](https://app.trophy.so/branding) para correos electrónicos profesionales. -## Server Actions +## Acciones del Servidor -We'll create a `src/app/actions.ts` file to handle all interactions with the Trophy API. This keeps our API keys secure and allows us to leverage Next.js Server Actions. +Crearemos un archivo `src/app/actions.ts` para gestionar todas las interacciones con la API de Trophy. Esto mantiene nuestras claves API seguras y nos permite aprovechar las Server Actions de Next.js. ```tsx src/app/actions.ts [expandable] "use server"; @@ -457,11 +460,11 @@ export async function getUserIdFromCookies() { } ``` -## The Leveling System +## El Sistema de Niveles -Trophy stores each user's tier on the `xp` points system when you configure [Points Levels](/platform/points#points-levels). The [user points](/api-reference/endpoints/users/get-a-users-points) payload includes a `level` object. The [list levels API](/api-reference/endpoints/points/get-points-levels) returns every tier and threshold so you can show progress toward the next one. +Trophy almacena el nivel de cada usuario en el sistema de puntos `xp` cuando configuras [Niveles de Puntos](/platform/points#points-levels). El payload de [puntos del usuario](/api-reference/endpoints/users/get-a-users-points) incluye un objeto `level`. La [API de listar niveles](/api-reference/endpoints/points/get-points-levels) devuelve cada nivel y umbral para que puedas mostrar el progreso hacia el siguiente. -Add a small helper next to your app constants in `src/lib/constants.ts`: +Añade un pequeño helper junto a las constantes de tu aplicación en `src/lib/constants.ts`: ```tsx src/lib/constants.ts export type ActivityType = "run" | "cycle" | "swim"; @@ -537,9 +540,9 @@ export function getLevelProgress( } ``` -## Building the Dashboard +## Construyendo el Panel de Control -The dashboard aggregates all user stats. We fetch data server-side and derive progress bars from thresholds returned by the API. +El panel de control agrega todas las estadísticas del usuario. Obtenemos los datos del lado del servidor y derivamos barras de progreso de los umbrales devueltos por la API. ```tsx src/app/page.tsx [expandable] import { @@ -669,19 +672,19 @@ export default async function Dashboard() { } ``` -Note the `LogActivityDialog` wrapper around the button—we'll build that next. +Nota el wrapper `LogActivityDialog` alrededor del botón—lo construiremos a continuación. -## Logging Workouts +## Registrando Entrenamientos -The Log Workout button opens a dialog where users select their activity type, enter distance, and submit. This triggers the `logActivity` server action which sends the event to Trophy. +El botón Registrar Entrenamiento abre un diálogo donde los usuarios seleccionan su tipo de actividad, ingresan la distancia y envían. Esto activa la acción del servidor `logActivity` que envía el evento a Trophy. -First, install the required shadcn/ui components: +Primero, instala los componentes de shadcn/ui requeridos: ```bash npx shadcn@latest add dialog tabs input label select ``` -Then create the dialog component: +Luego crea el componente de diálogo: ```tsx components/log-activity-dialog.tsx [expandable] "use client"; @@ -829,9 +832,9 @@ export function LogActivityDialog({ children }: { children: React.ReactNode }) { } ``` -### User Context +### Contexto del Usuario -The dialog needs access to the current user ID. Create a context provider: +El diálogo necesita acceso al ID del usuario actual. Crea un proveedor de contexto: ```tsx components/user-provider.tsx [expandable] "use client"; @@ -884,7 +887,7 @@ export function UserProvider({ children }: { children: ReactNode }) { } ``` -Wrap your app with the provider in `layout.tsx`: +Envuelve tu aplicación con el proveedor en `layout.tsx`: ```tsx app/layout.tsx import { UserProvider } from "@/components/user-provider"; @@ -902,9 +905,9 @@ export default function RootLayout({ children }) { } ``` -### Helper Utilities +### Utilidades de Ayuda -The user context relies on helper functions for managing user identity in localStorage: +El contexto del usuario depende de funciones auxiliares para gestionar la identidad del usuario en localStorage: ```tsx lib/user.ts [expandable] const USER_ID_KEY = "trophy-fitness-user-id"; @@ -936,7 +939,7 @@ export function getUserName(): string | null { } ``` -For city-based leaderboards, we infer the user's city from their timezone: +Para las clasificaciones basadas en ciudad, inferimos la ciudad del usuario a partir de su zona horaria: ```tsx lib/city.ts [expandable] const TIMEZONE_TO_CITY: Record = { @@ -965,9 +968,9 @@ export function setStoredCity(city: string): void { } ``` -## Implementing Leaderboards +## Implementación de Clasificaciones -We'll build a tabbed interface that allows users to switch between activities (Run/Cycle/Swim) and scopes (Global vs. Local City). +Construiremos una interfaz con pestañas que permite a los usuarios cambiar entre actividades (Correr/Ciclismo/Natación) y ámbitos (Global vs. Ciudad Local). ```tsx src/app/leaderboards/page.tsx [expandable] "use client"; @@ -1048,9 +1051,9 @@ export default function LeaderboardsPage() { } ``` -## Building the Achievements Page +## Construcción de la Página de Logros -A dedicated space to show off badges is essential for long-term retention. We'll use the `stats.achievements` data to render a grid of badges, visually distinguishing between earned (colorful) and locked (grayscale) states. +Un espacio dedicado para mostrar insignias es esencial para la retención a largo plazo. Usaremos los datos de `stats.achievements` para renderizar una cuadrícula de insignias, distinguiendo visualmente entre estados conseguidos (a color) y bloqueados (escala de grises). ```tsx src/app/achievements/page.tsx [expandable] import { getUserStats, getUserIdFromCookies } from "@/app/actions"; @@ -1117,9 +1120,9 @@ export default async function AchievementsPage() { } ``` -## Building the Profile Page +## Construcción de la Página de Perfil -Finally, the profile page brings it all together. It shows the user's "Lifetime Stats," their current XP progression, and allows them to update their settings (like their city). +Finalmente, la página de perfil lo integra todo. Muestra las "Estadísticas de por Vida" del usuario, su progresión actual de XP y les permite actualizar su configuración (como su ciudad). ```tsx src/app/profile/page.tsx [expandable] import { getUserStats, getUserIdFromCookies } from "@/app/actions"; @@ -1196,9 +1199,9 @@ export default async function ProfilePage() { } ``` -## The Result +## El Resultado -You now have a fully functional fitness gamification loop! Users can log workouts across multiple sports, progress through points levels, earn badges, and compete on leaderboards. +¡Ahora tienes un circuito de gamificación de fitness completamente funcional! Los usuarios pueden registrar entrenamientos en múltiples deportes, progresar a través de niveles de Puntos, conseguir Logros y competir en Clasificaciones. -### What You've Built +### Lo que has construido -- **Multi-sport tracking** with normalized XP across running, cycling, and swimming -- **Weekly leaderboards** with global and city-based competition -- **Leveled progression** from Rookie to Pro -- **Achievement system** with milestone badges -- **Daily streaks** to drive retention +- **Seguimiento multideporte** con XP normalizado para correr, ciclismo y natación +- **Clasificaciones semanales** con competencia global y por ciudad +- **Progresión por niveles** desde Novato hasta Profesional +- **Sistema de logros** con insignias de hitos +- **Rachas diarias** para impulsar la retención -### Next Steps +### Próximos pasos -- **Connect fitness wearables** — Integrate with Strava, Apple Health, or Google Fit to auto-log workouts -- **Add social features** — Let users follow friends, compare stats, and share achievements -- **Build a mobile experience** — Convert to a PWA or wrap with Capacitor for app store distribution -- **Add push notifications** — Alert users when their streak is at risk or they've been passed on the leaderboard +- **Conecta dispositivos de fitness** — Integra con Strava, Apple Health o Google Fit para registrar entrenamientos automáticamente +- **Agrega funciones sociales** — Permite que los usuarios sigan a amigos, comparen estadísticas y compartan logros +- **Crea una experiencia móvil** — Convierte a PWA o envuelve con Capacitor para distribución en tiendas de aplicaciones +- **Agrega notificaciones push** — Alerta a los usuarios cuando su racha está en riesgo o han sido superados en la clasificación diff --git a/es/guides/gamified-study-platform.mdx b/es/guides/gamified-study-platform.mdx index cd25818..00e2128 100644 --- a/es/guides/gamified-study-platform.mdx +++ b/es/guides/gamified-study-platform.mdx @@ -1,14 +1,16 @@ --- -title: How to Build a Gamified Study Platform -description: Follow along as we build out a gamified study platform using Trophy with achievements, a daily streak, XP and leaderboards. -subtitle: Follow along as we build out a gamified study platform using Trophy. +title: Cómo Construir una Plataforma de Estudio Gamificada +description: Sigue el proceso mientras construimos una plataforma de estudio + gamificada usando Trophy con logros, una racha diaria, XP y clasificaciones. +subtitle: Sigue el proceso mientras construimos una plataforma de estudio + gamificada usando Trophy. noindex: true --- import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; -In this tutorial we'll build an example study platform using Trophy for gamification. If you want to just skip to the end then feel free to check out the [example repository](https://github.com/trophyso/example-study-platform) or the [live demo](https://study.examples.trophy.so). +En este tutorial construiremos una plataforma de estudio de ejemplo usando Trophy para la gamificación. Si prefieres ir directamente al resultado final, no dudes en consultar el [repositorio de ejemplo](https://github.com/trophyso/example-study-platform) o la [demostración en vivo](https://study.examples.trophy.so). -## Table of Contents +## Tabla de Contenidos -- [Tech Stack](#tech-stack) -- [Pre-requisites](#pre-requisites) -- [Setup & Installation](#setup-%26-installation) -- [Building the Flashcards Feature](#building-the-flashcards-feature) -- [Adding Basic Gamification](#adding-basic-gamification) -- [Adding an XP System](#adding-an-xp-system) -- [Adding Leaderboards](#adding-leaderboards) -- [Adding an Energy System](#adding-an-energy-system) -- [The Result](#the-result) +- [Stack Tecnológico](#tech-stack) +- [Requisitos Previos](#pre-requisites) +- [Configuración e Instalación](#setup-%26-installation) +- [Construyendo la Funcionalidad de Flashcards](#building-the-flashcards-feature) +- [Agregando Gamificación Básica](#adding-basic-gamification) +- [Agregando un Sistema de XP](#adding-an-xp-system) +- [Agregando Clasificaciones](#adding-leaderboards) +- [Agregando un Sistema de Energía](#adding-an-energy-system) +- [El Resultado](#the-result) -## Tech Stack +## Stack Tecnológico - [NextJS 15](https://nextjs.org/docs) (React 19) - [Shadcn/Ui](https://ui.shadcn.com) -- [Lucide](https://lucide.dev/icons) for iconography -- [Motion](https://motion.dev/) for animations -- [HTML5 Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) for sound effects -- [Trophy](https://trophy.so) for gamification +- [Lucide](https://lucide.dev/icons) para iconografía +- [Motion](https://motion.dev/) para animaciones +- [HTML5 Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) para efectos de sonido +- [Trophy](https://trophy.so) para gamificación -## Pre-requisites +## Requisitos Previos -- A Trophy account (don't worry we'll set this up as we go along...) +- Una cuenta de Trophy (no te preocupes, la configuraremos sobre la marcha...) -## Setup & Installation +## Configuración e Instalación - Want to skip the setup? Head straight to [the fun - part](#setting-up-flashcard-data). + ¿Quieres saltarte la configuración? Ve directamente a [la parte + divertida](#setting-up-flashcard-data). -First we need to create a new NextJS project: +Primero necesitamos crear un nuevo proyecto NextJS: ```bash npx create-next-app@latest ``` -Feel free to configure this new project however you like but for the purposes of this tutorial we'll pretty much stick with the defaults: +Configura este nuevo proyecto como prefieras, pero para los propósitos de este tutorial nos apegaremos principalmente a los valores predeterminados: ```bash What is your project named? my-app @@ -72,13 +74,13 @@ Would you like to use Turbopack for `next dev`? Yes Would you like to customize the import alias (`@/*` by default)? No ``` -Next, we'll initialize a new install of everyone's favourite UI library, shadcn/ui: +A continuación, inicializaremos una nueva instalación de la biblioteca de interfaz favorita de todos, shadcn/ui: ```bash npx shadcn@latest init ``` -I ran into a warning with React 19, which [looks to be a common issue](https://ui.shadcn.com/docs/react-19) when initializing with `npm`: +Me encontré con una advertencia con React 19, que [parece ser un problema común](https://ui.shadcn.com/docs/react-19) al inicializar con `npm`: ```bash It looks like you are using React 19. @@ -89,15 +91,15 @@ Some packages may fail to install due to peer dependency issues in npm (see http Use --legacy-peer-deps ``` -For the purposes of this tutorial I chose `--force` but you should choose whichever setting you feel suits your requirements. +Para los propósitos de este tutorial elegí `--force`, pero debes elegir la configuración que mejor se adapte a tus requisitos. -## Building the Flashcards Feature +## Construcción de la Funcionalidad de Tarjetas de Estudio -### Setting Up Flashcard Data +### Configuración de Datos de Tarjetas de Estudio -For the purposes of this tutorial, we're going to be using some simple types with an in-memory data store. In a production application you'd probably want to consider storing this information in a database. +Para los propósitos de este tutorial, vamos a usar algunos tipos simples con un almacén de datos en memoria. En una aplicación de producción probablemente querrías considerar almacenar esta información en una base de datos. -Here we'll have a very simple type that stores information about each flashcard where we'll use the `front` property to store questions that the student wants to learn the answer to, and the `back` property to store the answers to each question: +Aquí tendremos un tipo muy simple que almacena información sobre cada tarjeta de estudio donde usaremos la propiedad `front` para almacenar las preguntas cuyas respuestas el estudiante quiere aprender, y la propiedad `back` para almacenar las respuestas a cada pregunta: ```ts src/types/flashcard.ts export interface IFlashcard { @@ -107,7 +109,7 @@ export interface IFlashcard { } ``` -Then to get us started we'll store a few flashcards in memory centered around learning capital cities: +Luego, para comenzar, almacenaremos algunas tarjetas de estudio en memoria centradas en el aprendizaje de ciudades capitales: ```ts src/data.ts [expandable] import { IFlashcard } from "./types/flashcard"; @@ -216,17 +218,17 @@ export const flashcards: IFlashcard[] = [ ]; ``` -### Basic Flashcard Layout +### Diseño Básico de Tarjetas de Estudio -With some basic data set up, we need to add a way for users to flick through their flashcards. +Con algunos datos básicos configurados, necesitamos agregar una forma para que los usuarios naveguen por sus tarjetas de estudio. -For this we'll use the `carousel` and `card` components from shadcn/ui so we need to add these to our project: +Para esto usaremos los componentes `carousel` e `card` de shadcn/ui, por lo que necesitamos agregarlos a nuestro proyecto: ```bash npx shadcn@latest add carousel card ``` -Then, we'll create a new `` component that combines these into a working solution, specifying that we can pass along any list of `IFlashcard` objects as props +Luego, crearemos un nuevo componente `` que combine estos en una solución funcional, especificando que podemos pasar cualquier lista de objetos `IFlashcard` como props ```tsx src/app/flashcards.tsx [expandable] import { @@ -268,7 +270,7 @@ export default function Flashcards({ flashcards }: Props) { } ``` -Then we'll update our `page.tsx` file to display our `` component, passing in our example flashcard data: +Después actualizaremos nuestro archivo `page.tsx` para mostrar nuestro componente ``, pasando nuestros datos de ejemplo de tarjetas de estudio: ```tsx src/app/page.tsx import { flashcards } from "@/data"; @@ -283,7 +285,7 @@ export default function Home() { } ``` -At the end of this step, you should have a working flashcard UI that allows you to flick through each flashcard in our cities data set. +Al final de este paso, debes tener una interfaz de tarjetas de estudio funcional que te permita navegar por cada tarjeta en nuestro conjunto de datos de ciudades. -### Flipping Flashcards +### Voltear Tarjetas de Estudio -Now this is great, but it's not much use as a study app right now as there's no way to see if you got the answer right! We need to add a way to flip flashcards over and check our answer... +Ahora esto está genial, pero no es muy útil como aplicación de estudio en este momento, ya que no hay forma de ver si obtuviste la respuesta correcta. Necesitamos agregar una forma de voltear las tarjetas de estudio y verificar nuestra respuesta... -To make this simpler, we'll first create a `` component that will be responsible for all the logic for each flashcard: +Para simplificar esto, primero crearemos un componente `` que será responsable de toda la lógica para cada tarjeta de estudio: ```tsx src/app/flashcard.tsx [expandable] import { Card, CardContent } from "@/components/ui/card"; @@ -328,7 +330,7 @@ export default function Flashcard({ flashcard }: Props) { } ``` -Then we'll simplify our `` component to instead just render out a list of the individual `` components: +Luego simplificaremos nuestro componente `` para que en su lugar simplemente renderice una lista de los componentes individuales ``: ```tsx src/app/flashcards.tsx [expandable] import { @@ -359,13 +361,13 @@ export default function Flashcards({ flashcards }: Props) { } ``` -Now we're ready to add interactivity to each flashcard. Here's what we'll do: +Ahora estamos listos para agregar interactividad a cada tarjeta de estudio. Esto es lo que haremos: -- First, we'll add a `side` state variable that will hold the current side of the flashcard that's showing. -- Next, we'll add an `onClick()` callback to the `` component that will update the `side` state to `back` when clicked if the front of the card is currently showing. -- Finally, we'll conditional render the text in the `` based on the value of the `side` state variable. +- Primero, agregaremos una variable de estado `side` que contendrá el lado actual de la tarjeta de estudio que se está mostrando. +- A continuación, agregaremos un callback `onClick()` al componente `` que actualizará el estado `side` a `back` cuando se haga clic, si el frente de la tarjeta se está mostrando actualmente. +- Finalmente, renderizaremos condicionalmente el texto en el `` según el valor de la variable de estado `side`. -Here's the finished file: +Aquí está el archivo terminado: ```tsx src/app/flashcard.tsx [expandable] {10,12-16,21,24} import { Card, CardContent } from "@/components/ui/card"; @@ -401,13 +403,13 @@ export default function Flashcard({ flashcard }: Props) { } ``` -Then, we'll use [Motion](https://motion.dev) to add a neat flip animation to the card when we click on it. For this we first need to install the package into our project: +Luego, usaremos [Motion](https://motion.dev) para agregar una animación de volteo elegante a la tarjeta cuando hagamos clic en ella. Para esto, primero necesitamos instalar el paquete en nuestro proyecto: ```bash npm install motion ``` -If you think about it, when you flip a flashcard, you tend to do it in the Y-axis. So here we'll use a `` with a light spring animation in the y-axis to create the effect: +Si lo piensas bien, cuando volteas una tarjeta de estudio, tiendes a hacerlo en el eje Y. Así que aquí usaremos un `` con una animación de resorte ligera en el eje y para crear el efecto: ```tsx src/app/flashcard.tsx [expandable] {7-8,26-37} "use client"; @@ -458,10 +460,10 @@ export default function Flashcard({ flashcard }: Props) { } ``` -You'll notice we also added a couple of styles here. These do a couple of things: +Notarás que también agregamos un par de estilos aquí. Estos hacen un par de cosas: -- Ensure that when a `` is flipping, the 'back face' isn't visible during the animation with `backface-visibility: hidden;` -- As the `` component is a child of the ``, usually it would appear flat when it's parent rotates in 3D. Adding `transform-style: preserve-3d;` to the `` ensures it keeps it's 3D effect when it's parent animates. +- Asegúrate de que cuando un `` está girando, la 'cara posterior' no sea visible durante la animación con `backface-visibility: hidden;` +- Como el componente `` es hijo de ``, normalmente aparecería plano cuando su padre rota en 3D. Agregar `transform-style: preserve-3d;` al `` asegura que mantenga su efecto 3D cuando su padre se anima. ```css src/app/flashcard.module.css .backface-hidden { @@ -475,9 +477,9 @@ You'll notice we also added a couple of styles here. These do a couple of things } ``` -### A Flippin' Bug! +### ¡Un Error de Volteo! -Sweet! Our project is now starting to feel like a real study tool! However the keen eyed (or maybe not so keen...) will notice there's one major bug here. When we flip a card over, the answer on the back appears in reverse 😢... +¡Genial! Nuestro proyecto ahora está empezando a sentirse como una verdadera herramienta de estudio. Sin embargo, los observadores atentos (o quizás no tanto...) notarán que hay un error importante aquí. Cuando volteamos una tarjeta, la respuesta en el reverso aparece al revés 😢... -I you think about it, when you write a flashcard, you actually write the answer on the back in the opposite direction to the question on the front. +Si lo piensas bien, cuando escribes una tarjeta de estudio, en realidad escribes la respuesta en el reverso en dirección opuesta a la pregunta del frente. -And as we're using `motion` to literally flip over our card in the Y-axis, we need to make sure we write our answers backwards as well. +Y como estamos usando `motion` para literalmente voltear nuestra tarjeta en el eje Y, necesitamos asegurarnos de escribir nuestras respuestas al revés también. -First, we'll add a little CSS snippet to handle writing text backwards: +Primero, agregaremos un pequeño fragmento de CSS para manejar la escritura de texto al revés: ```css src/app/flascard.module.css {11-14} .backface-hidden { @@ -513,7 +515,7 @@ First, we'll add a little CSS snippet to handle writing text backwards: } ``` -Then we'll conditionally add this style to our card text based on which side of the card is showing: +Luego agregaremos condicionalmente este estilo al texto de nuestra tarjeta según qué lado de la tarjeta se esté mostrando: ```tsx src/app/flashcard.tsx ``` -Ok awesome. Now when we flip over a card the answer on the back should read in the right direction: +Perfecto. Ahora cuando volteemos una tarjeta, la respuesta en el reverso debería leerse en la dirección correcta: -### Tracking Progress +### Seguimiento del Progreso -The next step is to add some UI to show the user how many flashcards they've looked at, and how many in the set they have left. We'll use a simple progress bar to achieve this. +El siguiente paso es agregar una interfaz para mostrar al usuario cuántas tarjetas de estudio ha revisado y cuántas le quedan en el conjunto. Usaremos una barra de progreso simple para lograr esto. -Before we can start tracking this level of information, we need to set up tracking for a new state variable that holds the index of the flashcard the user is currently looking at. We'll use the [carousel api](https://ui.shadcn.com/docs/components/carousel#api) to hook into the functionality here and keep our state variable up to date: +Antes de comenzar a rastrear este nivel de información, necesitamos configurar el seguimiento de una nueva variable de estado que contenga el índice de la tarjeta de estudio que el usuario está viendo actualmente. Utilizaremos la [API del carrusel](https://ui.shadcn.com/docs/components/carousel#api) para integrarnos con esta funcionalidad y mantener nuestra variable de estado actualizada: ```tsx src/app/flashcards.tsx [expandable] {1,8,19-34,37} "use client"; @@ -594,13 +596,13 @@ export default function Flashcards({ flashcards }: Props) { } ``` -Then we need to add the `progress` component from shadcn/ui to our project: +Luego necesitamos agregar el componente `progress` de shadcn/ui a nuestro proyecto: ```bash npx shadcn@latest add progress ``` -Finally we can add a progress bar above the carousel: +Finalmente podemos agregar una barra de progreso encima del carrusel: ```tsx [expandable] {13,38-39,49} "use client"; @@ -656,7 +658,7 @@ export default function Flashcards({ flashcards }: Props) { } ``` -Sweet! Now things are really starting to come together. +¡Excelente! Ahora las cosas realmente están empezando a tomar forma. -Now the real fun begins... +Ahora comienza la verdadera diversión... -## Adding Basic Gamification +## Agregando Gamificación Básica -In this section we'll be adding the following gamification elements to the flashcard project: +En esta sección agregaremos los siguientes elementos de gamificación al proyecto de tarjetas de estudio: -- Achievements for completing: - - 10 flashcards - - 50 flashcards - - 100 flashcards - - 250 flashcards -- Daily streak for completing at least one flashcard a day. -- Automated emails for: - - Achievement unlocked - - Weekly progress summaries +- Logros por completar: + - 10 tarjetas de estudio + - 50 tarjetas de estudio + - 100 tarjetas de estudio + - 250 tarjetas de estudio +- Racha diaria por completar al menos una tarjeta de estudio al día. +- Correos electrónicos automatizados para: + - Logro desbloqueado + - Resúmenes de progreso semanales -This might sound like a lot, but would you be surprised if I told you could build all this in less than 10 lines of code? +Esto puede sonar como mucho, ¿pero te sorprendería si te dijera que puedes construir todo esto en menos de 10 líneas de código? -Yup, you can. Trophy makes it super easy to build these features with a few lines of integration code. +Así es. Trophy hace que sea muy fácil construir estas funcionalidades con unas pocas líneas de código de integración. -So, let's get started... +Entonces, comencemos... -### How Trophy Works +### Cómo Funciona Trophy -First off, we need a new [Trophy account](https://app.trophy.so/sign-up). Then we can start to piece together the various parts of our gamification experience. +Primero, necesitamos una nueva [cuenta de Trophy](https://app.trophy.so/sign-up). Luego podemos empezar a ensamblar las diferentes partes de nuestra experiencia de gamificación. -In Trophy, [Metrics](/platform/metrics) represent different interactions users can make and can drive features like [Achievements](/platform/achievements), [Streaks](/platform/streaks) and [Emails](/platform/emails). +En Trophy, las [Métricas](/platform/metrics) representan diferentes interacciones que los usuarios pueden realizar y pueden impulsar funcionalidades como [Logros](/platform/achievements), [Rachas](/platform/streaks) y [Correos electrónicos](/platform/emails). ```mermaid flowchart TD @@ -712,32 +714,32 @@ flowchart TD B-->F ``` -In Trophy we track user interactions by sending [Events](/platform/events) from our code to Trophy against a specific metric. +En Trophy rastreamos las interacciones del usuario enviando [Eventos](/platform/events) desde nuestro código a Trophy contra una métrica específica. -When events are recorded for a specific user, any achievements linked to the specified metric will be unlocked if the requirements are met, daily streaks will be automatically calculated and kept up to date, points are awarded, and leaderboards are updated. +Cuando se registran eventos para un usuario específico, cualquier logro vinculado a la métrica especificada se desbloqueará si se cumplen los requisitos, las rachas diarias se calcularán automáticamente y se mantendrán actualizadas, se otorgarán puntos y se actualizarán las clasificaciones. -This is what makes building gamified experiences with Trophy so easy, is does all the work for you behind the scenes. +Esto es lo que hace que construir experiencias gamificadas con Trophy sea tan fácil: hace todo el trabajo por ti detrás de escena. -Recording an event against a metric for a specific user makes use of Trophy's [metric event API](/api-reference/endpoints/metrics/send-a-metric-change-event), which in practice looks like this: +Registrar un evento contra una métrica para un usuario específico hace uso de la [API de eventos de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event) de Trophy, que en la práctica se ve así: -That's how in just a few lines of code we can power a whole suite of gamification features. However, before we can start sending events, we need to set up our new Trophy account. +Así es como con solo unas pocas líneas de código podemos impulsar todo un conjunto de funciones de gamificación. Sin embargo, antes de poder empezar a enviar eventos, necesitamos configurar nuestra nueva cuenta de Trophy. -### Setting Up Trophy +### Configurando Trophy -Here's how we'll setup our Trophy account for our study app: +Así es como configuraremos nuestra cuenta de Trophy para nuestra aplicación de estudio: -- First, we'll set up a _Flashcards Flipped_ metric -- Next, we'll setup achievements linked to this metric -- Then, we'll configure a daily streak linked to this metric -- Finally, we'll configure automated email sequences for this metric +- Primero, configuraremos una métrica _Tarjetas Volteadas_ +- Luego, configuraremos logros vinculados a esta métrica +- Después, configuraremos una racha diaria vinculada a esta métrica +- Finalmente, configuraremos secuencias de correo electrónico automatizadas para esta métrica -Let's get into it... +Comencemos... - - Head into the Trophy [metrics page](https://app.trophy.so/metrics) and create a new metric, making sure to specify `flashcards-flipped` as the metric `key`. This is what we'll use to reference the metric in our code when sending events. + + Dirígete a la [página de métricas](https://app.trophy.so/metrics) de Trophy y crea una nueva métrica, asegurándote de especificar `flashcards-flipped` como el `key` de la métrica. Esto es lo que usaremos para referenciar la métrica en nuestro código al enviar eventos. -### Integrating Trophy SDK +### Integrar el SDK de Trophy -First we need to grab our API key from the Trophy [integration page](https://app.trophy.so/integration) and add this as a **server-side only** environment variable. +Primero necesitamos obtener nuestra clave API de la [página de integración](https://app.trophy.so/integration) de Trophy y añadirla como una variable de entorno **únicamente del lado del servidor**. - Make sure you **don't** expose your API key in client-side code + Asegúrate de **no** exponer tu clave API en código del lado del cliente ```bash .env.local TROPHY_API_KEY='*******' ``` -Next, we'll install the Trophy SDK: +A continuación, instalaremos el SDK de Trophy: ```bash npm install @trophyso/node ``` -Then whenever a user views a flashcard, we simply send an event to Trophy with details of the user that performed the action (which we'll mock), and the number of flashcards they viewed (1 in this case). +Luego, cada vez que un usuario vea una tarjeta de estudio, simplemente enviamos un evento a Trophy con los detalles del usuario que realizó la acción (que simularemos), y el número de tarjetas de estudio que visualizó (1 en este caso). -In NextJS we'll do this with a server action that makes the call to Trophy to send the event, and we'll call this action when the user moves to the next flashcard in the carousel. +En NextJS haremos esto con una server action que realiza la llamada a Trophy para enviar el evento, y llamaremos a esta acción cuando el usuario pase a la siguiente tarjeta de estudio en el carrusel. -First, create the server action: +Primero, crea la server action: ```tsx src/app/actions.ts [expandable] "use server"; @@ -890,7 +889,7 @@ export async function viewFlashcard(): Promise { } ``` -Then call it when the user views the next flashcard: +Luego llámala cuando el usuario visualice la siguiente tarjeta de estudio: ```tsx src/app/flashcards.tsx [expandable] {14,36-37} "use client"; @@ -950,7 +949,7 @@ export default function Flashcards({ flashcards }: Props) { } ``` -We can validate this is working by checking the Trophy [dashboard](https://app.trophy.so) which should show our first user tracked in the _Top Users_ table: +Podemos validar que esto funciona verificando el [panel de control](https://app.trophy.so) de Trophy, que debería mostrar nuestro primer usuario registrado en la tabla _Mejores Usuarios_: -Next, we'll add some UI to show off our gamification features in practice. +A continuación, añadiremos algo de interfaz de usuario para mostrar nuestras funciones de gamificación en la práctica. -### Adding Gamification UX +### Añadiendo UX de Gamificación -The response to the API call that we make to track events in Trophy helpfully gives us back any changes to the users progress as a result of that event: +La respuesta a la llamada de API que hacemos para rastrear eventos en Trophy nos devuelve útilmente cualquier cambio en el progreso del usuario como resultado de ese evento: -This includes: +Esto incluye: -- The `achievements` array which is a list of newly unlocked achievements as a result of the event. -- The `currentStreak` object which is the users most up to date streak data after the event has taken place. +- El array `achievements` que es una lista de logros recién desbloqueados como resultado del evento. +- El objeto `currentStreak` que son los datos de racha más actualizados del usuario después de que el evento haya tenido lugar. -This makes it really easy for us to react to changes in the users progress and do whatever we want. In this example, each time Trophy tells us a user has unlocked a new achievement, or extended their streak we'll: +Esto nos facilita reaccionar a los cambios en el progreso del usuario y hacer lo que queramos. En este ejemplo, cada vez que Trophy nos indique que un usuario ha desbloqueado un nuevo logro, o ha extendido su racha, haremos lo siguiente: -- Show a pop-up toast -- Play a sound effect +- Mostrar una notificación emergente tipo toast +- Reproducir un efecto de sonido -#### Achievement Unlocked Toasts +#### Notificaciones de Logro Desbloqueado -First, we'll show a toast when users unlock new achievements. For this we need to add the `sonner` component from shadcn/ui to our project: +Primero, mostraremos una notificación toast cuando los usuarios desbloqueen nuevos logros. Para esto necesitamos añadir el componente `sonner` de shadcn/ui a nuestro proyecto: ```bash npx shadcn@latest add sonner ``` -Then we'll create the `` UI component we need to display toasts and a `toast()` utility function to trigger them: +Luego crearemos el componente de UI `` que necesitamos para mostrar las notificaciones toast y una función de utilidad `toast()` para activarlas: ```tsx src/lib/toast.tsx [expandable] "use client"; @@ -1056,7 +1055,7 @@ export function Toast(props: ToastProps) { } ``` -Next, we need to update the `page.tsx` file with the main `` component. This is the component from `sonner` which is responsible for displaying our toasts on the screen when we trigger them: +A continuación, necesitamos actualizar el archivo `page.tsx` con el componente principal ``. Este es el componente de `sonner` que es responsable de mostrar nuestras notificaciones toast en la pantalla cuando las activamos: ```tsx src/app/page.tsx {3,9} import { flashcards } from "@/data"; @@ -1073,7 +1072,7 @@ export default function Home() { } ``` -Next, to make sure NextJS shows our badges, we need to configure Trophy's image host as a trusted domain. If you use custom badge URLs on your own domain or CDN, add those host names here too, or use a plain `` and skip remote patterns. +Luego, para asegurar que NextJS muestre nuestras insignias, necesitamos configurar el host de imágenes de Trophy como un dominio de confianza. Si usas URLs de insignias personalizadas en tu propio dominio o CDN, añade esos nombres de host aquí también, o usa un `` simple y omite los patrones remotos. ```ts next.config.ts {4-11} import type { NextConfig } from "next"; @@ -1092,7 +1091,7 @@ const nextConfig: NextConfig = { export default nextConfig; ``` -And finally, we update our `` component to read the response from Trophy and display toasts for any unlocked achievements: +Y finalmente, actualizamos nuestro componente `` para leer la respuesta de Trophy y mostrar notificaciones toast para cualquier logro desbloqueado: ```tsx src/app/flashcards.tsx [expandable] {15,33,38-56} "use client"; @@ -1169,7 +1168,7 @@ export default function Flashcards({ flashcards }: Props) { } ``` -To see this in action, all we need to do is unlock our first achievement by viewing 10 flashcards: +Para ver esto en acción, todo lo que necesitamos hacer es desbloquear nuestro primer logro viendo 10 tarjetas de estudio: -#### Streak Extended Toasts +#### Notificaciones de Racha Extendida -We'll use the same methods to show similar toasts when a user extends their streak. As we've set up a daily streak in Trophy, this will trigger the first time a user views a flashcard each day. +Usaremos los mismos métodos para mostrar notificaciones similares cuando un usuario extienda su racha. Como hemos configurado una racha diaria en Trophy, esto se activará la primera vez que un usuario vea una tarjeta de estudio cada día. - If we wanted to experiment with a different streak cadence, like a weekly - streak, then none of this code changes - the great thing about Trophy is - everything can be configured from within the dashboard and doesn't require any - code changes. + Si quisiéramos experimentar con una cadencia de racha diferente, como una + racha semanal, entonces nada de este código cambia - lo genial de Trophy es + que todo se puede configurar desde el panel de control y no requiere ningún + cambio de código. -All we need to do is read the `currentStreak.extended` property from Trophy and handle showing another toast with a new `if` statement: +Todo lo que necesitamos hacer es leer la propiedad `currentStreak.extended` de Trophy y manejar la visualización de otro toast con una nueva declaración `if`: ```tsx src/app/flashcards.tsx [expandable] {56-63} "use client"; @@ -1278,7 +1277,7 @@ export default function Flashcards({ flashcards }: Props) { } ``` -Now the first time a user views a flashcard each day, they'll see one of our streak extended toasts: +Ahora, la primera vez que un usuario vea una flashcard cada día, verá uno de nuestros toasts de racha extendida: -#### Sound Effects +#### Efectos de Sonido -Gamification is first and foremost about increasing user retention. The best way to do this is to make the features we build as engaging as possible. A great way to do this that's often overlooked is with sound effects. +La gamificación se trata principalmente de aumentar la retención de usuarios. La mejor forma de hacerlo es hacer que las funcionalidades que construimos sean lo más atractivas posible. Una excelente manera de lograrlo que a menudo se pasa por alto es mediante efectos de sonido. -Here we'll add two distinct sound effects for each case where we show a toast to further engage the user. This helps distinguish the different toasts and helps users build up an expectation of what each sound means. +Aquí agregaremos dos efectos de sonido distintos para cada caso en el que mostremos un toast para involucrar aún más al usuario. Esto ayuda a distinguir los diferentes toasts y ayuda a los usuarios a construir una expectativa de lo que significa cada sonido. -For this we'll make use of the [HTML5 Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) and use sounds from [freesounds.org](https://freesounds.org). +Para esto utilizaremos el [API de Audio HTML5](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) y usaremos sonidos de [freesounds.org](https://freesounds.org). - The sounds used in this example are in the repository in the `public/sounds` - directory. Feel free to use them or pick others you like! + Los sonidos utilizados en este ejemplo están en el repositorio en el directorio `public/sounds`. ¡Siéntete libre de usarlos o elegir otros que te gusten! -To load the sound files into our application, we'll create a ref for each one, then we simply call the `play()` method on each when we want to actually play the sound: +Para cargar los archivos de sonido en nuestra aplicación, crearemos un ref para cada uno, luego simplemente llamamos al método `play()` en cada uno cuando queramos reproducir el sonido: ```tsx src/app/flashcards.tsx [expandable] {25-31,53-57,73-77} "use client"; @@ -1411,11 +1409,11 @@ export default function Flashcards({ flashcards }: Props) { } ``` -#### Displaying Study Journey +#### Mostrando el Progreso de Estudio -The last piece of UI we'll add will be a dialog to display the user's study progress, including their streak and any achievements they've unlocked so far. +La última pieza de UI que agregaremos será un diálogo para mostrar el progreso de estudio del usuario, incluyendo su racha y cualquier logro que haya desbloqueado hasta ahora. -First, we'll add a couple of new server actions to fetch the users streak and achievements from Trophy. As we're using a daily streak here, we'll fetch the last 14 days of streak data from Trophy to give us enough to work with in our UI: +Primero, agregaremos un par de nuevas acciones de servidor para obtener la racha y logros del usuario desde Trophy. Como estamos usando una racha diaria aquí, obtendremos los últimos 14 días de datos de racha de Trophy para tener suficiente información con la que trabajar en nuestra UI: ```tsx src/app/actions.ts [expandable] {6-7,10-11,24,33,45-58,60-73} "use server"; @@ -1493,7 +1491,7 @@ export async function getStreak(): Promise { } ``` -Then we'll call these actions on the server when the page is requested: +Luego llamaremos a estas acciones en el servidor cuando se solicite la página: ```tsx src/app/page.tsx {7-8} import { flashcards } from "@/data"; @@ -1514,19 +1512,19 @@ export default async function Home() { } ``` -Then we'll create a new `` component that takes in the achievements and streak data as props and renders it nicely in a dialog. Before adding this we need to add the shadcn/ui `dialog` component to our project, and while we're at it, we'll add the `seperator` component too: +Luego crearemos un nuevo componente `` que reciba los datos de logros y racha como props y los renderice de forma atractiva en un diálogo. Antes de agregar esto, necesitamos añadir el componente `dialog` de shadcn/ui a nuestro proyecto, y ya que estamos, también añadiremos el componente `seperator`: ```bash npx shadcn@latest add dialog separator ``` -We'll also need `dayjs` to help us with displaying dates nicely as well: +También necesitaremos `dayjs` para ayudarnos a mostrar las fechas de forma clara: ```bash npm i dayjs ``` -Now we have everything we need to build our study journey component: +Ahora tenemos todo lo que necesitamos para construir nuestro componente de trayectoria de estudio: ```tsx src/app/study-journey.tsx [expandable] import { Separator } from "@/components/ui/separator"; @@ -1710,7 +1708,7 @@ export default function StudyJourney({ achievements, streak }: Props) { } ``` -Finally, we simply add this component to our page and we should be good to go: +Finalmente, simplemente agregamos este componente a nuestra página y deberíamos estar listos: ```tsx src/app/page.tsx {4,13} import { flashcards } from "@/data"; @@ -1744,13 +1742,13 @@ export default async function Home() { > -## Adding an XP System +## Añadiendo un Sistema de XP -Now let's take our study platform to the next level by adding an XP (points) system. This will give users a sense of progression and reward them for their study activity. +Ahora llevemos nuestra plataforma de estudio al siguiente nivel añadiendo un sistema de XP (puntos). Esto dará a los usuarios una sensación de progreso y los recompensará por su actividad de estudio. -### Setting Up Points in Trophy +### Configurando Puntos en Trophy -First, head to the Trophy [points page](https://app.trophy.so/points) and create a new points system. We'll call ours "XP" with the key `points`. +Primero, dirígete a la [página de puntos](https://app.trophy.so/points) de Trophy y crea un nuevo sistema de puntos. Llamaremos al nuestro "XP" con la clave `points`. -Configure your points system to award XP for viewing flashcards. You can also set up bonus XP for streak milestones. +Configura tu sistema de puntos para otorgar XP por ver tarjetas de estudio. También puedes configurar XP de bonificación por hitos de racha. -### Integrating Points API +### Integrando la API de Puntos -Add new server actions to fetch points data: +Agrega nuevas acciones de servidor para obtener los datos de puntos: ```tsx src/app/actions.ts [expandable] const POINTS_SYSTEM_KEY = "points"; @@ -1820,9 +1818,9 @@ export async function getPointsSummary( } ``` -### Creating a Points Context +### Creando un Contexto de Puntos -To manage points state across the app, create a React context: +Para gestionar el estado de puntos en toda la aplicación, crea un contexto de React: ```tsx src/contexts/UserPointsContext.tsx [expandable] "use client"; @@ -1913,9 +1911,9 @@ export function useUserPoints() { } ``` -### Building the Points Center UI +### Construyendo la UI del Centro de Puntos -Create a points display component that shows the user's XP with a satisfying number animation: +Crea un componente de visualización de puntos que muestre los XP del usuario con una animación de números satisfactoria: ```tsx src/app/user-center/points-center/points-center.tsx [expandable] import { @@ -1976,9 +1974,9 @@ export default function PointsCenter() { } ``` -### Updating Points on Flashcard View +### Actualización de Puntos en la Vista de Tarjetas -Finally, re-fetch points whenever a user views a flashcard to show real-time XP updates: +Finalmente, vuelve a consultar los puntos cada vez que un usuario visualice una tarjeta para mostrar actualizaciones de XP en tiempo real: ```tsx src/app/flashcards.tsx {4,8,45} import { useUserPoints } from "@/contexts/UserPointsContext"; @@ -2003,7 +2001,7 @@ export default function Flashcards({ flashcards }: Props) { } ``` -Now when users view flashcards, they'll see their XP update in real-time with a satisfying animation! +¡Ahora cuando los usuarios visualicen tarjetas, verán sus XP actualizarse en tiempo real con una animación satisfactoria! -## Adding Leaderboards +## Añadir Clasificaciones -Nothing drives engagement like friendly competition. Let's add a leaderboard to show how users stack up against each other. +Nada impulsa el compromiso como la competencia amistosa. Añadamos una clasificación para mostrar cómo se comparan los usuarios entre sí. -### Setting Up Leaderboards in Trophy +### Configuración de Clasificaciones en Trophy -Head to the Trophy [leaderboards page](https://app.trophy.so/leaderboards) and create a new leaderboard. We'll create a daily leaderboard with the key `daily-champions` that ranks users by flashcards viewed. +Dirígete a la [página de clasificaciones](https://app.trophy.so/leaderboards) de Trophy y crea una nueva clasificación. Crearemos una clasificación diaria con la clave `daily-champions` que clasifique a los usuarios por tarjetas visualizadas. -### Leaderboard API Integration +### Integración del API de Clasificación -Add server actions for fetching leaderboard data: +Añade acciones del servidor para obtener datos de la clasificación: ```tsx src/app/actions.ts [expandable] const LEADERBOARD_KEY = "daily-champions"; @@ -2082,9 +2080,9 @@ export async function getUserLeaderboard( } ``` -### Building the Leaderboard UI +### Construcción de la UI de Clasificación -Create a leaderboard component that displays rankings with pagination: +Crea un componente de clasificación que muestre las clasificaciones con paginación: ```tsx src/app/leaderboard-center/leaderboard-center.tsx [expandable] import { @@ -2140,19 +2138,19 @@ export default function LeaderboardCenter() { } ``` -The leaderboard automatically updates as users study, creating a dynamic competitive environment that encourages daily engagement. +La clasificación se actualiza automáticamente a medida que los usuarios estudian, creando un entorno competitivo dinámico que fomenta el compromiso diario. -## Adding an Energy System +## Añadir un Sistema de Energía -Energy (or "lives") systems are a proven retention mechanic that encourages users to return throughout the day. Let's add one to our study platform. +Los sistemas de energía (o "vidas") son una mecánica de retención comprobada que anima a los usuarios a regresar durante el día. Añadamos uno a nuestra plataforma de estudio. -### Setting Up Energy in Trophy +### Configuración de Energía en Trophy -Trophy's points system is flexible enough to handle energy mechanics. Create a new points system with the key `energy` and configure it with: +El sistema de puntos de Trophy es lo suficientemente flexible para manejar mecánicas de energía. Crea un nuevo sistema de puntos con la clave `energy` y configúralo con: -- A maximum cap (e.g., 5 energy) -- Automatic regeneration rules -- Costs per action (e.g., -1 energy per flashcard session) +- Un límite máximo (p. ej., 5 de energía) +- Reglas de regeneración automática +- Costos por acción (p. ej., -1 de energía por sesión de tarjeta) -### Energy API Integration +### Integración de la API de energía -Add server actions for energy management: +Agrega acciones del servidor para la gestión de energía: ```tsx src/app/actions.ts [expandable] const ENERGY_SYSTEM_KEY = "energy"; @@ -2199,9 +2197,9 @@ export async function getUserEnergy( } ``` -### Creating an Energy Context +### Creación de un contexto de energía -Similar to points, create a context for managing energy state: +Similar a los puntos, crea un contexto para gestionar el estado de energía: ```tsx src/contexts/UserEnergyContext.tsx [expandable] "use client"; @@ -2267,9 +2265,9 @@ export function useUserEnergy() { } ``` -### Building the Energy UI +### Construcción de la interfaz de energía -Create an energy display that shows remaining energy with visual indicators: +Crea una visualización de energía que muestre la energía restante con indicadores visuales: ```tsx src/app/energy-center/energy-center.tsx [expandable] import { useUserEnergy } from "@/contexts/UserEnergyContext"; @@ -2293,22 +2291,21 @@ export default function EnergyCenter() { } ``` -With energy in place, users have a reason to return multiple times throughout the day as their energy regenerates, boosting your daily active users. +Con la energía implementada, los usuarios tienen un motivo para regresar varias veces al día a medida que su energía se regenera, aumentando tus usuarios activos diarios. -## The Result +## El resultado -Congrats! If you're reading this you made it to the end of the tutorial and built yourself a fully-functioning study platform. Of course there's loads more we could do to this, so here's a few ideas: +¡Felicitaciones! Si estás leyendo esto, llegaste al final del tutorial y construiste una plataforma de estudio completamente funcional. Por supuesto, hay mucho más que podríamos hacer con esto, así que aquí hay algunas ideas: -- Persist flashcards to a database -- Create multiple flashcard sets for other topics -- Add authentication -- Allow users to create their own flashcard sets +- Persistir las tarjetas de estudio en una base de datos +- Crear múltiples conjuntos de tarjetas de estudio para otros temas +- Agregar autenticación +- Permitir a los usuarios crear sus propios conjuntos de tarjetas de estudio - If you had fun or think you learned something along the way then give the repo - a star on [GitHub](https://github.com/trophyso/example-study-platform)! + Si te divertiste o crees que aprendiste algo en el camino, ¡dale una estrella al repositorio en [GitHub](https://github.com/trophyso/example-study-platform)! -## Get Support +## Obtener soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/guides/how-to-build-a-leaderboards-feature.mdx b/es/guides/how-to-build-a-leaderboards-feature.mdx index 3bf8686..8b10790 100644 --- a/es/guides/how-to-build-a-leaderboards-feature.mdx +++ b/es/guides/how-to-build-a-leaderboards-feature.mdx @@ -1,7 +1,9 @@ --- -title: How To Build A Leaderboards Feature -description: Learn how to use Trophy to add a leaderboards feature to your web or mobile app. -subtitle: Learn how to use Trophy to add a leaderboards feature to your web or mobile app. +title: Cómo Crear una Función de Clasificaciones +description: Aprende a usar Trophy para añadir una función de clasificaciones a + tu aplicación web o móvil. +subtitle: Aprende a usar Trophy para añadir una función de clasificaciones a tu + aplicación web o móvil. noindex: true --- @@ -12,28 +14,26 @@ import LeaderboardRankingsResponse from "/snippets/leaderboard-rankings-response import UserLeaderboardRankingsRequest from "/snippets/user-leaderboard-rankings-request.mdx"; import UserLeaderboardRankingsResponse from "/snippets/user-leaderboard-rankings-response.mdx"; -The guide outlines the full process of adding a leaderboards feature to your web or mobile app using Trophy. +Esta guía describe el proceso completo para añadir una función de clasificaciones a tu aplicación web o móvil utilizando Trophy. -For illustration purposes we'll use the example of a study platform that uses a daily leaderboard to create friendly competition around viewing flashcards. +Para fines ilustrativos, usaremos el ejemplo de una plataforma de estudio que utiliza una clasificación diaria para crear competencia amistosa en torno a la visualización de tarjetas de estudio. - 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). + Para ver un ejemplo completamente funcional de esto en la práctica, consulta la [demostración en vivo](https://examples.trophy.so) o el [repositorio de github](https://github.com/trophyso/example-study-platform/tree/demo). -## Pre-requisites +## Requisitos previos -- A [Trophy](https://app.trophy.so/sign-up) account -- About 10 minutes +- Una cuenta de [Trophy](https://app.trophy.so/sign-up) +- Aproximadamente 10 minutos -## Trophy Setup +## Configuración de Trophy -In Trophy, [Metrics](/platform/metrics) are the building blocks of gamification and model the different interactions users make with your product. +En Trophy, las [Métricas](/platform/metrics) son los componentes fundamentales de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. -In this guide the interaction we're interested in is `flashcards-viewed`, but you can create a metric that best represents the interaction you want to build leaderboards around. +En esta guía, la interacción que nos interesa es `flashcards-viewed`, pero puedes crear una métrica que mejor represente la interacción en torno a la cual deseas crear clasificaciones. -In the Trophy dashboard, head to the [metrics page](https://app.trophy.so/metrics) and create a metric. +En el panel de Trophy, dirígete a la [página de métricas](https://app.trophy.so/metrics) y crea una métrica. -Once you've created your metric, head to the [leaderboards page](https://app.trophy.so/leaderboards) and create the leaderboards you want. You can find all the details on the types of leaderboards and the different use cases in the [leaderboards docs](/platform/leaderboards). +Una vez que hayas creado tu métrica, dirígete a la [página de clasificaciones](https://app.trophy.so/leaderboards) y crea las clasificaciones que desees. Puedes encontrar todos los detalles sobre los tipos de clasificaciones y los diferentes casos de uso en la [documentación de clasificaciones](/platform/leaderboards). -For the purposes of this guide we've set up a daily leaderboard that tracks the total XP a user earns by viewing flashcards. +Para los fines de esta guía, hemos configurado una clasificación diaria que rastrea el total de XP que un usuario gana al ver tarjetas de estudio. - For a full guide on adding an XP feature to your web or mobile app, check out - our [full guide](/guides/how-to-build-an-xp-feature). + Para una guía completa sobre cómo agregar una función de XP a tu aplicación web o móvil, consulta + nuestra [guía completa](/guides/how-to-build-an-xp-feature). -In Trophy you track user interactions by sending [Events](/platform/events) from your code to Trophy APIs against a specific metric. +En Trophy rastreas las interacciones de los usuarios enviando [Eventos](/platform/events) desde tu código a las API de Trophy contra una métrica específica. -When events are recorded for a specific user, Trophy automatically updates their ranking in each leaderboard they are a part of. +Cuando se registran eventos para un usuario específico, Trophy actualiza automáticamente su posición en cada clasificación de la que forma parte. -This is what makes building gamified experiences with Trophy so easy, it does all the work for you behind the scenes. +Esto es lo que hace que construir experiencias gamificadas con Trophy sea tan fácil: hace todo el trabajo por ti en segundo plano. -## Installing Trophy SDK +## Instalando el SDK de Trophy -To interact with Trophy from your code you'll use the Trophy SDK available in most major [programming languages](/api-reference/client-libraries). +Para interactuar con Trophy desde tu código, utilizarás el SDK de Trophy disponible en la mayoría de los principales [lenguajes de programación](/api-reference/client-libraries). -Install the Trophy SDK: +Instala el SDK de Trophy: -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. +A continuación, obtén tu clave API desde la [página de integración](https://app.trophy.so/integration) de Trophy y agrégala como una variable de entorno **exclusivamente del lado del servidor**. ```bash TROPHY_API_KEY='*******' ``` - Make sure you **don't** expose your API key in client-side code. + Asegúrate de **no** exponer tu clave API en código del lado del cliente. -## Tracking User Interactions +## Rastreando Interacciones de Usuarios -To track an event (user interaction) against your metric, use the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event). +Para rastrear un evento (interacción de usuario) contra tu métrica, utiliza la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event). -The response to this API call is the complete set of changes to any features you've built with Trophy, including any changes to their ranking in any leaderboards they are a part of. +La respuesta a esta llamada API es el conjunto completo de cambios en cualquier función que hayas construido con Trophy, incluyendo cualquier cambio en su posición en cualquier clasificación de la que formen parte. {/* vale off */} @@ -130,7 +130,7 @@ The response to this API call is the complete set of changes to any features you {/* vale on */} -Validate this is working by checking the Trophy [dashboard](https://app.trophy.so). +Valida que esto funcione revisando el [panel de control](https://app.trophy.so) de Trophy. -## Displaying Leaderboards +## Mostrando Clasificaciones -You have a number of options for displaying leaderboards in your application. Here we'll look at the most common options. +Tienes varias opciones para mostrar clasificaciones en tu aplicación. Aquí veremos las opciones más comunes. -### Pop-up Notifications +### Notificaciones Emergentes -We can use the response of the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event) to show pop-up notifications (or 'toasts') when users move up the rankings. +Podemos usar la respuesta de la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event) para mostrar notificaciones emergentes (o 'toasts') cuando los usuarios suban en las clasificaciones. -Here's an example of this in action: +Aquí hay un ejemplo de esto en acción: ```ts Leaderboard Rank Up Pop-up // Sends event to Trophy @@ -175,43 +175,43 @@ if (leaderboard.rank > leaderboard.previousRank) { ``` - 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. + Si quieres reproducir efectos de sonido, usa la [API de Audio + HTML5](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) y siéntete + libre de usar estos [archivos de + audio](https://github.com/trophyso/example-study-platform/tree/demo/public/sounds) + que recomendamos. -### Displaying Leaderboard Rankings +### Mostrar Clasificaciones de la Clasificación -To fetch a leaderboard and its most up to date rankings, use the [leaderboard API](/api-reference/endpoints/leaderboards/get-leaderboard). +Para obtener una clasificación y sus rankings más actualizados, usa la [API de clasificaciones](/api-reference/endpoints/leaderboards/get-leaderboard). - You can also use the `run` parameter with the date of the specific past 'run' - of a leaderboard you want to fetch data for. + También puedes usar el parámetro `run` con la fecha de la 'ejecución' pasada específica + de una clasificación para la que deseas obtener datos. -Here's an example of the data returned from the leaderboard API: +Aquí hay un ejemplo de los datos devueltos por la API de clasificaciones: -### Displaying User Rank History +### Mostrar Historial de Ranking del Usuario -Use the [user leaderboard API](/api-reference/endpoints/users/get-a-users-leaderboard) to fetch data on how a specific user's rank has changed over time in a particular leaderboard. +Usa la [API de clasificación de usuario](/api-reference/endpoints/users/get-a-users-leaderboard) para obtener datos sobre cómo ha cambiado el ranking de un usuario específico a lo largo del tiempo en una clasificación particular. -Here's an example of the data returned from the user leaderboard rankings API which includes the users current rank in the `rank` attribute and an array of previous ranks in the `history` attribute: +Aquí hay un ejemplo de los datos devueltos por la API de rankings de clasificación de usuario que incluye el ranking actual del usuario en el atributo `rank` y un array de rankings anteriores en el atributo `history`: -## Analytics +## Analítica -The [leaderboards page](https://app.trophy.so/achievements) in Trophy shows how many users are actively participating in a leaderboard, as well as a measure of competitiveness based on how many rank changes are occurring. +La [página de clasificaciones](https://app.trophy.so/achievements) en Trophy muestra cuántos usuarios están participando activamente en una clasificación, así como una medida de competitividad basada en cuántos cambios de ranking están ocurriendo. -The analytics page also shows a distribution of users scores to help identify clusters of users within rankings. +La página de analítica también muestra una distribución de las puntuaciones de los usuarios para ayudar a identificar grupos de usuarios dentro de los rankings. -Additionally the leaderboard rankings page shows current and past rankings of a leaderboard: +Además, la página de rankings de la clasificación muestra los rankings actuales y pasados de una clasificación: -## Get Support +## Obtener Soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/guides/how-to-build-a-streaks-feature.mdx b/es/guides/how-to-build-a-streaks-feature.mdx index ff0ce46..4ae4458 100644 --- a/es/guides/how-to-build-a-streaks-feature.mdx +++ b/es/guides/how-to-build-a-streaks-feature.mdx @@ -1,7 +1,9 @@ --- -title: How To Build A Streaks Feature -description: Learn how to use Trophy to add a streaks feature to your web or mobile app. -subtitle: Learn how to use Trophy to add a streaks feature to your web or mobile app. +title: Cómo construir una función de rachas +description: Aprende a usar Trophy para añadir una función de rachas a tu + aplicación web o móvil. +subtitle: Aprende a usar Trophy para añadir una función de rachas a tu + aplicación web o móvil. noindex: true --- @@ -10,28 +12,28 @@ import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx" import StreakRequestBlock from "/snippets/streak-request-block.mdx"; import StreakResponseBlock from "/snippets/streak-response-block.mdx"; -The guide outlines the full process of adding a streaks feature to your web or mobile app using Trophy. +Esta guía describe el proceso completo para añadir una función de rachas a tu aplicación web o móvil usando Trophy. -For illustration purposes we'll use the example of a study platform that uses a daily streak to incentivize and reward users for viewing flashcards. +Con fines ilustrativos, usaremos el ejemplo de una plataforma de estudio que utiliza una racha diaria para incentivar y recompensar a los usuarios por ver tarjetas de estudio. - 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). + Para ver un ejemplo completamente funcional en la práctica, consulta la [demostración + en vivo](https://examples.trophy.so) o el [repositorio de + github](https://github.com/trophyso/example-study-platform/tree/demo). -## Pre-requisites +## Requisitos previos -- A [Trophy](https://app.trophy.so/sign-up) account -- About 10 minutes +- Una cuenta de [Trophy](https://app.trophy.so/sign-up) +- Aproximadamente 10 minutos -## Trophy Setup +## Configuración de Trophy -In Trophy, [Metrics](/platform/metrics) are the building blocks of gamification and model the different interactions users make with your product. +En Trophy, las [Métricas](/platform/metrics) son los elementos fundamentales de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. -In this guide the interaction we're interested in is `flashcards-viewed`, but you can create a metric that best represents the interaction you want to drive streaks from. +En esta guía, la interacción que nos interesa es `flashcards-viewed`, pero puedes crear una métrica que mejor represente la interacción desde la cual quieres generar rachas. -In the Trophy dashboard, head to the [metrics page](https://app.trophy.so/metrics) and create a metric. +En el panel de Trophy, dirígete a la [página de métricas](https://app.trophy.so/metrics) y crea una métrica. -Once you've created your metric, head to the [streaks page](https://app.trophy.so/streaks/configure) and select the streak frequency you want (daily, weekly or monthly). +Una vez que hayas creado tu métrica, dirígete a la [página de rachas](https://app.trophy.so/streaks/configure) y selecciona la frecuencia de racha que deseas (diaria, semanal o mensual). -Then add your metric to the streak conditions and set the threshold users must meet (e.g. at least 1 to extend their streak each period). +Luego añade tu métrica a las condiciones de racha y establece el umbral que los usuarios deben cumplir (por ejemplo, al menos 1 para extender su racha cada período). -You can also add multiple metrics and choose whether users must meet all of them or just one—see the [streak conditions docs](/platform/streaks#streak-conditions) for details. +También puedes añadir múltiples métricas y elegir si los usuarios deben cumplir todas o solo una—consulta la [documentación de condiciones de racha](/platform/streaks#streak-conditions) para más detalles. -In Trophy you track user interactions by sending [Events](/platform/events) from your code to Trophy APIs against a specific metric. +En Trophy, registras las interacciones de los usuarios enviando [Eventos](/platform/events) desde tu código a las API de Trophy contra una métrica específica. -When events are recorded for a specific user, Trophy will automatically check whether they've met their streak conditions for the current period and update the user's streak accordingly. +Cuando se registran eventos para un usuario específico, Trophy verificará automáticamente si ha cumplido con las condiciones de su racha para el período actual y actualizará la racha del usuario en consecuencia. -This is what makes building gamified experiences with Trophy so easy, it does all the work for you behind the scenes. +Esto es lo que hace que construir experiencias gamificadas con Trophy sea tan fácil: realiza todo el trabajo por ti tras bambalinas. -## Installing Trophy SDK +## Instalación del SDK de Trophy -To interact with Trophy from your code you'll use the Trophy SDK available in most major [programming languages](/api-reference/client-libraries). +Para interactuar con Trophy desde tu código, utilizarás el SDK de Trophy disponible en la mayoría de los principales [lenguajes de programación](/api-reference/client-libraries). -Install the Trophy SDK: +Instala el SDK de Trophy: -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. +A continuación, obtén tu clave de API desde la [página de integración](https://app.trophy.so/integration) de Trophy y añádela como una variable de entorno **exclusivamente del lado del servidor**. ```bash TROPHY_API_KEY='*******' ``` - Make sure you **don't** expose your API key in client-side code. + Asegúrate de **no** exponer tu clave de API en código del lado del cliente. -## Tracking User Interactions +## Registro de Interacciones de Usuarios -To track an event (user interaction) against your metric, use the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event). +Para registrar un evento (interacción del usuario) contra tu métrica, utiliza la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event). - By including the user's timezone in the `tz` attribute, Trophy will - automatically track streaks according to the user's local clock and handle - awkward edge cases where users change time zones. + Al incluir la zona horaria del usuario en el atributo `tz`, Trophy + rastreará automáticamente las rachas según el reloj local del usuario y manejará + casos límite complicados donde los usuarios cambian de zona horaria. -The response to this API call is the complete set of changes to any features you've built with Trophy, including the latest data on the users streak. +La respuesta a esta llamada de API es el conjunto completo de cambios en cualquier función que hayas construido con Trophy, incluidos los datos más recientes sobre la racha del usuario. {/* vale off */} @@ -110,17 +112,17 @@ The response to this API call is the complete set of changes to any features you {/* vale on */} -Validate this is working by checking the Trophy [dashboard](https://app.trophy.so). +Valida que esto funciona verificando el [panel de control](https://app.trophy.so) de Trophy. -## Displaying Streaks +## Visualización de Rachas -You have a number of options for displaying streaks in your application. Here we'll look at the most common options. +Tienes varias opciones para mostrar rachas en tu aplicación. Aquí examinaremos las opciones más comunes. -### Pop-up Notifications +### Notificaciones Emergentes -We can use the response of the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event) to show pop-up notifications (or 'toasts') when user extends their streak. +Podemos usar la respuesta de la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event) para mostrar notificaciones emergentes (o 'toasts') cuando el usuario extiende su racha. -Here's an example of this in action: +Aquí hay un ejemplo de esto en acción: ```ts Streak Extended Pop-up // Sends event to Trophy @@ -151,24 +153,20 @@ if (response.currentStreak?.extended) { - 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. + Si deseas reproducir efectos de sonido, usa la [API de Audio HTML5](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) y siéntete libre de usar estos [archivos de audio](https://github.com/trophyso/example-study-platform/tree/demo/public/sounds) que recomendamos. -### Displaying User Streaks +### Mostrar las Rachas de Usuario -To fetch data on a user's streak use the [user streak API](/api-reference/endpoints/users/get-a-users-streak). +Para obtener datos sobre la racha de un usuario, usa la [API de racha de usuario](/api-reference/endpoints/users/get-a-users-streak). -This API not only returns data on the user's current streak like `length` and `expires`, but it can also return historical streak data which can be used to display any kind of streak UI you like through the `historyPeriods` parameter. +Esta API no solo devuelve datos sobre la racha actual del usuario como `length` y `expires`, sino que también puede devolver datos históricos de rachas que se pueden usar para mostrar cualquier tipo de interfaz de racha que desees mediante el parámetro `historyPeriods`. -Here's an example of a git-style streak calendar built using the data in the response above: +Aquí hay un ejemplo de un calendario de rachas estilo git construido usando los datos de la respuesta anterior: - Check this [example - repo](https://github.com/trophyso/example-study-platform/blob/demo/src/app/user-center/study-center/default-view.tsx) - for a React component that drives this UI using data from Trophy. + Consulta este [repositorio de ejemplo](https://github.com/trophyso/example-study-platform/blob/demo/src/app/user-center/study-center/default-view.tsx) para un componente React que maneja esta interfaz usando datos de Trophy. -## Analytics +## Analíticas -The [streaks page](https://app.trophy.so/streaks) in Trophy shows data on active streaks, the average length of streaks and longest streaks. +La [página de rachas](https://app.trophy.so/streaks) en Trophy muestra datos sobre rachas activas, la duración promedio de las rachas y las rachas más largas. -## Streak Freezes +## Congelaciones de Racha -Trophy also supports streak freezes which can help prevent users from losing their streak if they accidentally miss a period. +Trophy también admite congelaciones de racha que pueden ayudar a evitar que los usuarios pierdan su racha si accidentalmente pierden un período. -Learn more about streak freezes in the dedicated [streak freezes docs](/platform/streaks#streak-freezes). +Obtén más información sobre las congelaciones de racha en la [documentación dedicada de congelaciones de racha](/platform/streaks#streak-freezes). -## Get Support +## Obtener Soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/guides/how-to-build-an-achievements-feature.mdx b/es/guides/how-to-build-an-achievements-feature.mdx index 6abbd59..dcb7d60 100644 --- a/es/guides/how-to-build-an-achievements-feature.mdx +++ b/es/guides/how-to-build-an-achievements-feature.mdx @@ -1,7 +1,9 @@ --- -title: How To Build An Achievements Feature -description: Learn how to use Trophy to add an achievements feature to your web or mobile app. -subtitle: Learn how to use Trophy to add an achievements feature to your web or mobile app. +title: Cómo Crear una Función de Logros +description: Aprende a usar Trophy para agregar una función de logros a tu + aplicación web o móvil. +subtitle: Aprende a usar Trophy para agregar una función de logros a tu + aplicación web o móvil. noindex: true --- @@ -10,28 +12,28 @@ import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx" import UserAchievementsRequestBlock from "/snippets/user-achievements-request-block.mdx"; import AllAchievementsRequestBlock from "/snippets/all-achievements-request-block.mdx"; -The guide outlines the full process of adding an achievements feature to your web or mobile app using Trophy. +Esta guía describe el proceso completo para agregar una función de logros a tu aplicación web o móvil utilizando Trophy. -For illustration purposes we'll use the example of a study platform that uses achievements to incentivize and reward users for viewing flashcards. +A modo de ilustración, usaremos el ejemplo de una plataforma de estudio que utiliza logros para incentivar y recompensar a los usuarios por ver tarjetas de estudio. - 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). + Para ver un ejemplo completamente funcional de esto en la práctica, revisa la [demostración + en vivo](https://examples.trophy.so) o el [repositorio de + github](https://github.com/trophyso/example-study-platform/tree/demo). -## Pre-requisites +## Requisitos previos -- A [Trophy](https://app.trophy.so/sign-up) account -- About 10 minutes +- Una cuenta de [Trophy](https://app.trophy.so/sign-up) +- Aproximadamente 10 minutos -## Trophy Setup +## Configuración de Trophy -In Trophy, [Metrics](/platform/metrics) are the building blocks of gamification and model the different interactions users make with your product. +En Trophy, las [Métricas](/platform/metrics) son los componentes fundamentales de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. -In this guide the interaction we're interested in is `flashcards-viewed`, but you can create a metric that best represents the interaction you want to build achievements around. +En esta guía, la interacción que nos interesa es `flashcards-viewed`, pero puedes crear una métrica que represente mejor la interacción alrededor de la cual quieres construir logros. -In the Trophy dashboard, head to the [metrics page](https://app.trophy.so/metrics) and create a metric. +En el panel de Trophy, dirígete a la [página de métricas](https://app.trophy.so/metrics) y crea una métrica. -Once you've created your metric, head to the [achievements page](https://app.trophy.so/achievements) and create the achievements you want. You can find all the details on the types of achievements and the different use cases in the [achievements docs](/platform/achievements). +Una vez que hayas creado tu métrica, dirígete a la [página de logros](https://app.trophy.so/achievements) y crea los logros que desees. Puedes encontrar todos los detalles sobre los tipos de logros y los diferentes casos de uso en la [documentación de logros](/platform/achievements). -For the purposes of this guide we've set up a couple of achievements based on an increasing number of flashcards flipped: +Para los propósitos de esta guía, hemos configurado un par de logros basados en un número creciente de tarjetas de estudio volteadas: - 10 flashcards - 50 flashcards @@ -54,44 +56,44 @@ For the purposes of this guide we've set up a couple of achievements based on an - 250 flashcards - 1,000 flashcards -We've also set up a few achievements related to [Streaks](/platform/streaks), but we won't go into detail on these in this guide. +También hemos configurado algunos logros relacionados con [Rachas](/platform/streaks), pero no entraremos en detalle sobre estos en esta guía. - For a full guide on adding a streaks feature to your web or mobile app, check - out our [full guide](/guides/how-to-build-a-streaks-feature). + Para una guía completa sobre cómo agregar una función de rachas a tu aplicación web o móvil, consulta + nuestra [guía completa](/guides/how-to-build-a-streaks-feature). -In Trophy you track user interactions by sending [Events](/platform/events) from your code to Trophy APIs against a specific metric. +En Trophy realizas seguimiento de las interacciones de usuario enviando [Eventos](/platform/events) desde tu código a las API de Trophy contra una métrica específica. -When events are recorded for a specific user, any achievements linked to the specified metric will be **completed automatically** if the requirements are met. +Cuando se registran eventos para un usuario específico, cualquier logro vinculado a la métrica especificada se **completará automáticamente** si se cumplen los requisitos. -This is what makes building gamified experiences with Trophy so easy, it does all the work for you behind the scenes. +Esto es lo que hace que construir experiencias gamificadas con Trophy sea tan fácil: hace todo el trabajo por ti detrás de escena. -## Installing Trophy SDK +## Instalación del SDK de Trophy -To interact with Trophy from your code you'll use the Trophy SDK available in most major [programming languages](/api-reference/client-libraries). +Para interactuar con Trophy desde tu código, utilizarás el SDK de Trophy disponible en la mayoría de los principales [lenguajes de programación](/api-reference/client-libraries). -Install the Trophy SDK: +Instala el SDK de Trophy: -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. +A continuación, obtén tu clave API de la [página de integración](https://app.trophy.so/integration) de Trophy y agrégala como una variable de entorno **únicamente del lado del servidor**. ```bash TROPHY_API_KEY='*******' ``` - Make sure you **don't** expose your API key in client-side code. + Asegúrate de **no** exponer tu clave API en código del lado del cliente. -## Tracking User Interactions +## Seguimiento de Interacciones de Usuario -To track an event (user interaction) against your metric, use the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event). +Para realizar seguimiento de un evento (interacción de usuario) contra tu métrica, utiliza la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event). -The response to this API call is the complete set of changes to any features you've built with Trophy, including any achievements that were unlocked as a result of the event. +La respuesta a esta llamada API es el conjunto completo de cambios en cualquier función que hayas construido con Trophy, incluyendo cualquier logro que se haya desbloqueado como resultado del evento. {/* vale off */} @@ -118,7 +120,7 @@ The response to this API call is the complete set of changes to any features you {/* vale on */} -Validate this is working by checking the Trophy [dashboard](https://app.trophy.so). +Valida que esto funcione revisando el [panel](https://app.trophy.so) de Trophy. -## Displaying Achievements +## Mostrar Logros -You have a number of options for displaying achievements in your application. Here we'll look at the most common options. +Tienes varias opciones para mostrar logros en tu aplicación. Aquí veremos las opciones más comunes. -### Pop-up Notifications +### Notificaciones Emergentes -We can use the response of the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event) to show pop-up notifications (or 'toasts') when users complete achievements. +Podemos usar la respuesta de la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event) para mostrar notificaciones emergentes (o 'toasts') cuando los usuarios completen logros. -Here's an example of this in action: +Aquí hay un ejemplo de esto en acción: ```ts Achievement Completed Pop-up // Sends event to Trophy @@ -172,22 +174,17 @@ response.achievements.forEach((achievement) => { - 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. + Si deseas reproducir efectos de sonido, usa la [API de Audio HTML5](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) y siéntete libre de usar estos [archivos de audio](https://github.com/trophyso/example-study-platform/tree/demo/public/sounds) que recomendamos. -### Displaying User Achievements +### Mostrar Logros del Usuario -To fetch all achievements a user has completed, use the [user achievements API](/api-reference/endpoints/users/get-a-users-completed-achievements). +Para obtener todos los logros que un usuario ha completado, usa la [API de logros de usuario](/api-reference/endpoints/users/get-a-users-completed-achievements). - You can also fetch incomplete achievements at the same time by passing - `includeIncomplete` as `'true'`. + También puedes obtener logros incompletos al mismo tiempo pasando `includeIncomplete` como `'true'`. @@ -201,18 +198,15 @@ To fetch all achievements a user has completed, use the [user achievements API]( > -### Displaying All Achievements +### Mostrar Todos los Logros -If instead you want to display all achievements you've set up in Trophy as part of a globally accessible UI that isn't linked to a particular user, you can use the [all achievements API](/api-reference/endpoints/achievements/all-achievements). +Si en cambio deseas mostrar todos los logros que has configurado en Trophy como parte de una interfaz globalmente accesible que no esté vinculada a un usuario en particular, puedes usar la [API de todos los logros](/api-reference/endpoints/achievements/all-achievements). -### Achievement Completion Stats +### Estadísticas de Finalización de Logros -Both the user achievements API and the all achievements API include completion -statistics like `completions` (the number of users that have completed an -achievement) and `rarity` (the percentage of users that have completed an -achievement). +Tanto la API de logros de usuario como la API de todos los logros incluyen estadísticas de finalización como `completions` (el número de usuarios que han completado un logro) y `rarity` (el porcentaje de usuarios que han completado un logro). -## Analytics +## Analíticas -The [achievements page](https://app.trophy.so/achievements) in Trophy shows how many users have completed each achievement you've set up. +La [página de logros](https://app.trophy.so/achievements) en Trophy muestra cuántos usuarios han completado cada logro que has configurado. -Additionally the analytics page on any metric in Trophy includes a chart that shows the progress of users through your achievements. +Además, la página de análisis de cualquier métrica en Trophy incluye un gráfico que muestra el progreso de los usuarios a través de tus logros. -## Get Support +## Obtén Soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/guides/how-to-build-an-energy-feature.mdx b/es/guides/how-to-build-an-energy-feature.mdx index 2132070..d62e2f0 100644 --- a/es/guides/how-to-build-an-energy-feature.mdx +++ b/es/guides/how-to-build-an-energy-feature.mdx @@ -1,7 +1,9 @@ --- -title: How To Build An Energy Feature -description: Learn how to use Trophy to add an energy feature to your web or mobile app. -subtitle: Learn how to use Trophy to add an energy feature to your web or mobile app. +title: Cómo Crear una Función de Energía +description: Aprende a usar Trophy para añadir una función de energía a tu + aplicación web o móvil. +subtitle: Aprende a usar Trophy para añadir una función de energía a tu + aplicación web o móvil. noindex: true --- @@ -11,28 +13,26 @@ import UserPointsRequest from "/snippets/user-points-request-block.mdx"; import UserEnergyResponse from "/snippets/user-energy-response-block.mdx"; import UserPointsEventSummaryRequest from "/snippets/user-points-summary-request-block.mdx"; -The guide outlines the full process of adding an energy feature to your web or mobile app using Trophy. +Esta guía describe el proceso completo para añadir una función de energía a tu aplicación web o móvil usando Trophy. -For illustration purposes we'll use the example of a study platform that uses energy to meter the rate at which users can view flashcards. +Con fines ilustrativos, usaremos el ejemplo de una plataforma de estudio que utiliza energía para controlar la velocidad a la que los usuarios pueden ver tarjetas de memoria. - 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). + Para ver un ejemplo completamente funcional de esto en la práctica, consulta la [demostración en vivo](https://examples.trophy.so) o el [repositorio de github](https://github.com/trophyso/example-study-platform/tree/demo). -## Pre-requisites +## Requisitos previos -- A [Trophy](https://app.trophy.so/sign-up) account -- About 10 minutes +- Una cuenta de [Trophy](https://app.trophy.so/sign-up) +- Aproximadamente 10 minutos -## Trophy Setup +## Configuración de Trophy -In Trophy, [Metrics](/platform/metrics) are the building blocks of gamification and model the different interactions users make with your product. +En Trophy, las [Métricas](/platform/metrics) son los componentes básicos de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. -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 grant and consume energy from. +En esta guía, la interacción que nos interesa es `flashcards-viewed`, pero puedes crear cualquier número de métricas que mejor representen las interacciones desde las cuales deseas otorgar y consumir energía. -In the Trophy dashboard, head to the [metrics page](https://app.trophy.so/metrics) and create a metric. +En el panel de Trophy, dirígete a la [página de métricas](https://app.trophy.so/metrics) y crea una métrica. -Once you've created your metric, head to the [points page](https://app.trophy.so/points) and create a new points system called 'Energy'. +Una vez que hayas creado tu métrica, dirígete a la [página de puntos](https://app.trophy.so/points) y crea un nuevo sistema de puntos llamado 'Energía'. -Once created, you'll be taken to the configure page for the energy system where you can create [points triggers](/platform/points#types-of-triggers) for each of the ways you want to grant or consume energy. +Una vez creado, serás llevado a la página de configuración del sistema de energía donde podrás crear [activadores de puntos](/platform/points#types-of-triggers) para cada una de las formas en que deseas otorgar o consumir energía. -Use 'time' triggers to grant users with new energy on an hourly or daily basis, and use [other types](/platform/points#types-of-triggers) of triggers with negative values to consume energy from the different user interactions you want. +Usa disparadores 'time' para otorgar a los usuarios nueva energía cada hora o diariamente, y utiliza [otros tipos](/platform/points#types-of-triggers) de disparadores con valores negativos para consumir energía de las diferentes interacciones de usuario que desees. -In Trophy you track user interactions by sending [Events](/platform/events) from your code to Trophy APIs against a specific metric. +En Trophy rastreas las interacciones de los usuarios enviando [Eventos](/platform/events) desde tu código a las APIs de Trophy contra una métrica específica. -When events are recorded for a specific user, Trophy will automatically check if any of the triggers set up against your energy system should be triggered, and process them accordingly. +Cuando se registran eventos para un usuario específico, Trophy verificará automáticamente si alguno de los disparadores configurados contra tu sistema de energía debe activarse, y los procesará en consecuencia. -Trophy also takes care of automatically granting new energy to users over time in accordance with any 'time' triggers you've set up. +Trophy también se encarga de otorgar automáticamente nueva energía a los usuarios con el tiempo de acuerdo con cualquier disparador 'time' que hayas configurado. -This is what makes building gamified experiences with Trophy so easy, it does all the work for you behind the scenes. +Esto es lo que hace que construir experiencias gamificadas con Trophy sea tan fácil: realiza todo el trabajo por ti en segundo plano. -## Installing Trophy SDK +## Instalación del SDK de Trophy -To interact with Trophy from your code you'll use the Trophy SDK available in most major [programming languages](/api-reference/client-libraries). +Para interactuar con Trophy desde tu código utilizarás el SDK de Trophy disponible en los principales [lenguajes de programación](/api-reference/client-libraries). -Install the Trophy SDK: +Instala el SDK de Trophy: -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. +A continuación, obtén tu clave de API desde la [página de integración](https://app.trophy.so/integration) de Trophy y agrégala como una variable de entorno **exclusivamente del lado del servidor**. ```bash TROPHY_API_KEY='*******' ``` - Make sure you **don't** expose your API key in client-side code. + Asegúrate de **no** exponer tu clave de API en código del lado del cliente. -## Tracking User Interactions +## Rastreo de Interacciones de Usuario -To track an event (user interaction) against your metric, use the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event). +Para rastrear un evento (interacción de usuario) contra tu métrica, utiliza la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event). -The response to this API call is the complete set of changes to any features you've built with Trophy, including any changes in energy as a result of the event, and from what triggers energy was consumed. +La respuesta a esta llamada de API es el conjunto completo de cambios en cualquier funcionalidad que hayas construido con Trophy, incluyendo cualquier cambio en la energía como resultado del evento, y desde qué disparadores se consumió energía. {/* vale off */} @@ -146,19 +146,19 @@ The response to this API call is the complete set of changes to any features you {/* vale on */} -Validate this is working by checking the Trophy [dashboard](https://app.trophy.so). +Valida que esto funciona revisando el [panel de control](https://app.trophy.so) de Trophy. -## Metering Usage +## Medición del Uso -To prevent users from taking actions in your product based on energy, use the [user points API](/api-reference/endpoints/users/get-a-users-points) to fetch their current total energy. +Para evitar que los usuarios realicen acciones en tu producto basándose en energía, usa la [API de puntos de usuario](/api-reference/endpoints/users/get-a-users-points) para obtener su total actual de energía. -This returns data on the total energy the user has, allowing you to use the `total` property to control what actions a user can perform: +Esto devuelve datos sobre la energía total que tiene el usuario, permitiéndote usar la propiedad `total` para controlar qué acciones puede realizar un usuario: -Here's an example where a user is only allowed to view a flashcard if `total > 0` +Aquí hay un ejemplo donde a un usuario solo se le permite ver una tarjeta de estudio si `total > 0` ```ts const energy = await trophy.users.points("user-id", "energy"); @@ -172,23 +172,23 @@ if (energy.total > 0) { } ``` -You can then modify your trigger setup in Trophy and control the rate at which users can interact with your product right from the Trophy dashboard without needing to make any code changes. +Luego puedes modificar tu configuración de triggers en Trophy y controlar la velocidad a la que los usuarios pueden interactuar con tu producto directamente desde el panel de Trophy sin necesidad de realizar cambios en el código. -## Displaying Energy +## Visualización de la Energía -To fetch a users energy use the [user points API](/api-reference/endpoints/users/get-a-users-points). +Para obtener la energía de un usuario, usa la [API de puntos de usuario](/api-reference/endpoints/users/get-a-users-points). -This API returns data on the user's total energy but can be configured to also return between 1 and 100 of the user's most recent energy changes by using the `awards` [query parameter](/api-reference/endpoints/users/get-a-users-points#parameter-awards). +Esta API devuelve datos sobre la energía total del usuario, pero puede configurarse para también devolver entre 1 y 100 de los cambios de energía más recientes del usuario utilizando el [parámetro de consulta](/api-reference/endpoints/users/get-a-users-points#parameter-awards) `awards`. -The [user points summary API](/api-reference/endpoints/users/get-a-users-points-summary) can also be used to drive chart-based UI, like showing users their energy usage over time. +La [API de resumen de puntos de usuario](/api-reference/endpoints/users/get-a-users-points-summary) también puede utilizarse para impulsar interfaces basadas en gráficos, como mostrar a los usuarios su uso de energía a lo largo del tiempo. -Here's an example of a UI that shows users their current energy, a chart showing their usage over time, and a list of their most recent changes in energy. +Aquí hay un ejemplo de una interfaz que muestra a los usuarios su energía actual, un gráfico que muestra su uso a lo largo del tiempo y una lista de sus cambios más recientes en energía. -## Analytics +## Analíticas -In Trophy your [energy system page](https://app.trophy.so/points), includes analytics charts that shows data on total energy awarded/consumed and a breakdown of exactly what triggers cause the most frequent changes in energy. +En Trophy, tu [página del sistema de energía](https://app.trophy.so/points) incluye gráficos de analíticas que muestran datos sobre la energía total otorgada/consumida y un desglose de exactamente qué triggers causan los cambios más frecuentes en energía. -## Get Support +## Obtener soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres contactar con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/guides/how-to-build-an-xp-feature.mdx b/es/guides/how-to-build-an-xp-feature.mdx index e34e73c..e78b512 100644 --- a/es/guides/how-to-build-an-xp-feature.mdx +++ b/es/guides/how-to-build-an-xp-feature.mdx @@ -1,7 +1,9 @@ --- -title: How To Build An XP Feature -description: Learn how to use Trophy to add an XP feature to your web or mobile app. -subtitle: Learn how to use Trophy to add an XP feature to your web or mobile app. +title: Cómo construir una funcionalidad de XP +description: Aprende a usar Trophy para agregar una funcionalidad de XP a tu + aplicación web o móvil. +subtitle: Aprende a usar Trophy para agregar una funcionalidad de XP a tu + aplicación web o móvil. noindex: true --- @@ -13,28 +15,26 @@ import UserPointsResponse from "/snippets/user-points-response-block.mdx"; import UserPointsEventSummaryRequest from "/snippets/user-points-summary-request-block.mdx"; import UserPointsEventSummaryResponse from "/snippets/user-points-summary-response-block.mdx"; -The guide outlines the full process of adding an XP feature to your web or mobile app using Trophy. +Esta guía describe el proceso completo para agregar una funcionalidad de XP a tu aplicación web o móvil usando Trophy. -For illustration purposes we'll use the example of a study platform that uses XP to reward users for taking different interactions. +Con fines ilustrativos, usaremos el ejemplo de una plataforma de estudio que utiliza XP para recompensar a los usuarios por realizar diferentes interacciones. - 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). + Para ver un ejemplo completamente funcional de esto en la práctica, consulta la [demostración en vivo](https://examples.trophy.so) o el [repositorio de github](https://github.com/trophyso/example-study-platform/tree/demo). -## Pre-requisites +## Requisitos previos -- A [Trophy](https://app.trophy.so/sign-up) account -- About 10 minutes +- Una cuenta de [Trophy](https://app.trophy.so/sign-up) +- Aproximadamente 10 minutos -## Trophy Setup +## Configuración de Trophy -In Trophy, [Metrics](/platform/metrics) are the building blocks of gamification and model the different interactions users make with your product. +En Trophy, las [Métricas](/platform/metrics) son los bloques de construcción de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. -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. +En esta guía, la interacción que nos interesa es `flashcards-viewed`, pero puedes crear cualquier cantidad de métricas que mejor representen las interacciones desde las cuales deseas recompensar XP. -In the Trophy dashboard, head to the [metrics page](https://app.trophy.so/metrics) and create a metric. +En el panel de Trophy, dirígete a la [página de métricas](https://app.trophy.so/metrics) y crea una métrica. -Once you've created your metric, head to the [points page](https://app.trophy.so/points) and create a new points system called 'XP'. +Una vez que hayas creado tu métrica, dirígete a la [página de puntos](https://app.trophy.so/points) y crea un nuevo sistema de puntos llamado 'XP'. -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. +Una vez creado, serás dirigido a la página de configuración del sistema XP donde podrás crear 'disparadores' para cada una de las formas en que deseas recompensar a los usuarios con XP. -In Trophy you track user interactions by sending [Events](/platform/events) from your code to Trophy APIs against a specific metric. +En Trophy rastreás las interacciones del usuario enviando [Eventos](/platform/events) desde tu código a las APIs de Trophy contra una métrica específica. -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. +Cuando se registran eventos para un usuario específico, Trophy verificará automáticamente si el evento está asociado a una métrica configurada como parte de algún disparador de XP. -If so Trophy will award the user with the appropriate amount of XP according to the trigger configuration. +Si es así, Trophy otorgará al usuario la cantidad apropiada de XP según la configuración del disparador. -This is what makes building gamified experiences with Trophy so easy, it does all the work for you behind the scenes. +Esto es lo que hace que construir experiencias gamificadas con Trophy sea tan fácil: hace todo el trabajo por vos detrás de escena. - XP can also be awarded to users based on achievements, streaks and other - triggers. See the dedicated [points docs](/platform/points#points-triggers) - for more information. + Los XP también pueden otorgarse a los usuarios en base a logros, rachas y otros + disparadores. Consultá la [documentación de puntos](/platform/points#points-triggers) + para más información. -## Installing Trophy SDK +## Instalación del SDK de Trophy -To interact with Trophy from your code you'll use the Trophy SDK available in most major [programming languages](/api-reference/client-libraries). +Para interactuar con Trophy desde tu código usarás el SDK de Trophy disponible en la mayoría de los principales [lenguajes de programación](/api-reference/client-libraries). -Install the Trophy SDK: +Instalá el SDK de Trophy: -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. +A continuación, obtené tu clave de API desde la [página de integración](https://app.trophy.so/integration) de Trophy y agregala como una variable de entorno **solo del lado del servidor**. ```bash TROPHY_API_KEY='*******' ``` - Make sure you **don't** expose your API key in client-side code. + Asegurate de **no** exponer tu clave de API en código del lado del cliente. -## Tracking User Interactions +## Rastreo de Interacciones del Usuario -To track an event (user interaction) against your metric, use the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event). +Para rastrear un evento (interacción del usuario) contra tu métrica, usá la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event). -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. +La respuesta a esta llamada de API es el conjunto completo de cambios a cualquier funcionalidad que hayas construido con Trophy, incluyendo cualquier XP que se le haya otorgado al usuario como resultado del evento, y de qué disparadores fue otorgado. - If you use [Points Levels](/platform/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. + Si usás [Niveles de Puntos](/platform/points#points-levels), el objeto `xp` puede incluir un campo **`level`** **solo cuando** el nivel del usuario cambió en esta solicitud; puede omitirse cuando su nivel se mantuvo igual. Consultá la [referencia de API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event) para el esquema de respuesta completo. {/* vale off */} @@ -158,17 +158,17 @@ The response to this API call is the complete set of changes to any features you {/* vale on */} -Validate this is working by checking the Trophy [dashboard](https://app.trophy.so). +Valida que esto funciona verificando el panel de Trophy [dashboard](https://app.trophy.so). -## Displaying XP +## Mostrando XP -You have a number of options for displaying XP in your application. Here we'll look at the most common options. +Tienes varias opciones para mostrar XP en tu aplicación. Aquí veremos las opciones más comunes. -### Pop-up Notifications +### Notificaciones Emergentes -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. +Podemos usar la respuesta de la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event) para mostrar a los usuarios notificaciones emergentes cuando se les otorga nuevo XP. -Here's an example of this in action: +Aquí hay un ejemplo de esto en acción: ```ts XP Pop-ups // Sends event to Trophy @@ -196,24 +196,20 @@ if (xp.awards.length > 0) { ``` - 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. + Si quieres reproducir efectos de sonido, usa la [API de Audio HTML5](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) y siéntete libre de usar estos [archivos de audio](https://github.com/trophyso/example-study-platform/tree/demo/public/sounds) que recomendamos. -### Displaying User XP +### Mostrando el XP del Usuario -To fetch a users XP use the [user points API](/api-reference/endpoints/users/get-a-users-points). +Para obtener el XP de un usuario, usa la [API de puntos de usuario](/api-reference/endpoints/users/get-a-users-points). -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](/api-reference/endpoints/users/get-a-users-points#parameter-awards). +Esta API devuelve datos sobre el XP total del usuario, pero puede configurarse para devolver también entre 1 y 100 de los premios de XP más recientes del usuario usando el `awards` [parámetro de consulta](/api-reference/endpoints/users/get-a-users-points#parameter-awards). -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. +Aquí hay un ejemplo de una interfaz que muestra a los usuarios una lista de sus premios más recientes basada en los datos devueltos por la API de puntos de usuario. -### User XP Chart +### Gráfico de XP del Usuario -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. +La [API de resumen de puntos de usuario](/api-reference/endpoints/users/get-a-users-points-summary) devuelve datos históricos listos para gráficos que muestran cómo ha cambiado el XP de un usuario a lo largo del tiempo. -Use the `aggregation`, `start_date` and `end_date` query parameters to control the data returned. Here's an example of XP data aggregated daily: +Usa los parámetros de consulta `aggregation`, `start_date` y `end_date` para controlar los datos devueltos. Aquí hay un ejemplo de datos de XP agregados diariamente: -And here's an example of the types of charts you can build with this data: +Y aquí hay un ejemplo de los tipos de gráficos que puedes construir con estos datos: -## Analytics +## Analíticas -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. +En Trophy, tu [página del sistema de XP](https://app.trophy.so/points) incluye gráficos analíticos que muestran datos sobre el total de XP ganado y un desglose detallado de qué desencadenantes otorgan más XP. -## Get Support +## Obtener Soporte -Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! +¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/platform/achievements.mdx b/es/platform/achievements.mdx index ec0e08e..ac15d6c 100644 --- a/es/platform/achievements.mdx +++ b/es/platform/achievements.mdx @@ -1,29 +1,30 @@ --- -title: Achievements -description: Learn how to use Achievements in a gamified product experience with Trophy. -"og:description": Use metric, API, streak and composite achievements to reward users for continued progress and to encourage them to discover new parts of your app. +title: Logros +description: Aprende a usar Logros en una experiencia de producto gamificada con Trophy. +og:description: Usa logros de métrica, API, racha y compuestos para recompensar + a los usuarios por su progreso continuo y animarlos a descubrir nuevas partes + de tu aplicación. icon: trophy --- import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; import AllAchievementsResponseBlock from "/snippets/all-achievements-response-block.mdx"; -## What Are Achievements? +## ¿Qué Son los Logros? -Achievements are rewards that users can unlock as they use your platform. They can be used to reward users for making continued progress along core user journeys, or to motivate users to explore more nascent features. +Los logros son recompensas que los usuarios pueden desbloquear al usar tu plataforma. Se pueden utilizar para recompensar a los usuarios por avanzar continuamente en los recorridos principales del usuario, o para motivarlos a explorar funciones más nuevas. -Achievements work best when designed to incentivize users to take actions that -are likely to lead to increased retention. +Los logros funcionan mejor cuando están diseñados para incentivar a los usuarios a realizar acciones que probablemente conduzcan a una mayor retención. - Use Trophy's [metric analytics](/platform/metrics#metric-analytics) to compare - the retention of each user interaction, then configure achievements around - these interactions to maximize retention impact. + Usa las [analíticas de métricas](/platform/metrics#metric-analytics) de Trophy para comparar + la retención de cada interacción del usuario, luego configura logros en torno a + estas interacciones para maximizar el impacto en la retención. -Here we'll have a look of the types of achievements you can build with Trophy, the different ways to use them, and how to integrate them into your platform. +Aquí veremos los tipos de logros que puedes crear con Trophy, las diferentes formas de usarlos y cómo integrarlos en tu plataforma. -Watch Charlie run walk through using achievements in a NextJS application: +Mira a Charlie ejecutar un recorrido sobre el uso de logros en una aplicación NextJS: -## Achievement Types +## Tipos de Logros -Trophy offers four types of achievements, [Metric](#metric-achievements), [API](#api-achievements), [Streak](#streak-achievements) and [Composite achievements](#composite-achievements), detailed below. +Trophy ofrece cuatro tipos de logros: [Métrica](#metric-achievements), [API](#api-achievements), [Racha](#streak-achievements) y [Logros Compuestos](#composite-achievements), detallados a continuación. -### Metric Achievements +### Logros de Métrica -Metric achievements are tied to [Metrics](/platform/metrics) and are best used when you want to incentivize users to take the same action over and over again. +Los logros de métrica están vinculados a [Métricas](/platform/metrics) y se usan mejor cuando quieres incentivar a los usuarios a realizar la misma acción una y otra vez. -Let's take the example of a study platform that uses Trophy to encourage users to view more flashcards with metric achievements as follows: +Tomemos el ejemplo de una plataforma de estudio que usa Trophy para animar a los usuarios a ver más tarjetas de estudio con logros de métrica de la siguiente manera: -- 1,000 flashcards -- 2,500 flashcards -- 5,000 flashcards -- 10,000 flashcards -- 25,000 flashcards -- 50,000 flashcards +- 1.000 tarjetas de estudio +- 2.500 tarjetas de estudio +- 5.000 tarjetas de estudio +- 10.000 tarjetas de estudio +- 25.000 tarjetas de estudio +- 50.000 tarjetas de estudio -In this case you would create a metric called _Flashcards Flipped_ and create achievements against the metric for each milestone. +En este caso, crearías una métrica llamada _Tarjetas Repasadas_ y crearías logros asociados a la métrica para cada hito. -Since these achievements are directly tied to the _Flashcards Flipped_ metric, Trophy will automatically track when users unlock these achievements as they [increment the metric](/platform/events#tracking-metric-events). +Dado que estos logros están directamente vinculados a la métrica _Tarjetas Repasadas_, Trophy rastreará automáticamente cuándo los usuarios desbloquean estos logros a medida que [incrementan la métrica](/platform/events#tracking-metric-events). -When achievements are unlocked, Trophy includes information about the unlocked achievements in the [Event API](/api-reference/endpoints/metrics/send-a-metric-change-event) response, and automatically triggers [Achievement Emails](/platform/emails#achievement-emails) if configured. +Cuando se desbloquean logros, Trophy incluye información sobre los logros desbloqueados en la respuesta del [Event API](/api-reference/endpoints/metrics/send-a-metric-change-event), y activa automáticamente [Correos de Logros](/platform/emails#achievement-emails) si están configurados. -### API Achievements +### Logros de API -API achievements can only be completed once and are useful for rewarding users for taking specific actions. +Los logros de API solo se pueden completar una vez y son útiles para recompensar a los usuarios por realizar acciones específicas. -Common examples include: +Ejemplos comunes incluyen: -- A user completing their profile after signing up -- A user linking their social account to a platform -- A user sharing their product experience on social media +- Un usuario que completa su perfil después de registrarse +- Un usuario que vincula su cuenta de redes sociales a una plataforma +- Un usuario que comparte su experiencia con el producto en redes sociales -API achievements serve as an easy way to reward users for completing any action that you think is important for retention. +Los logros de API sirven como una forma sencilla de recompensar a los usuarios por completar cualquier acción que consideres importante para la retención. -Just like metric achievements, API achievements can also trigger automated [Achievement Emails](/platform/emails#achievement-emails) if configured. +Al igual que los logros de métricas, los logros de API también pueden activar [Correos de Logros](/platform/emails#achievement-emails) automatizados si están configurados. -### Streak Achievements +### Logros de Racha -Streak achievements are directly tied to a user's [Streak](/platform/streaks) and are automatically unlocked when users reach a particular streak length. +Los logros de racha están directamente vinculados a la [Racha](/platform/streaks) de un usuario y se desbloquean automáticamente cuando los usuarios alcanzan una duración de racha particular. -You can create as many streak achievements as you like for increasing lengths of streak, for example 7 days, 30 days and 365 days to motivate users to use your app more and more. +Puedes crear tantos logros de racha como desees para longitudes de racha crecientes, por ejemplo 7 días, 30 días y 365 días para motivar a los usuarios a usar tu aplicación cada vez más. -Just like metric and API achievements, you can add a custom name and assign a badge to streak achievements. +Al igual que los logros de métricas y API, puedes agregar un nombre personalizado y asignar una insignia a los logros de racha. -### Composite Achievements +### Logros Compuestos -Composite achievements unlock automatically when a user has completed all of the prerequisite achievements you select. They're ideal for creating tiered or mastery-level rewards that recognize users who have accomplished a specific set of milestones. +Los logros compuestos se desbloquean automáticamente cuando un usuario ha completado todos los logros prerequisitos que selecciones. Son ideales para crear recompensas escalonadas o de nivel de maestría que reconocen a los usuarios que han alcanzado un conjunto específico de hitos. -For example, you could create a "Study Master" composite achievement that unlocks when a user has completed: -- 1,000 flashcards -- 7-day streak -- Complete onboarding +Por ejemplo, podrías crear un logro compuesto "Maestro del Estudio" que se desbloquea cuando un usuario ha completado: +- 1,000 tarjetas de estudio +- Racha de 7 días +- Completar la incorporación -Composite achievements are useful for creating progression pathways and encouraging users to engage across different areas of your app. Just like other achievement types, they can trigger [Achievement Emails](/platform/emails#achievement-emails) when unlocked. +Los logros compuestos son útiles para crear rutas de progresión y motivar a los usuarios a interactuar en diferentes áreas de tu aplicación. Al igual que otros tipos de logros, pueden activar [Correos de Logros](/platform/emails#achievement-emails) cuando se desbloquean. -## Creating Achievements +## Crear Logros -To create new achievements, head to the [achievements page](https://app.trophy.so/achievements) in the Trophy dashboard and hit the **New Achievement** button: +Para crear nuevos logros, dirígete a la [página de logros](https://app.trophy.so/achievements) en el panel de Trophy y presiona el botón **Nuevo Logro**: -- **Achievement emails** are sent to users each time they unlock an [Achievement](/platform/achievements). +- **Achievement emails** are sent to users each time they unlock an [Achievement](/en/platform/achievements). - **Recap emails** are sent to users on a pre-defined frequency to summarize progress. Recap Emails can be configured to be sent daily, weekly, monthly or yearly depending on your use case. - **Reactivation emails** are sent to users after they become inactive with the goal of bringing them back to your app. -- **Streak emails** are automatically sent to users reminding them to extend their [Streak](/platform/streaks). +- **Streak emails** are automatically sent to users reminding them to extend their [Streak](/en/platform/streaks). -## Sending Emails +## Sending Emails {#sending-emails} To start sending emails with Trophy, you'll need to verify your domain and add and activate email templates for the types you want to send. Users can control which email notifications they receive through Trophy's - [user preferences API](/platform/users#notification-preferences). This allows + [user preferences API](/en/platform/users#notification-preferences). This allows you to build a preference center in your application where users can opt in or out of specific email types. -### Domain Verification +### Domain Verification {#domain-verification} Trophy supports sending emails from your own domain out-of-the-box. There are two ways to set this up, Single Sender Verification and DNS Verification. @@ -67,7 +67,7 @@ All domain settings can be found in the [Domains](https://app.trophy.so/emails/d -#### Sender Verification (Basic) +#### Sender Verification (Basic) {#sender-verification-basic} During onboarding you'll be prompted to set up Sender Verification. This is a super simple way to quickly verify a single email address that you want to use with Trophy without needing to change any code or DNS settings. @@ -78,7 +78,7 @@ You'll be prompted to enter the email address, from name and reply-to email that height="200" width="75%" noZoom - src="../assets/platform/emails/sender_verification.jpg" + src="../../assets/platform/emails/sender_verification.jpg" /> @@ -93,10 +93,10 @@ Sender Verification is great for getting started but is limited in that you won' Also, if you want to change the address in the future, you'll have to go through Sender Verification again which you can do in the [settings screen](https://app.trophy.so/integration?tab=domains). -#### DNS Verification (Advanced) +#### DNS Verification (Advanced) {#dns-verification-advanced} - This feature is available on the [Starter](/account/billing#starter-plan) + This feature is available on the [Starter](/en/account/billing#starter-plan) plan. @@ -123,7 +123,7 @@ Follow the steps below to set up DNS verification: height="200" width="75%" noZoom - src="../assets/platform/emails/dns-verification-1.png" + src="../../assets/platform/emails/dns-verification-1.png" /> @@ -138,7 +138,7 @@ Follow the steps below to set up DNS verification: height="200" width="75%" noZoom - src="../assets/platform/emails/dns-verification-2.png" + src="../../assets/platform/emails/dns-verification-2.png" /> @@ -178,7 +178,7 @@ Follow the steps below to set up DNS verification: height="200" width="75%" noZoom - src="../assets/platform/emails/dns-verification-3.png" + src="../../assets/platform/emails/dns-verification-3.png" /> @@ -191,7 +191,7 @@ Follow the steps below to set up DNS verification: height="200" width="75%" noZoom - src="../assets/platform/emails/dns-verification-4.png" + src="../../assets/platform/emails/dns-verification-4.png" /> @@ -208,7 +208,7 @@ Follow the steps below to set up DNS verification: height="200" width="75%" noZoom - src="../assets/platform/emails/dns-verification-5.png" + src="../../assets/platform/emails/dns-verification-5.png" /> @@ -216,11 +216,11 @@ Follow the steps below to set up DNS verification: -### Email Triggers +### Email Triggers {#email-triggers} On the [email configuration](https://app.trophy.so/emails/configure) page, each email type has its own section. Add templates under the relevant type and activate one to start sending. -#### Recap Emails +#### Recap Emails {#recap-emails} **Recap** emails send weekly or monthly progress reports to users. You can change the frequency of these emails on the [integration](https://app.trophy.so/integration) page using the **Aggregation Period** setting. @@ -229,11 +229,11 @@ On the [email configuration](https://app.trophy.so/emails/configure) page, each height="200" width="100%" noZoom - src="../assets/platform/emails/recap_emails.png" + src="../../assets/platform/emails/recap_emails.png" /> -#### Reactivation Emails +#### Reactivation Emails {#reactivation-emails} **Reactivation** emails send win-back messages to users after they become inactive. These emails are sent according to the following timeline: @@ -248,11 +248,11 @@ On the [email configuration](https://app.trophy.so/emails/configure) page, each height="200" width="30%" noZoom - src="../assets/platform/emails/reactivation_emails.png" + src="../../assets/platform/emails/reactivation_emails.png" /> -#### Streak Emails +#### Streak Emails {#streak-emails} **Streak** emails remind users to extend their streak before it expires. These emails are sent according to the streak frequency you configured on the [streaks page](https://app.trophy.so/streaks): @@ -265,26 +265,26 @@ On the [email configuration](https://app.trophy.so/emails/configure) page, each height="200" width="50%" noZoom - src="../assets/platform/emails/streak_emails.png" + src="../../assets/platform/emails/streak_emails.png" /> -#### Achievement Emails +#### Achievement Emails {#achievement-emails} -**Achievement** emails congratulate users when they complete an [achievement](/platform/achievements) you've configured in Trophy. These emails send between 5-9PM on the day the achievement is completed, or the next day at 5PM if it is past 9PM when the user completes the achievement. +**Achievement** emails congratulate users when they complete an [achievement](/en/platform/achievements) you've configured in Trophy. These emails send between 5-9PM on the day the achievement is completed, or the next day at 5PM if it is past 9PM when the user completes the achievement. -#### Limiting Emails to Specific Types of Users +#### Limiting Emails to Specific Types of Users {#limiting-emails-to-specific-types-of-users} -Each email template can be limited to users with specific [custom user attribute](/platform/users#custom-user-attributes) values. +Each email template can be limited to users with specific [custom user attribute](/en/platform/users#custom-user-attributes) values. For each attribute you'd like to restrict the email to, click the plus icon next to the **User Filters** header for the email template, then select the attribute and enter the desired value. Only users that have **all** specified attribute values will receive emails from this template. @@ -293,15 +293,15 @@ For each attribute you'd like to restrict the email to, click the plus icon next height="200" width="75%" noZoom - src="../assets/platform/emails/user_filters.png" + src="../../assets/platform/emails/user_filters.png" /> -#### Time Zones +#### Time Zones {#time-zones} -If you specify a timezone for each user through [User Identification](/platform/users#param-tz), Trophy will use that local time zone to schedule emails according to the logic specified above. If you do not provide time zones for the users you identify, Trophy will default to Eastern Time. +If you specify a timezone for each user through [User Identification](/en/platform/users#param-tz), Trophy will use that local time zone to schedule emails according to the logic specified above. If you do not provide time zones for the users you identify, Trophy will default to Eastern Time. -## Designing Emails +## Designing Emails {#designing-emails} Trophy has a fully-featured block-based email builder that allows you to design templates, controlling all email copy and subject lines. On the [emails](https://app.trophy.so/emails/configure) page, add templates under each [email type](#email-triggers) section you want to use. @@ -312,11 +312,11 @@ Trophy has a fully-featured block-based email builder that allows you to design loop playsInline className="w-full aspect-15/4" - src="../assets/platform/emails/designing_emails.mp4" + src="../../assets/platform/emails/designing_emails.mp4" > -### Default Templates +### Default Templates {#default-templates} By default, Trophy provides a template for each [email type](#types-of-emails) as a good starting point. The default templates can't be changed, but you can duplicate and customize these as you wish. @@ -324,7 +324,7 @@ By default, Trophy provides a template for each [email type](#types-of-emails) a You can also create blank templates if you just want to start from scratch. -### Creating A New Template +### Creating A New Template {#creating-a-new-template} To create a new email template, follow the steps below. @@ -341,7 +341,7 @@ To create a new email template, follow the steps below. height="200" width="100%" noZoom - src="../assets/platform/emails/choose_template.png" + src="../../assets/platform/emails/choose_template.png" /> @@ -356,14 +356,14 @@ To create a new email template, follow the steps below. loop playsInline className="w-full aspect-15/4" - src="../assets/platform/emails/designing_emails.mp4" + src="../../assets/platform/emails/designing_emails.mp4" > -### Block Types +### Block Types {#block-types} Trophy's email builder supports a number of different block types that serve different purposes. All the basic components you'd expect to find in an email editor like paragraphs, headers, and images are called [Basic Blocks](#basic-blocks). There is also a set of more powerful components like charts and streaks that leverage Trophy's user activity data and gamification features. These are called [Smart Blocks](#smart-blocks). @@ -372,11 +372,11 @@ Trophy's email builder supports a number of different block types that serve dif height="200" width="100%" noZoom - src="../assets/platform/emails/block_types_dialog.png" + src="../../assets/platform/emails/block_types_dialog.png" /> -#### Basic Blocks +#### Basic Blocks {#basic-blocks} Here's the full list of un-opinionated basic blocks that Trophy supports: @@ -391,7 +391,7 @@ Here's the full list of un-opinionated basic blocks that Trophy supports: - **Columns** - Useful for creating up to 3-column layouts with any content. - **Logo** - Will render your organization's logo, set on the [Branding](https://app.trophy.so/branding) page. -#### Conditional Blocks +#### Conditional Blocks {#conditional-blocks} Trophy also has a powerful conditional rendering system powered by the _Conditional_ block type. @@ -420,13 +420,13 @@ One common use case for the conditional block is to conditionally show a user th height="100" width="50%" noZoom - src="../assets/platform/emails/conditional_block.png" + src="../../assets/platform/emails/conditional_block.png" /> This creates a powerful framework to design emails based on highly relevant and personalized user data and can be used to create a wide-range of emails for common gamification use cases. -#### Smart Blocks +#### Smart Blocks {#smart-blocks} Smart blocks are powerful components designed to support common gamification use cases and integrate with all of Trophy's features including metrics, achievements and streaks. @@ -461,7 +461,7 @@ Read more about the use case of each smart block: loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/achievement_progress_block.mp4" + src="../../assets/platform/emails/achievement_progress_block.mp4" > @@ -481,7 +481,7 @@ Where achievements have badges, these will be automatically shown, as well as th loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/achievements_unlocked_block.mp4" + src="../../assets/platform/emails/achievements_unlocked_block.mp4" > @@ -498,7 +498,7 @@ Where achievements have badges, these will be automatically shown, as well as th loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/progress_chart_block.mp4" + src="../../assets/platform/emails/progress_chart_block.mp4" > @@ -515,14 +515,14 @@ Where achievements have badges, these will be automatically shown, as well as th loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/streak_block.mp4" + src="../../assets/platform/emails/streak_block.mp4" > -### Email Variables +### Email Variables {#email-variables} Trophy provides an expansive set of email variables that can be used to insert highly relevant and personalized copy into the body of emails and subject lines. @@ -541,7 +541,7 @@ This will open up the variable editor window where you can configure variables a loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/email_variables_demo.mp4" + src="../../assets/platform/emails/email_variables_demo.mp4" > @@ -553,7 +553,7 @@ Variables related to the recipient of the email. - **Name**: The recipient's name, if set -Additionally all [custom user attributes](/platform/users#custom-user-attributes) are available as email variables. +Additionally all [custom user attributes](/en/platform/users#custom-user-attributes) are available as email variables. @@ -571,7 +571,7 @@ Each Trophy metric supports the following email variables: Additionally, the **Highest** and **Lowest** dynamic aliases support the same set of variables. When using these aliases, Trophy will pick the metric where the recipient has either the highest or lowest current total at send time. -When using metric variables, you also have the option of filtering the data that any of the above variables reference through a [custom event attribute](/platform/events#custom-event-attributes). +When using metric variables, you also have the option of filtering the data that any of the above variables reference through a [custom event attribute](/en/platform/events#custom-event-attributes). @@ -636,7 +636,7 @@ When configuring a template for use with achievement emails, the following varia -#### Advanced Usage +#### Advanced Usage {#advanced-usage} There are a couple of additional options to consider when using emails variables. @@ -649,7 +649,7 @@ You can provide a prefix and suffix for text variables like the recipients name, height="200" width="75%" noZoom - src="../assets/platform/emails/advanced_variables_prefix_suffix.png" + src="../../assets/platform/emails/advanced_variables_prefix_suffix.png" /> @@ -664,11 +664,11 @@ When using numeric variables like the recipients current points total or metric height="200" width="75%" noZoom - src="../assets/platform/emails/advanced_usage_singular_plural.png" + src="../../assets/platform/emails/advanced_usage_singular_plural.png" /> -### Text Variations +### Text Variations {#text-variations} Variations can be used to add randomness to text within emails sent by Trophy. This prevents emails from getting boring and helps improve open and click rates. @@ -697,7 +697,7 @@ To create a variation click the _Add Variation_ button on any block that support loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/creating_variations.mp4" + src="../../assets/platform/emails/creating_variations.mp4" > @@ -706,11 +706,11 @@ To create a variation click the _Add Variation_ button on any block that support tuned... -### Using The Editor +### Using The Editor {#using-the-editor} The email template editor is a blank canvas for designing emails that look great in the inbox. Using pre-configured [Blocks](#block-types) makes it really easy to create email templates that suit common gamification use cases. Here we'll walk through how to best use the editor to create awesome looking emails. -#### Adding Blocks +#### Adding Blocks {#adding-blocks} To add a new block an email template hit the key, this will open the block selection dialog where you can choose the block you want to add. @@ -721,7 +721,7 @@ To add a new block an email template hit the key, t loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/add_new_block_slash.mp4" + src="../../assets/platform/emails/add_new_block_slash.mp4" > @@ -734,11 +734,11 @@ Or if you want to add a block immediately after another block, use the block men loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/add_new_block_by_block_menu.mp4" + src="../../assets/platform/emails/add_new_block_by_block_menu.mp4" > -#### Arranging Blocks +#### Arranging Blocks {#arranging-blocks} Blocks can be dragged up and down using the block menu. @@ -749,11 +749,11 @@ Blocks can be dragged up and down using the block menu. loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/arranging_blocks.mp4" + src="../../assets/platform/emails/arranging_blocks.mp4" > -#### Text Formatting +#### Text Formatting {#text-formatting} The email editor offers rich text formatting including bold, italics, [hyperlinks](https://www.youtube.com/watch?v=dQw4w9WgXcQ) and `code` formatting. @@ -764,11 +764,11 @@ The email editor offers rich text formatting including bold, italics, [hyperlink loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/rich_text_formatting.mp4" + src="../../assets/platform/emails/rich_text_formatting.mp4" > -### Font Style +### Font Style {#font-style} On the [email configure page](https://app.trophy.so/emails/configure), you'll find a setting to change the font style used in all emails. @@ -777,13 +777,13 @@ On the [email configure page](https://app.trophy.so/emails/configure), you'll fi height="200" width="75%" noZoom - src="../assets/platform/emails/aggregation_period_setting.png" + src="../../assets/platform/emails/aggregation_period_setting.png" /> Other email styles are pulled from your Trophy account's [Branding](https://app.trophy.so/branding) settings. -## Handling Unsubscribes +## Handling Unsubscribes {#handling-unsubscribes} All emails that Trophy sends include an unsubscribe link and message. This is important for maintaining compliance with regulations and it's not possible to hide it. @@ -798,11 +798,11 @@ Any recipient that clicks this link will be taken to a branded page that allows As an alternative to the unsubscribe link, you can also build a preference center in your application using Trophy's [user preferences - API](/platform/users#notification-preferences). This gives users more granular + API](/en/platform/users#notification-preferences). This gives users more granular control over which types of emails they receive. -## Email Analytics +## Email Analytics {#email-analytics} Trophy has built-in analytics for all Emails that it sends. This includes: @@ -818,11 +818,11 @@ Trophy has built-in analytics for all Emails that it sends. This includes: loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/email_analytics.mp4" + src="../../assets/platform/emails/email_analytics.mp4" > -## Frequently Asked Questions +## Frequently Asked Questions {#frequently-asked-questions} @@ -838,6 +838,6 @@ Trophy has built-in analytics for all Emails that it sends. This includes: -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/en/platform/events.mdx b/en/platform/events.mdx index 792a440..585f468 100644 --- a/en/platform/events.mdx +++ b/en/platform/events.mdx @@ -5,19 +5,19 @@ description: Events are data objects that represent individual user interactions icon: radio --- -import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; -import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; -import IdempotentEventTracking from "/snippets/idempotent-event-tracking.mdx"; +import MetricChangeRequestBlock from "../../snippets/metric-change-request-block.mdx"; +import MetricChangeResponseBlock from "../../snippets/metric-change-response-block.mdx"; +import IdempotentEventTracking from "../../snippets/idempotent-event-tracking.mdx"; -## What are Events? +## What are Events? {#what-are-events} -Events represent individual user interactions against [Metrics](/platform/metrics) in Trophy. One event corresponds to a single interaction made by a single user. +Events represent individual user interactions against [Metrics](/en/platform/metrics) in Trophy. One event corresponds to a single interaction made by a single user. When you [integrate metrics](#tracking-metric-events) into your platform, you're setting up your platform to continuously stream events to your Trophy metrics for each user interaction. These interactions then drive all the gamification features you set up around these metrics. -## Key Attributes +## Key Attributes {#key-attributes} -### Event Value +### Event Value {#event-value} The `value` of an event is the numerical amount that will be added to the user's total metric count as a result of the user interaction it relates to. @@ -25,17 +25,17 @@ The `value` of an event is the numerical amount that will be added to the user's The value of an event can be positive or negative, and can be a whole number or a decimal. -## Custom Event Attributes +## Custom Event Attributes {#custom-event-attributes} - This feature is available on the [Pro](/account/billing#pro-plan) plan + This feature is available on the [Pro](/en/account/billing#pro-plan) plan @@ -47,7 +47,7 @@ Similarly a fitness app might use an 'Exercises Completed' metric and use a cust Using custom event attributes in this way allows you to enrich events in Trophy with additional context relevant to your use case and use it to power even more engaging gamification features. -### Creating Attributes +### Creating Attributes {#creating-attributes} To create a new custom event attribute, head to the metrics page in the Trophy dashboard and hit the _Add Event Attribute_ button. @@ -60,11 +60,11 @@ Give the attribute a name and a unique key, you'll use the key when referencing loop playsInline className="w-full aspect-15/4" - src="../assets/platform/events/create_custom_event_attribute.mp4" + src="../../assets/platform/events/create_custom_event_attribute.mp4" > -### Setting Attributes +### Setting Attributes {#setting-attributes} To set the value of a custom attribute on an event, pass its value in the `attributes` object in your metric tracking code. @@ -99,11 +99,11 @@ Here's an example of an event payload where the values of two attributes, `devic } ``` -### Using Attributes +### Using Attributes {#using-attributes} Custom event attributes can be used to power more advanced triggers for achievements and points and can be used in email templates to customize copy and to control the data shown in charts. -#### Advanced Feature Triggers +#### Advanced Feature Triggers {#advanced-feature-triggers} Custom event attributes can be used to set up achievements or points triggers that only track events with specific attribute values. Follow the links to the relevant pages below to learn more. @@ -111,7 +111,7 @@ Custom event attributes can be used to set up achievements or points triggers th Configure achievements that can only be unlocked by events with certain attribute values. @@ -119,16 +119,16 @@ Custom event attributes can be used to set up achievements or points triggers th Set up points triggers to only award points from events with specific attribute values. -#### Email Customization +#### Email Customization {#email-customization} -If you use any Trophy [Emails](/platform/emails), event attributes can be used to customize the data shown in certain email blocks. +If you use any Trophy [Emails](/en/platform/emails), event attributes can be used to customize the data shown in certain email blocks. Firstly, when using metric-based variables in email copy you can use event attributes to further control what data the variable refrences. @@ -141,7 +141,7 @@ For example here's a case where we use an email variable to tell users what thei loop playsInline className="w-full aspect-15/4" - src="../assets/platform/events/using_attributes_in_emails.mp4" + src="../../assets/platform/events/using_attributes_in_emails.mp4" > @@ -154,23 +154,23 @@ Secondly, here's an example where we add a chart to an email that shows users ho loop playsInline className="w-full aspect-15/4" - src="../assets/platform/events/using_attributes_in_email_charts.mp4" + src="../../assets/platform/events/using_attributes_in_email_charts.mp4" > There's a huge number of possibilities here, so get creative! -## Tracking Metric Events +## Tracking Metric Events {#tracking-metric-events} Each metric has a unique `key` which you can use to reference and track events against it in your code. You can find the `key` in the metric settings page. -To start tracking user interactions as events against your Trophy metrics, use the [Metrics API](/api-reference/endpoints/metrics/send-a-metric-change-event) or one of our type-safe [Client SDKs](/api-reference/client-libraries) supported in most major programming languages. +To start tracking user interactions as events against your Trophy metrics, use the [Metrics API](/en/api-reference/endpoints/metrics/send-a-metric-change-event) or one of our type-safe [Client SDKs](/en/api-reference/client-libraries) supported in most major programming languages. Here's an example where a fictional study platform is using a metric to track the number of flashcards flipped by each student. Each time an student interacts, the platform sends an event to Trophy telling it how many flashcards they viewed: -Any [Achievements](/platform/achievements), [Streaks](/platform/streaks), [Points](/platform/points) or [Leaderboards](/platform/leaderboards) that have been set up against this metric will be automatically processed, and the response will contain any updates to the user's progress that are a direct result of the event occurring: +Any [Achievements](/en/platform/achievements), [Streaks](/en/platform/streaks), [Points](/en/platform/points) or [Leaderboards](/en/platform/leaderboards) that have been set up against this metric will be automatically processed, and the response will contain any updates to the user's progress that are a direct result of the event occurring: @@ -187,7 +187,7 @@ With a little bit of custom code, this response data can be used to drive any in - Sound effects - Animations -Watch Charlie integrate metric tracking into a simple NextJS application using the Trophy [NodeJS SDK](/api-reference/client-libraries): +Watch Charlie integrate metric tracking into a simple NextJS application using the Trophy [NodeJS SDK](/en/api-reference/client-libraries): -### Idempotent Events +### Idempotent Events {#idempotent-events} Trophy supports enforcing uniqueness on events so that users cannot increase a metric by taking the same exact action over and over. @@ -212,10 +212,10 @@ For example, a language learning app could specify that users can only increase This helps keep your codebase free of logic that checks if users have completed actions before, and can instead trust Trophy to uphold the constraints you need. -To use idempotent events, use the `Idempotency-Key` header in the [metric event API](/api-reference/endpoints/metrics/send-a-metric-change-event). +To use idempotent events, use the `Idempotency-Key` header in the [metric event API](/en/api-reference/endpoints/metrics/send-a-metric-change-event). -[Learn more about idempotency](/api-reference/idempotency). +[Learn more about idempotency](/en/api-reference/idempotency). -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/en/platform/leaderboards.mdx b/en/platform/leaderboards.mdx index 4b38191..818dac2 100644 --- a/en/platform/leaderboards.mdx +++ b/en/platform/leaderboards.mdx @@ -5,10 +5,10 @@ description: Learn how to use Leaderboards in a gamified product experience with icon: /icons/leaderboard.svg --- -import LeaderboardSingleAttributeRequest from "/snippets/leaderboard-rankings-request-single-attribute.mdx"; -import LeaderboardMultiAttributeRequest from "/snippets/leaderboard-rankings-request-multiple-attributes.mdx"; +import LeaderboardSingleAttributeRequest from "../../snippets/leaderboard-rankings-request-single-attribute.mdx"; +import LeaderboardMultiAttributeRequest from "../../snippets/leaderboard-rankings-request-multiple-attributes.mdx"; -## What are Leaderboards? +## What are Leaderboards? {#what-are-leaderboards} Leaderboards are social competitions between users of your application. Use leaderboards to increase engagement and foster social interaction. @@ -17,27 +17,27 @@ Leaderboards are social competitions between users of your application. Use lead height="100" width="100%" noZoom - src="../assets/platform/leaderboards/hero.png" + src="../../assets/platform/leaderboards/hero.png" /> -## Types of Leaderboards +## Types of Leaderboards {#types-of-leaderboards} In this section we outline the different types of leaderboards supported in Trophy and when to use each one. -### Perpetual Leaderboards +### Perpetual Leaderboards {#perpetual-leaderboards} Perpetual leaderboards never reset. Once started they continually track and rank users progress over time forever, or until the configured [end date](#end-dates). Use perpetual leaderboards when you want to create all-time rankings of user activity. -### Repeating Leaderboards +### Repeating Leaderboards {#repeating-leaderboards} Repeating leaderboards can be configured to reset after any arbitrary number of days, months or years. In Trophy each instance of a repeating leaderboard is called a **'run'**. For example, a monthly leaderboard would have 12 runs in a year, but a daily leaderboard would have `n` runs in a month where `n` is the number of days in a given month. -Trophy tracks the rankings in each run of a repeating leaderboard individually and provides [APIs](/api-reference/endpoints/leaderboards/get-leaderboard) to fetch ranking data on historical runs. +Trophy tracks the rankings in each run of a repeating leaderboard individually and provides [APIs](/en/api-reference/endpoints/leaderboards/get-leaderboard) to fetch ranking data on historical runs. We recommend using repeating leaderboards over perpetual where possible as @@ -45,23 +45,23 @@ Trophy tracks the rankings in each run of a repeating leaderboard individually a users, helping to prevent leaderboards from becoming stale. -#### Handling Time Zones +#### Handling Time Zones {#handling-time-zones} -If you have tracked users' [time zones](/platform/users#param-tz) with Trophy, these will be used to ensure that each user has an equal chance of winning no matter where they are in the world. +If you have tracked users' [time zones](/en/platform/users#param-tz) with Trophy, these will be used to ensure that each user has an equal chance of winning no matter where they are in the world. In practice this means leaderboards are finalized and winners chosen about 12 hours after they naturally finish in UTC to allow users in all time zones to make their final push. -#### Tips for Weekly Leaderboards +#### Tips for Weekly Leaderboards {#tips-for-weekly-leaderboards} To create a weekly leaderboard, set up a [repeating leaderboard](#repeating-leaderboards) on a 7 day schedule and set the start date to be the next occurring first day of the week. While you wait for the start date to come around, the leaderboard will be in `scheduled` status and will automatically go live on the start date. -## Ranking Logic +## Ranking Logic {#ranking-logic} Leaderboards in Trophy are configurable to rank participants in a number of different ways to support common use cases. -### Ranking Methods +### Ranking Methods {#ranking-methods} The ranking method of a leaderboard determines on what dimension participants will be ordered. @@ -70,23 +70,23 @@ The ranking method of a leaderboard determines on what dimension participants wi height="200" width="50%" noZoom - src="../assets/platform/leaderboards/ranking_methods.png" + src="../../assets/platform/leaderboards/ranking_methods.png" /> -#### Metric Rankings +#### Metric Rankings {#metric-rankings} -Metric leaderboards are linked to an existing Trophy [Metric](/platform/metrics) and rank users based on their total metric value. +Metric leaderboards are linked to an existing Trophy [Metric](/en/platform/metrics) and rank users based on their total metric value. Use metric leaderboards if you only want to rank users based on a single interaction. -#### Points Rankings +#### Points Rankings {#points-rankings} -Points leaderboards are linked to an existing Trophy [Points System](/platform/points) and automatically rank users according to their total points. +Points leaderboards are linked to an existing Trophy [Points System](/en/platform/points) and automatically rank users according to their total points. Use a points leaderboard if you want to rank users based on a combination of metrics, achievements or other Trophy features. -#### Streak Rankings +#### Streak Rankings {#streak-rankings} Streak leaderboards rank users based on their current streak length. @@ -94,7 +94,7 @@ Streak leaderboards rank users based on their current streak length. Streak leaderboards can only be [perpetual](#perpetual-leaderboards). -### Ranking Breakdowns +### Ranking Breakdowns {#ranking-breakdowns} If you have a large user base, it's best practice to split up leaderboard participants into smaller, more socially-connected groups. This often leads to higher engagement than when using global leaderboards. @@ -116,11 +116,11 @@ Trophy will automatically start grouping users into smaller leaderboards based o loop playsInline className="w-full aspect-15/4" - src="../assets/platform/leaderboards/breakdowns.mp4" + src="../../assets/platform/leaderboards/breakdowns.mp4" > -To fetch rankings for a particular group of users with a specific attribute value, use the [leaderboard rankings API](/api-reference/endpoints/leaderboards/get-leaderboard), specifying the attribute value in the [`userAttributes` parameter](/api-reference/endpoints/leaderboards/get-leaderboard#parameter-user-attributes) as follows: +To fetch rankings for a particular group of users with a specific attribute value, use the [leaderboard rankings API](/en/api-reference/endpoints/leaderboards/get-leaderboard), specifying the attribute value in the [`userAttributes` parameter](/en/api-reference/endpoints/leaderboards/get-leaderboard#parameter-user-attributes) as follows: @@ -128,7 +128,7 @@ If you wish to fetch rankings for a particular group of users with a specific co -## Start & End Dates +## Start & End Dates {#start-end-dates} Use start and end dates to control the window within which leaderboards are actively ranking users. @@ -137,17 +137,17 @@ Use start and end dates to control the window within which leaderboards are acti height="200" width="50%" noZoom - src="../assets/platform/leaderboards/start_end_dates.png" + src="../../assets/platform/leaderboards/start_end_dates.png" /> -### Start Dates +### Start Dates {#start-dates} Leaderboards in Trophy can be set to start at a future date of your choice. This is often useful to allow some time for last minute changes or adjustments before leaderboards start ranking users. Leaderboards with a start date in the future are scheduled and automatically go live on the start date you choose. -### End Dates +### End Dates {#end-dates} Leaderboards in Trophy can have end dates. If you set an end date on a leaderboard then after that date it will enter `finished` status and rankings will be finalized and winners chosen. @@ -157,7 +157,7 @@ Leaderboards in Trophy can have end dates. If you set an end date on a leaderboa the end date according to their local clock. -## Participant Limits +## Participant Limits {#participant-limits} Leaderboards in Trophy have a maximum number of participants of **1,000**. However a leaderboard can be configured to have any arbitrary number of participants to support use cases like _Top 100_ or similar. @@ -166,7 +166,7 @@ Leaderboards in Trophy have a maximum number of participants of **1,000**. Howev height="200" width="50%" noZoom - src="../assets/platform/leaderboards/max_participants.png" + src="../../assets/platform/leaderboards/max_participants.png" /> @@ -183,7 +183,7 @@ For more background on the negative effects on global leaderboards, read this [b 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. -## Creating Leaderboards +## Creating Leaderboards {#creating-leaderboards} To create a leaderboard, head to the [leaderboards page](https://app.trophy.so/leaderboards) in the Trophy dashboard and hit the _New Leaderboard_ button. @@ -194,7 +194,7 @@ To create a leaderboard, head to the [leaderboards page](https://app.trophy.so/l loop playsInline className="w-full aspect-15/4" - src="../assets/platform/leaderboards/creating_leaderboards.mp4" + src="../../assets/platform/leaderboards/creating_leaderboards.mp4" > @@ -228,11 +228,11 @@ To create a leaderboard, head to the [leaderboards page](https://app.trophy.so/l -## Managing Leaderboards +## Managing Leaderboards {#managing-leaderboards} Leaderboards in Trophy have a number of statuses to help you control when users and how users can join them. -### Leaderboard Statuses +### Leaderboard Statuses {#leaderboard-statuses} Leaderboards can have one of the following statuses: @@ -256,14 +256,14 @@ If you decide you no longer need a leaderboard, you can move it to `Archived` st Once a leaderboard is archived, it can only be restored by contacting support. -## Displaying Leaderboards +## Displaying Leaderboards {#displaying-leaderboards} - Check out our [full guide](/guides/how-to-build-a-leaderboards-feature) on + Check out our [full guide](/en/guides/how-to-build-a-leaderboards-feature) on adding leaderboards to your app for more details. -## Leaderboard Analytics +## Leaderboard Analytics {#leaderboard-analytics} Trophy has built-in analytics to help you understand how users are engaging with your leaderboards. @@ -272,11 +272,11 @@ Trophy has built-in analytics to help you understand how users are engaging with height="200" width="50%" noZoom - src="../assets/platform/leaderboards/analytics.png" + src="../../assets/platform/leaderboards/analytics.png" /> -### Total Unique Participants +### Total Unique Participants {#total-unique-participants} This chart shows how many unique users have participated in any run of a leaderboard over time. This is useful to understand how many of your users actually take part in leaderboards and how [participant limits](#participant-limits) are affecting this. @@ -285,11 +285,11 @@ This chart shows how many unique users have participated in any run of a leaderb height="200" width="50%" noZoom - src="../assets/platform/leaderboards/total_unique_participants.png" + src="../../assets/platform/leaderboards/total_unique_participants.png" /> -### Active Users +### Active Users {#active-users} This chart show the number of users who have changed rank at least once in a given leaderboard. This is useful to get a sense of how competitive the average user is in a particular leaderboard. @@ -298,11 +298,11 @@ This chart show the number of users who have changed rank at least once in a giv height="200" width="50%" noZoom - src="../assets/platform/leaderboards/active_users.png" + src="../../assets/platform/leaderboards/active_users.png" /> -### Rank Changes +### Rank Changes {#rank-changes} This chart shows the total number of rank changes in a particular leaderboard over time. This is useful to understand how competitive users are across the board. @@ -311,11 +311,11 @@ This chart shows the total number of rank changes in a particular leaderboard ov height="200" width="50%" noZoom - src="../assets/platform/leaderboards/rank_changes.png" + src="../../assets/platform/leaderboards/rank_changes.png" /> -### Score Distribution +### Score Distribution {#score-distribution} This chart is a histogram of users' scores in a particular leaderboard. This is useful to get a sense of how bunched up or spread out users are, and what sections of the rankings are the most competitive. @@ -324,11 +324,11 @@ This chart is a histogram of users' scores in a particular leaderboard. This is height="200" width="50%" noZoom - src="../assets/platform/leaderboards/score_distribution.png" + src="../../assets/platform/leaderboards/score_distribution.png" /> -## Frequently Asked Questions +## Frequently Asked Questions {#frequently-asked-questions} @@ -348,6 +348,6 @@ This chart is a histogram of users' scores in a particular leaderboard. This is -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/en/platform/metrics.mdx b/en/platform/metrics.mdx index 55a7c03..3664344 100644 --- a/en/platform/metrics.mdx +++ b/en/platform/metrics.mdx @@ -5,7 +5,7 @@ description: Learn how to model user interactions using Trophy Metrics. Model an icon: box --- -## What are Metrics? +## What are Metrics? {#what-are-metrics} All gamification features are centered around user interactions. At Trophy, we use the term **Metrics** to refer to the data objects that model those interactions within your web or mobile app. @@ -18,22 +18,22 @@ Metrics are un-opinionated, meaning they can be used to model absolutely any use - Words written - ... -Metrics are the building blocks of the infrastructure that powers Trophy's gamification features. Each user interaction that relates to a metric is stored as an [Event](/platform/events). +Metrics are the building blocks of the infrastructure that powers Trophy's gamification features. Each user interaction that relates to a metric is stored as an [Event](/en/platform/events). Events are stored and processed in chronological order. At any particular point in time, the combined state of a user's event history reflects their overall progress on your platform. -## Key Attributes +## Key Attributes {#key-attributes} Here we describe the key attributes that allow you to create metrics to best fit your use case. -### Units +### Units {#units} You can easily assign units to metrics in the dashboard for either: - Arbitrary numbers (tasks, posts, messages etc.) - Currencies ($, £, €) -## Creating Metrics +## Creating Metrics {#creating-metrics} To create a Metric, head over to the [Metrics](https://app.trophy.so/metrics) page within Trophy and hit the **New Metric** button: @@ -44,7 +44,7 @@ To create a Metric, head over to the [Metrics](https://app.trophy.so/metrics) pa loop playsInline className="w-full aspect-video" - src="../assets/platform/metrics/create_new_metric.mp4" + src="../../assets/platform/metrics/create_new_metric.mp4" > @@ -62,7 +62,7 @@ To create a Metric, head over to the [Metrics](https://app.trophy.so/metrics) pa -## Metric Analytics +## Metric Analytics {#metric-analytics} Trophy has a built-in analytics dashboard for each metric you create. It shows you: @@ -74,25 +74,25 @@ Trophy has a built-in analytics dashboard for each metric you create. It shows y -### Achievement Completion Chart +### Achievement Completion Chart {#achievement-completion-chart} The achievement completion chart shows the current state of the userbase in terms of the number of users who have completed each achievement that you've configured against this metric. -[Learn more about achievements](/platform/achievements). +[Learn more about achievements](/en/platform/achievements). -## Frequently Asked Questions +## Frequently Asked Questions {#frequently-asked-questions} @@ -107,6 +107,6 @@ The achievement completion chart shows the current state of the userbase in term -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/en/platform/overview.mdx b/en/platform/overview.mdx index 0d282a5..bdcc1b5 100644 --- a/en/platform/overview.mdx +++ b/en/platform/overview.mdx @@ -6,34 +6,34 @@ description: Learn the key concepts behind Trophy's gamification system. Trophy is built around a set of key concepts that all gamification experiences have in common, while still offering enough customization for you to build any kind of gamified experience you need. -## Platform Concepts +## Platform Concepts {#platform-concepts} Trophy's platform concepts are scalable building blocks that support all of Trophy's features and are the main things for developers to get comfortable with. - + Model how users interact with your application. - + Track user interactions against metrics. - + Tell Trophy about the people using your application. -## Gamification Concepts +## Gamification Concepts {#gamification-concepts} Trophy's gamification concepts are flexible primitives built on the infrastructure concepts that support a wide range of common gamification use cases. - + Encourage users to make continued progress or to take specific actions. - + Motivate users to build regular usage habits. - + Reward users with sophisticated points systems. } - href="/platform/leaderboards" + href="/en/platform/leaderboards" > Create friendly competitions to increase user engagement. - + Deliver personalized lifecycle emails to users to increase retention. Drive automated notification flows using personalized gamification data -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/en/platform/points.mdx b/en/platform/points.mdx index fffab11..ca14ba0 100644 --- a/en/platform/points.mdx +++ b/en/platform/points.mdx @@ -5,35 +5,35 @@ description: Learn how to build points-based systems using Trophy icon: sparkle --- -import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; -import MetricChangeEventPointsLevelExcerpt from "/snippets/metric-change-event-points-level-excerpt-block.mdx"; -import PointsHistogramSummaryResponse from "/snippets/points-histogram-summary-response-block.mdx"; -import PointsLevelSummaryResponse from "/snippets/points-level-summary-response-block.mdx"; -import PointsLevelsListResponse from "/snippets/points-levels-list-response-block.mdx"; -import PointsSystemResponse from "/snippets/points-system-response-block.mdx"; -import UserPointsResponse from "/snippets/user-points-response-block.mdx"; -import UserPointsEventSummaryResponse from "/snippets/user-points-summary-response-block.mdx"; -import WebhookPointsLevelChangedPayload from "/snippets/webhook-points-level-changed-payload-block.mdx"; +import MetricChangeResponseBlock from "../../snippets/metric-change-response-block.mdx"; +import MetricChangeEventPointsLevelExcerpt from "../../snippets/metric-change-event-points-level-excerpt-block.mdx"; +import PointsHistogramSummaryResponse from "../../snippets/points-histogram-summary-response-block.mdx"; +import PointsLevelSummaryResponse from "../../snippets/points-level-summary-response-block.mdx"; +import PointsLevelsListResponse from "../../snippets/points-levels-list-response-block.mdx"; +import PointsSystemResponse from "../../snippets/points-system-response-block.mdx"; +import UserPointsResponse from "../../snippets/user-points-response-block.mdx"; +import UserPointsEventSummaryResponse from "../../snippets/user-points-summary-response-block.mdx"; +import WebhookPointsLevelChangedPayload from "../../snippets/webhook-points-level-changed-payload-block.mdx"; -## What is a Points System? +## What is a Points System? {#what-is-a-points-system} -Points systems are used to create counters that track users' interactions with [Metrics](/platform/metrics), [Achievements](/platform/achievements) and [Streaks](/platform/streaks). You can then build features like 'XP' and 'Energy' around these counters within your product. +Points systems are used to create counters that track users' interactions with [Metrics](/en/platform/metrics), [Achievements](/en/platform/achievements) and [Streaks](/en/platform/streaks). You can then build features like 'XP' and 'Energy' around these counters within your product. -## Use Cases +## Use Cases {#use-cases} -### Rewards +### Rewards {#rewards} Points systems can be used to create features like 'XP' or 'Gems' that reward users for a number of interactions at different rates. In this way points can be used to weight the value of certain interactions differently to others to reward users for taking the actions you consider most closely correlated to retention. -### Metering +### Metering {#metering} Points systems can also be used to create features like 'Energy' that meter usage of your product in a way that gives you control over promoting and restricting user activity. This allows you to control the rate at which users can use your product with a flexible mechanic that sits outside your codebase. -## Creating Points Systems +## Creating Points Systems {#creating-points-systems} Trophy let's you set up multiple points systems for different use cases within your application. @@ -44,7 +44,7 @@ Trophy let's you set up multiple points systems for different use cases within y loop playsInline className="w-full aspect-video" - src="../assets/platform/points/create_system.mp4" + src="../../assets/platform/points/create_system.mp4" > @@ -74,37 +74,37 @@ To create a points system, head to the [points page](https://app.trophy.so/point -## Points Triggers +## Points Triggers {#points-triggers} In Trophy, points are awarded to or deducted from users through triggers. These define the different mechanics that make up your points system. You can add as many triggers as you like to each points system you set up, allowing you to create different logic for how points are awarded or deducted for different points systems. -### Types of Triggers +### Types of Triggers {#types-of-triggers} There are multiple types of triggers in Trophy that can be used to award or deduct points in different ways. -#### Metric Triggers +#### Metric Triggers {#metric-triggers} -Points can be awarded or deducted continually as users increment [Metrics](/platform/metrics). You can choose to award or deduct any arbitrary number of points at any arbitrary metric threshold, for example "award 10 points for every 3 tasks completed". +Points can be awarded or deducted continually as users increment [Metrics](/en/platform/metrics). You can choose to award or deduct any arbitrary number of points at any arbitrary metric threshold, for example "award 10 points for every 3 tasks completed". -#### Streak Triggers +#### Streak Triggers {#streak-triggers} -Points can be awarded or deducted for reaching any arbitrary length of a [Streak](/platform/streaks), for example "award 50 points for every 7 days streak". +Points can be awarded or deducted for reaching any arbitrary length of a [Streak](/en/platform/streaks), for example "award 50 points for every 7 days streak". -#### Achievement Triggers +#### Achievement Triggers {#achievement-triggers} -Points can be awarded or deducted when users unlock specific [Achievements](/platform/achievements), for example "award 100 points when users completed the `profile-completed` achievement". +Points can be awarded or deducted when users unlock specific [Achievements](/en/platform/achievements), for example "award 100 points when users completed the `profile-completed` achievement". -#### Time-based Triggers +#### Time-based Triggers {#time-based-triggers} Points can be awarded or deducted at repeating time intervals, every hour or every day. For example "award 10 points every 3 hours". -#### User Identification Triggers +#### User Identification Triggers {#user-identification-triggers} Points can be awarded when users are first identified in Trophy, useful for granting an initial amount of points when they sign up to your product. -### Creating Triggers +### Creating Triggers {#creating-triggers} To create a new points trigger, head to the points system that you want to create a trigger for and follow the steps below. @@ -120,7 +120,7 @@ To create a new points trigger, head to the points system that you want to creat loop playsInline className="w-full aspect-video" - src="../assets/platform/points/create_trigger.mp4" + src="../../assets/platform/points/create_trigger.mp4" > @@ -152,9 +152,9 @@ To create a new points trigger, head to the points system that you want to creat You can assign attribute filters to a points trigger to further restrict when they apply. -- To limit a **Metric trigger** to only apply to events with specific [custom event attributes](/platform/events#custom-event-attributes), select an attribute and enter a value in the **Event Attribute** section. +- To limit a **Metric trigger** to only apply to events with specific [custom event attributes](/en/platform/events#custom-event-attributes), select an attribute and enter a value in the **Event Attribute** section. -- To limit any type of trigger to only apply to a user with one or more specific [custom user attributes](/platform/users#custom-user-attributes), add attributes and the desired values in the **User Attributes** section. +- To limit any type of trigger to only apply to a user with one or more specific [custom user attributes](/en/platform/users#custom-user-attributes), add attributes and the desired values in the **User Attributes** section. @@ -166,7 +166,7 @@ To create a new points trigger, head to the points system that you want to creat -## Balancing Points +## Balancing Points {#balancing-points} Running an effective points system requires finding the optimal pace at which users earn points. Too fast, and users will get points fatigue, rendering them useless. Too slow, and users may get bored and churn. @@ -179,31 +179,31 @@ Trophy's preview tool can model different scenarios to help you determine how fr loop playsInline className="w-full aspect-video" - src="../assets/platform/points/points_preview.mp4" + src="../../assets/platform/points/points_preview.mp4" > -## Points Boosts +## Points Boosts {#points-boosts} Points boosts are multipliers that you can use to increase the number of points awarded to users during a specific time period. This section explains how boosts work and how they can be used to increase your application's retention and engagement. -### Boost Targeting +### Boost Targeting {#boost-targeting} There are a few different ways you can use boosts in Trophy. -- **Global boosts** multiply points earned by all users during the boost window, or can be scoped to a specific user cohort by using [custom user attributes](/platform/users#custom-user-attributes). +- **Global boosts** multiply points earned by all users during the boost window, or can be scoped to a specific user cohort by using [custom user attributes](/en/platform/users#custom-user-attributes). - **User-specific boosts** only multiply points earned by a single user within the boost window. Typically, global boosts are used to increase platform-wide engagement during key calendar events like Black Friday/Cyber Monday in e-commerce, New Year in fitness and exam season in ed-tech platforms. Conversely, user-specific boosts are commonly used to provide additional incentives for users to take actions that are correlated with higher retention. -### Creating Boosts +### Creating Boosts {#creating-boosts} -User-specific boosts can only be created programmatically through the [Admin API](/admin-api/endpoints/points/create-boosts). +User-specific boosts can only be created programmatically through the [Admin API](/en/admin-api/endpoints/points/create-boosts). To create a global points boost in the Trophy dashboard, follow the steps below. @@ -215,7 +215,7 @@ To create a global points boost in the Trophy dashboard, follow the steps below. loop playsInline className="w-full aspect-15/4" - src="../assets/platform/points/create_points_boost.mp4" + src="../../assets/platform/points/create_points_boost.mp4" > @@ -235,7 +235,7 @@ To create a global points boost in the Trophy dashboard, follow the steps below. - This feature requires [custom user attributes](/platform/users#custom-user-attributes) which are available on the [Pro](/account/billing#pro-plan) plan. + This feature requires [custom user attributes](/en/platform/users#custom-user-attributes) which are available on the [Pro](/en/account/billing#pro-plan) plan. If you want your boost to only affect a specific user cohort, set user attribute conditions that match those of your target cohort. Trophy will take care of only applying the boost to points earned by users with matching user attribute values. @@ -245,7 +245,7 @@ To create a global points boost in the Trophy dashboard, follow the steps below. height="200" width="100%" noZoom - src="../assets/platform/points/points_boost_attributes.png" + src="../../assets/platform/points/points_boost_attributes.png" /> @@ -254,13 +254,13 @@ To create a global points boost in the Trophy dashboard, follow the steps below. -### Boost Multipliers +### Boost Multipliers {#boost-multipliers} Every boost has a single multiplier value that increases the total points earned by users affected by that boost. Boost multipliers must be positive numbers, but can be decimals to support scenarios where percentage boosts like '50% more points' may be required. -### Boost Stacking +### Boost Stacking {#boost-stacking} Points boosts in Trophy stack through [multiplication](#boost-multipliers). This is to support allowing users to benefit from multiple boosts simultaneously. @@ -270,9 +270,9 @@ To demonstrate stacking consider a 2X, 1.5X and a 3X boost all active within the Overall Multiplier (3 boosts) = 2X * 1.5X * 3X = 9X ``` -### Boost Rounding +### Boost Rounding {#boost-rounding} -Trophy supports [floating point metric event values](/platform/events#event-value), but takes care of rounding points to integers automatically. +Trophy supports [floating point metric event values](/en/platform/events#event-value), but takes care of rounding points to integers automatically. However, when using decimal [boost multipliers](#boost-multipliers), there may be some scenarios where this default rounding is not enough to always produce integer points values. @@ -286,9 +286,9 @@ Therefore Trophy offers three rounding modes to provide additional control on ro In scenarios where points are used to mimic platform credit mechanics, it's recommended to use the default rounding down behavior to protect against slippage. -Simply choose your preferred rounding mode when [creating boosts](/platform/points#creating-boosts) through the Trophy dashboard. +Simply choose your preferred rounding mode when [creating boosts](/en/platform/points#creating-boosts) through the Trophy dashboard. -## Points Levels +## Points Levels {#points-levels} Points levels are discrete milestones you define on a points system in Trophy. @@ -296,7 +296,7 @@ Each level has a threshold. When a user's total points in the system exceeds thi Use levels for rank tiers, progression UI, reward tiers, or analytics. Trophy keeps each user's current level in sync whenever they earn or lose points in that system. -### Configuring levels +### Configuring levels {#configuring-levels} To set up levels for a points system, open it from the [points page](https://app.trophy.so/points) in the Trophy dashboard and use the levels tab to add or manage levels. @@ -307,7 +307,7 @@ To set up levels for a points system, open it from the [points page](https://app loop playsInline className="w-full aspect-video" - src="../assets/platform/points/create_points_level.mp4" + src="../../assets/platform/points/create_points_level.mp4" > @@ -328,29 +328,29 @@ To set up levels for a points system, open it from the [points page](https://app -### Displaying Levels +### Displaying Levels {#displaying-levels} Most teams combine three approaches: a static picture of every level (for progression screens), the user’s current level (for headers and profile), and immediate feedback when an action pushes them into a new level. -#### Displaying All Levels +#### Displaying All Levels {#displaying-all-levels} -Call the [get levels API](/api-reference/endpoints/points/get-points-levels) once per points system key (for example on app load or from your server when rendering a progression page). +Call the [get levels API](/en/api-reference/endpoints/points/get-points-levels) once per points system key (for example on app load or from your server when rendering a progression page). The response is an array of levels with `key`, `name`, `description`, optional `badgeUrl`, and the `points` threshold for each level. By binding your UI to the Trophy API response, you'll ensure that it automatically updates when you make changes to levels in the Trophy dashboard. -#### Displaying User Level +#### Displaying User Level {#displaying-user-level} -The [user points API](/api-reference/endpoints/users/get-a-users-points) returns the user’s `total`, their current `level` object, and recent `awards`. The `level` field contains the user's current level, or null if no levels exist or they have not reached the points threshold for any level yet. +The [user points API](/en/api-reference/endpoints/users/get-a-users-points) returns the user’s `total`, their current `level` object, and recent `awards`. The `level` field contains the user's current level, or null if no levels exist or they have not reached the points threshold for any level yet. Pair `total` with the ordered level list from the previous step to draw a progress bar between the current threshold and the next. -#### Level Change Notifications +#### Level Change Notifications {#level-change-notifications} -When you send a [metric change event](/api-reference/endpoints/metrics/send-a-metric-change-event), the response will include a `points` map keyed by your points system keys for each system that changed as a result of the event. +When you send a [metric change event](/en/api-reference/endpoints/metrics/send-a-metric-change-event), the response will include a `points` map keyed by your points system keys for each system that changed as a result of the event. The data contains a `level` key only when the user’s level changed because of this event. If their level stayed the same, that key is omitted, so you can safely treat a present `level` as a level change signal without extra bookkeeping. @@ -367,26 +367,26 @@ if (pts?.level) { } ``` -The same `level` behavior applies when points change from an [achievement completion](/api-reference/endpoints/achievements/mark-an-achievement-as-completed). +The same `level` behavior applies when points change from an [achievement completion](/en/api-reference/endpoints/achievements/mark-an-achievement-as-completed). -For server-driven notifications (email, push, CRM) that must not depend on the client seeing the HTTP response, subscribe to the [`points.level_changed`](/webhooks/events/points/points-level-changed) webhook instead. +For server-driven notifications (email, push, CRM) that must not depend on the client seeing the HTTP response, subscribe to the [`points.level_changed`](/en/webhooks/events/points/points-level-changed) webhook instead. This webhook fires when a user’s level changes as a result of earning or losing points, and includes `previousLevel` and `newLevel`. -#### Account-level analytics +#### Account-level analytics {#account-level-analytics} -The [get level summary API](/api-reference/endpoints/points/get-points-level-summary) returns how many users are currently at each level which is useful for admin dashboards, funnel views, or balancing progression. +The [get level summary API](/en/api-reference/endpoints/points/get-points-level-summary) returns how many users are currently at each level which is useful for admin dashboards, funnel views, or balancing progression. -## Displaying Points +## Displaying Points {#displaying-points} There are a few ways to use Trophy to fetch and display points in your app. For working examples check out our guides on adding an [XP - feature](/guides/how-to-build-an-xp-feature) or an [energy - feature](/guides/how-to-build-an-energy-feature) to your web or mobile app. + feature](/en/guides/how-to-build-an-xp-feature) or an [energy + feature](/en/guides/how-to-build-an-energy-feature) to your web or mobile app. @@ -396,15 +396,15 @@ There are a few ways to use Trophy to fetch and display points in your app. loop playsInline className="w-full aspect-video" - src="../assets/platform/points/displaying_points.mp4" + src="../../assets/platform/points/displaying_points.mp4" > -### Triggering Transactional UI +### Triggering Transactional UI {#triggering-transactional-ui} -Firstly, any points awarded to or deducted from users as a result of a metric change event are returned in the response when using the [metric change event API](/api-reference/endpoints/metrics/send-a-metric-change-event). +Firstly, any points awarded to or deducted from users as a result of a metric change event are returned in the response when using the [metric change event API](/en/api-reference/endpoints/metrics/send-a-metric-change-event). -The response includes the user's new total points, how many points were awarded or deducted as a result of the event, and the details of the specific points triggers that fired. When [Points Levels](#points-levels) are enabled, each points object in that response may also include **`level`**: Trophy includes the user's **new** level **only when it changed** as a result of that event. The same optional `level` behavior applies when points change from an [achievement completion](/api-reference/endpoints/achievements/mark-an-achievement-as-completed). +The response includes the user's new total points, how many points were awarded or deducted as a result of the event, and the details of the specific points triggers that fired. When [Points Levels](#points-levels) are enabled, each points object in that response may also include **`level`**: Trophy includes the user's **new** level **only when it changed** as a result of that event. The same optional `level` behavior applies when points change from an [achievement completion](/en/api-reference/endpoints/achievements/mark-an-achievement-as-completed). Example `POST /metrics/{key}/event` response (fields match the Trophy API spec; your keys under `points` and `leaderboards` depend on your account): @@ -415,11 +415,11 @@ This makes it really simple to read the response and trigger any of the followin - Displaying in-app notifications and pop-ups - Playing sound effects -### Displaying User's Points +### Displaying User's Points {#displaying-users-points} Trophy also has APIs that allow you fetch user's points data whenever you want. -First, the [user points API](/api-reference/endpoints/users/get-a-users-points) returns the user's total points for a particular points system, their current **`level`** (or `null` if levels are not in use or they have not reached a level yet), and up to 100 of the most recent events that awarded points to or deducted points from them. +First, the [user points API](/en/api-reference/endpoints/users/get-a-users-points) returns the user's total points for a particular points system, their current **`level`** (or `null` if levels are not in use or they have not reached a level yet), and up to 100 of the most recent events that awarded points to or deducted points from them. You can use this API to display the user's total points anywhere in your platform as well as a 'Latest awards' section or similar. @@ -428,10 +428,10 @@ You can use this API to display the user's total points anywhere in your platfor - + -Then, the [user points summary API](/api-reference/endpoints/users/get-a-users-points-summary) can be used to fetch historical points data for a particular user. +Then, the [user points summary API](/en/api-reference/endpoints/users/get-a-users-points-summary) can be used to fetch historical points data for a particular user. Data can be aggregated daily, weekly or monthly between a start and end date. Use this API to display points progress charts to users over any time frame. @@ -443,46 +443,46 @@ Example `GET /users/{id}/points/{key}/event-summary` **200** response (Trophy AP -### Displaying Aggregate Data +### Displaying Aggregate Data {#displaying-aggregate-data} Additionally there are a number of APIs that can be used to fetch and display points data at the account level. -First, the [points summary API](/api-reference/endpoints/points/get-points-summary) returns aggregate points system data across your entire user base. +First, the [points summary API](/en/api-reference/endpoints/points/get-points-summary) returns aggregate points system data across your entire user base. -Use this data to display a histogram of points for a particular points system and show users how they compare to others on the platform. When you use [Points Levels](#points-levels), the [level summary API](/api-reference/endpoints/points/get-points-level-summary) complements this with a per-level user count. +Use this data to display a histogram of points for a particular points system and show users how they compare to others on the platform. When you use [Points Levels](#points-levels), the [level summary API](/en/api-reference/endpoints/points/get-points-level-summary) complements this with a per-level user count. `GET /points/{key}/summary` — example **200** response (Trophy API spec): -Finally, the [points system API](/api-reference/endpoints/points/get-points) returns the system metadata and triggers. Example **200** response (Trophy API spec): +Finally, the [points system API](/en/api-reference/endpoints/points/get-points) returns the system metadata and triggers. Example **200** response (Trophy API spec): The points summary API can also be filtered to only return data for users with specific - [custom user attributes](/platform/users#custom-user-attributes). + [custom user attributes](/en/platform/users#custom-user-attributes). - + -Use the [points system API](/api-reference/endpoints/points/get-points) response to show users how they can earn points on your platform. Any new triggers you add will automatically be returned from this API, reducing code changes in your platform and shifting operations to Trophy. +Use the [points system API](/en/api-reference/endpoints/points/get-points) response to show users how they can earn points on your platform. Any new triggers you add will automatically be returned from this API, reducing code changes in your platform and shifting operations to Trophy. -## Points Analytics +## Points Analytics {#points-analytics} Trophy has built-in analytics to track points awards for each points system you configure across your users in real time including: @@ -495,10 +495,10 @@ Trophy has built-in analytics to track points awards for each points system you -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/en/platform/push-notifications.mdx b/en/platform/push-notifications.mdx index 5a89462..be9b705 100644 --- a/en/platform/push-notifications.mdx +++ b/en/platform/push-notifications.mdx @@ -5,12 +5,12 @@ description: Learn how to use push notifications in a gamified product experienc icon: bell --- -import IdentifyUserWithDeviceTokensRequestBlock from "/snippets/identify-user-with-device-tokens-request-block.mdx"; -import MetricEventWithDeviceTokensRequestBlock from "/snippets/metric-event-with-device-tokens-request-block.mdx"; +import IdentifyUserWithDeviceTokensRequestBlock from "../../snippets/identify-user-with-device-tokens-request-block.mdx"; +import MetricEventWithDeviceTokensRequestBlock from "../../snippets/metric-event-with-device-tokens-request-block.mdx"; Trophy can send automated push notifications to users based on key triggers without requiring any code. Here we’ll look at what these triggers are, and how they can form part of your product’s gamification experience. -## Types Of Push Notifications +## Types Of Push Notifications {#types-of-push-notifications} Trophy supports 4 types of push notifications, each of which is designed to suit a common scenario in building gamification experiences. @@ -23,20 +23,20 @@ All push notifications are optional, but all four can be used simultaneously and loop playsInline className="w-full aspect-15/4" - src="../assets/platform/push/push_notification_types.mp4" + src="../../assets/platform/push/push_notification_types.mp4" > -- **Achievement notifications** are sent to users each time they unlock an [Achievement](/platform/achievements). +- **Achievement notifications** are sent to users each time they unlock an [Achievement](/en/platform/achievements). - **Recap notifications** are sent to users on a pre-defined frequency to summarize progress. Recap notifications can be configured to be sent daily, weekly, monthly or yearly depending on your use case. - **Reactivation notifications** are sent to users after they become inactive with the goal of bringing them back to your app. -- **Streak notifications** are automatically sent to users reminding them to extend their [Streak](/platform/streaks). +- **Streak notifications** are automatically sent to users reminding them to extend their [Streak](/en/platform/streaks). -## Supported Channels +## Supported Channels {#supported-channels} Trophy supports sending push notifications using 3 channels which can all be configured on the [Channels](https://app.trophy.so/push/channels) page of the Trophy dashboard. -### Apple Push Notification Service (APNs) +### Apple Push Notification Service (APNs) {#apple-push-notification-service-apns} Trophy supports sending push notifications to users via APNs through a certificate-based connection. @@ -53,7 +53,7 @@ To send push notifications using APNs, you'll need to provide Trophy with the fo guide](https://developer.apple.com/documentation/usernotifications/establishing-a-certificate-based-connection-to-apns). -### Firebase Cloud Messaging (FCM) +### Firebase Cloud Messaging (FCM) {#firebase-cloud-messaging-fcm} Trophy supports sending push notifications to users via FCM. To achieve this you'll need to provide Trophy with your Firebase Service Account JSON in the following format: @@ -82,7 +82,7 @@ Trophy supports sending push notifications to users via FCM. To achieve this you guide](https://firebase.google.com/support/guides/service-accounts). -### Expo Push Service +### Expo Push Service {#expo-push-service} Trophy supports sending push notifications to users via Expo Push Service. @@ -99,13 +99,13 @@ To achieve this you must provide Trophy with a few key details from your Expo ac guide](https://docs.expo.dev/push-notifications/sending-notifications). -## Sending Push Notifications +## Sending Push Notifications {#sending-push-notifications} Follow the steps below to start sending push notifications using Trophy. Users can control which push notifications they receive through Trophy's [user - preferences API](/platform/users#notification-preferences). This allows you to + preferences API](/en/platform/users#notification-preferences). This allows you to build a preference center in your application where users can opt in or out of specific notification types. @@ -123,7 +123,7 @@ Follow the steps below to start sending push notifications using Trophy. loop playsInline className="w-full aspect-15/4" - src="../assets/platform/push/push_channels.mp4" + src="../../assets/platform/push/push_channels.mp4" > @@ -142,7 +142,7 @@ Follow the steps below to start sending push notifications using Trophy. height="200" width="100%" noZoom - src="../assets/platform/push/create_push_template.png" + src="../../assets/platform/push/create_push_template.png" /> @@ -173,7 +173,7 @@ Follow the steps below to start sending push notifications using Trophy. On the [configure page](https://app.trophy.so/push/configure), activate one template in each type section you want to enable. Once activated, Trophy will start sending push notifications automatically. - Users can control which push notification types they receive through Trophy's [user preferences API](/platform/users#notification-preferences). When a user has disabled a notification type in their preferences, Trophy will respect that setting and won't send those notifications to that user. + Users can control which push notification types they receive through Trophy's [user preferences API](/en/platform/users#notification-preferences). When a user has disabled a notification type in their preferences, Trophy will respect that setting and won't send those notifications to that user. @@ -184,14 +184,14 @@ Follow the steps below to start sending push notifications using Trophy. loop playsInline className="w-full aspect-15/4" - src="../assets/platform/push/activating_templates.mp4" + src="../../assets/platform/push/activating_templates.mp4" > -## Designing Push Notifications +## Designing Push Notifications {#designing-push-notifications} By default, Trophy provides a template for each [type of push notification](#types-of-push-notifications) as a good starting point. The default templates can’t be changed, but you can duplicate and customize these as you wish. @@ -199,7 +199,7 @@ By default, Trophy provides a template for each [type of push notification](#typ You can also create blank templates if you just want to start from scratch. -### Template Structure +### Template Structure {#template-structure} Each push notification has a title and body, and Trophy's no-code push notification template editor allows you to fully customize both fields. @@ -208,11 +208,11 @@ Each push notification has a title and body, and Trophy's no-code push notificat height="200" width="50%" noZoom - src="../assets/platform/push/push_template_editor.png" + src="../../assets/platform/push/push_template_editor.png" /> -### Using Variables +### Using Variables {#using-variables} Trophy provides an expansive set of variables that can be used to insert highly relevant and personalized data into your push notifications. @@ -227,11 +227,11 @@ This will open up the variable editor window where you can configure variables a loop playsInline className="w-full aspect-15/4" - src="../assets/platform/push/push_variables.mp4" + src="../../assets/platform/push/push_variables.mp4" > -### Using Variations +### Using Variations {#using-variations} Variations can be used to add randomness to the title and body of push notifications sent by Trophy. This prevents notifications from getting boring and helps improve engagement rates. @@ -244,11 +244,11 @@ At send time, Trophy automatically picks one of your variations of the title and loop playsInline className="w-full aspect-15/4" - src="../assets/platform/push/push_variations.mp4" + src="../../assets/platform/push/push_variations.mp4" > -### Using Conditions +### Using Conditions {#using-conditions} Trophy's push notification template builder supports using conditions to send different content to each recipient based on their unique context. @@ -263,10 +263,10 @@ At send time Trophy computes all conditions using each recipients unique context loop playsInline className="w-full aspect-15/4" - src="../assets/platform/push/push_conditions.mp4" + src="../../assets/platform/push/push_conditions.mp4" > -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/en/platform/streaks.mdx b/en/platform/streaks.mdx index 3d69234..bfe043e 100644 --- a/en/platform/streaks.mdx +++ b/en/platform/streaks.mdx @@ -5,25 +5,25 @@ description: Learn how to use streaks in a gamified product experience with Trop icon: flame --- -import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; +import MetricChangeResponseBlock from "../../snippets/metric-change-response-block.mdx"; -## What are Streaks? +## What are Streaks? {#what-are-streaks} A streak is a period of consecutive days, weeks or months that a user has performed a key action on your platform. Streaks have been shown to meaningfully increase retention, particularly when the user action being tracked aligns with the core value of your product. -## Streak Frequency +## Streak Frequency {#streak-frequency} Streaks in Trophy can be **daily, weekly or monthly**. This means that a user must meet their [streak conditions](#streak-conditions) at least once every calendar day, week or month to maintain their streak. -If you've [configured time zones](/platform/users#param-tz) for your users, Trophy will automatically track each user's streak in their local time zone (including taking into account when users change time zones) and keep streaks in tact. +If you've [configured time zones](/en/platform/users#param-tz) for your users, Trophy will automatically track each user's streak in their local time zone (including taking into account when users change time zones) and keep streaks in tact. Trophy automatically computes streak data for every streak frequency, meaning you can switch at any time. -## Streak Conditions +## Streak Conditions {#streak-conditions} -In Trophy you can set the thresholds that a user must meet in order to extend their streak based on your configured [Metrics](/platform/metrics). +In Trophy you can set the thresholds that a user must meet in order to extend their streak based on your configured [Metrics](/en/platform/metrics). You can choose which metrics should be part of your streak, and for those that you chose, you can set a custom threshold that users must meet. @@ -32,7 +32,7 @@ You can choose which metrics should be part of your streak, and for those that y height="200" width="100%" noZoom - src="../assets/platform/streaks/streak_config.png" + src="../../assets/platform/streaks/streak_config.png" /> @@ -42,7 +42,7 @@ When combining metrics in this way, you can choose between two evaluation modes: In this way you can design your streak logic to match your application use case. -## Streak Freezes +## Streak Freezes {#streak-freezes} Streak freezes help users keep their streaks for longer by allowing them to miss periods without it resetting to zero. This helps keep streaks motivating even if users don't maintain a perfect usage habit. @@ -51,13 +51,13 @@ Streak freezes help users keep their streaks for longer by allowing them to miss height="200" width="100%" noZoom - src="../assets/platform/streaks/freezes_config.png" + src="../../assets/platform/streaks/freezes_config.png" /> Streak freezes are optional in Trophy but can be configured on the [streaks page](https://app.trophy.so/streaks) of the Trophy dashboard. -### Granting Initial Freezes +### Granting Initial Freezes {#granting-initial-freezes} You can configure any number of arbitrary freezes to grant to new users when you first identify them with Trophy. @@ -65,36 +65,36 @@ You can configure any number of arbitrary freezes to grant to new users when you Giving users too many freezes may decrease their perceived value, but granting too few freezes might result it a higher number of lost streaks. A good starting point is one freeze per user, but the trick is to experiment! -### Freeze Accumulation +### Freeze Accumulation {#freeze-accumulation} As users use up streak freezes, they'll need a continuous supply of new ones to keep them going. To facilitate this, Trophy can automatically grant streak freezes to users over time. You can choose an arbitrary number of days over which to grant an arbitrary number of freezes to each user. -If you've [configured time zones](/platform/users#param-tz) for your users, Trophy will automatically consume freezes at midnight in the user's time zone when necessary to extend their streak, and if any new freezes are due to be granted to a user, they will be granted up to ten minutes later. +If you've [configured time zones](/en/platform/users#param-tz) for your users, Trophy will automatically consume freezes at midnight in the user's time zone when necessary to extend their streak, and if any new freezes are due to be granted to a user, they will be granted up to ten minutes later. -### Maximum Freeze Count +### Maximum Freeze Count {#maximum-freeze-count} In Trophy you also configure the maximum number of freezes that each user can have, up to a limit of 100. [Freeze accumulation](#freeze-accumulation) will only ever grant freezes up to this limit. -## Tracking Streaks +## Tracking Streaks {#tracking-streaks} -Trophy automatically calculates streaks for all users based on the [metric events](/platform/events#tracking-metric-events) you report to Trophy. +Trophy automatically calculates streaks for all users based on the [metric events](/en/platform/events#tracking-metric-events) you report to Trophy. There's no extra work required to track streaks, and you can start using them right away. Just make sure that streaks are enabled in the Trophy dashboard. -For a full walk through on how to set up a streak feature using Trophy, check out our [official guide](/guides/how-to-build-a-streaks-feature). +For a full walk through on how to set up a streak feature using Trophy, check out our [official guide](/en/guides/how-to-build-a-streaks-feature). -## Managing Streaks +## Managing Streaks {#managing-streaks} This section outlines some of the operations you can perform to manage user's streaks in your application. -### Restoring A Users Streak +### Restoring A Users Streak {#restoring-a-users-streak} To restore a user's streak, head to the user details page and use the 'Restore Streak' action. Restoring a user's streak sets it to the length it was when they last lost it. -You can also use the [restore streak admin API](/admin-api/endpoints/streaks/restore-streaks) to restore streaks programmatically. +You can also use the [restore streak admin API](/en/admin-api/endpoints/streaks/restore-streaks) to restore streaks programmatically. @@ -104,22 +104,22 @@ You can also use the [restore streak admin API](/admin-api/endpoints/streaks/res loop playsInline className="w-full aspect-15/4" - src="../assets/platform/streaks/restoring_streaks.mp4" + src="../../assets/platform/streaks/restoring_streaks.mp4" > -## Displaying Streaks +## Displaying Streaks {#displaying-streaks} Trophy exposes streak data in two ways, which can be used to build UI elements within your applications and display streaks to users. - Check out our [full guide](/guides/how-to-build-a-streaks-feature) on adding a + Check out our [full guide](/en/guides/how-to-build-a-streaks-feature) on adding a streaks feature to your app for more details. -### Metric Event Response +### Metric Event Response {#metric-event-response} -When you [increment a metric](/platform/events#tracking-metric-events) for a user, the [metric API](/api-reference/endpoints/metrics/send-a-metric-change-event) response will include the user's current +When you [increment a metric](/en/platform/events#tracking-metric-events) for a user, the [metric API](/en/api-reference/endpoints/metrics/send-a-metric-change-event) response will include the user's current streak. @@ -129,9 +129,9 @@ This can be used to transactionally trigger UI/UX elements including: - Showing in-app pop-ups - Playing sound effects -### User Streaks API +### User Streaks API {#user-streaks-api} -The [user streaks API](/api-reference/endpoints/users/get-a-users-streak) returns the current streak for a single user, along with their recent streak history. Use the [`historyPeriods`](/api-reference/endpoints/users/get-a-users-streak#parameter-history-periods) query parameter to control how many periods to return. +The [user streaks API](/en/api-reference/endpoints/users/get-a-users-streak) returns the current streak for a single user, along with their recent streak history. Use the [`historyPeriods`](/en/api-reference/endpoints/users/get-a-users-streak#parameter-history-periods) query parameter to control how many periods to return. {/* vale off */} @@ -192,13 +192,13 @@ Use this data to display a user's streak history within your application. -### List Multiple User's Streaks +### List Multiple User's Streaks {#list-multiple-users-streaks} -If you want to display streaks for multiple users at once, for example to support a friend streak or user group feature, then use the [list user streaks API](/api-reference/endpoints/streaks/get-streaks). +If you want to display streaks for multiple users at once, for example to support a friend streak or user group feature, then use the [list user streaks API](/en/api-reference/endpoints/streaks/get-streaks). ```json [expandable] [ @@ -220,6 +220,6 @@ If you want to display streaks for multiple users at once, for example to suppor ] ``` -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/en/platform/users.mdx b/en/platform/users.mdx index 37506a4..d0a79b1 100644 --- a/en/platform/users.mdx +++ b/en/platform/users.mdx @@ -5,22 +5,22 @@ description: Learn how to track interactions across your userbase using Trophy. icon: users --- -import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; -import IdentifyUserRequestBlock from "/snippets/identify-user-request-block.mdx"; -import UpdateUserNotificationPreferencesBlock from "/snippets/update-user-notification-preferences-block.mdx"; -import GetUserNotificationPreferencesBlock from "/snippets/get-user-notification-preferences-block.mdx"; +import MetricChangeRequestBlock from "../../snippets/metric-change-request-block.mdx"; +import IdentifyUserRequestBlock from "../../snippets/identify-user-request-block.mdx"; +import UpdateUserNotificationPreferencesBlock from "../../snippets/update-user-notification-preferences-block.mdx"; +import GetUserNotificationPreferencesBlock from "../../snippets/get-user-notification-preferences-block.mdx"; -## What are Users? +## What are Users? {#what-are-users} Users are the individual people that use your product. You tell Trophy about your users through APIs and use the dashboard to design gamification experiences around them. Users must be individuals, they cannot be companies or organisations. To create organizational structures or user groupings, consider using a [custom user attribute](#custom-user-attributes). -## Key Attributes +## Key Attributes {#key-attributes} Key attributes are properties of users controlled and managed by Trophy and are for things like `id` or `email`, some are required while others are optional. -### Required Attributes +### Required Attributes {#required-attributes} Trophy only requires one key attribute, `id`. Every user you tell Trophy about must have an `id`, this is what identifies them as a unique person. @@ -33,7 +33,7 @@ Trophy only requires one key attribute, `id`. Every user you tell Trophy about m in your database instead of needing to manage another one just for Trophy. -### Optional Attributes +### Optional Attributes {#optional-attributes} Additionally, you can tell Trophy about any of the following optional key attributes and it will make them available to you as part of your gamification experience: @@ -44,7 +44,7 @@ Additionally, you can tell Trophy about any of the following optional key attrib The user's email address. This address will be used in any - [Emails](/platform/emails) that you set up as part of your gamification + [Emails](/en/platform/emails) that you set up as part of your gamification experience with Trophy. @@ -64,28 +64,28 @@ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; - If you've configured any Trophy [Emails](/platform/emails), then they'll only + If you've configured any Trophy [Emails](/en/platform/emails), then they'll only be sent to a user when this field is true. Note: If you don't provide an `email`, attempting to set this field to true will result in an error. The list of device tokens to associate with the user. If you've configured any - Trophy [Push Notifications](/platform/push-notifications), then they'll only + Trophy [Push Notifications](/en/platform/push-notifications), then they'll only be sent to a user when this field is provided. -## Custom User Attributes +## Custom User Attributes {#custom-user-attributes} - This feature is available on the [Pro](/account/billing#pro-plan) plan + This feature is available on the [Pro](/en/account/billing#pro-plan) plan @@ -95,7 +95,7 @@ For example a language learning app might use a custom user attribute for the la Custom user attributes let you tell Trophy about this contextual information and use it to personalize gamification features. -### Creating Attributes +### Creating Attributes {#creating-attributes} To create a new custom user attribute, head to the [attributes tab](https://app.trophy.so/users/attributes) of the users page in the Trophy dashboard and hit the _Add User Attribute_ button. @@ -108,13 +108,13 @@ Give the attribute a name and a unique key. The key is what you'll use to refere loop playsInline className="w-full aspect-15/4" - src="../assets/platform/users/create_custom_user_attribute.mp4" + src="../../assets/platform/users/create_custom_user_attribute.mp4" > -### Setting Attributes +### Setting Attributes {#setting-attributes} -Attributes can be assigned values for specific users using their unique key either inline, as users increment metrics through the [metric increment API](/api-reference/endpoints/metrics/send-a-metric-change-event), or explicitly through the [user identification](/api-reference/endpoints/users/identify-a-user), [create user](/api-reference/endpoints/users/create-a-user), or [update user](api-reference/endpoints/users/update-a-user) APIs. +Attributes can be assigned values for specific users using their unique key either inline, as users increment metrics through the [metric increment API](/en/api-reference/endpoints/metrics/send-a-metric-change-event), or explicitly through the [user identification](/en/api-reference/endpoints/users/identify-a-user), [create user](/en/api-reference/endpoints/users/create-a-user), or [update user](api-reference/endpoints/users/update-a-user) APIs. Trophy will only set values of attributes that have first been created @@ -148,11 +148,11 @@ Across all APIs, the schema for setting attribute values is consistent. Here's a } ``` -### Using Attributes +### Using Attributes {#using-attributes} Use custom user attributes across Trophy to personalize gamification features , aggregate and filter data returned from APIs, and to segment and compare user cohorts in analytics. -#### Feature Personalization +#### Feature Personalization {#feature-personalization} Custom user attributes can be used to personalize achievements, customize the way points are earned by different users and more. Follow the links to the relevant pages below to learn more. @@ -160,34 +160,34 @@ Custom user attributes can be used to personalize achievements, customize the wa Configure achievements that can only be unlocked by specific users. Personalize how different users earn points. Control which users receive gamified emails. Personalize email copy and subject lines with custom user attributes. -#### Data Aggregation and Filtering +#### Data Aggregation and Filtering {#data-aggregation-and-filtering} You can use custom user attributes to aggregate and filter data returned from some APIs to support a wide range of gamification use cases. In all cases, attributes are included in the `userAttributes` query parameter using a consistent schema: @@ -195,9 +195,9 @@ You can use custom user attributes to aggregate and filter data returned from so Here's a list of all APIs that support the `userAttributes` query parameter: -- [Points Summary](/api-reference/endpoints/points/get-points-summary) +- [Points Summary](/en/api-reference/endpoints/points/get-points-summary) -#### Segmented Analytics +#### Segmented Analytics {#segmented-analytics} Custom user attributes can be used to segment and compare retention and engagement charts between user groups. @@ -210,11 +210,11 @@ This is useful to understand which cohorts are making full use of your gamificat loop playsInline className="w-full aspect-15/4" - src="../assets/platform/users/segmenting_analytics.mp4" + src="../../assets/platform/users/segmenting_analytics.mp4" > -## Identifying Users +## Identifying Users {#identifying-users} When you tell Trophy about a user in your platform, we call this **identification**. There are two ways you can identify users with Trophy, [inline](#inline-identification) and [explicit](#explicit-identification) identification. @@ -223,11 +223,11 @@ When you tell Trophy about a user in your platform, we call this **identificatio Trophy. If you decide you need more control, try explicit identification. -### Inline Identification +### Inline Identification {#inline-identification} Inline identification is the easiest way of telling Trophy about your users as it doesn't require any specific user identification code. You simply tell Trophy about users as they go about normal use of your platform. -In practice this means whenever you use the [Metric Event API](/api-reference/endpoints/metrics/send-a-metric-change-event), you pass along full details of the user who triggered the event. +In practice this means whenever you use the [Metric Event API](/en/api-reference/endpoints/metrics/send-a-metric-change-event), you pass along full details of the user who triggered the event. Here's an example where an API call is made to the metric events API, and the details of the user who made the interaction are passed along in the request body: @@ -268,7 +268,7 @@ In this way inline identification allows you to keep your entire userbase in con This is why we recommend starting with inline identification first, then exploring explicit identification if you discover you need more control. -### Explicit Identification +### Explicit Identification {#explicit-identification} Explicit identification is when you write code in your application that explicitly tells Trophy about users separate from any metric interactions. This is useful if you want complete control over how and when Trophy learns about your users. @@ -278,7 +278,7 @@ Scenarios where you might want to use explicit identification might be: - You track a lot of metrics in Trophy, and don't want to repeat inline identification code. - You want to only tell Trophy about a specific cohort of users, of which you control the conditions around -In this case, you can tell Trophy about new users using the [User Identification API](/api-reference/endpoints/users/identify-a-user). +In this case, you can tell Trophy about new users using the [User Identification API](/en/api-reference/endpoints/users/identify-a-user). @@ -288,13 +288,13 @@ In this case, you can tell Trophy about new users using the [User Identification with Trophy automatically. -## Creating Users +## Creating Users {#creating-users} -To explicitly create a new user in Trophy, use the [Create User API](/api-reference/endpoints/users/create-a-user). +To explicitly create a new user in Trophy, use the [Create User API](/en/api-reference/endpoints/users/create-a-user). -## Keeping Users Up To Date +## Keeping Users Up To Date {#keeping-users-up-to-date} -To tell Trophy about an update to a user in your platform you can use the [Update User API](/api-reference/endpoints/users/update-a-user). +To tell Trophy about an update to a user in your platform you can use the [Update User API](/en/api-reference/endpoints/users/update-a-user). Any properties that you pass to Trophy will be updated to the new values you specify. @@ -305,23 +305,23 @@ Any properties that you pass to Trophy will be updated to the new values you spe update API is there if you really need it. -## Setting User Preferences +## Setting User Preferences {#setting-user-preferences} Trophy has APIs to help you power a preference center that your users can use to control and customize how they interact with your gamification features. This section walks through all the preferences that you can expose to your users, and their function. -### Notification Preferences +### Notification Preferences {#notification-preferences} -You can use Trophy to send gamified [Emails](/platform/emails) and [Push Notifications](/platform/push-notifications) to your users. +You can use Trophy to send gamified [Emails](/en/platform/emails) and [Push Notifications](/en/platform/push-notifications) to your users. -Trophy's [update preferences API](/api-reference/endpoints/users/update-user-preferences) allows you to build a preference center within your application that your users can interact with to control which notifications they receive, and through which channels. +Trophy's [update preferences API](/en/api-reference/endpoints/users/update-user-preferences) allows you to build a preference center within your application that your users can interact with to control which notifications they receive, and through which channels. @@ -338,7 +338,7 @@ For example, the following code snippet updates a user's notification preference -To build a preference center UI in your application, you'll first need to fetch the user's current preferences using the [get preferences API](/api-reference/endpoints/users/get-user-preferences). +To build a preference center UI in your application, you'll first need to fetch the user's current preferences using the [get preferences API](/en/api-reference/endpoints/users/get-user-preferences). This will return the user's current notification settings, which you can then display in your UI and allow users to modify. @@ -363,13 +363,13 @@ You can use this response to: - **Show current state**: Display which notifications are enabled and through which channels - **Handle missing preferences**: If a notification type is not present in the response, it means the user hasn't set preferences for it yet, and you can default to your application's default settings -Once you've fetched and displayed the preferences, users can make changes in your UI, and you can use the [update preferences API](/api-reference/endpoints/users/update-user-preferences) to save their selections back to Trophy. +Once you've fetched and displayed the preferences, users can make changes in your UI, and you can use the [update preferences API](/en/api-reference/endpoints/users/update-user-preferences) to save their selections back to Trophy. -## Retrieving User Information +## Retrieving User Information {#retrieving-user-information} -To fetch the details of a user that you've already identified with Trophy, use the [Get User API](/api-reference/endpoints/users/get-a-single-user). +To fetch the details of a user that you've already identified with Trophy, use the [Get User API](/en/api-reference/endpoints/users/get-a-single-user). -This will return the full details of the user along with the `control` attribute that you can use to conditionally enroll users in any gamification features. Learn more about [Experimentation](/experimentation/overview). +This will return the full details of the user along with the `control` attribute that you can use to conditionally enroll users in any gamification features. Learn more about [Experimentation](/en/experimentation/overview). ```json Response {3} { @@ -384,9 +384,9 @@ This will return the full details of the user along with the `control` attribute } ``` -## User Analytics +## User Analytics {#user-analytics} -### Basic Analytics +### Basic Analytics {#basic-analytics} By default Trophy includes high-level user analytics including on the [Users page](https://app.trophy.so/users) including: @@ -395,18 +395,18 @@ By default Trophy includes high-level user analytics including on the [Users pag - The number of users that are active on a monthly basis - + On this page you can also search through every user that Trophy has recorded, which can be useful for debugging. -### Top Users +### Top Users {#top-users} Additionally, on the [Dashboard](https://app.trophy.so), Trophy shows a _Top Users_ list with the set of users who have made the most progress against your platform's metrics. These are your most engaged users, so it's useful to know who they are! -## Frequently Asked Questions +## Frequently Asked Questions {#frequently-asked-questions} @@ -420,7 +420,7 @@ These are your most engaged users, so it's useful to know who they are! @@ -430,6 +430,6 @@ These are your most engaged users, so it's useful to know who they are! -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/en/webhooks/events/achievements/achievement-completed.mdx b/en/webhooks/events/achievements/achievement-completed.mdx index 674b47d..a671229 100644 --- a/en/webhooks/events/achievements/achievement-completed.mdx +++ b/en/webhooks/events/achievements/achievement-completed.mdx @@ -4,5 +4,5 @@ openapi: webhook achievement.completed This webhook doesn't fire for achievements that are - [backdated](/platform/achievements#backdating-achievements). + [backdated](/en/platform/achievements#backdating-achievements). diff --git a/en/webhooks/events/leaderboards/leaderboard-finished.mdx b/en/webhooks/events/leaderboards/leaderboard-finished.mdx index 727719c..84da2ae 100644 --- a/en/webhooks/events/leaderboards/leaderboard-finished.mdx +++ b/en/webhooks/events/leaderboards/leaderboard-finished.mdx @@ -11,5 +11,5 @@ openapi: webhook leaderboard.finished Events only include the top 10 user rankings. To fetch more, use the [single - leaderboard API](/api-reference/endpoints/leaderboards/get-leaderboard). + leaderboard API](/en/api-reference/endpoints/leaderboards/get-leaderboard). diff --git a/en/webhooks/events/points/points-changed.mdx b/en/webhooks/events/points/points-changed.mdx index c83305b..76efb3c 100644 --- a/en/webhooks/events/points/points-changed.mdx +++ b/en/webhooks/events/points/points-changed.mdx @@ -4,5 +4,5 @@ openapi: webhook points.changed Does not apply to [user identification trigger - type](/platform/points#types-of-triggers). + type](/en/platform/points#types-of-triggers). diff --git a/en/webhooks/events/points/points-level-changed.mdx b/en/webhooks/events/points/points-level-changed.mdx index 2c1ecdd..352b965 100644 --- a/en/webhooks/events/points/points-level-changed.mdx +++ b/en/webhooks/events/points/points-level-changed.mdx @@ -3,5 +3,5 @@ openapi: webhook points.level_changed --- - This event fires when a user's **level** changes within a points system **because they earned or lost points**. It is separate from [`points.changed`](/webhooks/events/points/points-changed), which reflects points balance changes more broadly. Use `points.level_changed` when you care specifically about level transitions. + This event fires when a user's **level** changes within a points system **because they earned or lost points**. It is separate from [`points.changed`](/en/webhooks/events/points/points-changed), which reflects points balance changes more broadly. Use `points.level_changed` when you care specifically about level transitions. diff --git a/en/webhooks/events/streaks/streak-extended.mdx b/en/webhooks/events/streaks/streak-extended.mdx index 1635d2b..52552be 100644 --- a/en/webhooks/events/streaks/streak-extended.mdx +++ b/en/webhooks/events/streaks/streak-extended.mdx @@ -3,6 +3,6 @@ openapi: webhook streak.extended --- - Does not apply to [streak freeze](/platform/streaks#streak-freezes) + Does not apply to [streak freeze](/en/platform/streaks#streak-freezes) consumption. diff --git a/en/webhooks/idempotency.mdx b/en/webhooks/idempotency.mdx index 1168888..17c2b6f 100644 --- a/en/webhooks/idempotency.mdx +++ b/en/webhooks/idempotency.mdx @@ -5,15 +5,15 @@ subtitle: Learn how to ensure your webhook endpoints only process events once to icon: repeat --- -## What is Idempotency? +## What is Idempotency? {#what-is-idempotency} In the context of webhooks, [idempotence](https://en.wikipedia.org/wiki/Idempotence) is the property of an endpoint to ensure it processes duplicate events exactly once. Most webhook emitters, including Trophy, operate on an "at least once" delivery guarantee. This means you may eventually receive the same webhook multiple times and your code needs to handle those scenarios without unintended consequences. -For example, if your webhook handler is subscribed to [`leaderboard.finished`](/webhooks/events/leaderboards/leaderboard-finished) events and sends users gift cards if they place in the top 10, then your code needs to only send the gift cards once even if your code receives the same event from Trophy multiple times. +For example, if your webhook handler is subscribed to [`leaderboard.finished`](/en/webhooks/events/leaderboards/leaderboard-finished) events and sends users gift cards if they place in the top 10, then your code needs to only send the gift cards once even if your code receives the same event from Trophy multiple times. -## Implementing Webhook Idempotency +## Implementing Webhook Idempotency {#implementing-webhook-idempotency} Making your webhook handlers more robust is trivial in most cases. Let's take the gift card example above. @@ -52,6 +52,6 @@ if (!receipt) { If your code receives a duplicate event, the gift card will not be sent again. -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/en/webhooks/introduction.mdx b/en/webhooks/introduction.mdx index 9a396e2..2ea5708 100644 --- a/en/webhooks/introduction.mdx +++ b/en/webhooks/introduction.mdx @@ -5,7 +5,7 @@ subtitle: Learn about webhooks in Trophy and how to use them to power custom gam --- - This feature is available on the [Pro](/account/billing#pro-plan) plan + This feature is available on the [Pro](/en/account/billing#pro-plan) plan Trophy has support for a number of webhooks that fire based on key events such as `achievement.completed`, `leaderboard.finished` and `streak.lost`. @@ -17,24 +17,24 @@ All Trophy webhooks are delivered over HTTP which allows customers to subscribe Read more about getting started with webhooks below. - + Start receiving webhook events from Trophy in under 10 minutes. - + Secure webhook endpoints using signature verification. - + Learn about how Trophy handles event retries. - + Handle duplicate events and prevent unintended side effects. - + Learn how to monitor webhook delivery and diagnose issues. -## Common Use Cases +## Common Use Cases {#common-use-cases} Common use cases include: @@ -48,6 +48,6 @@ Common use cases include: case and we'll build it!. -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/en/webhooks/observability.mdx b/en/webhooks/observability.mdx index ad88a33..e26ad03 100644 --- a/en/webhooks/observability.mdx +++ b/en/webhooks/observability.mdx @@ -5,7 +5,7 @@ subtitle: Learn how to monitor and diagnose issues with webhook delivery. icon: activity --- -## Monitoring Webhook Delivery +## Monitoring Webhook Delivery {#monitoring-webhook-delivery} The Trophy dashboard has built-in analytics for all webhook events sent to your endpoints, including viewing the status of sent events and the payload. @@ -14,14 +14,14 @@ The Trophy dashboard has built-in analytics for all webhook events sent to your height="200" width="100%" noZoom - src="../assets/webhooks/observability/logs.png" + src="../../assets/webhooks/observability/logs.png" /> -## Data Retention +## Data Retention {#data-retention} Trophy retains all webhook logs for **7 days**. If you would like to discuss increasing your data retention window, please [get in touch](mailto:support@trophy.so) and we'll happily discuss it with you. -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/en/webhooks/quickstart.mdx b/en/webhooks/quickstart.mdx index 21cdf92..3cf49a3 100644 --- a/en/webhooks/quickstart.mdx +++ b/en/webhooks/quickstart.mdx @@ -5,11 +5,11 @@ subtitle: Start receiving webhook events from Trophy in under 10 minutes. icon: circle-play --- -## Getting Started +## Getting Started {#getting-started} If you don't already have a Trophy account, follow the [Trophy quick start - guide](/getting-started/quickstart) first to get started. + guide](/en/getting-started/quickstart) first to get started. In Trophy you can have multiple webhooks for handling one or more types of event. Follow the steps below to get started receiving webhook events from Trophy. @@ -41,7 +41,7 @@ Then, set up this endpoint to proxy events it receives to your local development height="200" width="100%" noZoom - src="../assets/webhooks/quickstart/hookdeck.png" + src="../../assets/webhooks/quickstart/hookdeck.png" /> @@ -64,7 +64,7 @@ Then, set up this endpoint to proxy events it receives to your local development height="200" width="100%" noZoom - src="../assets/webhooks/quickstart/dashboard.png" + src="../../assets/webhooks/quickstart/dashboard.png" /> @@ -99,7 +99,7 @@ Trophy will send requests to your handler, telling it what `type` of event it's ``` - Make sure your webhook handler is set up to return a `200` status code. If Trophy detects your handler returns a non-`2XX` status code, it will treat this as a failure and [retry the request](/webhooks/retries). + Make sure your webhook handler is set up to return a `200` status code. If Trophy detects your handler returns a non-`2XX` status code, it will treat this as a failure and [retry the request](/en/webhooks/retries). @@ -129,7 +129,7 @@ Grab your secure webhook secret from the webhooks page in Trophy. loop playsInline className="w-full aspect-15/4" - src="../assets/webhooks/copy_secret.mp4" + src="../../assets/webhooks/copy_secret.mp4" > @@ -176,7 +176,7 @@ Once you have your webhook secret, you're ready to start validating events. Here -## Full Webhook Example +## Full Webhook Example {#full-webhook-example} Here's a full working NextJS example webhook endpoint capable of securely receiving webhook events from Trophy. @@ -232,6 +232,6 @@ function handleEvent(payload: WebhookPayload) { } ``` -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/en/webhooks/retries.mdx b/en/webhooks/retries.mdx index 1f48a2b..cb36008 100644 --- a/en/webhooks/retries.mdx +++ b/en/webhooks/retries.mdx @@ -16,6 +16,6 @@ Trophy will automatically retry any event that fails for one of the following re with a 1 minute interval. -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/en/webhooks/security.mdx b/en/webhooks/security.mdx index dab5c74..556f281 100644 --- a/en/webhooks/security.mdx +++ b/en/webhooks/security.mdx @@ -5,13 +5,13 @@ subtitle: Build secure webhook endpoints using webhook signature verification. icon: shield-check --- -## Webhook Signatures +## Webhook Signatures {#webhook-signatures} To help you secure your webhook handlers so that they only respond to events sent from Trophy and not malicious attackers, Trophy includes a webhook signature with every event. This signature is sent in the `X-Trophy-Signature` header and is a `base64` encoded hash of the request payload, hashed using a secure webhook secret provided by Trophy. -## Webhook Secrets +## Webhook Secrets {#webhook-secrets} Each webhook you set up in Trophy has a unique webhook secret which you can access from the [webhooks page](https://app.trophy.so/integration/webhooks) in the Trophy dashboard. @@ -20,7 +20,7 @@ Each webhook you set up in Trophy has a unique webhook secret which you can acce and do not commit it to source control. -## Securing Webhook Handlers +## Securing Webhook Handlers {#securing-webhook-handlers} To validate that events your webhook handler receives do actually come from Trophy, you need to create your own hash using your secure [webhook secret](#webhook-secrets) and compare it to the [webhook signature](#webhook-signatures) in the `X-Trophy-Signature` header. @@ -31,7 +31,7 @@ To validate that events your webhook handler receives do actually come from Trop loop playsInline className="w-full aspect-15/4" - src="../assets/webhooks/copy_secret.mp4" + src="../../assets/webhooks/copy_secret.mp4" > @@ -62,6 +62,6 @@ Once you have your webhook secret, you're ready to start validating events. Here important to reject the request as early as possible with a `4XX` status code. -## Get Support +## Get Support {#get-support} Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help! diff --git a/es/account/billing.mdx b/es/account/billing.mdx index a0b2f49..a1a305b 100644 --- a/es/account/billing.mdx +++ b/es/account/billing.mdx @@ -7,9 +7,9 @@ og:description: Aprende cómo Trophy controla los costos cobrando según los icon: gauge --- -import { PlanBadge } from "/snippets/plan-badge.jsx"; +import { PlanBadge } from "../../snippets/plan-badge.jsx"; -## Facturación basada en uso +## Facturación basada en uso {#usage-based-billing} Trophy sigue un modelo de precios basado en uso donde los clientes solo pagan por las unidades de uso que consumen. Para Trophy, una unidad de uso corresponde a un único [Usuario Activo Mensual](##monthly-active-users-maus) (MAU). @@ -18,9 +18,9 @@ Trophy sigue un modelo de precios basado en uso donde los clientes solo pagan po costos según tu uso esperado. -## Usuarios Activos Mensuales (MAU) +## Usuarios Activos Mensuales (MAU) {#monthly-active-users-maus} -Trophy define un MAU como un único usuario que envía al menos un [evento métrico](/platform/events) a Trophy en un mes determinado. +Trophy define un MAU como un único usuario que envía al menos un [evento métrico](/es/platform/events) a Trophy en un mes determinado. Ten en cuenta que **nunca pagas por usuarios que abandonan**. Si un usuario se registra en tu @@ -28,7 +28,7 @@ Trophy define un MAU como un único usuario que envía al menos un [evento métr y nunca más. -## Nivel gratuito +## Nivel gratuito {#free-tier} El nivel gratuito permite a los equipos probar y evaluar Trophy hasta **100 MAU** sin generar cargos por uso. @@ -37,11 +37,11 @@ El nivel gratuito permite a los equipos probar y evaluar Trophy hasta **100 MAU* nos pondremos en contacto contigo directamente con un recordatorio amigable para actualizar. -## Planes de pago +## Planes de pago {#paid-plans} Trophy tiene dos planes de pago, [Starter](#starter-plan) y [Pro](#pro-plan). -### Plan Starter +### Plan Starter {#starter-plan} El plan starter es para clientes que han superado la fase de prueba y evaluación y están usando Trophy en implementaciones de producción a pequeña escala. @@ -52,11 +52,11 @@ A diferencia del [Nivel gratuito](#free-tier), el plan starter no tiene límites Pro](#pro-plan). -### Plan Pro +### Plan Pro {#pro-plan} El plan pro está diseñado para clientes que utilizan Trophy en implementaciones de mayor escala o que requieren [funciones](#features) más avanzadas. -### Límites del Plan +### Límites del Plan {#plan-allowances} Aquí hay una comparación de los límites de cada plan de pago: @@ -67,7 +67,7 @@ Aquí hay una comparación de los límites de cada plan de pago: | Emails incluidos | `5,000` | `50,000` | | Notificaciones push incluidas | `5,000` | `50,000` | -### Excedentes +### Excedentes {#overages} Los excedentes se cobran en los planes de pago por encima de los [límites](#plan-allowances) incluidos a las siguientes tarifas: @@ -79,33 +79,33 @@ Los excedentes se cobran en los planes de pago por encima de los [límites](#pla Los descuentos por volumen están disponibles como parte de [contratos personalizados](#custom-contracts). -## Funciones +## Funciones {#features} Aquí hay una lista de todas las funciones de Trophy y el plan en el que están disponibles: -- [Logros](/platform/achievements) -- [Rachas](/platform/streaks) -- [Puntos](/platform/points) -- [Clasificaciones](/platform/leaderboards) -- [Emails](/platform/emails) -- [Notificaciones push](/platform/push-notifications) +- [Logros](/es/platform/achievements) +- [Rachas](/es/platform/streaks) +- [Puntos](/es/platform/points) +- [Clasificaciones](/es/platform/leaderboards) +- [Emails](/es/platform/emails) +- [Notificaciones push](/es/platform/push-notifications) -- [Verificación DNS](/platform/emails#dns-verification-advanced) +- [Verificación DNS](/es/platform/emails#dns-verification-advanced) -- [Webhooks](/webhooks) -- [Atributos Personalizados](/platform/users#custom-user-attributes) +- [Webhooks](/es/webhooks) +- [Atributos Personalizados](/es/platform/users#custom-user-attributes) -## Contratos Personalizados +## Contratos Personalizados {#custom-contracts} Si tiene más de 100K MAU y desea discutir contratos personalizados que incluyan descuentos por volumen adaptados a las necesidades de su negocio, [contáctenos](mailto:hello@trophy.so) y estaremos encantados de ayudarle. -## Ver su Uso +## Ver su Uso {#viewing-your-usage} Puede ver su uso para el período de facturación actual en la [página de facturación](https://app.trophy.so/billing) del panel de Trophy y ver todas las facturas anteriores en su portal de facturación. @@ -114,11 +114,11 @@ Puede ver su uso para el período de facturación actual en la [página de factu height="200" width="50%" noZoom - src="../assets/account/billing/plan-usage.png" + src="../../assets/account/billing/plan-usage.png" /> -## Preguntas frecuentes +## Preguntas frecuentes {#frequently-asked-questions} @@ -126,6 +126,6 @@ Puede ver su uso para el período de facturación actual en la [página de factu -## Obtener soporte +## Obtener soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/account/branding.mdx b/es/account/branding.mdx index b86c577..839e5b4 100644 --- a/es/account/branding.mdx +++ b/es/account/branding.mdx @@ -7,12 +7,12 @@ og:description: Descubra cómo Trophy realiza el seguimiento del uso según el icon: palette --- -## Configurar Marca +## Configurar Marca {#configure-branding} -La [página de marca](https://app.trophy.so/branding) en el panel de Trophy le permite configurar los siguientes ajustes a nivel de cuenta. Estos ajustes se utilizarán en cualquier comunicación que configure Trophy para enviar a los usuarios, como [Correos electrónicos](/platform/emails). +La [página de marca](https://app.trophy.so/branding) en el panel de Trophy le permite configurar los siguientes ajustes a nivel de cuenta. Estos ajustes se utilizarán en cualquier comunicación que configure Trophy para enviar a los usuarios, como [Correos electrónicos](/es/platform/emails). - + - **Logotipo**: Cargue el logotipo de su marca para mostrarlo en el encabezado de los correos electrónicos de Trophy. Recomendamos usar un logotipo horizontal con fondo transparente o un color de fondo liso con esquinas redondeadas. @@ -21,6 +21,6 @@ La [página de marca](https://app.trophy.so/branding) en el panel de Trophy le p - **Nombre de la Aplicación**: Introduzca el nombre de su aplicación o servicio. Trophy utilizará este nombre en varios lugares de sus correos electrónicos. - **URL de la Aplicación**: Introduzca una URL predeterminada para usar cuando no se anule por correo electrónico. Normalmente, esta es la URL de la página de inicio de sesión de su aplicación o servicio. -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Desea ponerse en contacto con el equipo de Trophy? Comuníquese con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarle! diff --git a/es/account/members.mdx b/es/account/members.mdx index 3d13e24..8aed9b1 100644 --- a/es/account/members.mdx +++ b/es/account/members.mdx @@ -7,7 +7,7 @@ og:description: Otorga acceso a Trophy a los miembros del equipo y administra la icon: user-plus --- -## Invita a tu equipo +## Invita a tu equipo {#invite-your-team} Trophy admite hasta 5 miembros de equipo por organización. Sin embargo, si consideras que necesitas más, [simplemente solicítalo](mailto:support@trophy.so) y estaremos encantados de darte más espacio. @@ -20,7 +20,7 @@ Para invitar a un miembro del equipo a tu organización de Trophy, abre el diál loop playsInline className="w-full aspect-15/4" - src="../assets/account/members/add_new_member.mp4" + src="../../assets/account/members/add_new_member.mp4" > @@ -28,7 +28,7 @@ Haz clic en _Invitar_ y añade la dirección de correo electrónico de los miemb Una vez que estés satisfecho, haz clic en _Enviar invitaciones_ y cada miembro del equipo recibirá una invitación por correo electrónico a Trophy. -## Administra los roles de usuario +## Administra los roles de usuario {#manage-user-roles} Solo los administradores de la organización pueden administrar los roles de usuario @@ -48,12 +48,12 @@ Para administrar roles, abre el diálogo de administración de la organización loop playsInline className="w-full aspect-15/4" - src="../assets/account/members/manage_roles.mp4" + src="../../assets/account/members/manage_roles.mp4" > Aquí puedes promover a cualquier miembro existente a administrador, o degradar a cualquier administrador existente a miembro. -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/account/overview.mdx b/es/account/overview.mdx index d74f373..c95edbc 100644 --- a/es/account/overview.mdx +++ b/es/account/overview.mdx @@ -6,17 +6,17 @@ description: Aprende a gestionar tu cuenta de Trophy. Desde invitar a miembros del equipo hasta comprender la facturación, esta sección de la documentación está dedicada a la gestión de tu cuenta de Trophy. - + Otorga acceso a Trophy a los miembros del equipo y gestiona la configuración de tu cuenta. - + Configura tu logotipo y los colores de marca utilizados en las funciones creadas con Trophy. - + Descubre cómo Trophy realiza el seguimiento del uso según el número de usuarios que utilizan tu producto. -## Obtener soporte +## Obtener soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Escríbenos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/admin-api/endpoints/points/archive-a-boost.mdx b/es/admin-api/endpoints/points/archive-a-boost.mdx index 821a4ae..74bfbee 100644 --- a/es/admin-api/endpoints/points/archive-a-boost.mdx +++ b/es/admin-api/endpoints/points/archive-a-boost.mdx @@ -2,7 +2,7 @@ openapi: delete /points/boosts/{id} --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de velocidad** diff --git a/es/admin-api/endpoints/points/archive-boosts-batch.mdx b/es/admin-api/endpoints/points/archive-boosts-batch.mdx index 6084a25..052b675 100644 --- a/es/admin-api/endpoints/points/archive-boosts-batch.mdx +++ b/es/admin-api/endpoints/points/archive-boosts-batch.mdx @@ -2,7 +2,7 @@ openapi: delete /points/boosts --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de tasa** diff --git a/es/admin-api/endpoints/points/create-boosts.mdx b/es/admin-api/endpoints/points/create-boosts.mdx index d3b4edf..8c1a241 100644 --- a/es/admin-api/endpoints/points/create-boosts.mdx +++ b/es/admin-api/endpoints/points/create-boosts.mdx @@ -2,7 +2,7 @@ openapi: post /points/boosts --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Tasa** diff --git a/es/admin-api/endpoints/streaks/grant-freezes.mdx b/es/admin-api/endpoints/streaks/grant-freezes.mdx index e1da976..4db566a 100644 --- a/es/admin-api/endpoints/streaks/grant-freezes.mdx +++ b/es/admin-api/endpoints/streaks/grant-freezes.mdx @@ -2,7 +2,7 @@ openapi: post /streaks/freezes --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Tasa** diff --git a/es/admin-api/endpoints/streaks/restore-streaks.mdx b/es/admin-api/endpoints/streaks/restore-streaks.mdx index 02c74cf..1a2b08d 100644 --- a/es/admin-api/endpoints/streaks/restore-streaks.mdx +++ b/es/admin-api/endpoints/streaks/restore-streaks.mdx @@ -2,7 +2,7 @@ openapi: post /streaks/restore --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de velocidad** diff --git a/es/admin-api/introduction.mdx b/es/admin-api/introduction.mdx index fff2107..3b1ded4 100644 --- a/es/admin-api/introduction.mdx +++ b/es/admin-api/introduction.mdx @@ -8,7 +8,7 @@ subtitle: Aprende sobre la API de administración de Trophy y cómo utilizarla La API de administración de Trophy es un conjunto de endpoints para crear flujos de trabajo de gamificación personalizados. -Mientras que la [API de aplicación](/api-reference/introduction) modela los datos y las interacciones necesarias para ofrecer una experiencia de gamificación dentro de la aplicación, la API de administración modela las acciones administrativas que solo realiza tu equipo o la lógica de negocio fuera de tu aplicación. +Mientras que la [API de aplicación](/es/api-reference/introduction) modela los datos y las interacciones necesarias para ofrecer una experiencia de gamificación dentro de la aplicación, la API de administración modela las acciones administrativas que solo realiza tu equipo o la lógica de negocio fuera de tu aplicación. La API de administración es accesible a través de **los mismos SDKs** que la API de aplicación o, para quienes administran sus propios clientes HTTP, está disponible en la siguiente URL base. @@ -21,6 +21,6 @@ https://admin.trophy.so/v1 ello](mailto:support@trophy.so) y lo construiremos. -## Obtener soporte +## Obtener soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/api-reference/authentication.mdx b/es/api-reference/authentication.mdx index e6faba7..cbcb533 100644 --- a/es/api-reference/authentication.mdx +++ b/es/api-reference/authentication.mdx @@ -9,17 +9,17 @@ og:description: Integre de forma segura con la API de Trophy utilizando claves icon: key --- -## Claves API +## Claves API {#api-keys} Cada cuenta puede crear un máximo de 3 claves API desde la [Página de Integración](https://app.trophy.so/integration). Si ya se ha registrado, habrá creado una durante la incorporación inicial. Trophy registra y muestra cuándo se creó cada clave API en su cuenta y cuándo se usó por última vez para que pueda hacer un seguimiento fácil del uso. - + -### Estructura de una clave API +### Estructura de una clave API {#anatomy-of-an-api-key} Cada clave API se compone de 2 partes separadas por un punto: @@ -34,7 +34,7 @@ Cada clave API se compone de 2 partes separadas por un punto: Al usar la API, ambas partes de su clave API deben enviarse en el encabezado `X-API-KEY`. -### Autenticación de solicitudes +### Autenticación de solicitudes {#authenticating-requests} Al realizar solicitudes a la API, asegúrese de incluir **ambas** partes de su clave API en el encabezado `X-API-KEY` como en este ejemplo: @@ -45,7 +45,7 @@ curl https://app.trophy.so/api/users//metrics/ \ Si no pasa una clave API o su clave API no es válida, recibirá un código de respuesta `401`. -## Gestión de claves API +## Gestión de claves API {#managing-api-keys} Hay varias operaciones diferentes que puede realizar en las claves API desde su panel de Trophy para gestionar su integración. @@ -53,11 +53,11 @@ Hay varias operaciones diferentes que puede realizar en las claves API desde su -### Rotación de claves +### Rotación de claves {#rotating-keys} Las claves de API se pueden rotar si deseas cambiarlas por cualquier motivo. En el momento de la rotación, la clave de API original dejará de funcionar y cualquier solicitud que todavía la utilice comenzará a recibir respuestas `401` inmediatamente. @@ -65,7 +65,7 @@ Las claves de API se pueden rotar si deseas cambiarlas por cualquier motivo. En Ten en cuenta que al rotar las claves, tanto el prefijo como el cuerpo cambiarán. -### Revocación de claves +### Revocación de claves {#revoking-keys} Las claves de API también se pueden revocar por completo, momento en el que pasan a estar _Inactivas_. En el momento de la revocación, la clave de API dejará de funcionar y cualquier solicitud que todavía la utilice comenzará a recibir respuestas `401` inmediatamente. @@ -75,12 +75,12 @@ Una vez revocada, puedes reactivar la clave de API en cualquier momento. Ni el prefijo ni el cuerpo de la clave cambian cuando se revoca o se reactiva. -### Eliminación de claves de API +### Eliminación de claves de API {#deleting-api-keys} Si estás 100% seguro de que ya no necesitas una clave de API, se pueden eliminar. Una vez que las claves de API se eliminan, no se pueden recuperar. -## Obtener soporte +## Obtener soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/api-reference/client-libraries.mdx b/es/api-reference/client-libraries.mdx index e984292..52c0cbe 100644 --- a/es/api-reference/client-libraries.mdx +++ b/es/api-reference/client-libraries.mdx @@ -95,6 +95,6 @@ Trophy ofrece actualmente los siguientes SDKs para interactuar con la API de Tro /> -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/api-reference/endpoints/achievements/all-achievements.mdx b/es/api-reference/endpoints/achievements/all-achievements.mdx index 2e5ffaf..0842702 100644 --- a/es/api-reference/endpoints/achievements/all-achievements.mdx +++ b/es/api-reference/endpoints/achievements/all-achievements.mdx @@ -2,7 +2,7 @@ openapi: get /achievements --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Tasa** diff --git a/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx b/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx index 5162a9d..3393cd1 100644 --- a/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx +++ b/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed.mdx @@ -2,7 +2,7 @@ openapi: post /achievements/{key}/complete --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de tasa** diff --git a/es/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx b/es/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx index 7a894e9..de3463f 100644 --- a/es/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx +++ b/es/api-reference/endpoints/leaderboards/get-all-active-leaderboards.mdx @@ -2,7 +2,7 @@ openapi: get /leaderboards --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Tasa** diff --git a/es/api-reference/endpoints/leaderboards/get-leaderboard.mdx b/es/api-reference/endpoints/leaderboards/get-leaderboard.mdx index 6fd1cb0..a8bf32e 100644 --- a/es/api-reference/endpoints/leaderboards/get-leaderboard.mdx +++ b/es/api-reference/endpoints/leaderboards/get-leaderboard.mdx @@ -2,7 +2,7 @@ openapi: get /leaderboards/{key} --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de tasa** diff --git a/es/api-reference/endpoints/metrics/send-a-metric-change-event.mdx b/es/api-reference/endpoints/metrics/send-a-metric-change-event.mdx index 0a66f0d..995e929 100644 --- a/es/api-reference/endpoints/metrics/send-a-metric-change-event.mdx +++ b/es/api-reference/endpoints/metrics/send-a-metric-change-event.mdx @@ -2,7 +2,7 @@ openapi: post /metrics/{key}/event --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de tasa** diff --git a/es/api-reference/endpoints/points/get-points-boosts.mdx b/es/api-reference/endpoints/points/get-points-boosts.mdx index 4732fa7..6f5b7da 100644 --- a/es/api-reference/endpoints/points/get-points-boosts.mdx +++ b/es/api-reference/endpoints/points/get-points-boosts.mdx @@ -2,7 +2,7 @@ openapi: get /points/{key}/boosts --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de velocidad** diff --git a/es/api-reference/endpoints/points/get-points-level-summary.mdx b/es/api-reference/endpoints/points/get-points-level-summary.mdx index 5e4b7a3..39af7c6 100644 --- a/es/api-reference/endpoints/points/get-points-level-summary.mdx +++ b/es/api-reference/endpoints/points/get-points-level-summary.mdx @@ -2,7 +2,7 @@ openapi: get /points/{key}/level-summary --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Tasa** diff --git a/es/api-reference/endpoints/points/get-points-levels.mdx b/es/api-reference/endpoints/points/get-points-levels.mdx index fb9de3a..bb05407 100644 --- a/es/api-reference/endpoints/points/get-points-levels.mdx +++ b/es/api-reference/endpoints/points/get-points-levels.mdx @@ -2,7 +2,7 @@ openapi: get /points/{key}/levels --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de tasa** diff --git a/es/api-reference/endpoints/points/get-points-summary.mdx b/es/api-reference/endpoints/points/get-points-summary.mdx index bae543a..5fa6a61 100644 --- a/es/api-reference/endpoints/points/get-points-summary.mdx +++ b/es/api-reference/endpoints/points/get-points-summary.mdx @@ -2,7 +2,7 @@ openapi: get /points/{key}/summary --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Tasa** diff --git a/es/api-reference/endpoints/points/get-points.mdx b/es/api-reference/endpoints/points/get-points.mdx index 078c817..6b5af64 100644 --- a/es/api-reference/endpoints/points/get-points.mdx +++ b/es/api-reference/endpoints/points/get-points.mdx @@ -2,7 +2,7 @@ openapi: get /points/{key} --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Velocidad** diff --git a/es/api-reference/endpoints/streaks/get-streak-rankings.mdx b/es/api-reference/endpoints/streaks/get-streak-rankings.mdx index 172d6f2..f6f3923 100644 --- a/es/api-reference/endpoints/streaks/get-streak-rankings.mdx +++ b/es/api-reference/endpoints/streaks/get-streak-rankings.mdx @@ -2,7 +2,7 @@ openapi: get /streaks/rankings --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Tasa** diff --git a/es/api-reference/endpoints/streaks/get-streaks.mdx b/es/api-reference/endpoints/streaks/get-streaks.mdx index 93d0ddd..9ddff34 100644 --- a/es/api-reference/endpoints/streaks/get-streaks.mdx +++ b/es/api-reference/endpoints/streaks/get-streaks.mdx @@ -2,7 +2,7 @@ openapi: get /streaks --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Tasa** diff --git a/es/api-reference/endpoints/users/create-a-user.mdx b/es/api-reference/endpoints/users/create-a-user.mdx index 99d05db..0dae3b6 100644 --- a/es/api-reference/endpoints/users/create-a-user.mdx +++ b/es/api-reference/endpoints/users/create-a-user.mdx @@ -2,7 +2,7 @@ openapi: post /users --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de tasa** diff --git a/es/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx b/es/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx index e6c36f4..c3e0f60 100644 --- a/es/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx +++ b/es/api-reference/endpoints/users/get-a-single-metric-event-summary-for-a-user.mdx @@ -2,7 +2,7 @@ openapi: get /users/{id}/metrics/{key}/event-summary --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Tasa** diff --git a/es/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx b/es/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx index 743ee5e..897a3dd 100644 --- a/es/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx +++ b/es/api-reference/endpoints/users/get-a-single-metric-for-a-user.mdx @@ -2,7 +2,7 @@ openapi: get /users/{id}/metrics/{key} --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Tasa** diff --git a/es/api-reference/endpoints/users/get-a-single-user.mdx b/es/api-reference/endpoints/users/get-a-single-user.mdx index 7c10ecd..4a9982e 100644 --- a/es/api-reference/endpoints/users/get-a-single-user.mdx +++ b/es/api-reference/endpoints/users/get-a-single-user.mdx @@ -2,7 +2,7 @@ openapi: get /users/{id} --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de frecuencia** diff --git a/es/api-reference/endpoints/users/get-a-users-completed-achievements.mdx b/es/api-reference/endpoints/users/get-a-users-completed-achievements.mdx index ccb06a6..c1ad4ca 100644 --- a/es/api-reference/endpoints/users/get-a-users-completed-achievements.mdx +++ b/es/api-reference/endpoints/users/get-a-users-completed-achievements.mdx @@ -2,7 +2,7 @@ openapi: get /users/{id}/achievements --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de velocidad** diff --git a/es/api-reference/endpoints/users/get-a-users-leaderboard.mdx b/es/api-reference/endpoints/users/get-a-users-leaderboard.mdx index 4e5aece..3c853fc 100644 --- a/es/api-reference/endpoints/users/get-a-users-leaderboard.mdx +++ b/es/api-reference/endpoints/users/get-a-users-leaderboard.mdx @@ -2,7 +2,7 @@ openapi: get /users/{id}/leaderboards/{key} --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Tasa** diff --git a/es/api-reference/endpoints/users/get-a-users-points-boosts.mdx b/es/api-reference/endpoints/users/get-a-users-points-boosts.mdx index bc6fa25..ea04c3e 100644 --- a/es/api-reference/endpoints/users/get-a-users-points-boosts.mdx +++ b/es/api-reference/endpoints/users/get-a-users-points-boosts.mdx @@ -2,7 +2,7 @@ openapi: get /users/{id}/points/{key}/boosts --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de tasa** diff --git a/es/api-reference/endpoints/users/get-a-users-points-summary.mdx b/es/api-reference/endpoints/users/get-a-users-points-summary.mdx index 1ce9c86..77169ef 100644 --- a/es/api-reference/endpoints/users/get-a-users-points-summary.mdx +++ b/es/api-reference/endpoints/users/get-a-users-points-summary.mdx @@ -2,7 +2,7 @@ openapi: get /users/{id}/points/{key}/event-summary --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de velocidad** diff --git a/es/api-reference/endpoints/users/get-a-users-points.mdx b/es/api-reference/endpoints/users/get-a-users-points.mdx index a32f78c..93d0b5a 100644 --- a/es/api-reference/endpoints/users/get-a-users-points.mdx +++ b/es/api-reference/endpoints/users/get-a-users-points.mdx @@ -2,7 +2,7 @@ openapi: get /users/{id}/points/{key} --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Tasa** diff --git a/es/api-reference/endpoints/users/get-a-users-streak.mdx b/es/api-reference/endpoints/users/get-a-users-streak.mdx index a357cef..6b62fcd 100644 --- a/es/api-reference/endpoints/users/get-a-users-streak.mdx +++ b/es/api-reference/endpoints/users/get-a-users-streak.mdx @@ -2,7 +2,7 @@ openapi: get /users/{id}/streak --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de tasa** diff --git a/es/api-reference/endpoints/users/get-a-users-wrapped.mdx b/es/api-reference/endpoints/users/get-a-users-wrapped.mdx index ca199f3..6f1facd 100644 --- a/es/api-reference/endpoints/users/get-a-users-wrapped.mdx +++ b/es/api-reference/endpoints/users/get-a-users-wrapped.mdx @@ -2,7 +2,7 @@ openapi: get /users/{id}/wrapped --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Velocidad** diff --git a/es/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx b/es/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx index 50bf0a8..f2570e1 100644 --- a/es/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx +++ b/es/api-reference/endpoints/users/get-all-metrics-for-a-user.mdx @@ -2,7 +2,7 @@ openapi: get /users/{id}/metrics --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de velocidad** diff --git a/es/api-reference/endpoints/users/get-user-preferences.mdx b/es/api-reference/endpoints/users/get-user-preferences.mdx index 4769c50..8fe2fde 100644 --- a/es/api-reference/endpoints/users/get-user-preferences.mdx +++ b/es/api-reference/endpoints/users/get-user-preferences.mdx @@ -2,7 +2,7 @@ openapi: get /users/{id}/preferences --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Frecuencia** diff --git a/es/api-reference/endpoints/users/identify-a-user.mdx b/es/api-reference/endpoints/users/identify-a-user.mdx index f957af7..2be7019 100644 --- a/es/api-reference/endpoints/users/identify-a-user.mdx +++ b/es/api-reference/endpoints/users/identify-a-user.mdx @@ -2,7 +2,7 @@ openapi: put /users/{id} --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de Tasa** diff --git a/es/api-reference/endpoints/users/update-a-user.mdx b/es/api-reference/endpoints/users/update-a-user.mdx index 838bc08..c099d8c 100644 --- a/es/api-reference/endpoints/users/update-a-user.mdx +++ b/es/api-reference/endpoints/users/update-a-user.mdx @@ -2,7 +2,7 @@ openapi: patch /users/{id} --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de velocidad** diff --git a/es/api-reference/endpoints/users/update-user-preferences.mdx b/es/api-reference/endpoints/users/update-user-preferences.mdx index 1b03440..b3fe2c7 100644 --- a/es/api-reference/endpoints/users/update-user-preferences.mdx +++ b/es/api-reference/endpoints/users/update-user-preferences.mdx @@ -2,7 +2,7 @@ openapi: patch /users/{id}/preferences --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../../../snippets/rate-limit-badge.jsx"; **Límites de velocidad** diff --git a/es/api-reference/idempotency.mdx b/es/api-reference/idempotency.mdx index f1a6459..bdaab01 100644 --- a/es/api-reference/idempotency.mdx +++ b/es/api-reference/idempotency.mdx @@ -8,19 +8,19 @@ og:description: Previene efectos secundarios no deseados al reintentar icon: repeat --- -import IdempotentEventTracking from "/snippets/idempotent-event-tracking.mdx"; +import IdempotentEventTracking from "../../snippets/idempotent-event-tracking.mdx"; -## ¿Qué es la Idempotencia? +## ¿Qué es la Idempotencia? {#what-is-idempotency} Al describir APIs, la [idempotencia](https://en.wikipedia.org/wiki/Idempotence) es una propiedad de una operación particular mediante la cual las invocaciones subsecuentes después de la primera no tienen efecto adicional sobre el estado del sistema. -La [API de seguimiento de eventos](/api-reference/endpoints/metrics/send-a-metric-change-event) de Trophy puede utilizarse para garantizar la idempotencia, evitando que los reintentos del cliente tengan efectos secundarios no deseados como el conteo excesivo de interacciones de usuario o la concesión de Puntos a los usuarios múltiples veces por la misma acción. +La [API de seguimiento de eventos](/es/api-reference/endpoints/metrics/send-a-metric-change-event) de Trophy puede utilizarse para garantizar la idempotencia, evitando que los reintentos del cliente tengan efectos secundarios no deseados como el conteo excesivo de interacciones de usuario o la concesión de Puntos a los usuarios múltiples veces por la misma acción. Esto es particularmente importante cuando las recompensas están vinculadas a acciones de usuario para prevenir que los usuarios 'manipulen el sistema'. -## Envío de Solicitudes Idempotentes +## Envío de Solicitudes Idempotentes {#sending-idempotent-requests} -Para garantizar que se respete la idempotencia al enviar eventos a Trophy, incluye un encabezado `Idempotency-Key` al usar la [API de seguimiento de eventos](/api-reference/endpoints/metrics/send-a-metric-change-event). Además, todos los [SDKs de cliente](/api-reference/client-libraries) soportan idempotencia con seguridad de tipos integrada. +Para garantizar que se respete la idempotencia al enviar eventos a Trophy, incluye un encabezado `Idempotency-Key` al usar la [API de seguimiento de eventos](/es/api-reference/endpoints/metrics/send-a-metric-change-event). Además, todos los [SDKs de cliente](/es/api-reference/client-libraries) soportan idempotencia con seguridad de tipos integrada. Puedes elegir qué usar como tu clave de idempotencia, pero debe reflejar el nivel de 'unicidad' que deseas que Trophy respete. @@ -34,7 +34,7 @@ Aquí hay un ejemplo de cómo se ve el uso de una clave de idempotencia: -## Cómo Funciona la Idempotencia +## Cómo Funciona la Idempotencia {#how-idempotency-works} Cuando Trophy detecta que se ha enviado una clave de idempotencia con un evento, primero verificará si el usuario al que se refiere el evento la ha utilizado antes. Luego procederá a tomar una de las siguientes acciones: @@ -42,7 +42,7 @@ Cuando Trophy detecta que se ha enviado una clave de idempotencia con un evento, - Si por el contrario Trophy detecta que el usuario no ha utilizado la clave de idempotencia antes, procesará el evento de forma habitual y devolverá una respuesta `201 Created`. Finalmente, Trophy almacenará la clave de idempotencia para su consulta en solicitudes posteriores. - Todas las [métricas](/platform/metrics) de Trophy gestionan la idempotencia de forma aislada. + Todas las [métricas](/es/platform/metrics) de Trophy gestionan la idempotencia de forma aislada. Trophy aceptará que un usuario utilice la misma clave de idempotencia para eventos en métricas diferentes como solicitudes aisladas independientes. @@ -67,6 +67,6 @@ Además, al utilizar una clave de idempotencia, la respuesta contendrá dos prop contacto](mailto:support@trophy.so) y con gusto lo configuraremos para usted. -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Desea ponerse en contacto con el equipo de Trophy? Comuníquese con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudar! diff --git a/es/api-reference/introduction.mdx b/es/api-reference/introduction.mdx index 5c01bd5..2cd66df 100644 --- a/es/api-reference/introduction.mdx +++ b/es/api-reference/introduction.mdx @@ -6,30 +6,30 @@ subtitle: Aprende sobre la API de Trophy y comienza a construir tu integración. La API de aplicación de Trophy es un conjunto de endpoints que proporcionan interfaces simples para crear experiencias de gamificación y cubre toda la gama de acciones que un usuario final de tu aplicación puede realizar. -No incluye funciones administrativas globales ni acciones que solo deben ser realizadas por tus sistemas internos. Si buscas funcionalidad de administración, consulta la [API de Administración](/admin-api/introduction). +No incluye funciones administrativas globales ni acciones que solo deben ser realizadas por tus sistemas internos. Si buscas funcionalidad de administración, consulta la [API de Administración](/es/admin-api/introduction). -La API de Aplicación es accesible a través de [SDKs](/api-reference/client-libraries) disponibles en la mayoría de los principales lenguajes de programación o, para quienes deseen gestionar sus propios clientes HTTP, está disponible en la siguiente URL base. +La API de Aplicación es accesible a través de [SDKs](/es/api-reference/client-libraries) disponibles en la mayoría de los principales lenguajes de programación o, para quienes deseen gestionar sus propios clientes HTTP, está disponible en la siguiente URL base. ```bash Base URL https://api.trophy.so/v1 ``` - + Integra de forma segura tus aplicaciones con la API de Trophy utilizando claves API. Aprende sobre la API de Trophy y la limitación de tasa de solicitudes. - + Utiliza los SDKs con tipado seguro de Trophy para integrar con la API. -## Obtener soporte +## Obtener soporte {#get-support} ¿Quieres contactar con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/api-reference/rate-limiting.mdx b/es/api-reference/rate-limiting.mdx index 8eac3a8..0ff3487 100644 --- a/es/api-reference/rate-limiting.mdx +++ b/es/api-reference/rate-limiting.mdx @@ -5,11 +5,11 @@ og:description: Aprende sobre la API de Trophy y la limitación de tasa de solic icon: circle-gauge --- -import { RateLimitBadge } from "/snippets/rate-limit-badge.jsx"; +import { RateLimitBadge } from "../../snippets/rate-limit-badge.jsx"; Trophy opera un modelo de limitación de tasa por niveles para cada endpoint de la API. Todos los límites de tasa están definidos a nivel de organización. Superar los límites de tasa resultará en que tu aplicación reciba una respuesta `429 Too Many Requests`. -## Niveles de Limitación de Tasa +## Niveles de Limitación de Tasa {#rate-limit-tiers} | Nivel | Límite de Tasa | | ---- | ---------- | @@ -20,6 +20,6 @@ Trophy opera un modelo de limitación de tasa por niveles para cada endpoint de Si consideras que necesitas un límite de tasa mayor, [contáctanos](mailto:support@trophy.so) y estaremos encantados de discutir tus necesidades. -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/experimentation/engagement.mdx b/es/experimentation/engagement.mdx index 3bf0ddc..05386c4 100644 --- a/es/experimentation/engagement.mdx +++ b/es/experimentation/engagement.mdx @@ -5,11 +5,11 @@ og:description: Aprende a medir el impacto de la gamificación en el engagement icon: heart --- -## ¿Qué es el Engagement? +## ¿Qué es el Engagement? {#what-is-engagement} El engagement de usuarios en Trophy se refiere al nivel promedio de actividad que muestran tus usuarios al utilizar tu producto. -Como Trophy rastrea las interacciones de usuarios a través de [Métricas](/platform/metrics), puede brindarte información sobre qué tan activos están tus usuarios con respecto a cada una y en conjunto. +Como Trophy rastrea las interacciones de usuarios a través de [Métricas](/es/platform/metrics), puede brindarte información sobre qué tan activos están tus usuarios con respecto a cada una y en conjunto. El engagement de usuarios en Trophy es el valor promedio total de eventos de métrica por usuario activo diario. La fórmula es: @@ -17,7 +17,7 @@ El engagement de usuarios en Trophy es el valor promedio total de eventos de mé sum of all metric event values in a day / number of users active that day ``` -## Analíticas de Engagement +## Analíticas de Engagement {#engagement-analytics} Los dashboards de Trophy para cada métrica muestran gráficos de engagement para esa métrica, y el dashboard principal de Trophy presenta el engagement de usuarios de forma agregada. @@ -25,7 +25,7 @@ Los dashboards de Trophy para cada métrica muestran gráficos de engagement par @@ -35,7 +35,7 @@ Trophy también muestra gráficos que presentan el engagement de usuarios durant @@ -45,12 +45,12 @@ Nos referimos a esto como 'engagement temprano' y es el engagement de usuarios m Este es un gráfico útil para comprender el engagement de usuarios durante sus primeros días con tu producto y para identificar dónde pueden estar ocurriendo obstáculos para la activación inicial del usuario. -## Obtén Soporte +## Obtén Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/experimentation/overview.mdx b/es/experimentation/overview.mdx index d048a6d..341fec1 100644 --- a/es/experimentation/overview.mdx +++ b/es/experimentation/overview.mdx @@ -6,7 +6,7 @@ og:description: Aprende a usar Trophy para experimentar con la experiencia de gamificación y aumentar la retención y el engagement de los usuarios. --- -import ControlFlagBlock from "/snippets/control-flag-block.mdx"; +import ControlFlagBlock from "../../snippets/control-flag-block.mdx"; Trophy proporciona un conjunto básico de primitivas para ayudarte a implementar experiencias de gamificación más rápido. Pero la gamificación no es algo que puedas configurar y olvidar. @@ -19,7 +19,7 @@ Normalmente, tendrías que construir estas herramientas tú mismo, pero con Trop admitir pruebas A/B más granulares de tus funciones de gamificación. -## Ratio de control +## Ratio de control {#control-ratio} Trophy tiene una capacidad integrada para realizar pruebas A/B automáticas de las funciones de gamificación mediante un ratio de control. @@ -32,7 +32,7 @@ Ubicado en la [página de integración](https://app.trophy.so/integration/config loop playsInline className="w-full aspect-15/4" - src="../assets/experimentation/control_ratio.mp4" + src="../../assets/experimentation/control_ratio.mp4" > @@ -43,29 +43,29 @@ Si planeas medir el impacto general de las funciones de gamificación, asegúrat - Trophy tampoco envía [Correos electrónicos](/platform/emails) ni [Notificaciones push](/platform/push-notifications) a usuarios que están en el + Trophy tampoco envía [Correos electrónicos](/es/platform/emails) ni [Notificaciones push](/es/platform/push-notifications) a usuarios que están en el grupo de control. Los paneles de análisis comparan la retención y la interacción de usuarios entre los grupos de control y experimentales, lo que te permite medir el impacto de las funcionalidades que construyes con Trophy y ajustar las mecánicas cuando sea necesario. - + -## Retención e Interacción +## Retención e Interacción {#retention-engagement} Tanto la retención como la interacción de usuarios son métricas importantes para medir el impacto de los cambios en la experiencia del usuario. Las siguientes secciones explican qué significa cada una y cómo se relacionan con las funcionalidades que construyes con Trophy. - + Aprende a medir el impacto de la gamificación en la retención - + Aprende a medir el impacto de la gamificación en la interacción de usuarios -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres contactar al equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/experimentation/retention.mdx b/es/experimentation/retention.mdx index d20599b..c720e3b 100644 --- a/es/experimentation/retention.mdx +++ b/es/experimentation/retention.mdx @@ -5,13 +5,13 @@ og:description: Aprende a medir el impacto de la gamificación en la retención icon: refresh-cw --- -## ¿Qué es la retención? +## ¿Qué es la retención? {#what-is-retention} La retención de usuarios es el porcentaje de usuarios que siguen utilizando tu producto después de un cierto periodo. Los marcos temporales comunes incluyen retención a 7, 14 y 30 días, donde los periodos más cortos proporcionan información sobre la experiencia inicial del usuario y los periodos más largos ofrecen información sobre el ajuste producto-mercado a largo plazo. -## Analítica de retención +## Analítica de retención {#retention-analytics} Trophy incluye un gráfico de retención para cada métrica en su panel de métricas y un gráfico agregado de retención de usuarios en el panel principal. @@ -19,7 +19,7 @@ Trophy incluye un gráfico de retención para cada métrica en su panel de métr @@ -29,10 +29,10 @@ La [página de integración](https://app.trophy.so/integration/configure) te per -## Obtener soporte +## Obtener soporte {#get-support} ¿Quieres contactar con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/getting-started/introduction.mdx b/es/getting-started/introduction.mdx index 0a0472c..9e3f66c 100644 --- a/es/getting-started/introduction.mdx +++ b/es/getting-started/introduction.mdx @@ -6,31 +6,31 @@ description: Documentación de Trophy para APIs de gamificación, guías de inic Trophy es un conjunto de herramientas amigable para desarrolladores que permite crear experiencias de producto gamificadas en cualquier aplicación móvil o web. -Facilita la creación de funciones como [Logros](/platform/achievements), [Rachas](/platform/streaks), [Puntos](/platform/points) y [Clasificaciones](/platform/leaderboards) con solo unas pocas líneas de código, y puede enviar [Emails](/platform/emails) y [Notificaciones push](/platform/push-notifications) gamificados para aumentar tu retención y engagement. +Facilita la creación de funciones como [Logros](/es/platform/achievements), [Rachas](/es/platform/streaks), [Puntos](/es/platform/points) y [Clasificaciones](/es/platform/leaderboards) con solo unas pocas líneas de código, y puede enviar [Emails](/es/platform/emails) y [Notificaciones push](/es/platform/push-notifications) gamificados para aumentar tu retención y engagement. Integra Trophy en tu backend en menos de 10 minutos. - + Usa Trophy para crear soluciones prácticas a casos de uso comunes de gamificación. - + Aprende los conceptos clave detrás de la creación de experiencias de gamificación con Trophy. - + Explora la API de Trophy y descubre lo que es posible. -## Por qué Trophy +## Por qué Trophy {#why-trophy} - + Si tu objetivo es que tus usuarios creen el hábito regular de usar tu aplicación—para aprender algo, publicar contenido o incluso hacer ejercicio—la gamificación podría ser para ti. @@ -41,6 +41,6 @@ Pero construir gamificación a escala puede ser complejo. Tienes una base de usu Trophy elimina el trabajo pesado involucrado en crear funciones como logros, rachas, sistemas de puntos y clasificaciones, al tiempo que proporciona APIs confiables y escalables para lanzar experiencias de gamificación más rápido que construyéndolas internamente. Además, proporciona una plataforma web para configurar, medir y experimentar fácilmente con la experiencia del producto sin cambios constantes en el código. -## Obtén soporte +## Obtén soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/getting-started/quickstart.mdx b/es/getting-started/quickstart.mdx index bb14c9e..3f10d80 100644 --- a/es/getting-started/quickstart.mdx +++ b/es/getting-started/quickstart.mdx @@ -5,10 +5,10 @@ og:description: Sigue esta guía de inicio rápido para integrar Trophy en tu backend y añadir funciones gamificadas como logros y rachas a tu aplicación. --- -import SdkInstallCommand from "/snippets/sdk-install-command.mdx"; -import AddEnvVarBlock from "/snippets/add-env-var-block.mdx"; -import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; -import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; +import SdkInstallCommand from "../../snippets/sdk-install-command.mdx"; +import AddEnvVarBlock from "../../snippets/add-env-var-block.mdx"; +import MetricChangeRequestBlock from "../../snippets/metric-change-request-block.mdx"; +import MetricChangeResponseBlock from "../../snippets/metric-change-response-block.mdx"; Aquí integrarás tu aplicación web backend con Trophy y comenzarás a construir tu primera función gamificada. @@ -36,7 +36,7 @@ Aquí integrarás tu aplicación web backend con Trophy y comenzarás a construi - Todas las funciones de gamificación están impulsadas por las interacciones del usuario. En Trophy, utilizas [Métricas](/platform/metrics) para definir y modelar esas interacciones y [Eventos](/platform/events) para rastrearlas. + Todas las funciones de gamificación están impulsadas por las interacciones del usuario. En Trophy, utilizas [Métricas](/es/platform/metrics) para definir y modelar esas interacciones y [Eventos](/es/platform/events) para rastrearlas. Aquí crearás tu primera métrica para comenzar. En el panel de Trophy, dirígete a la [página de métricas](https://app.trophy.so/metrics) y pulsa el botón _Nueva Métrica_: @@ -47,7 +47,7 @@ Aquí integrarás tu aplicación web backend con Trophy y comenzarás a construi loop playsInline className="w-full aspect-video" - src="../assets/platform/metrics/create_new_metric.mp4" + src="../../assets/platform/metrics/create_new_metric.mp4" > @@ -64,11 +64,11 @@ Aquí integrarás tu aplicación web backend con Trophy y comenzarás a construi loop playsInline className="w-full aspect-video" - src="../assets/quick-start/copy_metric_key.mp4" + src="../../assets/quick-start/copy_metric_key.mp4" > - Para registrar un evento contra esta métrica cuando un usuario interactúa con tu producto, llama a la [API de evento de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event), pasando los detalles del usuario que realizó la interacción. En este ejemplo, la clave de métrica sería `flashcards-flipped`: + Para registrar un evento contra esta métrica cuando un usuario interactúa con tu producto, llama a la [API de evento de cambio de métrica](/es/api-reference/endpoints/metrics/send-a-metric-change-event), pasando los detalles del usuario que realizó la interacción. En este ejemplo, la clave de métrica sería `flashcards-flipped`: @@ -81,13 +81,13 @@ Aquí integrarás tu aplicación web backend con Trophy y comenzarás a construi Sigue los enlaces a continuación para obtener más información sobre cada función que puedes construir con Trophy: - + Recompensa a los usuarios por el progreso continuo o por realizar acciones específicas. - + Motiva a los usuarios a desarrollar hábitos de uso regulares. - + Construye sistemas de puntos sofisticados para recompensar y retener usuarios. } - href="/platform/leaderboards"> + href="/es/platform/leaderboards"> Crea competiciones amistosas para aumentar la participación de los usuarios. - + Envía correos electrónicos personalizados del ciclo de vida a los usuarios en el momento perfecto. - + Impulsa flujos de notificación automatizados utilizando datos de gamificación personalizados. - O explora nuestra [referencia de API](/api-reference/introduction) para familiarizarte con lo que Trophy puede hacer. + O explora nuestra [referencia de API](/es/api-reference/introduction) para familiarizarte con lo que Trophy puede hacer. -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/guides/gamified-fitness-platform.mdx b/es/guides/gamified-fitness-platform.mdx index f33e95b..d52590c 100644 --- a/es/guides/gamified-fitness-platform.mdx +++ b/es/guides/gamified-fitness-platform.mdx @@ -18,15 +18,15 @@ Si quieres ir directo al código, consulta el [repositorio de ejemplo](https://g loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-fitness-app/demo.mp4" + src="../../assets/guides/example-apps/example-fitness-app/demo.mp4" > -## Tabla de contenidos +## Tabla de contenidos {#table-of-contents} - [Stack tecnológico](#tech-stack) - [Requisitos previos](#prerequisites) -- [Configuración e instalación](#setup--installation) +- [Configuración e instalación](#setup-installation) - [Diseño del modelo de datos](#designing-the-data-model) - [Cómo funciona Trophy](#how-trophy-works) - [Configuración de Trophy](#setting-up-trophy) @@ -39,19 +39,19 @@ Si quieres ir directo al código, consulta el [repositorio de ejemplo](https://g - [Construcción de la página de perfil](#building-the-profile-page) - [El resultado](#the-result) -## Stack tecnológico +## Stack tecnológico {#tech-stack} - **Framework:** [Next.js 15](https://nextjs.org) (App Router) - **UI:** [Shadcn/UI](https://ui.shadcn.com) + TailwindCSS - **Iconos:** [Lucide React](https://lucide.dev) - **Gamificación:** [Trophy](https://trophy.so) -## Requisitos previos +## Requisitos previos {#prerequisites} - Una cuenta de Trophy (regístrate [aquí](https://app.trophy.so/sign-up)). - Node.js 18+ instalado. -## Configuración e instalación +## Configuración e instalación {#setup-installation} Primero, clona el repositorio inicial o crea una nueva aplicación Next.js: @@ -71,11 +71,11 @@ Configura tus variables de entorno en `.env.local`: TROPHY_API_KEY=your_api_key_here ``` -## Diseño del modelo de datos +## Diseño del modelo de datos {#designing-the-data-model} Para una aplicación de fitness multideportivo, necesitamos normalizar los esfuerzos. Un ciclo de 10 km no es lo mismo que una carrera de 10 km. Usaremos tres métricas distintas para rastrear datos sin procesar, y un sistema unificado de XP para la progresión. -### 1. Las Métricas +### 1. Las Métricas {#1-the-metrics} Rastrearemos la distancia como valor principal. @@ -83,13 +83,13 @@ Rastrearemos la distancia como valor principal. - `distance_cycled` (km) - `distance_swum` (m) - con atributo `style` (estilo libre/braza). -### 2. Los Atributos +### 2. Los Atributos {#2-the-attributes} Para habilitar clasificaciones locales, etiquetaremos cada usuario con un atributo `city`. -## Cómo Funciona Trophy +## Cómo Funciona Trophy {#how-trophy-works} -Antes de profundizar en el código, entendamos cómo Trophy impulsa nuestra capa de gamificación. En Trophy, las [Métricas](/platform/metrics) representan diferentes interacciones que los usuarios pueden realizar e impulsan funciones como [Logros](/platform/achievements), [Rachas](/platform/streaks) y [Correos electrónicos](/platform/emails). +Antes de profundizar en el código, entendamos cómo Trophy impulsa nuestra capa de gamificación. En Trophy, las [Métricas](/es/platform/metrics) representan diferentes interacciones que los usuarios pueden realizar e impulsan funciones como [Logros](/es/platform/achievements), [Rachas](/es/platform/streaks) y [Correos electrónicos](/es/platform/emails). ```mermaid flowchart TD @@ -112,7 +112,7 @@ Cuando se registran eventos para un usuario específico, cualquier logro vincula Esto es lo que hace tan poderosa la construcción de experiencias gamificadas con Trophy: hace todo el trabajo entre bastidores. -## Configuración de Trophy +## Configuración de Trophy {#setting-up-trophy} Así es como configuraremos Trophy para nuestra aplicación de fitness: @@ -131,7 +131,7 @@ Así es como configuraremos Trophy para nuestra aplicación de fitness: height="200" width="100%" noZoom - src="../assets/guides/example-apps/example-fitness-app/metrics.png" + src="../../assets/guides/example-apps/example-fitness-app/metrics.png" /> @@ -145,8 +145,8 @@ Así es como configuraremos Trophy para nuestra aplicación de fitness: @@ -158,7 +158,7 @@ Así es como configuraremos Trophy para nuestra aplicación de fitness: height="200" width="100%" noZoom - src="../assets/guides/example-apps/example-fitness-app/user-attributes.png" + src="../../assets/guides/example-apps/example-fitness-app/user-attributes.png" /> @@ -190,7 +190,7 @@ Así es como configuraremos Trophy para nuestra aplicación de fitness: height="200" width="100%" noZoom - src="../assets/guides/example-apps/example-fitness-app/achievements.png" + src="../../assets/guides/example-apps/example-fitness-app/achievements.png" /> @@ -206,7 +206,7 @@ Así es como configuraremos Trophy para nuestra aplicación de fitness: height="200" width="100%" noZoom - src="../assets/guides/example-apps/example-fitness-app/leaderboard-config.png" + src="../../assets/guides/example-apps/example-fitness-app/leaderboard-config.png" /> @@ -227,7 +227,7 @@ Así es como configuraremos Trophy para nuestra aplicación de fitness: height="200" width="100%" noZoom - src="../assets/guides/example-apps/example-fitness-app/leaderboards.png" + src="../../assets/guides/example-apps/example-fitness-app/leaderboards.png" /> @@ -247,11 +247,11 @@ Así es como configuraremos Trophy para nuestra aplicación de fitness: height="200" width="100%" noZoom - src="../assets/guides/example-apps/example-fitness-app/points-triggers.png" + src="../../assets/guides/example-apps/example-fitness-app/points-triggers.png" /> - Luego configura **[Niveles de Puntos](/platform/points#points-levels)** en el mismo sistema `xp` para que Trophy pueda rastrear el nivel de cada usuario. Utiliza las claves y umbrales a continuación para que el código de esta guía se alinee con tu panel de control: + Luego configura **[Niveles de Puntos](/es/platform/points#points-levels)** en el mismo sistema `xp` para que Trophy pueda rastrear el nivel de cada usuario. Utiliza las claves y umbrales a continuación para que el código de esta guía se alinee con tu panel de control: | Nombre | Clave | Umbral de puntos | | ------- | --------- | ---------------- | @@ -279,7 +279,7 @@ Así es como configuraremos Trophy para nuestra aplicación de fitness: -## Acciones del Servidor +## Acciones del Servidor {#server-actions} Crearemos un archivo `src/app/actions.ts` para gestionar todas las interacciones con la API de Trophy. Esto mantiene nuestras claves API seguras y nos permite aprovechar las Server Actions de Next.js. @@ -460,9 +460,9 @@ export async function getUserIdFromCookies() { } ``` -## El Sistema de Niveles +## El Sistema de Niveles {#the-leveling-system} -Trophy almacena el nivel de cada usuario en el sistema de puntos `xp` cuando configuras [Niveles de Puntos](/platform/points#points-levels). El payload de [puntos del usuario](/api-reference/endpoints/users/get-a-users-points) incluye un objeto `level`. La [API de listar niveles](/api-reference/endpoints/points/get-points-levels) devuelve cada nivel y umbral para que puedas mostrar el progreso hacia el siguiente. +Trophy almacena el nivel de cada usuario en el sistema de puntos `xp` cuando configuras [Niveles de Puntos](/es/platform/points#points-levels). El payload de [puntos del usuario](/es/api-reference/endpoints/users/get-a-users-points) incluye un objeto `level`. La [API de listar niveles](/es/api-reference/endpoints/points/get-points-levels) devuelve cada nivel y umbral para que puedas mostrar el progreso hacia el siguiente. Añade un pequeño helper junto a las constantes de tu aplicación en `src/lib/constants.ts`: @@ -540,7 +540,7 @@ export function getLevelProgress( } ``` -## Construyendo el Panel de Control +## Construyendo el Panel de Control {#building-the-dashboard} El panel de control agrega todas las estadísticas del usuario. Obtenemos los datos del lado del servidor y derivamos barras de progreso de los umbrales devueltos por la API. @@ -674,7 +674,7 @@ export default async function Dashboard() { Nota el wrapper `LogActivityDialog` alrededor del botón—lo construiremos a continuación. -## Registrando Entrenamientos +## Registrando Entrenamientos {#logging-workouts} El botón Registrar Entrenamiento abre un diálogo donde los usuarios seleccionan su tipo de actividad, ingresan la distancia y envían. Esto activa la acción del servidor `logActivity` que envía el evento a Trophy. @@ -832,7 +832,7 @@ export function LogActivityDialog({ children }: { children: React.ReactNode }) { } ``` -### Contexto del Usuario +### Contexto del Usuario {#user-context} El diálogo necesita acceso al ID del usuario actual. Crea un proveedor de contexto: @@ -905,7 +905,7 @@ export default function RootLayout({ children }) { } ``` -### Utilidades de Ayuda +### Utilidades de Ayuda {#helper-utilities} El contexto del usuario depende de funciones auxiliares para gestionar la identidad del usuario en localStorage: @@ -968,7 +968,7 @@ export function setStoredCity(city: string): void { } ``` -## Implementación de Clasificaciones +## Implementación de Clasificaciones {#implementing-leaderboards} Construiremos una interfaz con pestañas que permite a los usuarios cambiar entre actividades (Correr/Ciclismo/Natación) y ámbitos (Global vs. Ciudad Local). @@ -1051,7 +1051,7 @@ export default function LeaderboardsPage() { } ``` -## Construcción de la Página de Logros +## Construcción de la Página de Logros {#building-the-achievements-page} Un espacio dedicado para mostrar insignias es esencial para la retención a largo plazo. Usaremos los datos de `stats.achievements` para renderizar una cuadrícula de insignias, distinguiendo visualmente entre estados conseguidos (a color) y bloqueados (escala de grises). @@ -1120,7 +1120,7 @@ export default async function AchievementsPage() { } ``` -## Construcción de la Página de Perfil +## Construcción de la Página de Perfil {#building-the-profile-page} Finalmente, la página de perfil lo integra todo. Muestra las "Estadísticas de por Vida" del usuario, su progresión actual de XP y les permite actualizar su configuración (como su ciudad). @@ -1199,7 +1199,7 @@ export default async function ProfilePage() { } ``` -## El Resultado +## El Resultado {#the-result} ¡Ahora tienes un circuito de gamificación de fitness completamente funcional! Los usuarios pueden registrar entrenamientos en múltiples deportes, progresar a través de niveles de Puntos, conseguir Logros y competir en Clasificaciones. @@ -1210,11 +1210,11 @@ export default async function ProfilePage() { loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-fitness-app/demo.mp4" + src="../../assets/guides/example-apps/example-fitness-app/demo.mp4" > -### Lo que has construido +### Lo que has construido {#what-youve-built} - **Seguimiento multideporte** con XP normalizado para correr, ciclismo y natación - **Clasificaciones semanales** con competencia global y por ciudad @@ -1222,7 +1222,7 @@ export default async function ProfilePage() { - **Sistema de logros** con insignias de hitos - **Rachas diarias** para impulsar la retención -### Próximos pasos +### Próximos pasos {#next-steps} - **Conecta dispositivos de fitness** — Integra con Strava, Apple Health o Google Fit para registrar entrenamientos automáticamente - **Agrega funciones sociales** — Permite que los usuarios sigan a amigos, comparen estadísticas y compartan logros diff --git a/es/guides/gamified-study-platform.mdx b/es/guides/gamified-study-platform.mdx index 00e2128..9373563 100644 --- a/es/guides/gamified-study-platform.mdx +++ b/es/guides/gamified-study-platform.mdx @@ -7,8 +7,8 @@ subtitle: Sigue el proceso mientras construimos una plataforma de estudio noindex: true --- -import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; -import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; +import MetricChangeRequestBlock from "../../snippets/metric-change-request-block.mdx"; +import MetricChangeResponseBlock from "../../snippets/metric-change-response-block.mdx"; En este tutorial construiremos una plataforma de estudio de ejemplo usando Trophy para la gamificación. Si prefieres ir directamente al resultado final, no dudes en consultar el [repositorio de ejemplo](https://github.com/trophyso/example-study-platform) o la [demostración en vivo](https://study.examples.trophy.so). @@ -19,11 +19,11 @@ En este tutorial construiremos una plataforma de estudio de ejemplo usando Troph loop playsInline className="w-full aspect-15/4" - src="../assets/guides/example-apps/example-study-app/intro-demo.mp4" + src="../../assets/guides/example-apps/example-study-app/intro-demo.mp4" > -## Tabla de Contenidos +## Tabla de Contenidos {#table-of-contents} - [Stack Tecnológico](#tech-stack) - [Requisitos Previos](#pre-requisites) @@ -35,7 +35,7 @@ En este tutorial construiremos una plataforma de estudio de ejemplo usando Troph - [Agregando un Sistema de Energía](#adding-an-energy-system) - [El Resultado](#the-result) -## Stack Tecnológico +## Stack Tecnológico {#tech-stack} - [NextJS 15](https://nextjs.org/docs) (React 19) - [Shadcn/Ui](https://ui.shadcn.com) @@ -44,11 +44,11 @@ En este tutorial construiremos una plataforma de estudio de ejemplo usando Troph - [HTML5 Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) para efectos de sonido - [Trophy](https://trophy.so) para gamificación -## Requisitos Previos +## Requisitos Previos {#pre-requisites} - Una cuenta de Trophy (no te preocupes, la configuraremos sobre la marcha...) -## Configuración e Instalación +## Configuración e Instalación {#setup-installation} ¿Quieres saltarte la configuración? Ve directamente a [la parte @@ -93,9 +93,9 @@ Some packages may fail to install due to peer dependency issues in npm (see http Para los propósitos de este tutorial elegí `--force`, pero debes elegir la configuración que mejor se adapte a tus requisitos. -## Construcción de la Funcionalidad de Tarjetas de Estudio +## Construcción de la Funcionalidad de Tarjetas de Estudio {#building-the-flashcards-feature} -### Configuración de Datos de Tarjetas de Estudio +### Configuración de Datos de Tarjetas de Estudio {#setting-up-flashcard-data} Para los propósitos de este tutorial, vamos a usar algunos tipos simples con un almacén de datos en memoria. En una aplicación de producción probablemente querrías considerar almacenar esta información en una base de datos. @@ -218,7 +218,7 @@ export const flashcards: IFlashcard[] = [ ]; ``` -### Diseño Básico de Tarjetas de Estudio +### Diseño Básico de Tarjetas de Estudio {#basic-flashcard-layout} Con algunos datos básicos configurados, necesitamos agregar una forma para que los usuarios naveguen por sus tarjetas de estudio. @@ -294,11 +294,11 @@ Al final de este paso, debes tener una interfaz de tarjetas de estudio funcional loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-study-app/basic-flashcard-ui.mp4" + src="../../assets/guides/example-apps/example-study-app/basic-flashcard-ui.mp4" > -### Voltear Tarjetas de Estudio +### Voltear Tarjetas de Estudio {#flipping-flashcards} Ahora esto está genial, pero no es muy útil como aplicación de estudio en este momento, ya que no hay forma de ver si obtuviste la respuesta correcta. Necesitamos agregar una forma de voltear las tarjetas de estudio y verificar nuestra respuesta... @@ -477,7 +477,7 @@ Notarás que también agregamos un par de estilos aquí. Estos hacen un par de c } ``` -### ¡Un Error de Volteo! +### ¡Un Error de Volteo! {#a-flippin-bug} ¡Genial! Nuestro proyecto ahora está empezando a sentirse como una verdadera herramienta de estudio. Sin embargo, los observadores atentos (o quizás no tanto...) notarán que hay un error importante aquí. Cuando volteamos una tarjeta, la respuesta en el reverso aparece al revés 😢... @@ -488,7 +488,7 @@ Notarás que también agregamos un par de estilos aquí. Estos hacen un par de c loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-study-app/flipping-flashcards.mp4" + src="../../assets/guides/example-apps/example-study-app/flipping-flashcards.mp4" > @@ -536,11 +536,11 @@ Perfecto. Ahora cuando volteemos una tarjeta, la respuesta en el reverso deberí loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-study-app/flipping-flashcards-fixed.mp4" + src="../../assets/guides/example-apps/example-study-app/flipping-flashcards-fixed.mp4" > -### Seguimiento del Progreso +### Seguimiento del Progreso {#tracking-progress} El siguiente paso es agregar una interfaz para mostrar al usuario cuántas tarjetas de estudio ha revisado y cuántas le quedan en el conjunto. Usaremos una barra de progreso simple para lograr esto. @@ -667,13 +667,13 @@ export default function Flashcards({ flashcards }: Props) { loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-study-app/progress-tracking.mp4" + src="../../assets/guides/example-apps/example-study-app/progress-tracking.mp4" > Ahora comienza la verdadera diversión... -## Agregando Gamificación Básica +## Agregando Gamificación Básica {#adding-basic-gamification} En esta sección agregaremos los siguientes elementos de gamificación al proyecto de tarjetas de estudio: @@ -693,11 +693,11 @@ Así es. Trophy hace que sea muy fácil construir estas funcionalidades con unas Entonces, comencemos... -### Cómo Funciona Trophy +### Cómo Funciona Trophy {#how-trophy-works} Primero, necesitamos una nueva [cuenta de Trophy](https://app.trophy.so/sign-up). Luego podemos empezar a ensamblar las diferentes partes de nuestra experiencia de gamificación. -En Trophy, las [Métricas](/platform/metrics) representan diferentes interacciones que los usuarios pueden realizar y pueden impulsar funcionalidades como [Logros](/platform/achievements), [Rachas](/platform/streaks) y [Correos electrónicos](/platform/emails). +En Trophy, las [Métricas](/es/platform/metrics) representan diferentes interacciones que los usuarios pueden realizar y pueden impulsar funcionalidades como [Logros](/es/platform/achievements), [Rachas](/es/platform/streaks) y [Correos electrónicos](/es/platform/emails). ```mermaid flowchart TD @@ -714,19 +714,19 @@ flowchart TD B-->F ``` -En Trophy rastreamos las interacciones del usuario enviando [Eventos](/platform/events) desde nuestro código a Trophy contra una métrica específica. +En Trophy rastreamos las interacciones del usuario enviando [Eventos](/es/platform/events) desde nuestro código a Trophy contra una métrica específica. Cuando se registran eventos para un usuario específico, cualquier logro vinculado a la métrica especificada se desbloqueará si se cumplen los requisitos, las rachas diarias se calcularán automáticamente y se mantendrán actualizadas, se otorgarán puntos y se actualizarán las clasificaciones. Esto es lo que hace que construir experiencias gamificadas con Trophy sea tan fácil: hace todo el trabajo por ti detrás de escena. -Registrar un evento contra una métrica para un usuario específico hace uso de la [API de eventos de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event) de Trophy, que en la práctica se ve así: +Registrar un evento contra una métrica para un usuario específico hace uso de la [API de eventos de métrica](/es/api-reference/endpoints/metrics/send-a-metric-change-event) de Trophy, que en la práctica se ve así: Así es como con solo unas pocas líneas de código podemos impulsar todo un conjunto de funciones de gamificación. Sin embargo, antes de poder empezar a enviar eventos, necesitamos configurar nuestra nueva cuenta de Trophy. -### Configurando Trophy +### Configurando Trophy {#setting-up-trophy} Así es como configuraremos nuestra cuenta de Trophy para nuestra aplicación de estudio: @@ -748,7 +748,7 @@ Comencemos... loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-study-app/create-metric.mp4" + src="../../assets/guides/example-apps/example-study-app/create-metric.mp4" > @@ -765,7 +765,7 @@ Comencemos... Descarga y usa libremente este archivo zip de insignias (ya preparadas para esta aplicación de ejemplo): - [flashcard_badges.zip](../assets/guides/example-apps/example-study-app/flashcard_badges.zip) + [flashcard_badges.zip](../../assets/guides/example-apps/example-study-app/flashcard_badges.zip) @@ -773,7 +773,7 @@ Comencemos... height="200" width="100%" noZoom - src="../assets/guides/example-apps/example-study-app/achievements.png" + src="../../assets/guides/example-apps/example-study-app/achievements.png" /> @@ -789,7 +789,7 @@ Comencemos... loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-study-app/configure-streak.mp4" + src="../../assets/guides/example-apps/example-study-app/configure-streak.mp4" > @@ -813,20 +813,20 @@ Trophy también nos ofrece una vista previa de los correos electrónicos, lo cua loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-study-app/configure-emails.mp4" + src="../../assets/guides/example-apps/example-study-app/configure-emails.mp4" > {" "} - Antes de poder empezar a enviar correos, necesitarás [configurar tu dirección de envío](/platform/emails#sending-emails) con Trophy. Esto puede hacerse más adelante si lo prefieres. + Antes de poder empezar a enviar correos, necesitarás [configurar tu dirección de envío](/es/platform/emails#sending-emails) con Trophy. Esto puede hacerse más adelante si lo prefieres. -### Integrar el SDK de Trophy +### Integrar el SDK de Trophy {#integrating-trophy-sdk} Primero necesitamos obtener nuestra clave API de la [página de integración](https://app.trophy.so/integration) de Trophy y añadirla como una variable de entorno **únicamente del lado del servidor**. @@ -956,13 +956,13 @@ Podemos validar que esto funciona verificando el [panel de control](https://app. height="200" width="100%" noZoom - src="../assets/guides/example-apps/example-study-app/top-users.png" + src="../../assets/guides/example-apps/example-study-app/top-users.png" /> A continuación, añadiremos algo de interfaz de usuario para mostrar nuestras funciones de gamificación en la práctica. -### Añadiendo UX de Gamificación +### Añadiendo UX de Gamificación {#adding-gamification-ux} La respuesta a la llamada de API que hacemos para rastrear eventos en Trophy nos devuelve útilmente cualquier cambio en el progreso del usuario como resultado de ese evento: @@ -978,7 +978,7 @@ Esto nos facilita reaccionar a los cambios en el progreso del usuario y hacer lo - Mostrar una notificación emergente tipo toast - Reproducir un efecto de sonido -#### Notificaciones de Logro Desbloqueado +#### Notificaciones de Logro Desbloqueado {#achievement-unlocked-toasts} Primero, mostraremos una notificación toast cuando los usuarios desbloqueen nuevos logros. Para esto necesitamos añadir el componente `sonner` de shadcn/ui a nuestro proyecto: @@ -1177,11 +1177,11 @@ Para ver esto en acción, todo lo que necesitamos hacer es desbloquear nuestro p loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-study-app/displaying-toasts.mp4" + src="../../assets/guides/example-apps/example-study-app/displaying-toasts.mp4" > -#### Notificaciones de Racha Extendida +#### Notificaciones de Racha Extendida {#streak-extended-toasts} Usaremos los mismos métodos para mostrar notificaciones similares cuando un usuario extienda su racha. Como hemos configurado una racha diaria en Trophy, esto se activará la primera vez que un usuario vea una tarjeta de estudio cada día. @@ -1286,11 +1286,11 @@ Ahora, la primera vez que un usuario vea una flashcard cada día, verá uno de n loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-study-app/streak-toasts.mp4" + src="../../assets/guides/example-apps/example-study-app/streak-toasts.mp4" > -#### Efectos de Sonido +#### Efectos de Sonido {#sound-effects} La gamificación se trata principalmente de aumentar la retención de usuarios. La mejor forma de hacerlo es hacer que las funcionalidades que construimos sean lo más atractivas posible. Una excelente manera de lograrlo que a menudo se pasa por alto es mediante efectos de sonido. @@ -1409,7 +1409,7 @@ export default function Flashcards({ flashcards }: Props) { } ``` -#### Mostrando el Progreso de Estudio +#### Mostrando el Progreso de Estudio {#displaying-study-journey} La última pieza de UI que agregaremos será un diálogo para mostrar el progreso de estudio del usuario, incluyendo su racha y cualquier logro que haya desbloqueado hasta ahora. @@ -1738,15 +1738,15 @@ export default async function Home() { loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-study-app/study-journey.mp4" + src="../../assets/guides/example-apps/example-study-app/study-journey.mp4" > -## Añadiendo un Sistema de XP +## Añadiendo un Sistema de XP {#adding-an-xp-system} Ahora llevemos nuestra plataforma de estudio al siguiente nivel añadiendo un sistema de XP (puntos). Esto dará a los usuarios una sensación de progreso y los recompensará por su actividad de estudio. -### Configurando Puntos en Trophy +### Configurando Puntos en Trophy {#setting-up-points-in-trophy} Primero, dirígete a la [página de puntos](https://app.trophy.so/points) de Trophy y crea un nuevo sistema de puntos. Llamaremos al nuestro "XP" con la clave `points`. @@ -1757,13 +1757,13 @@ Primero, dirígete a la [página de puntos](https://app.trophy.so/points) de Tro loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-study-app/configure-points.mp4" + src="../../assets/guides/example-apps/example-study-app/configure-points.mp4" > Configura tu sistema de puntos para otorgar XP por ver tarjetas de estudio. También puedes configurar XP de bonificación por hitos de racha. -### Integrando la API de Puntos +### Integrando la API de Puntos {#integrating-points-api} Agrega nuevas acciones de servidor para obtener los datos de puntos: @@ -1818,7 +1818,7 @@ export async function getPointsSummary( } ``` -### Creando un Contexto de Puntos +### Creando un Contexto de Puntos {#creating-a-points-context} Para gestionar el estado de puntos en toda la aplicación, crea un contexto de React: @@ -1911,7 +1911,7 @@ export function useUserPoints() { } ``` -### Construyendo la UI del Centro de Puntos +### Construyendo la UI del Centro de Puntos {#building-the-points-center-ui} Crea un componente de visualización de puntos que muestre los XP del usuario con una animación de números satisfactoria: @@ -1974,7 +1974,7 @@ export default function PointsCenter() { } ``` -### Actualización de Puntos en la Vista de Tarjetas +### Actualización de Puntos en la Vista de Tarjetas {#updating-points-on-flashcard-view} Finalmente, vuelve a consultar los puntos cada vez que un usuario visualice una tarjeta para mostrar actualizaciones de XP en tiempo real: @@ -2010,15 +2010,15 @@ export default function Flashcards({ flashcards }: Props) { loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-study-app/points-animation.mp4" + src="../../assets/guides/example-apps/example-study-app/points-animation.mp4" > -## Añadir Clasificaciones +## Añadir Clasificaciones {#adding-leaderboards} Nada impulsa el compromiso como la competencia amistosa. Añadamos una clasificación para mostrar cómo se comparan los usuarios entre sí. -### Configuración de Clasificaciones en Trophy +### Configuración de Clasificaciones en Trophy {#setting-up-leaderboards-in-trophy} Dirígete a la [página de clasificaciones](https://app.trophy.so/leaderboards) de Trophy y crea una nueva clasificación. Crearemos una clasificación diaria con la clave `daily-champions` que clasifique a los usuarios por tarjetas visualizadas. @@ -2029,11 +2029,11 @@ Dirígete a la [página de clasificaciones](https://app.trophy.so/leaderboards) loop playsInline className="w-full aspect-video" - src="../assets/guides/example-apps/example-study-app/configure-leaderboard.mp4" + src="../../assets/guides/example-apps/example-study-app/configure-leaderboard.mp4" > -### Integración del API de Clasificación +### Integración del API de Clasificación {#leaderboard-api-integration} Añade acciones del servidor para obtener datos de la clasificación: @@ -2080,7 +2080,7 @@ export async function getUserLeaderboard( } ``` -### Construcción de la UI de Clasificación +### Construcción de la UI de Clasificación {#building-the-leaderboard-ui} Crea un componente de clasificación que muestre las clasificaciones con paginación: @@ -2140,11 +2140,11 @@ export default function LeaderboardCenter() { La clasificación se actualiza automáticamente a medida que los usuarios estudian, creando un entorno competitivo dinámico que fomenta el compromiso diario. -## Añadir un Sistema de Energía +## Añadir un Sistema de Energía {#adding-an-energy-system} Los sistemas de energía (o "vidas") son una mecánica de retención comprobada que anima a los usuarios a regresar durante el día. Añadamos uno a nuestra plataforma de estudio. -### Configuración de Energía en Trophy +### Configuración de Energía en Trophy {#setting-up-energy-in-trophy} El sistema de puntos de Trophy es lo suficientemente flexible para manejar mecánicas de energía. Crea un nuevo sistema de puntos con la clave `energy` y configúralo con: @@ -2157,11 +2157,11 @@ El sistema de puntos de Trophy es lo suficientemente flexible para manejar mecá height="200" width="100%" noZoom - src="../assets/guides/example-apps/example-study-app/energy-triggers.png" + src="../../assets/guides/example-apps/example-study-app/energy-triggers.png" /> -### Integración de la API de energía +### Integración de la API de energía {#energy-api-integration} Agrega acciones del servidor para la gestión de energía: @@ -2197,7 +2197,7 @@ export async function getUserEnergy( } ``` -### Creación de un contexto de energía +### Creación de un contexto de energía {#creating-an-energy-context} Similar a los puntos, crea un contexto para gestionar el estado de energía: @@ -2265,7 +2265,7 @@ export function useUserEnergy() { } ``` -### Construcción de la interfaz de energía +### Construcción de la interfaz de energía {#building-the-energy-ui} Crea una visualización de energía que muestre la energía restante con indicadores visuales: @@ -2293,7 +2293,7 @@ export default function EnergyCenter() { Con la energía implementada, los usuarios tienen un motivo para regresar varias veces al día a medida que su energía se regenera, aumentando tus usuarios activos diarios. -## El resultado +## El resultado {#the-result} ¡Felicitaciones! Si estás leyendo esto, llegaste al final del tutorial y construiste una plataforma de estudio completamente funcional. Por supuesto, hay mucho más que podríamos hacer con esto, así que aquí hay algunas ideas: @@ -2306,6 +2306,6 @@ Con la energía implementada, los usuarios tienen un motivo para regresar varias Si te divertiste o crees que aprendiste algo en el camino, ¡dale una estrella al repositorio en [GitHub](https://github.com/trophyso/example-study-platform)! -## Obtener soporte +## Obtener soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/guides/how-to-build-a-leaderboards-feature.mdx b/es/guides/how-to-build-a-leaderboards-feature.mdx index 8b10790..f8fa87c 100644 --- a/es/guides/how-to-build-a-leaderboards-feature.mdx +++ b/es/guides/how-to-build-a-leaderboards-feature.mdx @@ -7,12 +7,12 @@ subtitle: Aprende a usar Trophy para añadir una función de clasificaciones a t noindex: true --- -import SDKInstallCommand from "/snippets/sdk-install-command.mdx"; -import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; -import LeaderboardRankingsRequest from "/snippets/leaderboard-rankings-request.mdx"; -import LeaderboardRankingsResponse from "/snippets/leaderboard-rankings-response.mdx"; -import UserLeaderboardRankingsRequest from "/snippets/user-leaderboard-rankings-request.mdx"; -import UserLeaderboardRankingsResponse from "/snippets/user-leaderboard-rankings-response.mdx"; +import SDKInstallCommand from "../../snippets/sdk-install-command.mdx"; +import MetricChangeRequestBlock from "../../snippets/metric-change-request-block.mdx"; +import LeaderboardRankingsRequest from "../../snippets/leaderboard-rankings-request.mdx"; +import LeaderboardRankingsResponse from "../../snippets/leaderboard-rankings-response.mdx"; +import UserLeaderboardRankingsRequest from "../../snippets/user-leaderboard-rankings-request.mdx"; +import UserLeaderboardRankingsResponse from "../../snippets/user-leaderboard-rankings-response.mdx"; Esta guía describe el proceso completo para añadir una función de clasificaciones a tu aplicación web o móvil utilizando Trophy. @@ -22,14 +22,14 @@ Para fines ilustrativos, usaremos el ejemplo de una plataforma de estudio que ut Para ver un ejemplo completamente funcional de esto en la práctica, consulta la [demostración en vivo](https://examples.trophy.so) o el [repositorio de github](https://github.com/trophyso/example-study-platform/tree/demo). -## Requisitos previos +## Requisitos previos {#pre-requisites} - Una cuenta de [Trophy](https://app.trophy.so/sign-up) - Aproximadamente 10 minutos -## Configuración de Trophy +## Configuración de Trophy {#trophy-setup} -En Trophy, las [Métricas](/platform/metrics) son los componentes fundamentales de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. +En Trophy, las [Métricas](/es/platform/metrics) son los componentes fundamentales de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. En esta guía, la interacción que nos interesa es `flashcards-viewed`, pero puedes crear una métrica que mejor represente la interacción en torno a la cual deseas crear clasificaciones. @@ -42,11 +42,11 @@ En el panel de Trophy, dirígete a la [página de métricas](https://app.trophy. loop playsInline className="w-full aspect-video" - src="../assets/guides/achievements-feature/create_new_metric.mp4" + src="../../assets/guides/achievements-feature/create_new_metric.mp4" > -Una vez que hayas creado tu métrica, dirígete a la [página de clasificaciones](https://app.trophy.so/leaderboards) y crea las clasificaciones que desees. Puedes encontrar todos los detalles sobre los tipos de clasificaciones y los diferentes casos de uso en la [documentación de clasificaciones](/platform/leaderboards). +Una vez que hayas creado tu métrica, dirígete a la [página de clasificaciones](https://app.trophy.so/leaderboards) y crea las clasificaciones que desees. Puedes encontrar todos los detalles sobre los tipos de clasificaciones y los diferentes casos de uso en la [documentación de clasificaciones](/es/platform/leaderboards). @@ -63,18 +63,18 @@ Para los fines de esta guía, hemos configurado una clasificación diaria que ra Para una guía completa sobre cómo agregar una función de XP a tu aplicación web o móvil, consulta - nuestra [guía completa](/guides/how-to-build-an-xp-feature). + nuestra [guía completa](/es/guides/how-to-build-an-xp-feature). -En Trophy rastreas las interacciones de los usuarios enviando [Eventos](/platform/events) desde tu código a las API de Trophy contra una métrica específica. +En Trophy rastreas las interacciones de los usuarios enviando [Eventos](/es/platform/events) desde tu código a las API de Trophy contra una métrica específica. Cuando se registran eventos para un usuario específico, Trophy actualiza automáticamente su posición en cada clasificación de la que forma parte. Esto es lo que hace que construir experiencias gamificadas con Trophy sea tan fácil: hace todo el trabajo por ti en segundo plano. -## Instalando el SDK de Trophy +## Instalando el SDK de Trophy {#installing-trophy-sdk} -Para interactuar con Trophy desde tu código, utilizarás el SDK de Trophy disponible en la mayoría de los principales [lenguajes de programación](/api-reference/client-libraries). +Para interactuar con Trophy desde tu código, utilizarás el SDK de Trophy disponible en la mayoría de los principales [lenguajes de programación](/es/api-reference/client-libraries). Instala el SDK de Trophy: @@ -90,9 +90,9 @@ TROPHY_API_KEY='*******' Asegúrate de **no** exponer tu clave API en código del lado del cliente. -## Rastreando Interacciones de Usuarios +## Rastreando Interacciones de Usuarios {#tracking-user-interactions} -Para rastrear un evento (interacción de usuario) contra tu métrica, utiliza la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event). +Para rastrear un evento (interacción de usuario) contra tu métrica, utiliza la [API de cambio de métrica](/es/api-reference/endpoints/metrics/send-a-metric-change-event). @@ -137,17 +137,17 @@ Valida que esto funcione revisando el [panel de control](https://app.trophy.so) height="200" width="100%" noZoom - src="../assets/guides/achievements-feature/top-users.png" + src="../../assets/guides/achievements-feature/top-users.png" /> -## Mostrando Clasificaciones +## Mostrando Clasificaciones {#displaying-leaderboards} Tienes varias opciones para mostrar clasificaciones en tu aplicación. Aquí veremos las opciones más comunes. -### Notificaciones Emergentes +### Notificaciones Emergentes {#pop-up-notifications} -Podemos usar la respuesta de la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event) para mostrar notificaciones emergentes (o 'toasts') cuando los usuarios suban en las clasificaciones. +Podemos usar la respuesta de la [API de cambio de métrica](/es/api-reference/endpoints/metrics/send-a-metric-change-event) para mostrar notificaciones emergentes (o 'toasts') cuando los usuarios suban en las clasificaciones. Aquí hay un ejemplo de esto en acción: @@ -182,9 +182,9 @@ if (leaderboard.rank > leaderboard.previousRank) { que recomendamos. -### Mostrar Clasificaciones de la Clasificación +### Mostrar Clasificaciones de la Clasificación {#displaying-leaderboard-rankings} -Para obtener una clasificación y sus rankings más actualizados, usa la [API de clasificaciones](/api-reference/endpoints/leaderboards/get-leaderboard). +Para obtener una clasificación y sus rankings más actualizados, usa la [API de clasificaciones](/es/api-reference/endpoints/leaderboards/get-leaderboard). @@ -197,9 +197,9 @@ Aquí hay un ejemplo de los datos devueltos por la API de clasificaciones: -### Mostrar Historial de Ranking del Usuario +### Mostrar Historial de Ranking del Usuario {#displaying-user-rank-history} -Usa la [API de clasificación de usuario](/api-reference/endpoints/users/get-a-users-leaderboard) para obtener datos sobre cómo ha cambiado el ranking de un usuario específico a lo largo del tiempo en una clasificación particular. +Usa la [API de clasificación de usuario](/es/api-reference/endpoints/users/get-a-users-leaderboard) para obtener datos sobre cómo ha cambiado el ranking de un usuario específico a lo largo del tiempo en una clasificación particular. @@ -207,7 +207,7 @@ Aquí hay un ejemplo de los datos devueltos por la API de rankings de clasificac -## Analítica +## Analítica {#analytics} La [página de clasificaciones](https://app.trophy.so/achievements) en Trophy muestra cuántos usuarios están participando activamente en una clasificación, así como una medida de competitividad basada en cuántos cambios de ranking están ocurriendo. @@ -218,7 +218,7 @@ La página de analítica también muestra una distribución de las puntuaciones height="200" width="100%" noZoom - src="../assets/guides/leaderboards-feature/analytics.png" + src="../../assets/guides/leaderboards-feature/analytics.png" /> @@ -229,10 +229,10 @@ Además, la página de rankings de la clasificación muestra los rankings actual height="200" width="100%" noZoom - src="../assets/guides/leaderboards-feature/rankings.png" + src="../../assets/guides/leaderboards-feature/rankings.png" /> -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/guides/how-to-build-a-streaks-feature.mdx b/es/guides/how-to-build-a-streaks-feature.mdx index 4ae4458..22a31c7 100644 --- a/es/guides/how-to-build-a-streaks-feature.mdx +++ b/es/guides/how-to-build-a-streaks-feature.mdx @@ -7,10 +7,10 @@ subtitle: Aprende a usar Trophy para añadir una función de rachas a tu noindex: true --- -import SDKInstallCommand from "/snippets/sdk-install-command.mdx"; -import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; -import StreakRequestBlock from "/snippets/streak-request-block.mdx"; -import StreakResponseBlock from "/snippets/streak-response-block.mdx"; +import SDKInstallCommand from "../../snippets/sdk-install-command.mdx"; +import MetricChangeRequestBlock from "../../snippets/metric-change-request-block.mdx"; +import StreakRequestBlock from "../../snippets/streak-request-block.mdx"; +import StreakResponseBlock from "../../snippets/streak-response-block.mdx"; Esta guía describe el proceso completo para añadir una función de rachas a tu aplicación web o móvil usando Trophy. @@ -22,14 +22,14 @@ Con fines ilustrativos, usaremos el ejemplo de una plataforma de estudio que uti github](https://github.com/trophyso/example-study-platform/tree/demo). -## Requisitos previos +## Requisitos previos {#pre-requisites} - Una cuenta de [Trophy](https://app.trophy.so/sign-up) - Aproximadamente 10 minutos -## Configuración de Trophy +## Configuración de Trophy {#trophy-setup} -En Trophy, las [Métricas](/platform/metrics) son los elementos fundamentales de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. +En Trophy, las [Métricas](/es/platform/metrics) son los elementos fundamentales de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. En esta guía, la interacción que nos interesa es `flashcards-viewed`, pero puedes crear una métrica que mejor represente la interacción desde la cual quieres generar rachas. @@ -42,7 +42,7 @@ En el panel de Trophy, dirígete a la [página de métricas](https://app.trophy. loop playsInline className="w-full aspect-video" - src="../assets/guides/achievements-feature/create_new_metric.mp4" + src="../../assets/guides/achievements-feature/create_new_metric.mp4" > @@ -50,17 +50,17 @@ Una vez que hayas creado tu métrica, dirígete a la [página de rachas](https:/ Luego añade tu métrica a las condiciones de racha y establece el umbral que los usuarios deben cumplir (por ejemplo, al menos 1 para extender su racha cada período). -También puedes añadir múltiples métricas y elegir si los usuarios deben cumplir todas o solo una—consulta la [documentación de condiciones de racha](/platform/streaks#streak-conditions) para más detalles. +También puedes añadir múltiples métricas y elegir si los usuarios deben cumplir todas o solo una—consulta la [documentación de condiciones de racha](/es/platform/streaks#streak-conditions) para más detalles. -En Trophy, registras las interacciones de los usuarios enviando [Eventos](/platform/events) desde tu código a las API de Trophy contra una métrica específica. +En Trophy, registras las interacciones de los usuarios enviando [Eventos](/es/platform/events) desde tu código a las API de Trophy contra una métrica específica. Cuando se registran eventos para un usuario específico, Trophy verificará automáticamente si ha cumplido con las condiciones de su racha para el período actual y actualizará la racha del usuario en consecuencia. Esto es lo que hace que construir experiencias gamificadas con Trophy sea tan fácil: realiza todo el trabajo por ti tras bambalinas. -## Instalación del SDK de Trophy +## Instalación del SDK de Trophy {#installing-trophy-sdk} -Para interactuar con Trophy desde tu código, utilizarás el SDK de Trophy disponible en la mayoría de los principales [lenguajes de programación](/api-reference/client-libraries). +Para interactuar con Trophy desde tu código, utilizarás el SDK de Trophy disponible en la mayoría de los principales [lenguajes de programación](/es/api-reference/client-libraries). Instala el SDK de Trophy: @@ -76,9 +76,9 @@ TROPHY_API_KEY='*******' Asegúrate de **no** exponer tu clave de API en código del lado del cliente. -## Registro de Interacciones de Usuarios +## Registro de Interacciones de Usuarios {#tracking-user-interactions} -Para registrar un evento (interacción del usuario) contra tu métrica, utiliza la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event). +Para registrar un evento (interacción del usuario) contra tu métrica, utiliza la [API de cambio de métrica](/es/api-reference/endpoints/metrics/send-a-metric-change-event). @@ -114,13 +114,13 @@ La respuesta a esta llamada de API es el conjunto completo de cambios en cualqui Valida que esto funciona verificando el [panel de control](https://app.trophy.so) de Trophy. -## Visualización de Rachas +## Visualización de Rachas {#displaying-streaks} Tienes varias opciones para mostrar rachas en tu aplicación. Aquí examinaremos las opciones más comunes. -### Notificaciones Emergentes +### Notificaciones Emergentes {#pop-up-notifications} -Podemos usar la respuesta de la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event) para mostrar notificaciones emergentes (o 'toasts') cuando el usuario extiende su racha. +Podemos usar la respuesta de la [API de cambio de métrica](/es/api-reference/endpoints/metrics/send-a-metric-change-event) para mostrar notificaciones emergentes (o 'toasts') cuando el usuario extiende su racha. Aquí hay un ejemplo de esto en acción: @@ -148,7 +148,7 @@ if (response.currentStreak?.extended) { loop playsInline className="w-full aspect-video" - src="../assets/guides/streaks-feature/streak-toasts.mp4" + src="../../assets/guides/streaks-feature/streak-toasts.mp4" > @@ -156,9 +156,9 @@ if (response.currentStreak?.extended) { Si deseas reproducir efectos de sonido, usa la [API de Audio HTML5](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) y siéntete libre de usar estos [archivos de audio](https://github.com/trophyso/example-study-platform/tree/demo/public/sounds) que recomendamos. -### Mostrar las Rachas de Usuario +### Mostrar las Rachas de Usuario {#displaying-user-streaks} -Para obtener datos sobre la racha de un usuario, usa la [API de racha de usuario](/api-reference/endpoints/users/get-a-users-streak). +Para obtener datos sobre la racha de un usuario, usa la [API de racha de usuario](/es/api-reference/endpoints/users/get-a-users-streak). @@ -173,7 +173,7 @@ Aquí hay un ejemplo de un calendario de rachas estilo git construido usando los height="200" width="100%" noZoom - src="../assets/guides/streaks-feature/streak-calendar.png" + src="../../assets/guides/streaks-feature/streak-calendar.png" /> @@ -181,7 +181,7 @@ Aquí hay un ejemplo de un calendario de rachas estilo git construido usando los Consulta este [repositorio de ejemplo](https://github.com/trophyso/example-study-platform/blob/demo/src/app/user-center/study-center/default-view.tsx) para un componente React que maneja esta interfaz usando datos de Trophy. -## Analíticas +## Analíticas {#analytics} La [página de rachas](https://app.trophy.so/streaks) en Trophy muestra datos sobre rachas activas, la duración promedio de las rachas y las rachas más largas. @@ -190,16 +190,16 @@ La [página de rachas](https://app.trophy.so/streaks) en Trophy muestra datos so height="200" width="100%" noZoom - src="../assets/guides/streaks-feature/analytics.png" + src="../../assets/guides/streaks-feature/analytics.png" /> -## Congelaciones de Racha +## Congelaciones de Racha {#streak-freezes} Trophy también admite congelaciones de racha que pueden ayudar a evitar que los usuarios pierdan su racha si accidentalmente pierden un período. -Obtén más información sobre las congelaciones de racha en la [documentación dedicada de congelaciones de racha](/platform/streaks#streak-freezes). +Obtén más información sobre las congelaciones de racha en la [documentación dedicada de congelaciones de racha](/es/platform/streaks#streak-freezes). -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/guides/how-to-build-an-achievements-feature.mdx b/es/guides/how-to-build-an-achievements-feature.mdx index dcb7d60..3469c85 100644 --- a/es/guides/how-to-build-an-achievements-feature.mdx +++ b/es/guides/how-to-build-an-achievements-feature.mdx @@ -7,10 +7,10 @@ subtitle: Aprende a usar Trophy para agregar una función de logros a tu noindex: true --- -import SDKInstallCommand from "/snippets/sdk-install-command.mdx"; -import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; -import UserAchievementsRequestBlock from "/snippets/user-achievements-request-block.mdx"; -import AllAchievementsRequestBlock from "/snippets/all-achievements-request-block.mdx"; +import SDKInstallCommand from "../../snippets/sdk-install-command.mdx"; +import MetricChangeRequestBlock from "../../snippets/metric-change-request-block.mdx"; +import UserAchievementsRequestBlock from "../../snippets/user-achievements-request-block.mdx"; +import AllAchievementsRequestBlock from "../../snippets/all-achievements-request-block.mdx"; Esta guía describe el proceso completo para agregar una función de logros a tu aplicación web o móvil utilizando Trophy. @@ -22,14 +22,14 @@ A modo de ilustración, usaremos el ejemplo de una plataforma de estudio que uti github](https://github.com/trophyso/example-study-platform/tree/demo). -## Requisitos previos +## Requisitos previos {#pre-requisites} - Una cuenta de [Trophy](https://app.trophy.so/sign-up) - Aproximadamente 10 minutos -## Configuración de Trophy +## Configuración de Trophy {#trophy-setup} -En Trophy, las [Métricas](/platform/metrics) son los componentes fundamentales de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. +En Trophy, las [Métricas](/es/platform/metrics) son los componentes fundamentales de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. En esta guía, la interacción que nos interesa es `flashcards-viewed`, pero puedes crear una métrica que represente mejor la interacción alrededor de la cual quieres construir logros. @@ -42,11 +42,11 @@ En el panel de Trophy, dirígete a la [página de métricas](https://app.trophy. loop playsInline className="w-full aspect-video" - src="../assets/guides/achievements-feature/create_new_metric.mp4" + src="../../assets/guides/achievements-feature/create_new_metric.mp4" > -Una vez que hayas creado tu métrica, dirígete a la [página de logros](https://app.trophy.so/achievements) y crea los logros que desees. Puedes encontrar todos los detalles sobre los tipos de logros y los diferentes casos de uso en la [documentación de logros](/platform/achievements). +Una vez que hayas creado tu métrica, dirígete a la [página de logros](https://app.trophy.so/achievements) y crea los logros que desees. Puedes encontrar todos los detalles sobre los tipos de logros y los diferentes casos de uso en la [documentación de logros](/es/platform/achievements). Para los propósitos de esta guía, hemos configurado un par de logros basados en un número creciente de tarjetas de estudio volteadas: @@ -56,22 +56,22 @@ Para los propósitos de esta guía, hemos configurado un par de logros basados e - 250 flashcards - 1,000 flashcards -También hemos configurado algunos logros relacionados con [Rachas](/platform/streaks), pero no entraremos en detalle sobre estos en esta guía. +También hemos configurado algunos logros relacionados con [Rachas](/es/platform/streaks), pero no entraremos en detalle sobre estos en esta guía. Para una guía completa sobre cómo agregar una función de rachas a tu aplicación web o móvil, consulta - nuestra [guía completa](/guides/how-to-build-a-streaks-feature). + nuestra [guía completa](/es/guides/how-to-build-a-streaks-feature). -En Trophy realizas seguimiento de las interacciones de usuario enviando [Eventos](/platform/events) desde tu código a las API de Trophy contra una métrica específica. +En Trophy realizas seguimiento de las interacciones de usuario enviando [Eventos](/es/platform/events) desde tu código a las API de Trophy contra una métrica específica. Cuando se registran eventos para un usuario específico, cualquier logro vinculado a la métrica especificada se **completará automáticamente** si se cumplen los requisitos. Esto es lo que hace que construir experiencias gamificadas con Trophy sea tan fácil: hace todo el trabajo por ti detrás de escena. -## Instalación del SDK de Trophy +## Instalación del SDK de Trophy {#installing-trophy-sdk} -Para interactuar con Trophy desde tu código, utilizarás el SDK de Trophy disponible en la mayoría de los principales [lenguajes de programación](/api-reference/client-libraries). +Para interactuar con Trophy desde tu código, utilizarás el SDK de Trophy disponible en la mayoría de los principales [lenguajes de programación](/es/api-reference/client-libraries). Instala el SDK de Trophy: @@ -87,9 +87,9 @@ TROPHY_API_KEY='*******' Asegúrate de **no** exponer tu clave API en código del lado del cliente. -## Seguimiento de Interacciones de Usuario +## Seguimiento de Interacciones de Usuario {#tracking-user-interactions} -Para realizar seguimiento de un evento (interacción de usuario) contra tu métrica, utiliza la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event). +Para realizar seguimiento de un evento (interacción de usuario) contra tu métrica, utiliza la [API de cambio de métrica](/es/api-reference/endpoints/metrics/send-a-metric-change-event). @@ -127,17 +127,17 @@ Valida que esto funcione revisando el [panel](https://app.trophy.so) de Trophy. height="200" width="100%" noZoom - src="../assets/guides/achievements-feature/top-users.png" + src="../../assets/guides/achievements-feature/top-users.png" /> -## Mostrar Logros +## Mostrar Logros {#displaying-achievements} Tienes varias opciones para mostrar logros en tu aplicación. Aquí veremos las opciones más comunes. -### Notificaciones Emergentes +### Notificaciones Emergentes {#pop-up-notifications} -Podemos usar la respuesta de la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event) para mostrar notificaciones emergentes (o 'toasts') cuando los usuarios completen logros. +Podemos usar la respuesta de la [API de cambio de métrica](/es/api-reference/endpoints/metrics/send-a-metric-change-event) para mostrar notificaciones emergentes (o 'toasts') cuando los usuarios completen logros. Aquí hay un ejemplo de esto en acción: @@ -169,7 +169,7 @@ response.achievements.forEach((achievement) => { loop playsInline className="w-full aspect-video" - src="../assets/guides/achievements-feature/displaying-toasts.mp4" + src="../../assets/guides/achievements-feature/displaying-toasts.mp4" > @@ -177,9 +177,9 @@ response.achievements.forEach((achievement) => { Si deseas reproducir efectos de sonido, usa la [API de Audio HTML5](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) y siéntete libre de usar estos [archivos de audio](https://github.com/trophyso/example-study-platform/tree/demo/public/sounds) que recomendamos. -### Mostrar Logros del Usuario +### Mostrar Logros del Usuario {#displaying-user-achievements} -Para obtener todos los logros que un usuario ha completado, usa la [API de logros de usuario](/api-reference/endpoints/users/get-a-users-completed-achievements). +Para obtener todos los logros que un usuario ha completado, usa la [API de logros de usuario](/es/api-reference/endpoints/users/get-a-users-completed-achievements). @@ -194,17 +194,17 @@ Para obtener todos los logros que un usuario ha completado, usa la [API de logro loop playsInline className="w-full aspect-video" - src="../assets/guides/achievements-feature/displaying_trophy_cabinet.mp4" + src="../../assets/guides/achievements-feature/displaying_trophy_cabinet.mp4" > -### Mostrar Todos los Logros +### Mostrar Todos los Logros {#displaying-all-achievements} -Si en cambio deseas mostrar todos los logros que has configurado en Trophy como parte de una interfaz globalmente accesible que no esté vinculada a un usuario en particular, puedes usar la [API de todos los logros](/api-reference/endpoints/achievements/all-achievements). +Si en cambio deseas mostrar todos los logros que has configurado en Trophy como parte de una interfaz globalmente accesible que no esté vinculada a un usuario en particular, puedes usar la [API de todos los logros](/es/api-reference/endpoints/achievements/all-achievements). -### Estadísticas de Finalización de Logros +### Estadísticas de Finalización de Logros {#achievement-completion-stats} Tanto la API de logros de usuario como la API de todos los logros incluyen estadísticas de finalización como `completions` (el número de usuarios que han completado un logro) y `rarity` (el porcentaje de usuarios que han completado un logro). @@ -213,11 +213,11 @@ Tanto la API de logros de usuario como la API de todos los logros incluyen estad height="200" width="100%" noZoom - src="../assets/guides/achievements-feature/completion-stats.png" + src="../../assets/guides/achievements-feature/completion-stats.png" /> -## Analíticas +## Analíticas {#analytics} La [página de logros](https://app.trophy.so/achievements) en Trophy muestra cuántos usuarios han completado cada logro que has configurado. @@ -226,7 +226,7 @@ La [página de logros](https://app.trophy.so/achievements) en Trophy muestra cu height="200" width="100%" noZoom - src="../assets/guides/achievements-feature/check_user_completions.png" + src="../../assets/guides/achievements-feature/check_user_completions.png" /> @@ -237,10 +237,10 @@ Además, la página de análisis de cualquier métrica en Trophy incluye un grá height="200" width="100%" noZoom - src="../assets/guides/achievements-feature/achievement_completions.png" + src="../../assets/guides/achievements-feature/achievement_completions.png" /> -## Obtén Soporte +## Obtén Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/guides/how-to-build-an-energy-feature.mdx b/es/guides/how-to-build-an-energy-feature.mdx index d62e2f0..dcd6800 100644 --- a/es/guides/how-to-build-an-energy-feature.mdx +++ b/es/guides/how-to-build-an-energy-feature.mdx @@ -7,11 +7,11 @@ subtitle: Aprende a usar Trophy para añadir una función de energía a tu noindex: true --- -import SDKInstallCommand from "/snippets/sdk-install-command.mdx"; -import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; -import UserPointsRequest from "/snippets/user-points-request-block.mdx"; -import UserEnergyResponse from "/snippets/user-energy-response-block.mdx"; -import UserPointsEventSummaryRequest from "/snippets/user-points-summary-request-block.mdx"; +import SDKInstallCommand from "../../snippets/sdk-install-command.mdx"; +import MetricChangeRequestBlock from "../../snippets/metric-change-request-block.mdx"; +import UserPointsRequest from "../../snippets/user-points-request-block.mdx"; +import UserEnergyResponse from "../../snippets/user-energy-response-block.mdx"; +import UserPointsEventSummaryRequest from "../../snippets/user-points-summary-request-block.mdx"; Esta guía describe el proceso completo para añadir una función de energía a tu aplicación web o móvil usando Trophy. @@ -21,14 +21,14 @@ Con fines ilustrativos, usaremos el ejemplo de una plataforma de estudio que uti Para ver un ejemplo completamente funcional de esto en la práctica, consulta la [demostración en vivo](https://examples.trophy.so) o el [repositorio de github](https://github.com/trophyso/example-study-platform/tree/demo). -## Requisitos previos +## Requisitos previos {#pre-requisites} - Una cuenta de [Trophy](https://app.trophy.so/sign-up) - Aproximadamente 10 minutos -## Configuración de Trophy +## Configuración de Trophy {#trophy-setup} -En Trophy, las [Métricas](/platform/metrics) son los componentes básicos de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. +En Trophy, las [Métricas](/es/platform/metrics) son los componentes básicos de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. En esta guía, la interacción que nos interesa es `flashcards-viewed`, pero puedes crear cualquier número de métricas que mejor representen las interacciones desde las cuales deseas otorgar y consumir energía. @@ -41,7 +41,7 @@ En el panel de Trophy, dirígete a la [página de métricas](https://app.trophy. loop playsInline className="w-full aspect-video" - src="../assets/guides/achievements-feature/create_new_metric.mp4" + src="../../assets/guides/achievements-feature/create_new_metric.mp4" > @@ -54,11 +54,11 @@ Una vez que hayas creado tu métrica, dirígete a la [página de puntos](https:/ loop playsInline className="w-full aspect-video" - src="../assets/guides/xp-feature/create_system.mp4" + src="../../assets/guides/xp-feature/create_system.mp4" > -Una vez creado, serás llevado a la página de configuración del sistema de energía donde podrás crear [activadores de puntos](/platform/points#types-of-triggers) para cada una de las formas en que deseas otorgar o consumir energía. +Una vez creado, serás llevado a la página de configuración del sistema de energía donde podrás crear [activadores de puntos](/es/platform/points#types-of-triggers) para cada una de las formas en que deseas otorgar o consumir energía. -Usa disparadores 'time' para otorgar a los usuarios nueva energía cada hora o diariamente, y utiliza [otros tipos](/platform/points#types-of-triggers) de disparadores con valores negativos para consumir energía de las diferentes interacciones de usuario que desees. +Usa disparadores 'time' para otorgar a los usuarios nueva energía cada hora o diariamente, y utiliza [otros tipos](/es/platform/points#types-of-triggers) de disparadores con valores negativos para consumir energía de las diferentes interacciones de usuario que desees. -En Trophy rastreas las interacciones de los usuarios enviando [Eventos](/platform/events) desde tu código a las APIs de Trophy contra una métrica específica. +En Trophy rastreas las interacciones de los usuarios enviando [Eventos](/es/platform/events) desde tu código a las APIs de Trophy contra una métrica específica. Cuando se registran eventos para un usuario específico, Trophy verificará automáticamente si alguno de los disparadores configurados contra tu sistema de energía debe activarse, y los procesará en consecuencia. @@ -81,9 +81,9 @@ Trophy también se encarga de otorgar automáticamente nueva energía a los usua Esto es lo que hace que construir experiencias gamificadas con Trophy sea tan fácil: realiza todo el trabajo por ti en segundo plano. -## Instalación del SDK de Trophy +## Instalación del SDK de Trophy {#installing-trophy-sdk} -Para interactuar con Trophy desde tu código utilizarás el SDK de Trophy disponible en los principales [lenguajes de programación](/api-reference/client-libraries). +Para interactuar con Trophy desde tu código utilizarás el SDK de Trophy disponible en los principales [lenguajes de programación](/es/api-reference/client-libraries). Instala el SDK de Trophy: @@ -99,9 +99,9 @@ TROPHY_API_KEY='*******' Asegúrate de **no** exponer tu clave de API en código del lado del cliente. -## Rastreo de Interacciones de Usuario +## Rastreo de Interacciones de Usuario {#tracking-user-interactions} -Para rastrear un evento (interacción de usuario) contra tu métrica, utiliza la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event). +Para rastrear un evento (interacción de usuario) contra tu métrica, utiliza la [API de cambio de métrica](/es/api-reference/endpoints/metrics/send-a-metric-change-event). @@ -148,9 +148,9 @@ La respuesta a esta llamada de API es el conjunto completo de cambios en cualqui Valida que esto funciona revisando el [panel de control](https://app.trophy.so) de Trophy. -## Medición del Uso +## Medición del Uso {#metering-usage} -Para evitar que los usuarios realicen acciones en tu producto basándose en energía, usa la [API de puntos de usuario](/api-reference/endpoints/users/get-a-users-points) para obtener su total actual de energía. +Para evitar que los usuarios realicen acciones en tu producto basándose en energía, usa la [API de puntos de usuario](/es/api-reference/endpoints/users/get-a-users-points) para obtener su total actual de energía. @@ -174,17 +174,17 @@ if (energy.total > 0) { Luego puedes modificar tu configuración de triggers en Trophy y controlar la velocidad a la que los usuarios pueden interactuar con tu producto directamente desde el panel de Trophy sin necesidad de realizar cambios en el código. -## Visualización de la Energía +## Visualización de la Energía {#displaying-energy} -Para obtener la energía de un usuario, usa la [API de puntos de usuario](/api-reference/endpoints/users/get-a-users-points). +Para obtener la energía de un usuario, usa la [API de puntos de usuario](/es/api-reference/endpoints/users/get-a-users-points). -Esta API devuelve datos sobre la energía total del usuario, pero puede configurarse para también devolver entre 1 y 100 de los cambios de energía más recientes del usuario utilizando el [parámetro de consulta](/api-reference/endpoints/users/get-a-users-points#parameter-awards) `awards`. +Esta API devuelve datos sobre la energía total del usuario, pero puede configurarse para también devolver entre 1 y 100 de los cambios de energía más recientes del usuario utilizando el [parámetro de consulta](/es/api-reference/endpoints/users/get-a-users-points#parameter-awards) `awards`. -La [API de resumen de puntos de usuario](/api-reference/endpoints/users/get-a-users-points-summary) también puede utilizarse para impulsar interfaces basadas en gráficos, como mostrar a los usuarios su uso de energía a lo largo del tiempo. +La [API de resumen de puntos de usuario](/es/api-reference/endpoints/users/get-a-users-points-summary) también puede utilizarse para impulsar interfaces basadas en gráficos, como mostrar a los usuarios su uso de energía a lo largo del tiempo. @@ -195,11 +195,11 @@ Aquí hay un ejemplo de una interfaz que muestra a los usuarios su energía actu height="200" width="100%" noZoom - src="../assets/guides/energy-feature/ui.png" + src="../../assets/guides/energy-feature/ui.png" /> -## Analíticas +## Analíticas {#analytics} En Trophy, tu [página del sistema de energía](https://app.trophy.so/points) incluye gráficos de analíticas que muestran datos sobre la energía total otorgada/consumida y un desglose de exactamente qué triggers causan los cambios más frecuentes en energía. @@ -208,10 +208,10 @@ En Trophy, tu [página del sistema de energía](https://app.trophy.so/points) in height="200" width="100%" noZoom - src="../assets/guides/energy-feature/analytics.png" + src="../../assets/guides/energy-feature/analytics.png" /> -## Obtener soporte +## Obtener soporte {#get-support} ¿Quieres contactar con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/guides/how-to-build-an-xp-feature.mdx b/es/guides/how-to-build-an-xp-feature.mdx index e78b512..0df5ced 100644 --- a/es/guides/how-to-build-an-xp-feature.mdx +++ b/es/guides/how-to-build-an-xp-feature.mdx @@ -7,13 +7,13 @@ subtitle: Aprende a usar Trophy para agregar una funcionalidad de XP a tu noindex: true --- -import SDKInstallCommand from "/snippets/sdk-install-command.mdx"; -import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; -import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; -import UserPointsRequest from "/snippets/user-points-request-block.mdx"; -import UserPointsResponse from "/snippets/user-points-response-block.mdx"; -import UserPointsEventSummaryRequest from "/snippets/user-points-summary-request-block.mdx"; -import UserPointsEventSummaryResponse from "/snippets/user-points-summary-response-block.mdx"; +import SDKInstallCommand from "../../snippets/sdk-install-command.mdx"; +import MetricChangeRequestBlock from "../../snippets/metric-change-request-block.mdx"; +import MetricChangeResponseBlock from "../../snippets/metric-change-response-block.mdx"; +import UserPointsRequest from "../../snippets/user-points-request-block.mdx"; +import UserPointsResponse from "../../snippets/user-points-response-block.mdx"; +import UserPointsEventSummaryRequest from "../../snippets/user-points-summary-request-block.mdx"; +import UserPointsEventSummaryResponse from "../../snippets/user-points-summary-response-block.mdx"; Esta guía describe el proceso completo para agregar una funcionalidad de XP a tu aplicación web o móvil usando Trophy. @@ -23,14 +23,14 @@ Con fines ilustrativos, usaremos el ejemplo de una plataforma de estudio que uti Para ver un ejemplo completamente funcional de esto en la práctica, consulta la [demostración en vivo](https://examples.trophy.so) o el [repositorio de github](https://github.com/trophyso/example-study-platform/tree/demo). -## Requisitos previos +## Requisitos previos {#pre-requisites} - Una cuenta de [Trophy](https://app.trophy.so/sign-up) - Aproximadamente 10 minutos -## Configuración de Trophy +## Configuración de Trophy {#trophy-setup} -En Trophy, las [Métricas](/platform/metrics) son los bloques de construcción de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. +En Trophy, las [Métricas](/es/platform/metrics) son los bloques de construcción de la gamificación y modelan las diferentes interacciones que los usuarios realizan con tu producto. En esta guía, la interacción que nos interesa es `flashcards-viewed`, pero puedes crear cualquier cantidad de métricas que mejor representen las interacciones desde las cuales deseas recompensar XP. @@ -43,7 +43,7 @@ En el panel de Trophy, dirígete a la [página de métricas](https://app.trophy. loop playsInline className="w-full aspect-video" - src="../assets/guides/achievements-feature/create_new_metric.mp4" + src="../../assets/guides/achievements-feature/create_new_metric.mp4" > @@ -56,7 +56,7 @@ Una vez que hayas creado tu métrica, dirígete a la [página de puntos](https:/ loop playsInline className="w-full aspect-video" - src="../assets/guides/xp-feature/create_system.mp4" + src="../../assets/guides/xp-feature/create_system.mp4" > @@ -69,11 +69,11 @@ Una vez creado, serás dirigido a la página de configuración del sistema XP do loop playsInline className="w-full aspect-video" - src="../assets/guides/xp-feature/create_trigger.mp4" + src="../../assets/guides/xp-feature/create_trigger.mp4" > -En Trophy rastreás las interacciones del usuario enviando [Eventos](/platform/events) desde tu código a las APIs de Trophy contra una métrica específica. +En Trophy rastreás las interacciones del usuario enviando [Eventos](/es/platform/events) desde tu código a las APIs de Trophy contra una métrica específica. Cuando se registran eventos para un usuario específico, Trophy verificará automáticamente si el evento está asociado a una métrica configurada como parte de algún disparador de XP. @@ -83,13 +83,13 @@ Esto es lo que hace que construir experiencias gamificadas con Trophy sea tan f Los XP también pueden otorgarse a los usuarios en base a logros, rachas y otros - disparadores. Consultá la [documentación de puntos](/platform/points#points-triggers) + disparadores. Consultá la [documentación de puntos](/es/platform/points#points-triggers) para más información. -## Instalación del SDK de Trophy +## Instalación del SDK de Trophy {#installing-trophy-sdk} -Para interactuar con Trophy desde tu código usarás el SDK de Trophy disponible en la mayoría de los principales [lenguajes de programación](/api-reference/client-libraries). +Para interactuar con Trophy desde tu código usarás el SDK de Trophy disponible en la mayoría de los principales [lenguajes de programación](/es/api-reference/client-libraries). Instalá el SDK de Trophy: @@ -105,16 +105,16 @@ TROPHY_API_KEY='*******' Asegurate de **no** exponer tu clave de API en código del lado del cliente. -## Rastreo de Interacciones del Usuario +## Rastreo de Interacciones del Usuario {#tracking-user-interactions} -Para rastrear un evento (interacción del usuario) contra tu métrica, usá la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event). +Para rastrear un evento (interacción del usuario) contra tu métrica, usá la [API de cambio de métrica](/es/api-reference/endpoints/metrics/send-a-metric-change-event). La respuesta a esta llamada de API es el conjunto completo de cambios a cualquier funcionalidad que hayas construido con Trophy, incluyendo cualquier XP que se le haya otorgado al usuario como resultado del evento, y de qué disparadores fue otorgado. - Si usás [Niveles de Puntos](/platform/points#points-levels), el objeto `xp` puede incluir un campo **`level`** **solo cuando** el nivel del usuario cambió en esta solicitud; puede omitirse cuando su nivel se mantuvo igual. Consultá la [referencia de API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event) para el esquema de respuesta completo. + Si usás [Niveles de Puntos](/es/platform/points#points-levels), el objeto `xp` puede incluir un campo **`level`** **solo cuando** el nivel del usuario cambió en esta solicitud; puede omitirse cuando su nivel se mantuvo igual. Consultá la [referencia de API de cambio de métrica](/es/api-reference/endpoints/metrics/send-a-metric-change-event) para el esquema de respuesta completo. {/* vale off */} @@ -160,13 +160,13 @@ La respuesta a esta llamada de API es el conjunto completo de cambios a cualquie Valida que esto funciona verificando el panel de Trophy [dashboard](https://app.trophy.so). -## Mostrando XP +## Mostrando XP {#displaying-xp} Tienes varias opciones para mostrar XP en tu aplicación. Aquí veremos las opciones más comunes. -### Notificaciones Emergentes +### Notificaciones Emergentes {#pop-up-notifications} -Podemos usar la respuesta de la [API de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event) para mostrar a los usuarios notificaciones emergentes cuando se les otorga nuevo XP. +Podemos usar la respuesta de la [API de cambio de métrica](/es/api-reference/endpoints/metrics/send-a-metric-change-event) para mostrar a los usuarios notificaciones emergentes cuando se les otorga nuevo XP. Aquí hay un ejemplo de esto en acción: @@ -199,13 +199,13 @@ if (xp.awards.length > 0) { Si quieres reproducir efectos de sonido, usa la [API de Audio HTML5](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) y siéntete libre de usar estos [archivos de audio](https://github.com/trophyso/example-study-platform/tree/demo/public/sounds) que recomendamos. -### Mostrando el XP del Usuario +### Mostrando el XP del Usuario {#displaying-user-xp} -Para obtener el XP de un usuario, usa la [API de puntos de usuario](/api-reference/endpoints/users/get-a-users-points). +Para obtener el XP de un usuario, usa la [API de puntos de usuario](/es/api-reference/endpoints/users/get-a-users-points). -Esta API devuelve datos sobre el XP total del usuario, pero puede configurarse para devolver también entre 1 y 100 de los premios de XP más recientes del usuario usando el `awards` [parámetro de consulta](/api-reference/endpoints/users/get-a-users-points#parameter-awards). +Esta API devuelve datos sobre el XP total del usuario, pero puede configurarse para devolver también entre 1 y 100 de los premios de XP más recientes del usuario usando el `awards` [parámetro de consulta](/es/api-reference/endpoints/users/get-a-users-points#parameter-awards). @@ -216,13 +216,13 @@ Aquí hay un ejemplo de una interfaz que muestra a los usuarios una lista de sus height="200" width="100%" noZoom - src="../assets/guides/xp-feature/awards.png" + src="../../assets/guides/xp-feature/awards.png" /> -### Gráfico de XP del Usuario +### Gráfico de XP del Usuario {#user-xp-chart} -La [API de resumen de puntos de usuario](/api-reference/endpoints/users/get-a-users-points-summary) devuelve datos históricos listos para gráficos que muestran cómo ha cambiado el XP de un usuario a lo largo del tiempo. +La [API de resumen de puntos de usuario](/es/api-reference/endpoints/users/get-a-users-points-summary) devuelve datos históricos listos para gráficos que muestran cómo ha cambiado el XP de un usuario a lo largo del tiempo. @@ -237,11 +237,11 @@ Y aquí hay un ejemplo de los tipos de gráficos que puedes construir con estos height="200" width="100%" noZoom - src="../assets/guides/xp-feature/chart.png" + src="../../assets/guides/xp-feature/chart.png" /> -## Analíticas +## Analíticas {#analytics} En Trophy, tu [página del sistema de XP](https://app.trophy.so/points) incluye gráficos analíticos que muestran datos sobre el total de XP ganado y un desglose detallado de qué desencadenantes otorgan más XP. @@ -250,10 +250,10 @@ En Trophy, tu [página del sistema de XP](https://app.trophy.so/points) incluye height="200" width="100%" noZoom - src="../assets/guides/xp-feature/analytics.png" + src="../../assets/guides/xp-feature/analytics.png" /> -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/platform/achievements.mdx b/es/platform/achievements.mdx index ac15d6c..beb53a0 100644 --- a/es/platform/achievements.mdx +++ b/es/platform/achievements.mdx @@ -7,17 +7,17 @@ og:description: Usa logros de métrica, API, racha y compuestos para recompensar icon: trophy --- -import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; -import AllAchievementsResponseBlock from "/snippets/all-achievements-response-block.mdx"; +import MetricChangeResponseBlock from "../../snippets/metric-change-response-block.mdx"; +import AllAchievementsResponseBlock from "../../snippets/all-achievements-response-block.mdx"; -## ¿Qué Son los Logros? +## ¿Qué Son los Logros? {#what-are-achievements} Los logros son recompensas que los usuarios pueden desbloquear al usar tu plataforma. Se pueden utilizar para recompensar a los usuarios por avanzar continuamente en los recorridos principales del usuario, o para motivarlos a explorar funciones más nuevas. Los logros funcionan mejor cuando están diseñados para incentivar a los usuarios a realizar acciones que probablemente conduzcan a una mayor retención. - Usa las [analíticas de métricas](/platform/metrics#metric-analytics) de Trophy para comparar + Usa las [analíticas de métricas](/es/platform/metrics#metric-analytics) de Trophy para comparar la retención de cada interacción del usuario, luego configura logros en torno a estas interacciones para maximizar el impacto en la retención. @@ -38,13 +38,13 @@ Mira a Charlie ejecutar un recorrido sobre el uso de logros en una aplicación N > -## Tipos de Logros +## Tipos de Logros {#achievement-types} Trophy ofrece cuatro tipos de logros: [Métrica](#metric-achievements), [API](#api-achievements), [Racha](#streak-achievements) y [Logros Compuestos](#composite-achievements), detallados a continuación. -### Logros de Métrica +### Logros de Métrica {#metric-achievements} -Los logros de métrica están vinculados a [Métricas](/platform/metrics) y se usan mejor cuando quieres incentivar a los usuarios a realizar la misma acción una y otra vez. +Los logros de métrica están vinculados a [Métricas](/es/platform/metrics) y se usan mejor cuando quieres incentivar a los usuarios a realizar la misma acción una y otra vez. Tomemos el ejemplo de una plataforma de estudio que usa Trophy para animar a los usuarios a ver más tarjetas de estudio con logros de métrica de la siguiente manera: @@ -57,13 +57,13 @@ Tomemos el ejemplo de una plataforma de estudio que usa Trophy para animar a los En este caso, crearías una métrica llamada _Tarjetas Repasadas_ y crearías logros asociados a la métrica para cada hito. -Dado que estos logros están directamente vinculados a la métrica _Tarjetas Repasadas_, Trophy rastreará automáticamente cuándo los usuarios desbloquean estos logros a medida que [incrementan la métrica](/platform/events#tracking-metric-events). +Dado que estos logros están directamente vinculados a la métrica _Tarjetas Repasadas_, Trophy rastreará automáticamente cuándo los usuarios desbloquean estos logros a medida que [incrementan la métrica](/es/platform/events#tracking-metric-events). -Cuando se desbloquean logros, Trophy incluye información sobre los logros desbloqueados en la respuesta del [Event API](/api-reference/endpoints/metrics/send-a-metric-change-event), y activa automáticamente [Correos de Logros](/platform/emails#achievement-emails) si están configurados. +Cuando se desbloquean logros, Trophy incluye información sobre los logros desbloqueados en la respuesta del [Event API](/es/api-reference/endpoints/metrics/send-a-metric-change-event), y activa automáticamente [Correos de Logros](/es/platform/emails#achievement-emails) si están configurados. -### Logros de API +### Logros de API {#api-achievements} Los logros de API solo se pueden completar una vez y son útiles para recompensar a los usuarios por realizar acciones específicas. @@ -75,17 +75,17 @@ Ejemplos comunes incluyen: Los logros de API sirven como una forma sencilla de recompensar a los usuarios por completar cualquier acción que consideres importante para la retención. -Al igual que los logros de métricas, los logros de API también pueden activar [Correos de Logros](/platform/emails#achievement-emails) automatizados si están configurados. +Al igual que los logros de métricas, los logros de API también pueden activar [Correos de Logros](/es/platform/emails#achievement-emails) automatizados si están configurados. -### Logros de Racha +### Logros de Racha {#streak-achievements} -Los logros de racha están directamente vinculados a la [Racha](/platform/streaks) de un usuario y se desbloquean automáticamente cuando los usuarios alcanzan una duración de racha particular. +Los logros de racha están directamente vinculados a la [Racha](/es/platform/streaks) de un usuario y se desbloquean automáticamente cuando los usuarios alcanzan una duración de racha particular. Puedes crear tantos logros de racha como desees para longitudes de racha crecientes, por ejemplo 7 días, 30 días y 365 días para motivar a los usuarios a usar tu aplicación cada vez más. Al igual que los logros de métricas y API, puedes agregar un nombre personalizado y asignar una insignia a los logros de racha. -### Logros Compuestos +### Logros Compuestos {#composite-achievements} Los logros compuestos se desbloquean automáticamente cuando un usuario ha completado todos los logros prerequisitos que selecciones. Son ideales para crear recompensas escalonadas o de nivel de maestría que reconocen a los usuarios que han alcanzado un conjunto específico de hitos. @@ -94,9 +94,9 @@ Por ejemplo, podrías crear un logro compuesto "Maestro del Estudio" que se desb - Racha de 7 días - Completar la incorporación -Los logros compuestos son útiles para crear rutas de progresión y motivar a los usuarios a interactuar en diferentes áreas de tu aplicación. Al igual que otros tipos de logros, pueden activar [Correos de Logros](/platform/emails#achievement-emails) cuando se desbloquean. +Los logros compuestos son útiles para crear rutas de progresión y motivar a los usuarios a interactuar en diferentes áreas de tu aplicación. Al igual que otros tipos de logros, pueden activar [Correos de Logros](/es/platform/emails#achievement-emails) cuando se desbloquean. -## Crear Logros +## Crear Logros {#creating-achievements} Para crear nuevos logros, dirígete a la [página de logros](https://app.trophy.so/achievements) en el panel de Trophy y presiona el botón **Nuevo Logro**: @@ -107,7 +107,7 @@ Para crear nuevos logros, dirígete a la [página de logros](https://app.trophy. loop playsInline className="w-full aspect-video" - src="../assets/platform/achievements/create_new_achievement.mp4" + src="../../assets/platform/achievements/create_new_achievement.mp4" > @@ -132,11 +132,10 @@ Para crear nuevos logros, dirígete a la [página de logros](https://app.trophy. Elige cómo quieres que se desbloquee este logro. - Elegir **Métrica** significará que el logro se desbloqueará automáticamente cuando el total de métricas del usuario alcance el valor de activación del logro. - - Elegir **Racha** significará que el logro se desbloqueará automáticamente cuando la longitud de la racha del usuario alcance el valor de activación del logro. -- Elegir **Llamada API** significará que el logro solo se desbloqueará cuando tu código lo marque explícitamente como completado mediante una solicitud a la [API de completar logro](/api-reference/endpoints/achievements/mark-an-achievement-as-completed). +- Elegir **Llamada API** significará que el logro solo se desbloqueará cuando tu código lo marque explícitamente como completado mediante una solicitud a la [API de completar logro](/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed). - Elegir **Compuesto** significará que el logro se desbloqueará automáticamente cuando el usuario haya completado todos los logros prerequisito que selecciones. @@ -149,7 +148,7 @@ Para crear nuevos logros, dirígete a la [página de logros](https://app.trophy. - Si elegiste el activador **Racha**, deberás establecer la longitud de racha que debe desbloquear el logro. -- Si elegiste el activador **Llamada API**, deberás elegir una referencia única `key` que usarás para completar el logro mediante la [API](/api-reference/endpoints/achievements/mark-an-achievement-as-completed). +- Si elegiste el activador **Llamada API**, deberás elegir una referencia única `key` que usarás para completar el logro mediante la [API](/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed). - Si elegiste el activador **Compuesto**, deberás seleccionar los logros prerequisito que deben completarse todos para que este logro se desbloquee. El logro compuesto se desbloqueará automáticamente cuando un usuario complete el último prerequisito. @@ -158,9 +157,9 @@ Para crear nuevos logros, dirígete a la [página de logros](https://app.trophy. Puedes asignar filtros de atributos a un logro para restringir aún más quién puede desbloquearlo y cuándo. -- Para limitar un logro de **Métrica** a que solo aplique a eventos con [atributos de evento personalizados](/platform/events#custom-event-attributes) específicos, selecciona un atributo e ingresa un valor en la sección **Atributo de Evento**. +- Para limitar un logro de **Métrica** a que solo aplique a eventos con [atributos de evento personalizados](/es/platform/events#custom-event-attributes) específicos, selecciona un atributo e ingresa un valor en la sección **Atributo de Evento**. -- Para limitar cualquier tipo de logro y que solo se aplique a un usuario con uno o más [atributos de usuario personalizados](/platform/users#custom-user-attributes) específicos, agregue atributos y los valores deseados en la sección **Atributos de Usuario**. +- Para limitar cualquier tipo de logro y que solo se aplique a un usuario con uno o más [atributos de usuario personalizados](/es/platform/users#custom-user-attributes) específicos, agregue atributos y los valores deseados en la sección **Atributos de Usuario**. @@ -169,11 +168,11 @@ Puedes asignar filtros de atributos a un logro para restringir aún más quién -## Gestión de Logros +## Gestión de Logros {#managing-achievements} Trophy tiene herramientas integradas para ayudarlo a probar y controlar qué logros se pueden desbloquear, por quién y cuándo, sin afectar producción. -### Estados de Logros +### Estados de Logros {#achievement-statuses} A continuación se presenta una descripción general de los diferentes estados de logros y su significado. @@ -202,7 +201,7 @@ Los logros archivados no se pueden completar y no se devuelven en ninguna API de Los logros archivados se pueden restaurar [contactando a soporte](#get-support). -### Flujo de Trabajo de Logros +### Flujo de Trabajo de Logros {#achievement-workflow} Los logros se pueden mover a través de diferentes estados según el siguiente flujo de trabajo: @@ -233,15 +232,15 @@ flowchart LR ``` -## Completar Logros +## Completar Logros {#completing-achievements} -Si está utilizando logros basados en métricas, no es necesario _completar_ explícitamente los logros. Una vez que haya configurado el [seguimiento de métricas](/platform/events#tracking-metric-events) en su código, todos los logros vinculados a la métrica se rastrearán automáticamente. +Si está utilizando logros basados en métricas, no es necesario _completar_ explícitamente los logros. Una vez que haya configurado el [seguimiento de métricas](/es/platform/events#tracking-metric-events) en su código, todos los logros vinculados a la métrica se rastrearán automáticamente. Del mismo modo, si estás usando logros de racha, todos los logros relacionados con la racha del usuario se desbloquearán automáticamente cuando el usuario alcance la longitud de racha respectiva. Los logros compuestos también se completan automáticamente. Se desbloquean tan pronto como un usuario haya completado todos sus logros prerequisitos. No se necesitan llamadas API adicionales. -Sin embargo, si estás usando logros de API, tendrás que marcarlos como completados para cada usuario según corresponda. Para hacer esto, puedes usar la [API de Completar Logro](/api-reference/endpoints/achievements/mark-an-achievement-as-completed) utilizando el `key` del logro que deseas completar. +Sin embargo, si estás usando logros de API, tendrás que marcarlos como completados para cada usuario según corresponda. Para hacer esto, puedes usar la [API de Completar Logro](/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed) utilizando el `key` del logro que deseas completar. Esto devolverá una respuesta que contiene detalles del logro que se completó y que puede usarse en cualquier flujo de trabajo posterior a la finalización, como mostrar una notificación dentro de la aplicación. @@ -264,7 +263,7 @@ Esto devolverá una respuesta que contiene detalles del logro que se completó y {/* vale on */} -## Retroactuar Logros +## Retroactuar Logros {#backdating-achievements} Por defecto, cada vez que mueves un logro al [estado](#managing-achievements) 'Activo', Trophy verificará si algún usuario existente cumple los requisitos del logro y lo completará para ellos en segundo plano. @@ -280,11 +279,11 @@ Puedes verificar cuántos usuarios han completado logros en cualquier momento en -## Uso de Insignias +## Uso de Insignias {#using-badges} @@ -314,18 +313,18 @@ Puedes asignar una insignia a cualquier logro cargando una imagen—Trophy la si } ``` -## Visualización de Logros +## Visualización de Logros {#displaying-achievements} Trophy cuenta con varias APIs que permiten mostrar logros dentro de tus aplicaciones. Aquí veremos las diferentes formas de usarlas y los tipos de interfaces que puedes construir. - Consulta nuestra [guía completa](/guides/how-to-build-an-achievements-feature) sobre + Consulta nuestra [guía completa](/es/guides/how-to-build-an-achievements-feature) sobre cómo agregar una funcionalidad de logros a tu aplicación para más detalles. -### Todos los Logros +### Todos los Logros {#all-achievements} -Para mostrar una vista general de alto nivel de todos los logros que los usuarios pueden completar, utiliza el [endpoint de todos los logros](/api-reference/endpoints/achievements/all-achievements). Usa estos datos para construir interfaces que den a los usuarios una idea de las rutas de progresión dentro de tu aplicación. +Para mostrar una vista general de alto nivel de todos los logros que los usuarios pueden completar, utiliza el [endpoint de todos los logros](/es/api-reference/endpoints/achievements/all-achievements). Usa estos datos para construir interfaces que den a los usuarios una idea de las rutas de progresión dentro de tu aplicación. @@ -342,9 +341,9 @@ El endpoint de todos los logros devuelve una lista de todos los logros dentro de -### Logros del Usuario +### Logros del Usuario {#user-achievements} -Si en cambio estás construyendo elementos de interfaz específicos del usuario, entonces utiliza el [endpoint de logros del usuario](/api-reference/endpoints/users/get-a-users-completed-achievements) para devolver los logros que un usuario específico ha completado. +Si en cambio estás construyendo elementos de interfaz específicos del usuario, entonces utiliza el [endpoint de logros del usuario](/es/api-reference/endpoints/users/get-a-users-completed-achievements) para devolver los logros que un usuario específico ha completado. También puedes incluir logros que un usuario aún no ha completado incluyendo @@ -355,23 +354,23 @@ Si en cambio estás construyendo elementos de interfaz específicos del usuario, -## Analíticas de Logros +## Analíticas de Logros {#achievement-analytics} -Si tienes logros configurados para cualquiera de tus [Métricas](/platform/metrics), entonces la página de analíticas de métricas muestra un gráfico que te indica el progreso actual de todos los usuarios de la siguiente manera: +Si tienes logros configurados para cualquiera de tus [Métricas](/es/platform/metrics), entonces la página de analíticas de métricas muestra un gráfico que te indica el progreso actual de todos los usuarios de la siguiente manera: -## Preguntas Frecuentes +## Preguntas Frecuentes {#frequently-asked-questions} @@ -388,12 +387,12 @@ Si tienes logros configurados para cualquiera de tus [Métricas](/platform/metri Los logros, como toda la gamificación, ofrecen la mejor retención cuando están estrechamente alineados con la razón principal del usuario para usar tu plataforma. - Usa las [analíticas de métricas](/platform/metrics#metric-analytics) de Trophy para comparar la retención de cada interacción de usuario, luego configura logros en torno a estas interacciones para maximizar el impacto en la retención. + Usa las [analíticas de métricas](/es/platform/metrics#metric-analytics) de Trophy para comparar la retención de cada interacción de usuario, luego configura logros en torno a estas interacciones para maximizar el impacto en la retención. -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/platform/emails.mdx b/es/platform/emails.mdx index 2447e4e..7cf6e1c 100644 --- a/es/platform/emails.mdx +++ b/es/platform/emails.mdx @@ -8,11 +8,11 @@ og:description: Usa flujos de correo automatizados para extender tu experiencia icon: mail --- -import { PlanBadge } from "/snippets/plan-badge.jsx"; +import { PlanBadge } from "../../snippets/plan-badge.jsx"; Trophy puede enviar correos electrónicos automatizados a los usuarios basándose en activadores clave sin necesidad de código. Aquí veremos cuáles son estos activadores y cómo pueden formar parte de la experiencia de gamificación de tu producto. -## Tipos de correos electrónicos +## Tipos de correos electrónicos {#types-of-emails} Trophy admite 4 tipos de correos electrónicos, cada uno diseñado para adaptarse a un escenario común en la construcción de experiencias de gamificación. @@ -25,27 +25,27 @@ Todos los correos electrónicos son opcionales, pero los cuatro pueden usarse si loop playsInline className="w-full aspect-15/4" - src="../assets/platform/emails/email_types.mp4" + src="../../assets/platform/emails/email_types.mp4" > -- **Correos de logros** se envían a los usuarios cada vez que desbloquean un [Logro](/platform/achievements). +- **Correos de logros** se envían a los usuarios cada vez que desbloquean un [Logro](/es/platform/achievements). - **Correos de resumen** se envían a los usuarios con una frecuencia predefinida para resumir el progreso. Los correos de resumen pueden configurarse para enviarse diaria, semanal, mensual o anualmente según tu caso de uso. - **Correos de reactivación** se envían a los usuarios después de que se vuelven inactivos con el objetivo de traerlos de vuelta a tu aplicación. -- **Correos de rachas** se envían automáticamente a los usuarios recordándoles extender su [Racha](/platform/streaks). +- **Correos de rachas** se envían automáticamente a los usuarios recordándoles extender su [Racha](/es/platform/streaks). -## Envío de correos electrónicos +## Envío de correos electrónicos {#sending-emails} Para comenzar a enviar correos electrónicos con Trophy, necesitarás verificar tu dominio y agregar y activar plantillas de correo para los tipos que deseas enviar. Los usuarios pueden controlar qué notificaciones por correo electrónico reciben a través del - [API de preferencias de usuario](/platform/users#notification-preferences) de Trophy. Esto te permite + [API de preferencias de usuario](/es/platform/users#notification-preferences) de Trophy. Esto te permite construir un centro de preferencias en tu aplicación donde los usuarios pueden activar o desactivar tipos específicos de correos electrónicos. -### Verificación de dominio +### Verificación de dominio {#domain-verification} Trophy admite el envío de correos electrónicos desde tu propio dominio de forma nativa. Hay dos formas de configurar esto: Verificación del remitente y Verificación DNS. @@ -70,7 +70,7 @@ Toda la configuración de dominios se encuentra en la página [Dominios](https:/ -#### Verificación del remitente (Básica) +#### Verificación del remitente (Básica) {#sender-verification-basic} Durante la configuración inicial, se te pedirá que configures la Verificación del remitente. Esta es una forma muy sencilla de verificar rápidamente una dirección de correo electrónico que deseas usar con Trophy sin necesidad de cambiar ningún código o configuración DNS. @@ -81,7 +81,7 @@ Se te pedirá que ingreses la dirección de correo electrónico, el nombre del r height="200" width="75%" noZoom - src="../assets/platform/emails/sender_verification.jpg" + src="../../assets/platform/emails/sender_verification.jpg" /> @@ -96,10 +96,10 @@ La Verificación del remitente es excelente para comenzar, pero tiene la limitac Además, si deseas cambiar la dirección en el futuro, tendrás que pasar por la Verificación del remitente nuevamente, lo cual puedes hacer en la [pantalla de configuración](https://app.trophy.so/integration?tab=domains). -#### Verificación DNS (Avanzado) +#### Verificación DNS (Avanzado) {#dns-verification-advanced} - Esta funcionalidad está disponible en el plan [Starter](/account/billing#starter-plan). + Esta funcionalidad está disponible en el plan [Starter](/es/account/billing#starter-plan). Para uso en producción recomendamos la Verificación DNS. Una vez configurada, esto te permite configurar Trophy para enviar correos desde cualquier dirección en tu dominio. Así que si deseas cambiar la dirección en el futuro, no necesitarás verificar nuevamente. @@ -125,7 +125,7 @@ Sigue los pasos a continuación para configurar la verificación DNS: height="200" width="75%" noZoom - src="../assets/platform/emails/dns-verification-1.png" + src="../../assets/platform/emails/dns-verification-1.png" /> @@ -140,7 +140,7 @@ Sigue los pasos a continuación para configurar la verificación DNS: height="200" width="75%" noZoom - src="../assets/platform/emails/dns-verification-2.png" + src="../../assets/platform/emails/dns-verification-2.png" /> @@ -180,7 +180,7 @@ Sigue los pasos a continuación para configurar la verificación DNS: height="200" width="75%" noZoom - src="../assets/platform/emails/dns-verification-3.png" + src="../../assets/platform/emails/dns-verification-3.png" /> @@ -193,7 +193,7 @@ Sigue los pasos a continuación para configurar la verificación DNS: height="200" width="75%" noZoom - src="../assets/platform/emails/dns-verification-4.png" + src="../../assets/platform/emails/dns-verification-4.png" /> @@ -210,7 +210,7 @@ Sigue los pasos a continuación para configurar la verificación DNS: height="200" width="75%" noZoom - src="../assets/platform/emails/dns-verification-5.png" + src="../../assets/platform/emails/dns-verification-5.png" /> @@ -218,11 +218,11 @@ Sigue los pasos a continuación para configurar la verificación DNS: -### Activadores de Email +### Activadores de Email {#email-triggers} En la página de [configuración de email](https://app.trophy.so/emails/configure), cada tipo de email tiene su propia sección. Agrega plantillas bajo el tipo correspondiente y activa una para comenzar a enviar. -#### Emails de Resumen +#### Emails de Resumen {#recap-emails} Los emails de **Resumen** envían informes de progreso semanales o mensuales a los usuarios. Puedes cambiar la frecuencia de estos emails en la página de [integración](https://app.trophy.so/integration) usando la configuración **Período de Agregación**. @@ -231,11 +231,11 @@ Los emails de **Resumen** envían informes de progreso semanales o mensuales a l height="200" width="100%" noZoom - src="../assets/platform/emails/recap_emails.png" + src="../../assets/platform/emails/recap_emails.png" /> -#### Emails de Reactivación +#### Emails de Reactivación {#reactivation-emails} Los emails de **Reactivación** envían mensajes de recuperación a los usuarios después de que se vuelven inactivos. Estos emails se envían según el siguiente cronograma: @@ -250,11 +250,11 @@ Los emails de **Reactivación** envían mensajes de recuperación a los usuarios height="200" width="30%" noZoom - src="../assets/platform/emails/reactivation_emails.png" + src="../../assets/platform/emails/reactivation_emails.png" /> -#### Emails de Rachas +#### Emails de Rachas {#streak-emails} Los emails de **Rachas** recuerdan a los usuarios que extiendan su racha antes de que expire. Estos emails se envían según la frecuencia de racha que configuraste en la [página de rachas](https://app.trophy.so/streaks): @@ -267,26 +267,26 @@ Los emails de **Rachas** recuerdan a los usuarios que extiendan su racha antes d height="200" width="50%" noZoom - src="../assets/platform/emails/streak_emails.png" + src="../../assets/platform/emails/streak_emails.png" /> -#### Correos de Logros +#### Correos de Logros {#achievement-emails} -Los correos de **logros** felicitan a los usuarios cuando completan un [logro](/platform/achievements) que has configurado en Trophy. Estos correos se envían entre las 5-9PM el día en que se completa el logro, o al día siguiente a las 5PM si es después de las 9PM cuando el usuario completa el logro. +Los correos de **logros** felicitan a los usuarios cuando completan un [logro](/es/platform/achievements) que has configurado en Trophy. Estos correos se envían entre las 5-9PM el día en que se completa el logro, o al día siguiente a las 5PM si es después de las 9PM cuando el usuario completa el logro. -#### Limitar Correos a Tipos Específicos de Usuarios +#### Limitar Correos a Tipos Específicos de Usuarios {#limiting-emails-to-specific-types-of-users} -Cada plantilla de correo puede limitarse a usuarios con valores específicos de [atributos personalizados de usuario](/platform/users#custom-user-attributes). +Cada plantilla de correo puede limitarse a usuarios con valores específicos de [atributos personalizados de usuario](/es/platform/users#custom-user-attributes). Para cada atributo al que desees restringir el correo, haz clic en el icono más junto al encabezado **Filtros de Usuario** de la plantilla de correo, luego selecciona el atributo e ingresa el valor deseado. Solo los usuarios que tengan **todos** los valores de atributo especificados recibirán correos de esta plantilla. @@ -295,15 +295,15 @@ Para cada atributo al que desees restringir el correo, haz clic en el icono más height="200" width="75%" noZoom - src="../assets/platform/emails/user_filters.png" + src="../../assets/platform/emails/user_filters.png" /> -#### Zonas Horarias +#### Zonas Horarias {#time-zones} -Si especificas una zona horaria para cada usuario a través de [Identificación de Usuario](/platform/users#param-tz), Trophy utilizará esa zona horaria local para programar correos según la lógica especificada anteriormente. Si no proporcionas zonas horarias para los usuarios que identificas, Trophy utilizará por defecto la hora del Este. +Si especificas una zona horaria para cada usuario a través de [Identificación de Usuario](/es/platform/users#param-tz), Trophy utilizará esa zona horaria local para programar correos según la lógica especificada anteriormente. Si no proporcionas zonas horarias para los usuarios que identificas, Trophy utilizará por defecto la hora del Este. -## Diseñar Correos +## Diseñar Correos {#designing-emails} Trophy cuenta con un constructor de correos basado en bloques con todas las funciones, que te permite diseñar plantillas controlando todo el contenido y las líneas de asunto de los correos. En la página de [correos](https://app.trophy.so/emails/configure), agrega plantillas en cada sección de [tipo de correo](#email-triggers) que desees utilizar. @@ -314,11 +314,11 @@ Trophy cuenta con un constructor de correos basado en bloques con todas las func loop playsInline className="w-full aspect-15/4" - src="../assets/platform/emails/designing_emails.mp4" + src="../../assets/platform/emails/designing_emails.mp4" > -### Plantillas Predeterminadas +### Plantillas Predeterminadas {#default-templates} Por defecto, Trophy proporciona una plantilla para cada [tipo de correo electrónico](#types-of-emails) como punto de partida. Las plantillas predeterminadas no se pueden modificar, pero puedes duplicarlas y personalizarlas como desees. @@ -326,7 +326,7 @@ Por defecto, Trophy proporciona una plantilla para cada [tipo de correo electró También puedes crear plantillas en blanco si prefieres empezar desde cero. -### Crear una Nueva Plantilla +### Crear una Nueva Plantilla {#creating-a-new-template} Para crear una nueva plantilla de correo electrónico, sigue los pasos a continuación. @@ -343,7 +343,7 @@ Para crear una nueva plantilla de correo electrónico, sigue los pasos a continu height="200" width="100%" noZoom - src="../assets/platform/emails/choose_template.png" + src="../../assets/platform/emails/choose_template.png" /> @@ -358,14 +358,14 @@ Para crear una nueva plantilla de correo electrónico, sigue los pasos a continu loop playsInline className="w-full aspect-15/4" - src="../assets/platform/emails/designing_emails.mp4" + src="../../assets/platform/emails/designing_emails.mp4" > -### Tipos de Bloques +### Tipos de Bloques {#block-types} El constructor de correos electrónicos de Trophy admite diversos tipos de bloques que cumplen diferentes funciones. Todos los componentes básicos que esperarías encontrar en un editor de correo electrónico, como párrafos, encabezados e imágenes, se denominan [Bloques Básicos](#basic-blocks). También existe un conjunto de componentes más potentes, como gráficos y rachas, que aprovechan los datos de actividad del usuario y las funciones de gamificación de Trophy. Estos se denominan [Bloques Inteligentes](#smart-blocks). @@ -374,11 +374,11 @@ El constructor de correos electrónicos de Trophy admite diversos tipos de bloqu height="200" width="100%" noZoom - src="../assets/platform/emails/block_types_dialog.png" + src="../../assets/platform/emails/block_types_dialog.png" /> -#### Bloques Básicos +#### Bloques Básicos {#basic-blocks} Aquí está la lista completa de bloques básicos sin opinión que admite Trophy: @@ -393,7 +393,7 @@ Aquí está la lista completa de bloques básicos sin opinión que admite Trophy - **Columnas** - Útiles para crear diseños de hasta 3 columnas con cualquier contenido. - **Logotipo** - Renderizará el logotipo de tu organización, configurado en la página de [Marca](https://app.trophy.so/branding). -#### Bloques Condicionales +#### Bloques Condicionales {#conditional-blocks} Trophy también cuenta con un potente sistema de renderizado condicional impulsado por el tipo de bloque _Condicional_. @@ -422,13 +422,13 @@ Un caso de uso común para el bloque condicional es mostrar condicionalmente a u height="100" width="50%" noZoom - src="../assets/platform/emails/conditional_block.png" + src="../../assets/platform/emails/conditional_block.png" /> Esto crea un marco de trabajo potente para diseñar correos basados en datos de usuario altamente relevantes y personalizados, y puede utilizarse para crear una amplia gama de correos para casos de uso comunes de gamificación. -#### Bloques Inteligentes +#### Bloques Inteligentes {#smart-blocks} Los bloques inteligentes son componentes potentes diseñados para soportar casos de uso comunes de gamificación e integrarse con todas las funciones de Trophy, incluyendo métricas, logros y rachas. @@ -463,7 +463,7 @@ Lee más sobre el caso de uso de cada bloque inteligente: loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/achievement_progress_block.mp4" + src="../../assets/platform/emails/achievement_progress_block.mp4" > @@ -483,7 +483,7 @@ Cuando los logros tienen insignias, estas se mostrarán automáticamente, junto loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/achievements_unlocked_block.mp4" + src="../../assets/platform/emails/achievements_unlocked_block.mp4" > @@ -500,7 +500,7 @@ Cuando los logros tienen insignias, estas se mostrarán automáticamente, junto loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/progress_chart_block.mp4" + src="../../assets/platform/emails/progress_chart_block.mp4" > @@ -517,14 +517,14 @@ Cuando los logros tienen insignias, estas se mostrarán automáticamente, junto loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/streak_block.mp4" + src="../../assets/platform/emails/streak_block.mp4" > -### Variables de correo +### Variables de correo {#email-variables} Trophy proporciona un amplio conjunto de variables de correo que se pueden usar para insertar texto altamente relevante y personalizado en el cuerpo de los correos y en las líneas de asunto. @@ -543,7 +543,7 @@ Esto abrirá la ventana del editor de variables donde puedes configurar las vari loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/email_variables_demo.mp4" + src="../../assets/platform/emails/email_variables_demo.mp4" > @@ -555,7 +555,7 @@ Variables relacionadas con el destinatario del correo electrónico. - **Nombre**: El nombre del destinatario, si está configurado -Además, todos los [atributos personalizados de usuario](/platform/users#custom-user-attributes) están disponibles como variables de correo electrónico. +Además, todos los [atributos personalizados de usuario](/es/platform/users#custom-user-attributes) están disponibles como variables de correo electrónico. @@ -573,7 +573,7 @@ Cada métrica de Trophy admite las siguientes variables de correo electrónico: Además, los alias dinámicos **Más Alto** y **Más Bajo** admiten el mismo conjunto de variables. Al usar estos alias, Trophy seleccionará la métrica donde el destinatario tenga el total actual más alto o más bajo en el momento del envío. -Al usar variables de métricas, también tiene la opción de filtrar los datos que cualquiera de las variables anteriores referencian a través de un [atributo de evento personalizado](/platform/events#custom-event-attributes). +Al usar variables de métricas, también tiene la opción de filtrar los datos que cualquiera de las variables anteriores referencian a través de un [atributo de evento personalizado](/es/platform/events#custom-event-attributes). @@ -638,7 +638,7 @@ Al configurar una plantilla para usar con correos electrónicos de logros, las s -#### Uso Avanzado +#### Uso Avanzado {#advanced-usage} Hay un par de opciones adicionales a considerar al usar variables de correo. @@ -651,7 +651,7 @@ Puede proporcionar un prefijo y sufijo para variables de texto como el nombre de height="200" width="75%" noZoom - src="../assets/platform/emails/advanced_variables_prefix_suffix.png" + src="../../assets/platform/emails/advanced_variables_prefix_suffix.png" /> @@ -666,11 +666,11 @@ Al usar variables numéricas como el total de Puntos actual del destinatario o e height="200" width="75%" noZoom - src="../assets/platform/emails/advanced_usage_singular_plural.png" + src="../../assets/platform/emails/advanced_usage_singular_plural.png" /> -### Variaciones de Texto +### Variaciones de Texto {#text-variations} Las variaciones se pueden usar para agregar aleatoriedad al texto dentro de los correos enviados por Trophy. Esto evita que los correos se vuelvan aburridos y ayuda a mejorar las tasas de apertura y clics. @@ -699,7 +699,7 @@ Para crear una variación, haga clic en el botón _Agregar Variación_ en cualqu loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/creating_variations.mp4" + src="../../assets/platform/emails/creating_variations.mp4" > @@ -707,11 +707,11 @@ Para crear una variación, haga clic en el botón _Agregar Variación_ en cualqu Las variaciones aún no admiten pruebas A/B, pero está en nuestra hoja de ruta, así que mantente atento... -### Usar el Editor +### Usar el Editor {#using-the-editor} El editor de plantillas de correo electrónico es un lienzo en blanco para diseñar correos que se vean geniales en la bandeja de entrada. Usar [Bloques](#block-types) preconfigurados facilita mucho la creación de plantillas de correo electrónico que se adapten a casos de uso comunes de gamificación. Aquí te mostraremos cómo usar mejor el editor para crear correos electrónicos de aspecto increíble. -#### Agregar Bloques +#### Agregar Bloques {#adding-blocks} Para agregar un nuevo bloque a una plantilla de correo electrónico, presiona la tecla . Esto abrirá el cuadro de diálogo de selección de bloques donde puedes elegir el bloque que deseas agregar. @@ -722,7 +722,7 @@ Para agregar un nuevo bloque a una plantilla de correo electrónico, presiona la loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/add_new_block_slash.mp4" + src="../../assets/platform/emails/add_new_block_slash.mp4" > @@ -735,11 +735,11 @@ O si deseas agregar un bloque inmediatamente después de otro bloque, usa el men loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/add_new_block_by_block_menu.mp4" + src="../../assets/platform/emails/add_new_block_by_block_menu.mp4" > -#### Organizar Bloques +#### Organizar Bloques {#arranging-blocks} Los bloques se pueden arrastrar hacia arriba y hacia abajo usando el menú de bloques. @@ -750,11 +750,11 @@ Los bloques se pueden arrastrar hacia arriba y hacia abajo usando el menú de bl loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/arranging_blocks.mp4" + src="../../assets/platform/emails/arranging_blocks.mp4" > -#### Formato de Texto +#### Formato de Texto {#text-formatting} El editor de correo electrónico ofrece formato de texto enriquecido que incluye negrita, cursiva, [hipervínculos](https://www.youtube.com/watch?v=dQw4w9WgXcQ) y formato `code`. @@ -765,11 +765,11 @@ El editor de correo electrónico ofrece formato de texto enriquecido que incluye loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/rich_text_formatting.mp4" + src="../../assets/platform/emails/rich_text_formatting.mp4" > -### Estilo de Fuente +### Estilo de Fuente {#font-style} En la [página de configuración de correo electrónico](https://app.trophy.so/emails/configure), encontrarás una configuración para cambiar el estilo de fuente utilizado en todos los correos electrónicos. @@ -778,13 +778,13 @@ En la [página de configuración de correo electrónico](https://app.trophy.so/e height="200" width="75%" noZoom - src="../assets/platform/emails/aggregation_period_setting.png" + src="../../assets/platform/emails/aggregation_period_setting.png" /> Otros estilos de correo electrónico se obtienen de la configuración de [Marca](https://app.trophy.so/branding) de tu cuenta de Trophy. -## Gestión de Cancelaciones de Suscripción +## Gestión de Cancelaciones de Suscripción {#handling-unsubscribes} Todos los correos electrónicos que envía Trophy incluyen un enlace y mensaje para cancelar la suscripción. Esto es importante para mantener el cumplimiento de las regulaciones y no es posible ocultarlo. @@ -795,10 +795,10 @@ Todos los correos electrónicos que envía Trophy incluyen un enlace y mensaje p Cualquier destinatario que haga clic en este enlace será dirigido a una página de marca que le permite controlar qué notificaciones por correo electrónico recibe. - Como alternativa al enlace de cancelación de suscripción, también puede crear un centro de preferencias en su aplicación utilizando la [API de preferencias de usuario de Trophy](/platform/users#notification-preferences). Esto brinda a los usuarios un control más granular sobre qué tipos de correos electrónicos reciben. + Como alternativa al enlace de cancelación de suscripción, también puede crear un centro de preferencias en su aplicación utilizando la [API de preferencias de usuario de Trophy](/es/platform/users#notification-preferences). Esto brinda a los usuarios un control más granular sobre qué tipos de correos electrónicos reciben. -## Analíticas de Correo Electrónico +## Analíticas de Correo Electrónico {#email-analytics} Trophy tiene analíticas integradas para todos los correos electrónicos que envía. Esto incluye: @@ -814,11 +814,11 @@ Trophy tiene analíticas integradas para todos los correos electrónicos que env loop playsInline className="w-full aspect-video" - src="../assets/platform/emails/email_analytics.mp4" + src="../../assets/platform/emails/email_analytics.mp4" > -## Preguntas Frecuentes +## Preguntas Frecuentes {#frequently-asked-questions} @@ -833,6 +833,6 @@ Trophy tiene analíticas integradas para todos los correos electrónicos que env -## Obtener soporte +## Obtener soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/platform/events.mdx b/es/platform/events.mdx index 66209cb..2fc080e 100644 --- a/es/platform/events.mdx +++ b/es/platform/events.mdx @@ -7,19 +7,19 @@ og:description: Los eventos son objetos de datos que representan interacciones icon: radio --- -import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; -import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; -import IdempotentEventTracking from "/snippets/idempotent-event-tracking.mdx"; +import MetricChangeRequestBlock from "../../snippets/metric-change-request-block.mdx"; +import MetricChangeResponseBlock from "../../snippets/metric-change-response-block.mdx"; +import IdempotentEventTracking from "../../snippets/idempotent-event-tracking.mdx"; -## ¿Qué son los Eventos? +## ¿Qué son los Eventos? {#what-are-events} -Los eventos representan interacciones individuales de usuarios con [Métricas](/platform/metrics) en Trophy. Un evento corresponde a una única interacción realizada por un único usuario. +Los eventos representan interacciones individuales de usuarios con [Métricas](/es/platform/metrics) en Trophy. Un evento corresponde a una única interacción realizada por un único usuario. Cuando [integras métricas](#tracking-metric-events) en tu plataforma, estás configurando tu plataforma para transmitir continuamente eventos a tus métricas de Trophy por cada interacción de usuario. Estas interacciones impulsan todas las funciones de gamificación que configures en torno a estas métricas. -## Atributos Clave +## Atributos Clave {#key-attributes} -### Valor del Evento +### Valor del Evento {#event-value} El `value` de un evento es la cantidad numérica que se añadirá al recuento total de la métrica del usuario como resultado de la interacción de usuario con la que se relaciona. @@ -27,17 +27,17 @@ El `value` de un evento es la cantidad numérica que se añadirá al recuento to El valor de un evento puede ser positivo o negativo, y puede ser un número entero o un decimal. -## Atributos Personalizados de Eventos +## Atributos Personalizados de Eventos {#custom-event-attributes} - Esta función está disponible en el plan [Pro](/account/billing#pro-plan) + Esta función está disponible en el plan [Pro](/es/account/billing#pro-plan) @@ -49,7 +49,7 @@ De manera similar, una aplicación de fitness podría usar una métrica de 'Ejer El uso de atributos de eventos personalizados de esta manera te permite enriquecer los eventos en Trophy con contexto adicional relevante para tu caso de uso y utilizarlo para potenciar características de gamificación aún más atractivas. -### Creación de Atributos +### Creación de Atributos {#creating-attributes} Para crear un nuevo atributo de evento personalizado, dirígete a la página de métricas en el panel de Trophy y presiona el botón _Agregar Atributo de Evento_. @@ -62,11 +62,11 @@ Asigna un nombre y una clave única al atributo; usarás la clave al hacer refer loop playsInline className="w-full aspect-15/4" - src="../assets/platform/events/create_custom_event_attribute.mp4" + src="../../assets/platform/events/create_custom_event_attribute.mp4" > -### Configuración de Atributos +### Configuración de Atributos {#setting-attributes} Para establecer el valor de un atributo personalizado en un evento, pasa su valor en el objeto `attributes` en tu código de seguimiento de métricas. @@ -101,11 +101,11 @@ Aquí hay un ejemplo de una carga útil de evento donde se establecen los valore } ``` -### Uso de Atributos +### Uso de Atributos {#using-attributes} Los atributos de eventos personalizados se pueden utilizar para potenciar activadores más avanzados para logros y puntos, y pueden usarse en plantillas de correo electrónico para personalizar el texto y controlar los datos mostrados en gráficos. -#### Activadores de Características Avanzadas +#### Activadores de Características Avanzadas {#advanced-feature-triggers} Los atributos de eventos personalizados se pueden utilizar para configurar activadores de logros o puntos que solo rastreen eventos con valores de atributos específicos. Sigue los enlaces a las páginas relevantes a continuación para obtener más información. @@ -113,7 +113,7 @@ Los atributos de eventos personalizados se pueden utilizar para configurar activ Configura logros que solo se pueden desbloquear mediante eventos con ciertos valores de atributos. @@ -121,16 +121,16 @@ Los atributos de eventos personalizados se pueden utilizar para configurar activ Configura activadores de puntos para otorgar puntos únicamente desde eventos con valores de atributos específicos. -#### Personalización de correos electrónicos +#### Personalización de correos electrónicos {#email-customization} -Si utilizas cualquier [correo electrónico](/platform/emails) de Trophy, los atributos de eventos se pueden usar para personalizar los datos mostrados en ciertos bloques de correo. +Si utilizas cualquier [correo electrónico](/es/platform/emails) de Trophy, los atributos de eventos se pueden usar para personalizar los datos mostrados en ciertos bloques de correo. En primer lugar, al usar variables basadas en métricas en el contenido del correo, puedes utilizar atributos de eventos para controlar con mayor precisión qué datos referencia la variable. @@ -143,7 +143,7 @@ Por ejemplo, aquí hay un caso donde usamos una variable de correo para informar loop playsInline className="w-full aspect-15/4" - src="../assets/platform/events/using_attributes_in_emails.mp4" + src="../../assets/platform/events/using_attributes_in_emails.mp4" > @@ -156,23 +156,23 @@ En segundo lugar, aquí hay un ejemplo donde agregamos un gráfico a un correo q loop playsInline className="w-full aspect-15/4" - src="../assets/platform/events/using_attributes_in_email_charts.mp4" + src="../../assets/platform/events/using_attributes_in_email_charts.mp4" > Existen un gran número de posibilidades aquí, ¡así que sé creativo! -## Seguimiento de eventos de Métricas +## Seguimiento de eventos de Métricas {#tracking-metric-events} Cada métrica tiene un `key` único que puedes usar para referenciar y hacer seguimiento de eventos en tu código. Puedes encontrar el `key` en la página de configuración de la métrica. -Para empezar a hacer seguimiento de las interacciones de los usuarios como eventos en tus Métricas de Trophy, utiliza la [API de Métricas](/api-reference/endpoints/metrics/send-a-metric-change-event) o una de nuestras [bibliotecas cliente](/api-reference/client-libraries) con tipado seguro, compatibles con la mayoría de los lenguajes de programación principales. +Para empezar a hacer seguimiento de las interacciones de los usuarios como eventos en tus Métricas de Trophy, utiliza la [API de Métricas](/es/api-reference/endpoints/metrics/send-a-metric-change-event) o una de nuestras [bibliotecas cliente](/es/api-reference/client-libraries) con tipado seguro, compatibles con la mayoría de los lenguajes de programación principales. Aquí hay un ejemplo donde una plataforma de estudio ficticia está utilizando una métrica para rastrear el número de tarjetas de memoria volteadas por cada estudiante. Cada vez que un estudiante interactúa, la plataforma envía un evento a Trophy indicándole cuántas tarjetas vieron: -Cualquier [Logros](/platform/achievements), [Rachas](/platform/streaks), [Puntos](/platform/points) o [Clasificaciones](/platform/leaderboards) que se hayan configurado para esta métrica se procesarán automáticamente, y la respuesta contendrá cualquier actualización del progreso del usuario que sea resultado directo del evento ocurrido: +Cualquier [Logros](/es/platform/achievements), [Rachas](/es/platform/streaks), [Puntos](/es/platform/points) o [Clasificaciones](/es/platform/leaderboards) que se hayan configurado para esta métrica se procesarán automáticamente, y la respuesta contendrá cualquier actualización del progreso del usuario que sea resultado directo del evento ocurrido: @@ -189,7 +189,7 @@ Con un poco de código personalizado, estos datos de respuesta se pueden utiliza - Efectos de sonido - Animaciones -Mira a Charlie integrar el seguimiento de métricas en una aplicación simple de NextJS usando el SDK de [Node.js](/api-reference/client-libraries) de Trophy: +Mira a Charlie integrar el seguimiento de métricas en una aplicación simple de NextJS usando el SDK de [Node.js](/es/api-reference/client-libraries) de Trophy: -### Eventos Idempotentes +### Eventos Idempotentes {#idempotent-events} Trophy admite garantizar la unicidad de los eventos para que los usuarios no puedan aumentar una métrica realizando la misma acción una y otra vez. @@ -214,10 +214,10 @@ Por ejemplo, una aplicación de aprendizaje de idiomas podría especificar que l Esto ayuda a mantener tu código libre de lógica que verifica si los usuarios han completado acciones previamente, y en su lugar puedes confiar en que Trophy mantendrá las restricciones que necesitas. -Para usar eventos idempotentes, utiliza el encabezado `Idempotency-Key` en la [API de eventos de métricas](/api-reference/endpoints/metrics/send-a-metric-change-event). +Para usar eventos idempotentes, utiliza el encabezado `Idempotency-Key` en la [API de eventos de métricas](/es/api-reference/endpoints/metrics/send-a-metric-change-event). -[Más información sobre idempotencia](/api-reference/idempotency). +[Más información sobre idempotencia](/es/api-reference/idempotency). -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/platform/leaderboards.mdx b/es/platform/leaderboards.mdx index 78704df..18c0b22 100644 --- a/es/platform/leaderboards.mdx +++ b/es/platform/leaderboards.mdx @@ -8,10 +8,10 @@ og:description: Crea competiciones diarias, semanales o mensuales que clasifican icon: /icons/leaderboard.svg --- -import LeaderboardSingleAttributeRequest from "/snippets/leaderboard-rankings-request-single-attribute.mdx"; -import LeaderboardMultiAttributeRequest from "/snippets/leaderboard-rankings-request-multiple-attributes.mdx"; +import LeaderboardSingleAttributeRequest from "../../snippets/leaderboard-rankings-request-single-attribute.mdx"; +import LeaderboardMultiAttributeRequest from "../../snippets/leaderboard-rankings-request-multiple-attributes.mdx"; -## ¿Qué son las Clasificaciones? +## ¿Qué son las Clasificaciones? {#what-are-leaderboards} Las clasificaciones son competiciones sociales entre los usuarios de tu aplicación. Usa las clasificaciones para aumentar la participación y fomentar la interacción social. @@ -20,49 +20,49 @@ Las clasificaciones son competiciones sociales entre los usuarios de tu aplicaci height="100" width="100%" noZoom - src="../assets/platform/leaderboards/hero.png" + src="../../assets/platform/leaderboards/hero.png" /> -## Tipos de Clasificaciones +## Tipos de Clasificaciones {#types-of-leaderboards} En esta sección describimos los diferentes tipos de clasificaciones admitidas en Trophy y cuándo usar cada una. -### Clasificaciones Perpetuas +### Clasificaciones Perpetuas {#perpetual-leaderboards} Las clasificaciones perpetuas nunca se reinician. Una vez iniciadas, rastrean y clasifican continuamente el progreso de los usuarios a lo largo del tiempo para siempre, o hasta la [fecha de finalización](#end-dates) configurada. Usa clasificaciones perpetuas cuando quieras crear rankings históricos de la actividad de los usuarios. -### Clasificaciones Recurrentes +### Clasificaciones Recurrentes {#repeating-leaderboards} Las clasificaciones recurrentes se pueden configurar para reiniciarse después de cualquier número arbitrario de días, meses o años. En Trophy, cada instancia de una clasificación recurrente se denomina **'ejecución'**. Por ejemplo, una clasificación mensual tendría 12 ejecuciones en un año, pero una clasificación diaria tendría `n` ejecuciones en un mes donde `n` es el número de días en un mes determinado. -Trophy rastrea las clasificaciones en cada ejecución de una clasificación recurrente individualmente y proporciona [APIs](/api-reference/endpoints/leaderboards/get-leaderboard) para obtener datos de clasificación de ejecuciones históricas. +Trophy rastrea las clasificaciones en cada ejecución de una clasificación recurrente individualmente y proporciona [APIs](/es/api-reference/endpoints/leaderboards/get-leaderboard) para obtener datos de clasificación de ejecuciones históricas. Recomendamos usar clasificaciones recurrentes en lugar de perpetuas siempre que sea posible, ya que las clasificaciones recurrentes dan a los nuevos usuarios las mismas oportunidades de competir con los usuarios existentes, ayudando a evitar que las clasificaciones se vuelvan obsoletas. -#### Gestión de zonas horarias +#### Gestión de zonas horarias {#handling-time-zones} -Si has rastreado las [zonas horarias](/platform/users#param-tz) de los usuarios con Trophy, estas se utilizarán para garantizar que cada usuario tenga las mismas oportunidades de ganar sin importar dónde se encuentre en el mundo. +Si has rastreado las [zonas horarias](/es/platform/users#param-tz) de los usuarios con Trophy, estas se utilizarán para garantizar que cada usuario tenga las mismas oportunidades de ganar sin importar dónde se encuentre en el mundo. En la práctica, esto significa que las clasificaciones se finalizan y los ganadores se eligen aproximadamente 12 horas después de que terminen naturalmente en UTC para permitir que los usuarios de todas las zonas horarias realicen su último esfuerzo. -#### Consejos para clasificaciones semanales +#### Consejos para clasificaciones semanales {#tips-for-weekly-leaderboards} Para crear una clasificación semanal, configura una [clasificación recurrente](#repeating-leaderboards) con un calendario de 7 días y establece la fecha de inicio como el próximo primer día de la semana. Mientras esperas a que llegue la fecha de inicio, la clasificación estará en estado `scheduled` y se activará automáticamente en la fecha de inicio. -## Lógica de clasificación +## Lógica de clasificación {#ranking-logic} Las clasificaciones en Trophy son configurables para clasificar a los participantes de diferentes maneras y así soportar casos de uso comunes. -### Métodos de clasificación +### Métodos de clasificación {#ranking-methods} El método de clasificación de una clasificación determina en qué dimensión se ordenarán los participantes. @@ -71,23 +71,23 @@ El método de clasificación de una clasificación determina en qué dimensión height="200" width="50%" noZoom - src="../assets/platform/leaderboards/ranking_methods.png" + src="../../assets/platform/leaderboards/ranking_methods.png" /> -#### Clasificaciones por métricas +#### Clasificaciones por métricas {#metric-rankings} -Las clasificaciones por métricas están vinculadas a una [Métrica](/platform/metrics) existente de Trophy y clasifican a los usuarios según su valor total de métrica. +Las clasificaciones por métricas están vinculadas a una [Métrica](/es/platform/metrics) existente de Trophy y clasifican a los usuarios según su valor total de métrica. Utiliza clasificaciones por métricas si solo deseas clasificar a los usuarios según una única interacción. -#### Clasificaciones por puntos +#### Clasificaciones por puntos {#points-rankings} -Las clasificaciones por puntos están vinculadas a un [Sistema de puntos](/platform/points) existente de Trophy y clasifican automáticamente a los usuarios según sus puntos totales. +Las clasificaciones por puntos están vinculadas a un [Sistema de puntos](/es/platform/points) existente de Trophy y clasifican automáticamente a los usuarios según sus puntos totales. Utiliza una clasificación por puntos si deseas clasificar a los usuarios según una combinación de métricas, logros u otras funcionalidades de Trophy. -#### Clasificaciones de Racha +#### Clasificaciones de Racha {#streak-rankings} Las clasificaciones de racha clasifican a los usuarios según la longitud de su racha actual. @@ -95,7 +95,7 @@ Las clasificaciones de racha clasifican a los usuarios según la longitud de su Las clasificaciones de racha solo pueden ser [perpetuas](#perpetual-leaderboards). -### Desglose de Clasificaciones +### Desglose de Clasificaciones {#ranking-breakdowns} Si tienes una gran base de usuarios, es recomendable dividir a los participantes de la clasificación en grupos más pequeños y socialmente conectados. Esto a menudo genera mayor participación que cuando se utilizan clasificaciones globales. @@ -117,11 +117,11 @@ Trophy comenzará automáticamente a agrupar usuarios en clasificaciones más pe loop playsInline className="w-full aspect-15/4" - src="../assets/platform/leaderboards/breakdowns.mp4" + src="../../assets/platform/leaderboards/breakdowns.mp4" > -Para obtener las clasificaciones de un grupo particular de usuarios con un valor de atributo específico, utiliza la [API de clasificaciones](/api-reference/endpoints/leaderboards/get-leaderboard), especificando el valor del atributo en el [parámetro `userAttributes`](/api-reference/endpoints/leaderboards/get-leaderboard#parameter-user-attributes) de la siguiente manera: +Para obtener las clasificaciones de un grupo particular de usuarios con un valor de atributo específico, utiliza la [API de clasificaciones](/es/api-reference/endpoints/leaderboards/get-leaderboard), especificando el valor del atributo en el [parámetro `userAttributes`](/es/api-reference/endpoints/leaderboards/get-leaderboard#parameter-user-attributes) de la siguiente manera: @@ -129,7 +129,7 @@ Si deseas obtener las clasificaciones de un grupo particular de usuarios con una -## Fechas de Inicio y Fin +## Fechas de Inicio y Fin {#start-end-dates} Utiliza fechas de inicio y fin para controlar la ventana dentro de la cual las clasificaciones están clasificando activamente a los usuarios. @@ -138,17 +138,17 @@ Utiliza fechas de inicio y fin para controlar la ventana dentro de la cual las c height="200" width="50%" noZoom - src="../assets/platform/leaderboards/start_end_dates.png" + src="../../assets/platform/leaderboards/start_end_dates.png" /> -### Fechas de Inicio +### Fechas de Inicio {#start-dates} Las clasificaciones en Trophy se pueden configurar para iniciar en una fecha futura de tu elección. Esto suele ser útil para permitir algo de tiempo para cambios o ajustes de último momento antes de que las clasificaciones comiencen a clasificar a los usuarios. Las clasificaciones con una fecha de inicio en el futuro están programadas y se activan automáticamente en la fecha de inicio que elijas. -### Fechas de finalización +### Fechas de finalización {#end-dates} Las clasificaciones en Trophy pueden tener fechas de finalización. Si estableces una fecha de finalización en una clasificación, después de esa fecha entrará en estado `finished` y las clasificaciones se finalizarán y se elegirán los ganadores. @@ -158,7 +158,7 @@ Las clasificaciones en Trophy pueden tener fechas de finalización. Si establece la fecha de finalización según su reloj local. -## Límites de participantes +## Límites de participantes {#participant-limits} Las clasificaciones en Trophy tienen un número máximo de participantes de **1,000**. Sin embargo, una clasificación puede configurarse para tener cualquier número arbitrario de participantes para admitir casos de uso como _Top 100_ o similares. @@ -167,7 +167,7 @@ Las clasificaciones en Trophy tienen un número máximo de participantes de **1, height="200" width="50%" noZoom - src="../assets/platform/leaderboards/max_participants.png" + src="../../assets/platform/leaderboards/max_participants.png" /> @@ -184,7 +184,7 @@ Para obtener más información sobre los efectos negativos de las clasificacione La única excepción a esto es cuando se usan [atributos de desglose](#ranking-breakdowns) para agrupar participantes en cohortes más pequeñas. Cuando se usan atributos de desglose, el límite de participantes se aplica a cada grupo, no en general. -## Crear Clasificaciones +## Crear Clasificaciones {#creating-leaderboards} Para crear una clasificación, dirígete a la [página de clasificaciones](https://app.trophy.so/leaderboards) en el panel de Trophy y presiona el botón _Nueva Clasificación_. @@ -195,7 +195,7 @@ Para crear una clasificación, dirígete a la [página de clasificaciones](https loop playsInline className="w-full aspect-15/4" - src="../assets/platform/leaderboards/creating_leaderboards.mp4" + src="../../assets/platform/leaderboards/creating_leaderboards.mp4" > @@ -229,11 +229,11 @@ Para crear una clasificación, dirígete a la [página de clasificaciones](https -## Gestionar Clasificaciones +## Gestionar Clasificaciones {#managing-leaderboards} Las clasificaciones en Trophy tienen varios estados para ayudarte a controlar cuándo y cómo los usuarios pueden unirse a ellas. -### Estados de Clasificación +### Estados de Clasificación {#leaderboard-statuses} Las clasificaciones pueden tener uno de los siguientes estados: @@ -257,14 +257,14 @@ Si decides que ya no necesitas una clasificación, puedes moverla al estado `Arc Una vez que una clasificación está archivada, solo puede restaurarse contactando al soporte. -## Visualización de Clasificaciones +## Visualización de Clasificaciones {#displaying-leaderboards} - Consulta nuestra [guía completa](/guides/how-to-build-a-leaderboards-feature) sobre + Consulta nuestra [guía completa](/es/guides/how-to-build-a-leaderboards-feature) sobre cómo agregar clasificaciones a tu aplicación para más detalles. -## Analíticas de Clasificaciones +## Analíticas de Clasificaciones {#leaderboard-analytics} Trophy tiene analíticas integradas para ayudarte a entender cómo los usuarios están interactuando con tus clasificaciones. @@ -273,11 +273,11 @@ Trophy tiene analíticas integradas para ayudarte a entender cómo los usuarios height="200" width="50%" noZoom - src="../assets/platform/leaderboards/analytics.png" + src="../../assets/platform/leaderboards/analytics.png" /> -### Total de Participantes Únicos +### Total de Participantes Únicos {#total-unique-participants} Este gráfico muestra cuántos usuarios únicos han participado en cualquier ejecución de una clasificación a lo largo del tiempo. Esto es útil para entender cuántos de tus usuarios realmente participan en las clasificaciones y cómo los [límites de participantes](#participant-limits) están afectando esto. @@ -286,11 +286,11 @@ Este gráfico muestra cuántos usuarios únicos han participado en cualquier eje height="200" width="50%" noZoom - src="../assets/platform/leaderboards/total_unique_participants.png" + src="../../assets/platform/leaderboards/total_unique_participants.png" /> -### Usuarios Activos +### Usuarios Activos {#active-users} Este gráfico muestra el número de usuarios que han cambiado de posición al menos una vez en una clasificación determinada. Esto es útil para tener una idea de qué tan competitivo es el usuario promedio en una clasificación particular. @@ -299,11 +299,11 @@ Este gráfico muestra el número de usuarios que han cambiado de posición al me height="200" width="50%" noZoom - src="../assets/platform/leaderboards/active_users.png" + src="../../assets/platform/leaderboards/active_users.png" /> -### Cambios de Rango +### Cambios de Rango {#rank-changes} Este gráfico muestra el número total de cambios de rango en una clasificación particular a lo largo del tiempo. Esto es útil para entender qué tan competitivos son los usuarios en general. @@ -312,11 +312,11 @@ Este gráfico muestra el número total de cambios de rango en una clasificación height="200" width="50%" noZoom - src="../assets/platform/leaderboards/rank_changes.png" + src="../../assets/platform/leaderboards/rank_changes.png" /> -### Distribución de Puntuación +### Distribución de Puntuación {#score-distribution} Este gráfico es un histograma de las puntuaciones de los usuarios en una clasificación particular. Esto es útil para tener una idea de qué tan agrupados o dispersos están los usuarios, y qué secciones de las clasificaciones son las más competitivas. @@ -325,11 +325,11 @@ Este gráfico es un histograma de las puntuaciones de los usuarios en una clasif height="200" width="50%" noZoom - src="../assets/platform/leaderboards/score_distribution.png" + src="../../assets/platform/leaderboards/score_distribution.png" /> -## Preguntas Frecuentes +## Preguntas Frecuentes {#frequently-asked-questions} @@ -349,6 +349,6 @@ Este gráfico es un histograma de las puntuaciones de los usuarios en una clasif -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/platform/metrics.mdx b/es/platform/metrics.mdx index d2571bd..a86cdf5 100644 --- a/es/platform/metrics.mdx +++ b/es/platform/metrics.mdx @@ -9,7 +9,7 @@ og:description: Las métricas son primitivas de datos flexibles que pueden usars icon: box --- -## ¿Qué son las Métricas? +## ¿Qué son las Métricas? {#what-are-metrics} Todas las funciones de gamificación se centran en las interacciones de los usuarios. En Trophy, utilizamos el término **Métricas** para referirnos a los objetos de datos que modelan esas interacciones dentro de tu aplicación web o móvil. @@ -22,22 +22,22 @@ Las métricas no tienen opinión, lo que significa que pueden usarse para modela - Palabras escritas - ... -Las métricas son los componentes básicos de la infraestructura que impulsa las funciones de gamificación de Trophy. Cada interacción de usuario relacionada con una métrica se almacena como un [Evento](/platform/events). +Las métricas son los componentes básicos de la infraestructura que impulsa las funciones de gamificación de Trophy. Cada interacción de usuario relacionada con una métrica se almacena como un [Evento](/es/platform/events). Los eventos se almacenan y procesan en orden cronológico. En cualquier momento dado, el estado combinado del historial de eventos de un usuario refleja su progreso general en tu plataforma. -## Atributos Clave +## Atributos Clave {#key-attributes} Aquí describimos los atributos clave que te permiten crear métricas que se adapten mejor a tu caso de uso. -### Unidades +### Unidades {#units} Puedes asignar fácilmente unidades a las métricas en el panel de control para: - Números arbitrarios (tareas, publicaciones, mensajes, etc.) - Monedas ($, £, €) -## Creación de Métricas +## Creación de Métricas {#creating-metrics} Para crear una métrica, dirígete a la página de [Métricas](https://app.trophy.so/metrics) dentro de Trophy y haz clic en el botón **Nueva Métrica**: @@ -48,7 +48,7 @@ Para crear una métrica, dirígete a la página de [Métricas](https://app.troph loop playsInline className="w-full aspect-video" - src="../assets/platform/metrics/create_new_metric.mp4" + src="../../assets/platform/metrics/create_new_metric.mp4" > @@ -66,7 +66,7 @@ Para crear una métrica, dirígete a la página de [Métricas](https://app.troph -## Analíticas de Métricas +## Analíticas de Métricas {#metric-analytics} Trophy tiene un panel de analíticas integrado para cada métrica que crees. Te muestra: @@ -78,25 +78,25 @@ Trophy tiene un panel de analíticas integrado para cada métrica que crees. Te -### Gráfico de Completitud de Logros +### Gráfico de Completitud de Logros {#achievement-completion-chart} El gráfico de completitud de logros muestra el estado actual de la base de usuarios en términos del número de usuarios que han completado cada logro que has configurado para esta métrica. -[Más información sobre logros](/platform/achievements). +[Más información sobre logros](/es/platform/achievements). -## Preguntas Frecuentes +## Preguntas Frecuentes {#frequently-asked-questions} @@ -111,6 +111,6 @@ El gráfico de completitud de logros muestra el estado actual de la base de usua -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres contactar con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/platform/overview.mdx b/es/platform/overview.mdx index 9b1f1aa..73d9f85 100644 --- a/es/platform/overview.mdx +++ b/es/platform/overview.mdx @@ -7,34 +7,34 @@ og:description: Aprende los conceptos clave detrás del sistema de gamificación Trophy está construido sobre un conjunto de conceptos clave que todas las experiencias de gamificación tienen en común, mientras ofrece suficiente personalización para que puedas crear cualquier tipo de experiencia gamificada que necesites. -## Conceptos de la plataforma +## Conceptos de la plataforma {#platform-concepts} Los conceptos de la plataforma de Trophy son bloques de construcción escalables que soportan todas las funcionalidades de Trophy y son los elementos principales con los que los desarrolladores deben familiarizarse. - + Modela cómo los usuarios interactúan con tu aplicación. - + Rastrea las interacciones de los usuarios con respecto a las métricas. - + Informa a Trophy sobre las personas que usan tu aplicación. -## Conceptos de gamificación +## Conceptos de gamificación {#gamification-concepts} Los conceptos de gamificación de Trophy son primitivas flexibles construidas sobre los conceptos de infraestructura que soportan una amplia gama de casos de uso comunes de gamificación. - + Incentiva a los usuarios a seguir progresando o a realizar acciones específicas. - + Motiva a los usuarios a desarrollar hábitos de uso regulares. - + Recompensa a los usuarios con sistemas de puntos sofisticados. } - href="/platform/leaderboards" + href="/es/platform/leaderboards" > Crea competencias amistosas para aumentar la participación de los usuarios. - + Envía correos electrónicos personalizados del ciclo de vida a los usuarios para aumentar la retención. Impulsa flujos de notificaciones automatizadas usando datos de gamificación personalizados -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/platform/points.mdx b/es/platform/points.mdx index 46bbf9e..25c560b 100644 --- a/es/platform/points.mdx +++ b/es/platform/points.mdx @@ -6,35 +6,35 @@ og:description: Usa sistemas de puntos para recompensar a los usuarios por icon: sparkle --- -import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; -import MetricChangeEventPointsLevelExcerpt from "/snippets/metric-change-event-points-level-excerpt-block.mdx"; -import PointsHistogramSummaryResponse from "/snippets/points-histogram-summary-response-block.mdx"; -import PointsLevelSummaryResponse from "/snippets/points-level-summary-response-block.mdx"; -import PointsLevelsListResponse from "/snippets/points-levels-list-response-block.mdx"; -import PointsSystemResponse from "/snippets/points-system-response-block.mdx"; -import UserPointsResponse from "/snippets/user-points-response-block.mdx"; -import UserPointsEventSummaryResponse from "/snippets/user-points-summary-response-block.mdx"; -import WebhookPointsLevelChangedPayload from "/snippets/webhook-points-level-changed-payload-block.mdx"; +import MetricChangeResponseBlock from "../../snippets/metric-change-response-block.mdx"; +import MetricChangeEventPointsLevelExcerpt from "../../snippets/metric-change-event-points-level-excerpt-block.mdx"; +import PointsHistogramSummaryResponse from "../../snippets/points-histogram-summary-response-block.mdx"; +import PointsLevelSummaryResponse from "../../snippets/points-level-summary-response-block.mdx"; +import PointsLevelsListResponse from "../../snippets/points-levels-list-response-block.mdx"; +import PointsSystemResponse from "../../snippets/points-system-response-block.mdx"; +import UserPointsResponse from "../../snippets/user-points-response-block.mdx"; +import UserPointsEventSummaryResponse from "../../snippets/user-points-summary-response-block.mdx"; +import WebhookPointsLevelChangedPayload from "../../snippets/webhook-points-level-changed-payload-block.mdx"; -## ¿Qué es un Sistema de Puntos? +## ¿Qué es un Sistema de Puntos? {#what-is-a-points-system} -Los sistemas de puntos se utilizan para crear contadores que rastrean las interacciones de los usuarios con [Métricas](/platform/metrics), [Logros](/platform/achievements) y [Rachas](/platform/streaks). Luego puedes construir funcionalidades como 'XP' y 'Energía' alrededor de estos contadores dentro de tu producto. +Los sistemas de puntos se utilizan para crear contadores que rastrean las interacciones de los usuarios con [Métricas](/es/platform/metrics), [Logros](/es/platform/achievements) y [Rachas](/es/platform/streaks). Luego puedes construir funcionalidades como 'XP' y 'Energía' alrededor de estos contadores dentro de tu producto. -## Casos de Uso +## Casos de Uso {#use-cases} -### Recompensas +### Recompensas {#rewards} Los sistemas de puntos se pueden usar para crear funcionalidades como 'XP' o 'Gemas' que recompensan a los usuarios por múltiples interacciones a diferentes tasas. De esta manera, los puntos se pueden usar para ponderar el valor de ciertas interacciones de manera diferente a otras, recompensando a los usuarios por realizar las acciones que consideras más correlacionadas con la retención. -### Medición +### Medición {#metering} Los sistemas de puntos también se pueden usar para crear funcionalidades como 'Energía' que miden el uso de tu producto de manera que te da control sobre promover y restringir la actividad del usuario. Esto te permite controlar la velocidad a la que los usuarios pueden usar tu producto con una mecánica flexible que está fuera de tu base de código. -## Creación de Sistemas de Puntos +## Creación de Sistemas de Puntos {#creating-points-systems} Trophy te permite configurar múltiples sistemas de puntos para diferentes casos de uso dentro de tu aplicación. @@ -45,7 +45,7 @@ Trophy te permite configurar múltiples sistemas de puntos para diferentes casos loop playsInline className="w-full aspect-video" - src="../assets/platform/points/create_system.mp4" + src="../../assets/platform/points/create_system.mp4" > @@ -75,37 +75,37 @@ Para crear un sistema de puntos, dirígete a la [página de puntos](https://app. -## Disparadores de Puntos +## Disparadores de Puntos {#points-triggers} En Trophy, los puntos se otorgan o deducen a los usuarios mediante disparadores. Estos definen las diferentes mecánicas que conforman tu sistema de puntos. Puedes agregar tantos disparadores como desees a cada sistema de puntos que configures, lo que te permite crear diferentes lógicas para cómo se otorgan o deducen los puntos en diferentes sistemas de puntos. -### Tipos de Disparadores +### Tipos de Disparadores {#types-of-triggers} Existen múltiples tipos de disparadores en Trophy que se pueden usar para otorgar o deducir puntos de diferentes maneras. -#### Disparadores de Métricas +#### Disparadores de Métricas {#metric-triggers} -Los puntos se pueden otorgar o deducir continuamente a medida que los usuarios incrementan [Métricas](/platform/metrics). Puedes elegir otorgar o deducir cualquier número arbitrario de puntos en cualquier umbral de métrica arbitrario, por ejemplo "otorgar 10 puntos por cada 3 tareas completadas". +Los puntos se pueden otorgar o deducir continuamente a medida que los usuarios incrementan [Métricas](/es/platform/metrics). Puedes elegir otorgar o deducir cualquier número arbitrario de puntos en cualquier umbral de métrica arbitrario, por ejemplo "otorgar 10 puntos por cada 3 tareas completadas". -#### Disparadores de Racha +#### Disparadores de Racha {#streak-triggers} -Se pueden otorgar o deducir Puntos al alcanzar cualquier longitud arbitraria de una [Racha](/platform/streaks), por ejemplo "otorgar 50 Puntos por cada Racha de 7 días". +Se pueden otorgar o deducir Puntos al alcanzar cualquier longitud arbitraria de una [Racha](/es/platform/streaks), por ejemplo "otorgar 50 Puntos por cada Racha de 7 días". -#### Disparadores de Logro +#### Disparadores de Logro {#achievement-triggers} -Se pueden otorgar o deducir Puntos cuando los usuarios desbloquean [Logros](/platform/achievements) específicos, por ejemplo "otorgar 100 Puntos cuando los usuarios completen el Logro `profile-completed`". +Se pueden otorgar o deducir Puntos cuando los usuarios desbloquean [Logros](/es/platform/achievements) específicos, por ejemplo "otorgar 100 Puntos cuando los usuarios completen el Logro `profile-completed`". -#### Disparadores basados en tiempo +#### Disparadores basados en tiempo {#time-based-triggers} Se pueden otorgar o deducir Puntos en intervalos de tiempo repetidos, cada hora o cada día. Por ejemplo "otorgar 10 Puntos cada 3 horas". -#### Disparadores de identificación de usuario +#### Disparadores de identificación de usuario {#user-identification-triggers} Se pueden otorgar Puntos cuando los usuarios son identificados por primera vez en Trophy, útil para conceder una cantidad inicial de Puntos cuando se registran en tu producto. -### Crear disparadores +### Crear disparadores {#creating-triggers} Para crear un nuevo disparador de Puntos, dirígete al sistema de Puntos para el que deseas crear un disparador y sigue los pasos a continuación. @@ -121,7 +121,7 @@ Para crear un nuevo disparador de Puntos, dirígete al sistema de Puntos para el loop playsInline className="w-full aspect-video" - src="../assets/platform/points/create_trigger.mp4" + src="../../assets/platform/points/create_trigger.mp4" > @@ -153,9 +153,9 @@ Para crear un nuevo disparador de Puntos, dirígete al sistema de Puntos para el Puedes asignar filtros de atributos a un activador de puntos para restringir aún más cuándo se aplican. -- Para limitar un **activador de Métrica** a que solo se aplique a eventos con [atributos de evento personalizados](/platform/events#custom-event-attributes) específicos, selecciona un atributo e ingresa un valor en la sección **Atributo de Evento**. +- Para limitar un **activador de Métrica** a que solo se aplique a eventos con [atributos de evento personalizados](/es/platform/events#custom-event-attributes) específicos, selecciona un atributo e ingresa un valor en la sección **Atributo de Evento**. -- Para limitar cualquier tipo de activador a que solo se aplique a un usuario con uno o más [atributos de usuario personalizados](/platform/users#custom-user-attributes) específicos, agrega atributos y los valores deseados en la sección **Atributos de Usuario**. +- Para limitar cualquier tipo de activador a que solo se aplique a un usuario con uno o más [atributos de usuario personalizados](/es/platform/users#custom-user-attributes) específicos, agrega atributos y los valores deseados en la sección **Atributos de Usuario**. @@ -167,7 +167,7 @@ Para crear un nuevo disparador de Puntos, dirígete al sistema de Puntos para el -## Equilibrando los Puntos +## Equilibrando los Puntos {#balancing-points} Administrar un sistema de puntos efectivo requiere encontrar el ritmo óptimo al que los usuarios ganan puntos. Demasiado rápido, y los usuarios experimentarán fatiga de puntos, volviéndolos inútiles. Demasiado lento, y los usuarios pueden aburrirse y abandonar. @@ -180,31 +180,31 @@ La herramienta de vista previa de Trophy puede modelar diferentes escenarios par loop playsInline className="w-full aspect-video" - src="../assets/platform/points/points_preview.mp4" + src="../../assets/platform/points/points_preview.mp4" > -## Impulsos de Puntos +## Impulsos de Puntos {#points-boosts} Los impulsos de Puntos son multiplicadores que puedes usar para aumentar la cantidad de Puntos otorgados a los usuarios durante un período de tiempo específico. Esta sección explica cómo funcionan los impulsos y cómo pueden utilizarse para aumentar la retención y el compromiso de tu aplicación. -### Segmentación de Impulsos +### Segmentación de Impulsos {#boost-targeting} Hay varias formas diferentes en las que puedes usar impulsos en Trophy. -- **Impulsos globales** multiplican los Puntos ganados por todos los usuarios durante la ventana del impulso, o pueden limitarse a una cohorte específica de usuarios mediante el uso de [atributos de usuario personalizados](/platform/users#custom-user-attributes). +- **Impulsos globales** multiplican los Puntos ganados por todos los usuarios durante la ventana del impulso, o pueden limitarse a una cohorte específica de usuarios mediante el uso de [atributos de usuario personalizados](/es/platform/users#custom-user-attributes). - **Impulsos específicos de usuario** solo multiplican los Puntos ganados por un único usuario dentro de la ventana del impulso. Típicamente, los impulsos globales se utilizan para aumentar el compromiso en toda la plataforma durante eventos clave del calendario como Black Friday/Cyber Monday en comercio electrónico, Año Nuevo en fitness y temporada de exámenes en plataformas de tecnología educativa. Por el contrario, los impulsos específicos de usuario se usan comúnmente para proporcionar incentivos adicionales a los usuarios para que realicen acciones que estén correlacionadas con una mayor retención. -### Crear Impulsos +### Crear Impulsos {#creating-boosts} -Los impulsos específicos de usuario solo se pueden crear programáticamente a través de la [API de administración](/admin-api/endpoints/points/create-boosts). +Los impulsos específicos de usuario solo se pueden crear programáticamente a través de la [API de administración](/es/admin-api/endpoints/points/create-boosts). Para crear un impulso global de Puntos en el panel de Trophy, sigue los pasos a continuación. @@ -216,7 +216,7 @@ Para crear un impulso global de Puntos en el panel de Trophy, sigue los pasos a loop playsInline className="w-full aspect-15/4" - src="../assets/platform/points/create_points_boost.mp4" + src="../../assets/platform/points/create_points_boost.mp4" > @@ -236,7 +236,7 @@ Para crear un impulso global de Puntos en el panel de Trophy, sigue los pasos a - Esta función requiere [atributos de usuario personalizados](/platform/users#custom-user-attributes) que están disponibles en el plan [Pro](/account/billing#pro-plan). + Esta función requiere [atributos de usuario personalizados](/es/platform/users#custom-user-attributes) que están disponibles en el plan [Pro](/es/account/billing#pro-plan). Si desea que su impulso solo afecte a una cohorte de usuarios específica, establezca condiciones de atributos de usuario que coincidan con las de su cohorte objetivo. Trophy se encargará de aplicar el impulso únicamente a los puntos obtenidos por usuarios con valores de atributos de usuario coincidentes. @@ -246,7 +246,7 @@ Para crear un impulso global de Puntos en el panel de Trophy, sigue los pasos a height="200" width="100%" noZoom - src="../assets/platform/points/points_boost_attributes.png" + src="../../assets/platform/points/points_boost_attributes.png" /> @@ -255,13 +255,13 @@ Para crear un impulso global de Puntos en el panel de Trophy, sigue los pasos a -### Multiplicadores de Impulso +### Multiplicadores de Impulso {#boost-multipliers} Cada impulso tiene un único valor multiplicador que aumenta los puntos totales obtenidos por los usuarios afectados por ese impulso. Los multiplicadores de impulso deben ser números positivos, pero pueden ser decimales para admitir escenarios donde se requieran impulsos porcentuales como '50% más de puntos'. -### Acumulación de Impulsos +### Acumulación de Impulsos {#boost-stacking} Los impulsos de puntos en Trophy se acumulan mediante [multiplicación](#boost-multipliers). Esto permite que los usuarios se beneficien de múltiples impulsos simultáneamente. @@ -271,9 +271,9 @@ Para demostrar la acumulación, considere un impulso 2X, 1.5X y 3X todos activos Overall Multiplier (3 boosts) = 2X * 1.5X * 3X = 9X ``` -### Redondeo de Impulsos +### Redondeo de Impulsos {#boost-rounding} -Trophy admite [valores de evento métrico de punto flotante](/platform/events#event-value), pero se encarga de redondear los puntos a enteros automáticamente. +Trophy admite [valores de evento métrico de punto flotante](/es/platform/events#event-value), pero se encarga de redondear los puntos a enteros automáticamente. Sin embargo, al usar [multiplicadores de impulso](#boost-multipliers) decimales, puede haber algunos escenarios donde este redondeo predeterminado no sea suficiente para producir siempre valores de puntos enteros. @@ -287,9 +287,9 @@ Por lo tanto, Trophy ofrece tres modos de redondeo para proporcionar control adi En escenarios donde los Puntos se utilizan para imitar mecánicas de crédito de plataforma, se recomienda usar el comportamiento predeterminado de redondeo hacia abajo para protegerse contra deslizamientos. -Simplemente elige tu modo de redondeo preferido al [crear impulsos](/platform/points#creating-boosts) a través del panel de Trophy. +Simplemente elige tu modo de redondeo preferido al [crear impulsos](/es/platform/points#creating-boosts) a través del panel de Trophy. -## Niveles de Puntos +## Niveles de Puntos {#points-levels} Los niveles de Puntos son hitos discretos que defines en un sistema de Puntos en Trophy. @@ -297,7 +297,7 @@ Cada nivel tiene un umbral. Cuando el total de Puntos de un usuario en el sistem Usa niveles para rangos de jerarquía, interfaz de progresión, niveles de recompensa o análisis. Trophy mantiene sincronizado el nivel actual de cada usuario cada vez que gana o pierde Puntos en ese sistema. -### Configuración de niveles +### Configuración de niveles {#configuring-levels} Para configurar niveles para un sistema de Puntos, ábrelo desde la [página de Puntos](https://app.trophy.so/points) en el panel de Trophy y usa la pestaña de niveles para agregar o gestionar niveles. @@ -308,7 +308,7 @@ Para configurar niveles para un sistema de Puntos, ábrelo desde la [página de loop playsInline className="w-full aspect-video" - src="../assets/platform/points/create_points_level.mp4" + src="../../assets/platform/points/create_points_level.mp4" > @@ -329,29 +329,29 @@ Para configurar niveles para un sistema de Puntos, ábrelo desde la [página de -### Mostrar Niveles +### Mostrar Niveles {#displaying-levels} La mayoría de los equipos combinan tres enfoques: una vista estática de cada nivel (para pantallas de progresión), el nivel actual del usuario (para encabezados y perfil) y retroalimentación inmediata cuando una acción lo impulsa a un nuevo nivel. -#### Mostrar Todos los Niveles +#### Mostrar Todos los Niveles {#displaying-all-levels} -Llama al [API de obtener niveles](/api-reference/endpoints/points/get-points-levels) una vez por clave de sistema de puntos (por ejemplo, al cargar la aplicación o desde tu servidor al renderizar una página de progresión). +Llama al [API de obtener niveles](/es/api-reference/endpoints/points/get-points-levels) una vez por clave de sistema de puntos (por ejemplo, al cargar la aplicación o desde tu servidor al renderizar una página de progresión). La respuesta es un array de niveles con `key`, `name`, `description`, `badgeUrl` opcional y el umbral `points` para cada nivel. Al vincular tu interfaz a la respuesta del API de Trophy, te asegurarás de que se actualice automáticamente cuando realices cambios en los niveles desde el panel de Trophy. -#### Mostrar Nivel de Usuario +#### Mostrar Nivel de Usuario {#displaying-user-level} -El [API de puntos de usuario](/api-reference/endpoints/users/get-a-users-points) devuelve los `total` del usuario, su objeto `level` actual y `awards` recientes. El campo `level` contiene el nivel actual del usuario, o null si no existen niveles o aún no ha alcanzado el umbral de puntos para ningún nivel. +El [API de puntos de usuario](/es/api-reference/endpoints/users/get-a-users-points) devuelve los `total` del usuario, su objeto `level` actual y `awards` recientes. El campo `level` contiene el nivel actual del usuario, o null si no existen niveles o aún no ha alcanzado el umbral de puntos para ningún nivel. Empareja `total` con la lista ordenada de niveles del paso anterior para dibujar una barra de progreso entre el umbral actual y el siguiente. -#### Notificaciones de Cambio de Nivel +#### Notificaciones de Cambio de Nivel {#level-change-notifications} -Cuando envías un [evento de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event), la respuesta incluirá un mapa `points` indexado por las claves de tu sistema de puntos para cada sistema que cambió como resultado del evento. +Cuando envías un [evento de cambio de métrica](/es/api-reference/endpoints/metrics/send-a-metric-change-event), la respuesta incluirá un mapa `points` indexado por las claves de tu sistema de puntos para cada sistema que cambió como resultado del evento. Los datos contienen una clave `level` solo cuando el nivel del usuario cambió debido a este evento. Si su nivel permaneció igual, esa clave se omite, por lo que puedes tratar de forma segura un `level` presente como una señal de cambio de nivel sin necesidad de contabilidad adicional. @@ -368,26 +368,26 @@ if (pts?.level) { } ``` -El mismo comportamiento de `level` se aplica cuando los puntos cambian debido a la [finalización de un logro](/api-reference/endpoints/achievements/mark-an-achievement-as-completed). +El mismo comportamiento de `level` se aplica cuando los puntos cambian debido a la [finalización de un logro](/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed). -Para notificaciones impulsadas por el servidor (correo electrónico, push, CRM) que no deben depender de que el cliente vea la respuesta HTTP, suscríbete al webhook [`points.level_changed`](/webhooks/events/points/points-level-changed) en su lugar. +Para notificaciones impulsadas por el servidor (correo electrónico, push, CRM) que no deben depender de que el cliente vea la respuesta HTTP, suscríbete al webhook [`points.level_changed`](/es/webhooks/events/points/points-level-changed) en su lugar. Este webhook se activa cuando el nivel de un usuario cambia como resultado de ganar o perder puntos, e incluye `previousLevel` y `newLevel`. -#### Analíticas a Nivel de Cuenta +#### Analíticas a Nivel de Cuenta {#account-level-analytics} -La [API de resumen de nivel](/api-reference/endpoints/points/get-points-level-summary) devuelve cuántos usuarios se encuentran actualmente en cada nivel, lo cual es útil para paneles de administración, vistas de embudo o equilibrar la progresión. +La [API de resumen de nivel](/es/api-reference/endpoints/points/get-points-level-summary) devuelve cuántos usuarios se encuentran actualmente en cada nivel, lo cual es útil para paneles de administración, vistas de embudo o equilibrar la progresión. -## Mostrar Puntos +## Mostrar Puntos {#displaying-points} Hay varias formas de usar Trophy para obtener y mostrar puntos en tu aplicación. Para ver ejemplos prácticos, consulta nuestras guías sobre cómo agregar una [función de - XP](/guides/how-to-build-an-xp-feature) o una [función de - energía](/guides/how-to-build-an-energy-feature) a tu aplicación web o móvil. + XP](/es/guides/how-to-build-an-xp-feature) o una [función de + energía](/es/guides/how-to-build-an-energy-feature) a tu aplicación web o móvil. @@ -397,15 +397,15 @@ Hay varias formas de usar Trophy para obtener y mostrar puntos en tu aplicación loop playsInline className="w-full aspect-video" - src="../assets/platform/points/displaying_points.mp4" + src="../../assets/platform/points/displaying_points.mp4" > -### Activación de UI transaccional +### Activación de UI transaccional {#triggering-transactional-ui} -En primer lugar, cualquier punto otorgado o deducido de los usuarios como resultado de un evento de cambio de métrica se devuelve en la respuesta al usar la [API de eventos de cambio de métrica](/api-reference/endpoints/metrics/send-a-metric-change-event). +En primer lugar, cualquier punto otorgado o deducido de los usuarios como resultado de un evento de cambio de métrica se devuelve en la respuesta al usar la [API de eventos de cambio de métrica](/es/api-reference/endpoints/metrics/send-a-metric-change-event). -La respuesta incluye el nuevo total de Puntos del usuario, cuántos Puntos se otorgaron o dedujeron como resultado del evento, y los detalles de los activadores de Puntos específicos que se dispararon. Cuando los [Niveles de Puntos](#points-levels) están habilitados, cada objeto de Puntos en esa respuesta también puede incluir **`level`**: Trophy incluye el **nuevo** nivel del usuario **solo cuando cambió** como resultado de ese evento. El mismo comportamiento opcional de `level` se aplica cuando los Puntos cambian por una [finalización de Logro](/api-reference/endpoints/achievements/mark-an-achievement-as-completed). +La respuesta incluye el nuevo total de Puntos del usuario, cuántos Puntos se otorgaron o dedujeron como resultado del evento, y los detalles de los activadores de Puntos específicos que se dispararon. Cuando los [Niveles de Puntos](#points-levels) están habilitados, cada objeto de Puntos en esa respuesta también puede incluir **`level`**: Trophy incluye el **nuevo** nivel del usuario **solo cuando cambió** como resultado de ese evento. El mismo comportamiento opcional de `level` se aplica cuando los Puntos cambian por una [finalización de Logro](/es/api-reference/endpoints/achievements/mark-an-achievement-as-completed). Ejemplo de respuesta `POST /metrics/{key}/event` (los campos coinciden con la especificación de la API de Trophy; tus claves bajo `points` e `leaderboards` dependen de tu cuenta): @@ -416,11 +416,11 @@ Esto hace que sea muy sencillo leer la respuesta y activar cualquiera de las sig - Mostrar notificaciones y ventanas emergentes dentro de la aplicación - Reproducir efectos de sonido -### Mostrar los Puntos del usuario +### Mostrar los Puntos del usuario {#displaying-users-points} Trophy también cuenta con APIs que te permiten obtener los datos de Puntos del usuario cuando lo desees. -Primero, la [API de Puntos de usuario](/api-reference/endpoints/users/get-a-users-points) devuelve el total de Puntos del usuario para un sistema de Puntos particular, su **`level`** actual (o `null` si los niveles no están en uso o aún no han alcanzado un nivel), y hasta 100 de los eventos más recientes que otorgaron Puntos o los dedujeron de ellos. +Primero, la [API de Puntos de usuario](/es/api-reference/endpoints/users/get-a-users-points) devuelve el total de Puntos del usuario para un sistema de Puntos particular, su **`level`** actual (o `null` si los niveles no están en uso o aún no han alcanzado un nivel), y hasta 100 de los eventos más recientes que otorgaron Puntos o los dedujeron de ellos. Puedes usar esta API para mostrar el total de Puntos del usuario en cualquier lugar de tu plataforma, así como una sección de 'Últimas recompensas' o similar. @@ -429,10 +429,10 @@ Puedes usar esta API para mostrar el total de Puntos del usuario en cualquier lu - + -Luego, la [API de resumen de puntos de usuario](/api-reference/endpoints/users/get-a-users-points-summary) puede utilizarse para obtener datos históricos de puntos de un usuario específico. +Luego, la [API de resumen de puntos de usuario](/es/api-reference/endpoints/users/get-a-users-points-summary) puede utilizarse para obtener datos históricos de puntos de un usuario específico. Los datos pueden agregarse diariamente, semanalmente o mensualmente entre una fecha de inicio y una fecha de fin. Utiliza esta API para mostrar gráficos de progreso de puntos a los usuarios en cualquier período de tiempo. @@ -444,46 +444,46 @@ Respuesta **200** de ejemplo (especificación de la API de Trophy; la estructura -### Mostrar Datos Agregados +### Mostrar Datos Agregados {#displaying-aggregate-data} Además, hay varias APIs que pueden utilizarse para obtener y mostrar datos de puntos a nivel de cuenta. -Primero, la [API de resumen de puntos](/api-reference/endpoints/points/get-points-summary) devuelve datos agregados del sistema de puntos de toda tu base de usuarios. +Primero, la [API de resumen de puntos](/es/api-reference/endpoints/points/get-points-summary) devuelve datos agregados del sistema de puntos de toda tu base de usuarios. -Utiliza estos datos para mostrar un histograma de puntos de un sistema de puntos particular y mostrar a los usuarios cómo se comparan con otros en la plataforma. Cuando utilizas [Niveles de Puntos](#points-levels), la [API de resumen de niveles](/api-reference/endpoints/points/get-points-level-summary) complementa esto con un recuento de usuarios por nivel. +Utiliza estos datos para mostrar un histograma de puntos de un sistema de puntos particular y mostrar a los usuarios cómo se comparan con otros en la plataforma. Cuando utilizas [Niveles de Puntos](#points-levels), la [API de resumen de niveles](/es/api-reference/endpoints/points/get-points-level-summary) complementa esto con un recuento de usuarios por nivel. `GET /points/{key}/summary` — respuesta **200** de ejemplo (especificación de la API de Trophy): -Por último, la [API del sistema de puntos](/api-reference/endpoints/points/get-points) devuelve los metadatos y desencadenadores del sistema. Respuesta **200** de ejemplo (especificación de la API de Trophy): +Por último, la [API del sistema de puntos](/es/api-reference/endpoints/points/get-points) devuelve los metadatos y desencadenadores del sistema. Respuesta **200** de ejemplo (especificación de la API de Trophy): La API de resumen de puntos también puede filtrarse para devolver únicamente datos de usuarios con - [atributos de usuario personalizados](/platform/users#custom-user-attributes) específicos. + [atributos de usuario personalizados](/es/platform/users#custom-user-attributes) específicos. - + -Utiliza la respuesta de la [API del sistema de puntos](/api-reference/endpoints/points/get-points) para mostrar a los usuarios cómo pueden ganar puntos en tu plataforma. Cualquier nuevo desencadenador que agregues se devolverá automáticamente desde esta API, reduciendo cambios de código en tu plataforma y trasladando las operaciones a Trophy. +Utiliza la respuesta de la [API del sistema de puntos](/es/api-reference/endpoints/points/get-points) para mostrar a los usuarios cómo pueden ganar puntos en tu plataforma. Cualquier nuevo desencadenador que agregues se devolverá automáticamente desde esta API, reduciendo cambios de código en tu plataforma y trasladando las operaciones a Trophy. -## Analíticas de Puntos +## Analíticas de Puntos {#points-analytics} Trophy tiene analíticas integradas para rastrear las asignaciones de puntos para cada sistema de puntos que configures en tus usuarios en tiempo real, incluyendo: @@ -496,10 +496,10 @@ Trophy tiene analíticas integradas para rastrear las asignaciones de puntos par -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/platform/push-notifications.mdx b/es/platform/push-notifications.mdx index 5e8d7a6..5898dc4 100644 --- a/es/platform/push-notifications.mdx +++ b/es/platform/push-notifications.mdx @@ -8,12 +8,12 @@ og:description: Usa flujos automatizados de notificaciones push para extender tu icon: bell --- -import IdentifyUserWithDeviceTokensRequestBlock from "/snippets/identify-user-with-device-tokens-request-block.mdx"; -import MetricEventWithDeviceTokensRequestBlock from "/snippets/metric-event-with-device-tokens-request-block.mdx"; +import IdentifyUserWithDeviceTokensRequestBlock from "../../snippets/identify-user-with-device-tokens-request-block.mdx"; +import MetricEventWithDeviceTokensRequestBlock from "../../snippets/metric-event-with-device-tokens-request-block.mdx"; Trophy puede enviar notificaciones push automatizadas a los usuarios basándose en eventos clave sin requerir ningún código. Aquí veremos cuáles son estos eventos y cómo pueden formar parte de la experiencia de gamificación de tu producto. -## Tipos de notificaciones push +## Tipos de notificaciones push {#types-of-push-notifications} Trophy admite 4 tipos de notificaciones push, cada una diseñada para adaptarse a un escenario común en la construcción de experiencias de gamificación. @@ -26,20 +26,20 @@ Todas las notificaciones push son opcionales, pero las cuatro pueden usarse simu loop playsInline className="w-full aspect-15/4" - src="../assets/platform/push/push_notification_types.mp4" + src="../../assets/platform/push/push_notification_types.mp4" > -- **Notificaciones de logros** se envían a los usuarios cada vez que desbloquean un [Logro](/platform/achievements). +- **Notificaciones de logros** se envían a los usuarios cada vez que desbloquean un [Logro](/es/platform/achievements). - **Notificaciones de resumen** se envían a los usuarios con una frecuencia predefinida para resumir el progreso. Las notificaciones de resumen pueden configurarse para enviarse diaria, semanal, mensual o anualmente según tu caso de uso. - **Notificaciones de reactivación** se envían a los usuarios después de que se vuelven inactivos con el objetivo de traerlos de vuelta a tu aplicación. -- **Notificaciones de racha** se envían automáticamente a los usuarios recordándoles extender su [Racha](/platform/streaks). +- **Notificaciones de racha** se envían automáticamente a los usuarios recordándoles extender su [Racha](/es/platform/streaks). -## Canales compatibles +## Canales compatibles {#supported-channels} Trophy admite el envío de notificaciones push usando 3 canales que pueden configurarse en la página de [Canales](https://app.trophy.so/push/channels) del panel de Trophy. -### Servicio de notificaciones push de Apple (APNs) +### Servicio de notificaciones push de Apple (APNs) {#apple-push-notification-service-apns} Trophy admite el envío de notificaciones push a los usuarios a través de APNs mediante una conexión basada en certificados. @@ -56,7 +56,7 @@ Para enviar notificaciones push usando APNs, deberás proporcionar a Trophy las oficial](https://developer.apple.com/documentation/usernotifications/establishing-a-certificate-based-connection-to-apns). -### Firebase Cloud Messaging (FCM) +### Firebase Cloud Messaging (FCM) {#firebase-cloud-messaging-fcm} Trophy admite el envío de notificaciones push a los usuarios a través de FCM. Para lograrlo, deberás proporcionar a Trophy tu JSON de cuenta de servicio de Firebase en el siguiente formato: @@ -85,7 +85,7 @@ Trophy admite el envío de notificaciones push a los usuarios a través de FCM. servicio](https://firebase.google.com/support/guides/service-accounts). -### Expo Push Service +### Expo Push Service {#expo-push-service} Trophy admite el envío de notificaciones push a los usuarios a través de Expo Push Service. @@ -102,13 +102,13 @@ Para lograrlo, debes proporcionar a Trophy algunos detalles clave de tu cuenta d oficial](https://docs.expo.dev/push-notifications/sending-notifications). -## Envío de notificaciones push +## Envío de notificaciones push {#sending-push-notifications} Sigue los pasos a continuación para comenzar a enviar notificaciones push usando Trophy. Los usuarios pueden controlar qué notificaciones push reciben a través de la [API de - preferencias de usuario](/platform/users#notification-preferences) de Trophy. Esto te permite + preferencias de usuario](/es/platform/users#notification-preferences) de Trophy. Esto te permite construir un centro de preferencias en tu aplicación donde los usuarios pueden activar o desactivar tipos específicos de notificaciones. @@ -126,7 +126,7 @@ Sigue los pasos a continuación para comenzar a enviar notificaciones push usand loop playsInline className="w-full aspect-15/4" - src="../assets/platform/push/push_channels.mp4" + src="../../assets/platform/push/push_channels.mp4" > @@ -145,7 +145,7 @@ Sigue los pasos a continuación para comenzar a enviar notificaciones push usand height="200" width="100%" noZoom - src="../assets/platform/push/create_push_template.png" + src="../../assets/platform/push/create_push_template.png" /> @@ -176,7 +176,7 @@ Sigue los pasos a continuación para comenzar a enviar notificaciones push usand En la [página de configuración](https://app.trophy.so/push/configure), activa una plantilla en cada sección de tipo que quieras habilitar. Una vez activada, Trophy comenzará a enviar notificaciones push automáticamente. - Los usuarios pueden controlar qué tipos de notificaciones push reciben a través de la [API de preferencias de usuario](/platform/users#notification-preferences) de Trophy. Cuando un usuario ha deshabilitado un tipo de notificación en sus preferencias, Trophy respetará esa configuración y no enviará esas notificaciones a ese usuario. + Los usuarios pueden controlar qué tipos de notificaciones push reciben a través de la [API de preferencias de usuario](/es/platform/users#notification-preferences) de Trophy. Cuando un usuario ha deshabilitado un tipo de notificación en sus preferencias, Trophy respetará esa configuración y no enviará esas notificaciones a ese usuario. @@ -187,14 +187,14 @@ Sigue los pasos a continuación para comenzar a enviar notificaciones push usand loop playsInline className="w-full aspect-15/4" - src="../assets/platform/push/activating_templates.mp4" + src="../../assets/platform/push/activating_templates.mp4" > -## Diseñar Notificaciones Push +## Diseñar Notificaciones Push {#designing-push-notifications} Por defecto, Trophy proporciona una plantilla para cada [tipo de notificación push](#types-of-push-notifications) como un buen punto de partida. Las plantillas predeterminadas no se pueden modificar, pero puedes duplicarlas y personalizarlas como desees. @@ -202,7 +202,7 @@ Por defecto, Trophy proporciona una plantilla para cada [tipo de notificación p También puedes crear plantillas en blanco si simplemente quieres empezar desde cero. -### Estructura de la Plantilla +### Estructura de la Plantilla {#template-structure} Cada notificación push tiene un título y un cuerpo, y el editor de plantillas de notificaciones push sin código de Trophy te permite personalizar completamente ambos campos. @@ -211,11 +211,11 @@ Cada notificación push tiene un título y un cuerpo, y el editor de plantillas height="200" width="50%" noZoom - src="../assets/platform/push/push_template_editor.png" + src="../../assets/platform/push/push_template_editor.png" /> -### Uso de Variables +### Uso de Variables {#using-variables} Trophy proporciona un amplio conjunto de variables que se pueden utilizar para insertar datos altamente relevantes y personalizados en tus notificaciones push. @@ -230,11 +230,11 @@ Esto abrirá la ventana del editor de variables donde puedes configurar variable loop playsInline className="w-full aspect-15/4" - src="../assets/platform/push/push_variables.mp4" + src="../../assets/platform/push/push_variables.mp4" > -### Usar Variaciones +### Usar Variaciones {#using-variations} Las variaciones se pueden usar para agregar aleatoriedad al título y cuerpo de las notificaciones push enviadas por Trophy. Esto evita que las notificaciones se vuelvan aburridas y ayuda a mejorar las tasas de interacción. @@ -247,11 +247,11 @@ En el momento del envío, Trophy selecciona automáticamente una de tus variacio loop playsInline className="w-full aspect-15/4" - src="../assets/platform/push/push_variations.mp4" + src="../../assets/platform/push/push_variations.mp4" > -### Usar Condiciones +### Usar Condiciones {#using-conditions} El constructor de plantillas de notificaciones push de Trophy permite usar condiciones para enviar contenido diferente a cada destinatario según su contexto único. @@ -266,10 +266,10 @@ En el momento del envío, Trophy procesa todas las condiciones usando el context loop playsInline className="w-full aspect-15/4" - src="../assets/platform/push/push_conditions.mp4" + src="../../assets/platform/push/push_conditions.mp4" > -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudar! diff --git a/es/platform/streaks.mdx b/es/platform/streaks.mdx index 34293d6..45e786f 100644 --- a/es/platform/streaks.mdx +++ b/es/platform/streaks.mdx @@ -6,25 +6,25 @@ og:description: Usa rachas diarias, semanales o mensuales para crear patrones de icon: flame --- -import MetricChangeResponseBlock from "/snippets/metric-change-response-block.mdx"; +import MetricChangeResponseBlock from "../../snippets/metric-change-response-block.mdx"; -## ¿Qué son las Rachas? +## ¿Qué son las Rachas? {#what-are-streaks} Una racha es un período de días, semanas o meses consecutivos en los que un usuario ha realizado una acción clave en tu plataforma. Se ha demostrado que las rachas aumentan significativamente la retención, particularmente cuando la acción del usuario que se rastrea se alinea con el valor principal de tu producto. -## Frecuencia de Racha +## Frecuencia de Racha {#streak-frequency} Las rachas en Trophy pueden ser **diarias, semanales o mensuales**. Esto significa que un usuario debe cumplir sus [condiciones de racha](#streak-conditions) al menos una vez cada día, semana o mes calendario para mantener su racha. -Si has [configurado zonas horarias](/platform/users#param-tz) para tus usuarios, Trophy rastreará automáticamente la racha de cada usuario en su zona horaria local (incluyendo cuando los usuarios cambien de zona horaria) y mantendrá las rachas intactas. +Si has [configurado zonas horarias](/es/platform/users#param-tz) para tus usuarios, Trophy rastreará automáticamente la racha de cada usuario en su zona horaria local (incluyendo cuando los usuarios cambien de zona horaria) y mantendrá las rachas intactas. Trophy calcula automáticamente los datos de racha para cada frecuencia de racha, lo que significa que puedes cambiar en cualquier momento. -## Condiciones de Racha +## Condiciones de Racha {#streak-conditions} -En Trophy puedes establecer los umbrales que un usuario debe cumplir para extender su racha basándote en tus [Métricas](/platform/metrics) configuradas. +En Trophy puedes establecer los umbrales que un usuario debe cumplir para extender su racha basándote en tus [Métricas](/es/platform/metrics) configuradas. Puedes elegir qué métricas deben formar parte de tu racha, y para aquellas que elijas, puedes establecer un umbral personalizado que los usuarios deben cumplir. @@ -33,7 +33,7 @@ Puedes elegir qué métricas deben formar parte de tu racha, y para aquellas que height="200" width="100%" noZoom - src="../assets/platform/streaks/streak_config.png" + src="../../assets/platform/streaks/streak_config.png" /> @@ -43,7 +43,7 @@ Al combinar métricas de esta manera, puedes elegir entre dos modos de evaluaci De esta manera puedes diseñar la lógica de tus rachas para que se ajuste al caso de uso de tu aplicación. -## Congelaciones de Rachas +## Congelaciones de Rachas {#streak-freezes} Las congelaciones de rachas ayudan a los usuarios a mantener sus rachas por más tiempo al permitirles perder períodos sin que se reinicie a cero. Esto ayuda a mantener las rachas motivadoras incluso si los usuarios no mantienen un hábito de uso perfecto. @@ -52,13 +52,13 @@ Las congelaciones de rachas ayudan a los usuarios a mantener sus rachas por más height="200" width="100%" noZoom - src="../assets/platform/streaks/freezes_config.png" + src="../../assets/platform/streaks/freezes_config.png" /> Las congelaciones de rachas son opcionales en Trophy pero se pueden configurar en la [página de rachas](https://app.trophy.so/streaks) del panel de Trophy. -### Concesión de Congelaciones Iniciales +### Concesión de Congelaciones Iniciales {#granting-initial-freezes} Puedes configurar cualquier número arbitrario de congelaciones para otorgar a los nuevos usuarios cuando los identificas por primera vez con Trophy. @@ -66,36 +66,36 @@ Puedes configurar cualquier número arbitrario de congelaciones para otorgar a l Otorgar demasiadas congelaciones a los usuarios puede disminuir su valor percibido, pero conceder muy pocas congelaciones podría resultar en un mayor número de rachas perdidas. Un buen punto de partida es una congelación por usuario, ¡pero el secreto está en experimentar! -### Acumulación de Congelaciones +### Acumulación de Congelaciones {#freeze-accumulation} A medida que los usuarios agotan las congelaciones de rachas, necesitarán un suministro continuo de nuevas para mantenerlas. Para facilitar esto, Trophy puede otorgar automáticamente congelaciones de rachas a los usuarios con el tiempo. Puedes elegir un número arbitrario de días durante los cuales otorgar un número arbitrario de congelaciones a cada usuario. -Si has [configurado zonas horarias](/platform/users#param-tz) para tus usuarios, Trophy consumirá automáticamente congelaciones a medianoche en la zona horaria del usuario cuando sea necesario para extender su racha, y si hay nuevas congelaciones programadas para otorgarse a un usuario, se concederán hasta diez minutos después. +Si has [configurado zonas horarias](/es/platform/users#param-tz) para tus usuarios, Trophy consumirá automáticamente congelaciones a medianoche en la zona horaria del usuario cuando sea necesario para extender su racha, y si hay nuevas congelaciones programadas para otorgarse a un usuario, se concederán hasta diez minutos después. -### Número Máximo de Congelaciones +### Número Máximo de Congelaciones {#maximum-freeze-count} En Trophy también puedes configurar el número máximo de congelaciones que cada usuario puede tener, hasta un límite de 100. La [acumulación de congelaciones](#freeze-accumulation) solo otorgará congelaciones hasta este límite. -## Seguimiento de Rachas +## Seguimiento de Rachas {#tracking-streaks} -Trophy calcula automáticamente las rachas para todos los usuarios basándose en los [eventos de métrica](/platform/events#tracking-metric-events) que reportas a Trophy. +Trophy calcula automáticamente las rachas para todos los usuarios basándose en los [eventos de métrica](/es/platform/events#tracking-metric-events) que reportas a Trophy. No se requiere trabajo adicional para rastrear rachas, y puedes comenzar a usarlas de inmediato. Solo asegúrate de que las rachas estén habilitadas en el panel de Trophy. -Para un tutorial completo sobre cómo configurar una función de rachas usando Trophy, consulta nuestra [guía oficial](/guides/how-to-build-a-streaks-feature). +Para un tutorial completo sobre cómo configurar una función de rachas usando Trophy, consulta nuestra [guía oficial](/es/guides/how-to-build-a-streaks-feature). -## Gestión de Rachas +## Gestión de Rachas {#managing-streaks} Esta sección describe algunas de las operaciones que puedes realizar para gestionar las rachas de los usuarios en tu aplicación. -### Restaurar la Racha de un Usuario +### Restaurar la Racha de un Usuario {#restoring-a-users-streak} Para restaurar la racha de un usuario, dirígete a la página de detalles del usuario y utiliza la acción 'Restaurar Racha'. Restaurar la racha de un usuario la establece a la longitud que tenía cuando la perdió por última vez. -También puedes usar la [API de administración para restaurar rachas](/admin-api/endpoints/streaks/restore-streaks) para restaurar rachas de forma programática. +También puedes usar la [API de administración para restaurar rachas](/es/admin-api/endpoints/streaks/restore-streaks) para restaurar rachas de forma programática. @@ -105,22 +105,22 @@ También puedes usar la [API de administración para restaurar rachas](/admin-ap loop playsInline className="w-full aspect-15/4" - src="../assets/platform/streaks/restoring_streaks.mp4" + src="../../assets/platform/streaks/restoring_streaks.mp4" > -## Visualización de Rachas +## Visualización de Rachas {#displaying-streaks} Trophy expone los datos de rachas de dos maneras, que pueden utilizarse para construir elementos de interfaz dentro de tus aplicaciones y mostrar rachas a los usuarios. - Consulta nuestra [guía completa](/guides/how-to-build-a-streaks-feature) sobre cómo agregar una + Consulta nuestra [guía completa](/es/guides/how-to-build-a-streaks-feature) sobre cómo agregar una función de rachas a tu aplicación para más detalles. -### Respuesta del Evento de Métrica +### Respuesta del Evento de Métrica {#metric-event-response} -Cuando [incrementas una métrica](/platform/events#tracking-metric-events) para un usuario, la respuesta de la [API de métricas](/api-reference/endpoints/metrics/send-a-metric-change-event) incluirá la racha +Cuando [incrementas una métrica](/es/platform/events#tracking-metric-events) para un usuario, la respuesta de la [API de métricas](/es/api-reference/endpoints/metrics/send-a-metric-change-event) incluirá la racha actual del usuario. @@ -130,9 +130,9 @@ Esto puede utilizarse para activar transaccionalmente elementos de UI/UX incluye - Mostrar ventanas emergentes en la aplicación - Reproducir efectos de sonido -### API de Rachas de Usuario +### API de Rachas de Usuario {#user-streaks-api} -La [API de rachas de usuario](/api-reference/endpoints/users/get-a-users-streak) devuelve la racha actual de un solo usuario, junto con su historial reciente de rachas. Usa el parámetro de consulta [`historyPeriods`](/api-reference/endpoints/users/get-a-users-streak#parameter-history-periods) para controlar cuántos períodos devolver. +La [API de rachas de usuario](/es/api-reference/endpoints/users/get-a-users-streak) devuelve la racha actual de un solo usuario, junto con su historial reciente de rachas. Usa el parámetro de consulta [`historyPeriods`](/es/api-reference/endpoints/users/get-a-users-streak#parameter-history-periods) para controlar cuántos períodos devolver. {/* vale off */} @@ -193,13 +193,13 @@ Usa estos datos para mostrar el historial de rachas de un usuario dentro de tu a -### Listar Rachas de Múltiples Usuarios +### Listar Rachas de Múltiples Usuarios {#list-multiple-users-streaks} -Si deseas mostrar rachas para múltiples usuarios a la vez, por ejemplo para soportar una función de racha de amigos o grupo de usuarios, utiliza la [API de listar rachas de usuarios](/api-reference/endpoints/streaks/get-streaks). +Si deseas mostrar rachas para múltiples usuarios a la vez, por ejemplo para soportar una función de racha de amigos o grupo de usuarios, utiliza la [API de listar rachas de usuarios](/es/api-reference/endpoints/streaks/get-streaks). ```json [expandable] [ @@ -221,6 +221,6 @@ Si deseas mostrar rachas para múltiples usuarios a la vez, por ejemplo para sop ] ``` -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/platform/users.mdx b/es/platform/users.mdx index c19a9de..9bb7a36 100644 --- a/es/platform/users.mdx +++ b/es/platform/users.mdx @@ -6,22 +6,22 @@ og:description: Los usuarios son las personas que utilizan tu plataforma. icon: users --- -import MetricChangeRequestBlock from "/snippets/metric-change-request-block.mdx"; -import IdentifyUserRequestBlock from "/snippets/identify-user-request-block.mdx"; -import UpdateUserNotificationPreferencesBlock from "/snippets/update-user-notification-preferences-block.mdx"; -import GetUserNotificationPreferencesBlock from "/snippets/get-user-notification-preferences-block.mdx"; +import MetricChangeRequestBlock from "../../snippets/metric-change-request-block.mdx"; +import IdentifyUserRequestBlock from "../../snippets/identify-user-request-block.mdx"; +import UpdateUserNotificationPreferencesBlock from "../../snippets/update-user-notification-preferences-block.mdx"; +import GetUserNotificationPreferencesBlock from "../../snippets/get-user-notification-preferences-block.mdx"; -## ¿Qué son los Usuarios? +## ¿Qué son los Usuarios? {#what-are-users} Los usuarios son las personas individuales que utilizan tu producto. Informas a Trophy sobre tus usuarios a través de APIs y utilizas el panel de control para diseñar experiencias de gamificación en torno a ellos. Los usuarios deben ser individuos, no pueden ser empresas u organizaciones. Para crear estructuras organizacionales o agrupaciones de usuarios, considera usar un [atributo de usuario personalizado](#custom-user-attributes). -## Atributos Clave +## Atributos Clave {#key-attributes} Los atributos clave son propiedades de los usuarios controladas y gestionadas por Trophy y sirven para aspectos como `id` o `email`, algunos son obligatorios mientras que otros son opcionales. -### Atributos Obligatorios +### Atributos Obligatorios {#required-attributes} Trophy solo requiere un atributo clave, `id`. Cada usuario sobre el que informes a Trophy debe tener un `id`, esto es lo que los identifica como una persona única. @@ -34,7 +34,7 @@ Trophy solo requiere un atributo clave, `id`. Cada usuario sobre el que informes en tu base de datos en lugar de necesitar gestionar otro solo para Trophy. -### Atributos Opcionales +### Atributos Opcionales {#optional-attributes} Además, puedes informar a Trophy sobre cualquiera de los siguientes atributos clave opcionales y los pondrá a tu disposición como parte de tu experiencia de gamificación: @@ -45,7 +45,7 @@ Además, puedes informar a Trophy sobre cualquiera de los siguientes atributos c La dirección de correo electrónico del usuario. Esta dirección se utilizará en cualquier - [correo electrónico](/platform/emails) que configures como parte de tu experiencia + [correo electrónico](/es/platform/emails) que configures como parte de tu experiencia de gamificación con Trophy. @@ -54,7 +54,6 @@ Además, puedes informar a Trophy sobre cualquiera de los siguientes atributos c IANA](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). Se utiliza para rachas, clasificaciones del ranking y para enviar correos electrónicos a los usuarios de acuerdo con su hora local. - En JavaScript, puedes obtener la zona horaria del usuario usando este fragmento: @@ -66,28 +65,28 @@ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; - Si has configurado algún [correo electrónico](/platform/emails) de Trophy, solo se + Si has configurado algún [correo electrónico](/es/platform/emails) de Trophy, solo se enviarán a un usuario cuando este campo sea verdadero. Nota: Si no proporcionas un `email`, intentar establecer este campo como verdadero resultará en un error. La lista de tokens de dispositivo para asociar con el usuario. Si has configurado alguna - [notificación push](/platform/push-notifications) de Trophy, solo se + [notificación push](/es/platform/push-notifications) de Trophy, solo se enviarán a un usuario cuando se proporcione este campo. -## Atributos personalizados de usuario +## Atributos personalizados de usuario {#custom-user-attributes} - Esta función está disponible en el plan [Pro](/account/billing#pro-plan) + Esta función está disponible en el plan [Pro](/es/account/billing#pro-plan) @@ -97,7 +96,7 @@ Por ejemplo, una aplicación de aprendizaje de idiomas podría usar un atributo Los atributos personalizados de usuario te permiten informar a Trophy sobre esta información contextual y utilizarla para personalizar las funciones de gamificación. -### Creación de atributos +### Creación de atributos {#creating-attributes} Para crear un nuevo atributo personalizado de usuario, dirígete a la [pestaña de atributos](https://app.trophy.so/users/attributes) de la página de usuarios en el panel de Trophy y pulsa el botón _Agregar atributo de usuario_. @@ -110,13 +109,13 @@ Asigna un nombre al atributo y una clave única. La clave es lo que usarás para loop playsInline className="w-full aspect-15/4" - src="../assets/platform/users/create_custom_user_attribute.mp4" + src="../../assets/platform/users/create_custom_user_attribute.mp4" > -### Configurar Atributos +### Configurar Atributos {#setting-attributes} -Los atributos pueden asignarse valores para usuarios específicos usando su clave única, ya sea en línea, cuando los usuarios incrementan métricas a través de la [API de incremento de métricas](/api-reference/endpoints/metrics/send-a-metric-change-event), o explícitamente mediante las APIs de [identificación de usuario](/api-reference/endpoints/users/identify-a-user), [crear usuario](/api-reference/endpoints/users/create-a-user) o [actualizar usuario](api-reference/endpoints/users/update-a-user). +Los atributos pueden asignarse valores para usuarios específicos usando su clave única, ya sea en línea, cuando los usuarios incrementan métricas a través de la [API de incremento de métricas](/es/api-reference/endpoints/metrics/send-a-metric-change-event), o explícitamente mediante las APIs de [identificación de usuario](/es/api-reference/endpoints/users/identify-a-user), [crear usuario](/es/api-reference/endpoints/users/create-a-user) o [actualizar usuario](api-reference/endpoints/users/update-a-user). Trophy solo establecerá valores de atributos que hayan sido creados primero @@ -150,11 +149,11 @@ En todas las APIs, el esquema para establecer valores de atributos es consistent } ``` -### Usar Atributos +### Usar Atributos {#using-attributes} Usa atributos de usuario personalizados en Trophy para personalizar funciones de gamificación, agregar y filtrar datos devueltos por las APIs, y para segmentar y comparar cohortes de usuarios en análisis. -#### Personalización de Funciones +#### Personalización de Funciones {#feature-personalization} Los atributos de usuario personalizados se pueden usar para personalizar logros, personalizar la forma en que diferentes usuarios ganan puntos y más. Sigue los enlaces a las páginas relevantes a continuación para obtener más información. @@ -162,34 +161,34 @@ Los atributos de usuario personalizados se pueden usar para personalizar logros, Configura logros que solo puedan ser desbloqueados por usuarios específicos. Personaliza cómo diferentes usuarios ganan puntos. Controla qué usuarios reciben correos gamificados. Personaliza el texto y asuntos de correos con atributos de usuario personalizados. -#### Agregación y Filtrado de Datos +#### Agregación y Filtrado de Datos {#data-aggregation-and-filtering} Puedes usar atributos de usuario personalizados para agregar y filtrar los datos devueltos por algunas APIs, lo que permite soportar una amplia gama de casos de uso de gamificación. En todos los casos, los atributos se incluyen en el parámetro de consulta `userAttributes` usando un esquema consistente: @@ -197,9 +196,9 @@ Puedes usar atributos de usuario personalizados para agregar y filtrar los datos Aquí está la lista de todas las APIs que soportan el parámetro de consulta `userAttributes`: -- [Resumen de Puntos](/api-reference/endpoints/points/get-points-summary) +- [Resumen de Puntos](/es/api-reference/endpoints/points/get-points-summary) -#### Análisis Segmentado +#### Análisis Segmentado {#segmented-analytics} Los atributos de usuario personalizados pueden usarse para segmentar y comparar gráficos de retención y engagement entre grupos de usuarios. @@ -212,11 +211,11 @@ Esto es útil para entender qué cohortes están aprovechando al máximo tus fun loop playsInline className="w-full aspect-15/4" - src="../assets/platform/users/segmenting_analytics.mp4" + src="../../assets/platform/users/segmenting_analytics.mp4" > -## Identificación de Usuarios +## Identificación de Usuarios {#identifying-users} Cuando le informas a Trophy sobre un usuario en tu plataforma, llamamos a esto **identificación**. Hay dos formas en las que puedes identificar usuarios con Trophy: identificación [en línea](#inline-identification) e identificación [explícita](#explicit-identification). @@ -225,11 +224,11 @@ Cuando le informas a Trophy sobre un usuario en tu plataforma, llamamos a esto * Trophy. Si decides que necesitas más control, prueba la identificación explícita. -### Identificación en Línea +### Identificación en Línea {#inline-identification} La identificación en línea es la forma más sencilla de informar a Trophy sobre tus usuarios, ya que no requiere ningún código específico de identificación de usuarios. Simplemente le informas a Trophy sobre los usuarios mientras realizan el uso normal de tu plataforma. -En la práctica, esto significa que cada vez que usas la [API de Eventos de Métricas](/api-reference/endpoints/metrics/send-a-metric-change-event), pasas los detalles completos del usuario que desencadenó el evento. +En la práctica, esto significa que cada vez que usas la [API de Eventos de Métricas](/es/api-reference/endpoints/metrics/send-a-metric-change-event), pasas los detalles completos del usuario que desencadenó el evento. Aquí hay un ejemplo donde se realiza una llamada a la API de eventos de métricas, y los detalles del usuario que realizó la interacción se pasan en el cuerpo de la solicitud: @@ -270,7 +269,7 @@ De esta manera, la identificación en línea te permite mantener toda tu base de Por eso recomendamos comenzar primero con la identificación en línea y luego explorar la identificación explícita si descubres que necesitas más control. -### Identificación Explícita +### Identificación Explícita {#explicit-identification} La identificación explícita es cuando escribes código en tu aplicación que le indica explícitamente a Trophy sobre los usuarios de manera separada de cualquier interacción con métricas. Esto es útil si deseas tener control completo sobre cómo y cuándo Trophy conoce a tus usuarios. @@ -280,7 +279,7 @@ Escenarios en los que podrías querer usar la identificación explícita pueden - Rastreas muchas métricas en Trophy y no quieres repetir el código de identificación en línea - Quieres informar a Trophy solo sobre una cohorte específica de usuarios, de la cual controlas las condiciones -En este caso, puedes informar a Trophy sobre nuevos usuarios usando la [API de Identificación de Usuarios](/api-reference/endpoints/users/identify-a-user). +En este caso, puedes informar a Trophy sobre nuevos usuarios usando la [API de Identificación de Usuarios](/es/api-reference/endpoints/users/identify-a-user). @@ -290,13 +289,13 @@ En este caso, puedes informar a Trophy sobre nuevos usuarios usando la [API de I con Trophy automáticamente. -## Crear Usuarios +## Crear Usuarios {#creating-users} -Para crear explícitamente un nuevo usuario en Trophy, usa la [API de Crear Usuario](/api-reference/endpoints/users/create-a-user). +Para crear explícitamente un nuevo usuario en Trophy, usa la [API de Crear Usuario](/es/api-reference/endpoints/users/create-a-user). -## Mantener los Usuarios Actualizados +## Mantener los Usuarios Actualizados {#keeping-users-up-to-date} -Para informar a Trophy sobre una actualización de un usuario en tu plataforma puedes usar la [API de Actualizar Usuario](/api-reference/endpoints/users/update-a-user). +Para informar a Trophy sobre una actualización de un usuario en tu plataforma puedes usar la [API de Actualizar Usuario](/es/api-reference/endpoints/users/update-a-user). Todas las propiedades que pases a Trophy se actualizarán a los nuevos valores que especifiques. @@ -307,23 +306,23 @@ Todas las propiedades que pases a Trophy se actualizarán a los nuevos valores q actualización de usuarios está disponible si realmente la necesitas. -## Configurar Preferencias de Usuario +## Configurar Preferencias de Usuario {#setting-user-preferences} Trophy tiene APIs para ayudarte a implementar un centro de preferencias que tus usuarios puedan usar para controlar y personalizar cómo interactúan con tus funciones de gamificación. Esta sección recorre todas las preferencias que puedes exponer a tus usuarios y su función. -### Preferencias de Notificaciones +### Preferencias de Notificaciones {#notification-preferences} -Puedes usar Trophy para enviar [Emails](/platform/emails) y [Notificaciones push](/platform/push-notifications) gamificados a tus usuarios. +Puedes usar Trophy para enviar [Emails](/es/platform/emails) y [Notificaciones push](/es/platform/push-notifications) gamificados a tus usuarios. -La [API de actualizar preferencias](/api-reference/endpoints/users/update-user-preferences) de Trophy te permite construir un centro de preferencias dentro de tu aplicación con el que tus usuarios puedan interactuar para controlar qué notificaciones reciben y a través de qué canales. +La [API de actualizar preferencias](/es/api-reference/endpoints/users/update-user-preferences) de Trophy te permite construir un centro de preferencias dentro de tu aplicación con el que tus usuarios puedan interactuar para controlar qué notificaciones reciben y a través de qué canales. @@ -340,7 +339,7 @@ Por ejemplo, el siguiente fragmento de código actualiza las preferencias de not -Para construir una interfaz de centro de preferencias en tu aplicación, primero necesitarás obtener las preferencias actuales del usuario usando el [API de obtener preferencias](/api-reference/endpoints/users/get-user-preferences). +Para construir una interfaz de centro de preferencias en tu aplicación, primero necesitarás obtener las preferencias actuales del usuario usando el [API de obtener preferencias](/es/api-reference/endpoints/users/get-user-preferences). Esto devolverá la configuración de notificaciones actual del usuario, que luego puedes mostrar en tu interfaz y permitir que los usuarios modifiquen. @@ -365,13 +364,13 @@ Puedes usar esta respuesta para: - **Mostrar el estado actual**: Mostrar qué notificaciones están habilitadas y a través de qué canales - **Manejar preferencias faltantes**: Si un tipo de notificación no está presente en la respuesta, significa que el usuario aún no ha configurado preferencias para él, y puedes establecer por defecto la configuración predeterminada de tu aplicación -Una vez que hayas obtenido y mostrado las preferencias, los usuarios pueden realizar cambios en tu interfaz, y puedes usar el [API de actualizar preferencias](/api-reference/endpoints/users/update-user-preferences) para guardar sus selecciones de vuelta en Trophy. +Una vez que hayas obtenido y mostrado las preferencias, los usuarios pueden realizar cambios en tu interfaz, y puedes usar el [API de actualizar preferencias](/es/api-reference/endpoints/users/update-user-preferences) para guardar sus selecciones de vuelta en Trophy. -## Recuperar Información del Usuario +## Recuperar Información del Usuario {#retrieving-user-information} -Para obtener los detalles de un usuario que ya has identificado con Trophy, usa el [API Get User](/api-reference/endpoints/users/get-a-single-user). +Para obtener los detalles de un usuario que ya has identificado con Trophy, usa el [API Get User](/es/api-reference/endpoints/users/get-a-single-user). -Esto devolverá los detalles completos del usuario junto con el atributo `control` que puedes usar para inscribir condicionalmente a los usuarios en cualquier funcionalidad de gamificación. Obtén más información sobre [Experimentación](/experimentation/overview). +Esto devolverá los detalles completos del usuario junto con el atributo `control` que puedes usar para inscribir condicionalmente a los usuarios en cualquier funcionalidad de gamificación. Obtén más información sobre [Experimentación](/es/experimentation/overview). ```json Response {3} { @@ -386,9 +385,9 @@ Esto devolverá los detalles completos del usuario junto con el atributo `contro } ``` -## Analíticas de Usuarios +## Analíticas de Usuarios {#user-analytics} -### Analíticas Básicas +### Analíticas Básicas {#basic-analytics} Por defecto, Trophy incluye analíticas de usuario de alto nivel en la [página de Usuarios](https://app.trophy.so/users) que incluyen: @@ -397,18 +396,18 @@ Por defecto, Trophy incluye analíticas de usuario de alto nivel en la [página - El número de usuarios que están activos mensualmente - + En esta página también puedes buscar entre todos los usuarios que Trophy ha registrado, lo cual puede ser útil para depuración. -### Usuarios Destacados +### Usuarios Destacados {#top-users} Además, en el [Dashboard](https://app.trophy.so), Trophy muestra una lista de _Usuarios Destacados_ con el conjunto de usuarios que han logrado el mayor progreso en las métricas de tu plataforma. Estos son tus usuarios más comprometidos, así que es útil saber quiénes son. -## Preguntas Frecuentes +## Preguntas Frecuentes {#frequently-asked-questions} @@ -422,7 +421,7 @@ Estos son tus usuarios más comprometidos, así que es útil saber quiénes son. @@ -432,6 +431,6 @@ Estos son tus usuarios más comprometidos, así que es útil saber quiénes son. -## Obtener soporte +## Obtener soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/snippets/add-env-var-block.mdx b/es/snippets/add-env-var-block.mdx deleted file mode 100644 index 32f67e7..0000000 --- a/es/snippets/add-env-var-block.mdx +++ /dev/null @@ -1,5 +0,0 @@ -{/* vale off */} - -```bash -TROPHY_API_KEY='*******' -``` diff --git a/es/snippets/all-achievements-request-block.mdx b/es/snippets/all-achievements-request-block.mdx deleted file mode 100644 index e241cf3..0000000 --- a/es/snippets/all-achievements-request-block.mdx +++ /dev/null @@ -1,37 +0,0 @@ - - -```bash cURL -curl --request GET \ - --url https://api.trophy.so/v1/achievements \ - --header 'X-API-KEY: ' -``` - -```typescript Node -trophy.achievements(); -``` - -```python Python -client.achievements() -``` - -```php PHP -$trophy->achievements(); -``` - -```java Java -UserAchievementsResponse response = client.achievements(); -``` - -```go Go -response, err := client.Achievements() -``` - -```csharp C# -await trophy.AchievementsAsync(); -``` - -```ruby Ruby -result = client.achievements() -``` - - diff --git a/es/snippets/all-achievements-response-block.mdx b/es/snippets/all-achievements-response-block.mdx deleted file mode 100644 index bb4b9db..0000000 --- a/es/snippets/all-achievements-response-block.mdx +++ /dev/null @@ -1,48 +0,0 @@ -```json Response [expandable] -[ - { - "id": "5100fe51-6bce-6j44-b0hs-bddc4e123682", - "trigger": "api", - "name": "Finish onboarding", - "description": "Complete the onboarding process.", - "badgeUrl": "https://example.com/badge.png", - "key": "finish-onboarding", - "completions": 8, - "rarity": 80 - }, - { - "id": "5100fe51-6bce-6j44-b0hs-bddc4e123683", - "trigger": "metric", - "name": "500 Flashcards Flipped", - "description": "View 500 flashcards in the app.", - "badgeUrl": "https://example.com/badge.png", - "metricId": "5100fe51-6bce-6j44-b0hs-bddc4e123683", - "metricName": "Flashcards Flipped", - "metricValue": 500, - "completions": 6, - "rarity": 60 - }, - { - "id": "5100fe51-6bce-6j44-b0hs-bddc4e123684", - "trigger": "streak", - "name": "10 days of exercise", - "description": "Exercise at least once a day for 10 days in a row.", - "badgeUrl": "https://example.com/badge.png", - "streakLength": 10, - "completions": 2, - "rarity": 20 - }, - { - "id": "5100fe51-6bce-6j44-b0hs-bddc4e123685", - "trigger": "achievement", - "name": "Study Master", - "description": "Complete all introductory milestones.", - "badgeUrl": "https://example.com/badge.png", - "achievementIds": [ - "5100fe51-6bce-6j44-b0hs-bddc4e123682", "5100fe51-6bce-6j44-b0hs-bddc4e123683", "5100fe51-6bce-6j44-b0hs-bddc4e123684" - ], - "completions": 1, - "rarity": 5 - } -] -``` diff --git a/es/snippets/control-flag-block.mdx b/es/snippets/control-flag-block.mdx deleted file mode 100644 index 8d61184..0000000 --- a/es/snippets/control-flag-block.mdx +++ /dev/null @@ -1,15 +0,0 @@ -```json The control attribute {6} -{ - "id": "user-id", - "email": "user@example.com", - "tz": "Europe/London", - "subscribedToEmails": true, - "control": true, - "created": "2021-01-01T00:00:00Z", - "updated": "2021-01-01T00:00:00Z", - "attributes": { - "department": "engineering", - "role": "developer" - } -} -``` diff --git a/es/snippets/event-attributes-request-block.mdx b/es/snippets/event-attributes-request-block.mdx deleted file mode 100644 index b4d67b8..0000000 --- a/es/snippets/event-attributes-request-block.mdx +++ /dev/null @@ -1,109 +0,0 @@ - - -```bash cURL -curl -X POST https://app.trophy.so/api/metrics/flashcards-flipped/event \ - -H "X-API-KEY: " \ - -H "Content-Type: application/json" \ - -d '{ - "user": { - "id": "18", - "email": "user@example.com", - "tz": "Europe/London" - }, - "value": 750, - "attributes": { - "device": "ios" - } -}' -``` - -```typescript Node -trophy.metrics.event("flashcards-flipped", { - user: { - id: "18", - email: "user@example.com", - tz: "Europe/London", - }, - value: 750, -}); -``` - -```python Python -client.metrics.event( - key="flashcards-flipped", - user=EventRequestUser( - id="18", - email="user@example.com", - tz="Europe/London", - ), - value=750.0, -) -``` - -```php PHP -$user = new EventRequestUser([ - 'id' => '18', - 'email' => 'user@example.com' -]); - -$request = new MetricsEventRequest([ - 'user' => $user, - 'value' => 750 -]); - -$trophy->metrics->event("flashcards-flipped", $request); -``` - -```java Java -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 -response, err := client.Metrics.Event( - "flashcards-flipped", - &api.MetricsEventRequest{ - User: &api.EventRequestUser{ - Id: "18", - Email: "user@example.com", - }, - Value: 750, - }, -) -``` - -```csharp C# -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 -result = client.metrics.event( - :key => 'flashcards-flipped', - :user => { - :id => '18', - :email => 'user@example.com' - }, - :value => 750 -) -``` - - diff --git a/es/snippets/get-user-notification-preferences-block.mdx b/es/snippets/get-user-notification-preferences-block.mdx deleted file mode 100644 index 3071cf6..0000000 --- a/es/snippets/get-user-notification-preferences-block.mdx +++ /dev/null @@ -1,37 +0,0 @@ - - -```bash cURL -curl --request GET \ - --url https://api.trophy.so/v1/users/user-123/preferences \ - --header 'X-API-KEY: ' -``` - -```typescript Node -const response = await trophy.users.getPreferences("user-123"); -``` - -```python Python -response = client.users.get_preferences(id="user-123") -``` - -```php PHP -$response = $trophy->users()->getPreferences("user-123"); -``` - -```java Java -UserPreferencesResponse response = client.users().getPreferences("user-123"); -``` - -```go Go -response, err := client.Users.GetPreferences("user-123") -``` - -```csharp C# -var response = await trophy.Users.GetPreferencesAsync("user-123"); -``` - -```ruby Ruby -response = client.users.get_preferences(id: "user-123") -``` - - diff --git a/es/snippets/idempotent-event-tracking.mdx b/es/snippets/idempotent-event-tracking.mdx deleted file mode 100644 index 27e69d3..0000000 --- a/es/snippets/idempotent-event-tracking.mdx +++ /dev/null @@ -1,116 +0,0 @@ - - -```bash cURL {3} -curl -X POST https://app.trophy.so/api/metrics/lessons-completed/event \ - -H "X-API-KEY: " \ - -H "Idempotency-Key: " \ - -H "Content-Type: application/json" \ - -d '{ - "user": { - "id": "18", - "email": "user@example.com", - "tz": "Europe/London" - }, - "value": 1 -}' -``` - -```typescript Node {9} -trophy.metrics.event("lessons-completed", - { - user: { - id: "18", - email: "user@example.com", - tz: "Europe/London", - }, - value: 1, - idempotencyKey: "lesson-123", - } -); -``` - -```python Python {9} -client.metrics.event( - key="lessons-completed", - user=EventRequestUser( - id="18", - email="user@example.com", - tz="Europe/London", - ), - value=1, - idempotencyKey="lesson-123" -) -``` - -```php PHP {9} -$user = new EventRequestUser([ - 'id' => '18', - 'email' => 'user@example.com' -]); - -$request = new MetricsEventRequest([ - 'user' => $user, - 'value' => 1, - 'idempotencyKey' => 'lesson-123' -]); - -$trophy->metrics->event("lessons-completed", $request); -``` - -```java Java {9} -MetricsEventRequest request = MetricsEventRequest.builder() - .user( - EventRequestUser.builder() - .id("18") - .email("user@example.com") - .build() - ) - .value(1) - .idempotencyKey("lesson-123") - .build(); - -EventResponse response = client.metrics().event("lessons-completed", request); -``` - -```go Go {10} -response, err := client.Metrics.Event( - "lessons-completed", - &api.MetricsEventRequest{ - User: &api.EventRequestUser{ - Id: "18", - Email: "user@example.com", - }, - Value: 1, - IdempotencyKey: "lesson-123" - }, -) -``` - -```csharp C# {9} -var user = new EventRequestUser { - Id = "18", - Email = "user@example.com" -}; - -var request = new MetricsEventRequest { - User = user, - Value = 1, - IdempotencyKey = "lesson-123" -}; - -await trophy.Metrics.EventAsync("lessons-completed", request); -``` - -```ruby Ruby {8} -result = client.metrics.event( - :key => 'lessons-completed', - :user => { - :id => '18', - :email => 'user@example.com' - }, - :value => 1, - :idempotencyKey => 'lesson-123' -) -``` - - diff --git a/es/snippets/identify-user-request-block.mdx b/es/snippets/identify-user-request-block.mdx deleted file mode 100644 index 5a02a8f..0000000 --- a/es/snippets/identify-user-request-block.mdx +++ /dev/null @@ -1,16 +0,0 @@ - - -```bash cURL -curl --request PUT \ - --url https://app.trophy.so/api/users/{id} \ - --header 'Content-Type: application/json' \ - --header 'X-API-KEY: ' \ - --data '{ - "email": "user@example.com", - "name": "User", - "tz": "Europe/London", - "subscribeToEmails": true -}' -``` - - diff --git a/es/snippets/identify-user-with-device-tokens-request-block.mdx b/es/snippets/identify-user-with-device-tokens-request-block.mdx deleted file mode 100644 index 9d7051b..0000000 --- a/es/snippets/identify-user-with-device-tokens-request-block.mdx +++ /dev/null @@ -1,98 +0,0 @@ -{/* vale off */} - - - -```bash cURL {10} -curl --request PUT \ - --url https://app.trophy.so/api/users/{id} \ - --header 'Content-Type: application/json' \ - --header 'X-API-KEY: ' \ - --data '{ - "email": "user@example.com", - "name": "User", - "tz": "Europe/London", - "subscribeToEmails": true, - "deviceTokens": ["token1", "token2"] -}' -``` - -```typescript Node {6} -trophy.users.identify("user-id", { - email: "user@example.com", - name: "User", - tz: "Europe/London", - subscribeToEmails: true, - deviceTokens: ["token1", "token2"], -}); -``` - -```python Python {7} -client.users.identify( - id="user-id", - email="user@example.com", - name="User", - tz="Europe/London", - subscribeToEmails=True, - deviceTokens=["token1", "token2"], -) -``` - -```php PHP {6} -$trophy->users->identify("user-id", [ - 'email' => 'user@example.com', - 'name' => 'User', - 'tz' => 'Europe/London', - 'subscribeToEmails' => true, - 'deviceTokens' => ['token1', 'token2'], -]); -``` - -```java Java {6} -UpdatedUser user = UpdatedUser.builder() - .email("user@example.com") - .name("User") - .tz("Europe/London") - .subscribeToEmails(true) - .deviceTokens(Arrays.asList("token1", "token2")) - .build(); - -client.users().identify("user-id", user); -``` - -```go Go {8} -response, err := client.Users.Identify( - "user-id", - &api.UpdatedUser{ - Email: "user@example.com", - Name: "User", - Tz: "Europe/London", - SubscribeToEmails: true, - DeviceTokens: []string{"token1", "token2"}, - }, -) -``` - -```csharp C# {6} -var user = new UpdatedUser { - Email = "user@example.com", - Name = "User", - Tz = "Europe/London", - SubscribeToEmails = true, - DeviceTokens = new[] { "token1", "token2" } -}; - -await trophy.Users.IdentifyAsync("user-id", user); -``` - -```ruby Ruby {7} -result = client.users.identify( - "user-id", - :email => "user@example.com", - :name => "User", - :tz => "Europe/London", - :subscribeToEmails => true, - :deviceTokens => ["token1", "token2"] -) -``` - - diff --git a/es/snippets/leaderboard-rankings-request-multiple-attributes.mdx b/es/snippets/leaderboard-rankings-request-multiple-attributes.mdx deleted file mode 100644 index b0386c1..0000000 --- a/es/snippets/leaderboard-rankings-request-multiple-attributes.mdx +++ /dev/null @@ -1,83 +0,0 @@ - - -```bash cURL -curl --request GET \ - --url https://api.trophy.so/v1/leaderboards/{key}?userAttributes=region_city:southeast_london \ - --header 'X-API-KEY: ' -``` - -```typescript Node -trophy.leaderboards.get("daily_champions", { - offset: 0, - limit: 10, - run: "2025-01-15", - userAttributes: "region_city:southeast_london" -}); -``` - -```python Python -client.leaderboards.get( - key="daily_champions", - offset=0, - limit=10, - run="2025-01-15", - user_attributes="region_city:southeast_london" -) -``` - -```php PHP -$request = new LeaderboardsGetRequest([ - 'offset' => 0, - 'limit' => 10, - 'run' => "2025-01-15", - 'user_attributes' => "region_city:southeast_london" -]); - -$trophy->leaderboards->get("daily_champions", $request); -``` - -```java Java -LeaderboardsGetRequest request = LeaderboardsGetRequest.builder() - .offset(0) - .limit(10) - .run("2025-01-15") - .userAttributes("region_city:southeast_london") - .build(); - -LeaderboardsGetResponse response = client.leaderboards().get("daily_champions", request); -``` - -```go Go -response, err := client.Leaderboards.Get( - "daily_champions", - &api.LeaderboardsGetRequest{ - Offset: 0, - Limit: 10, - Run: "2025-01-15", - UserAttributes: "region_city:southeast_london", - }, -) -``` - -```csharp C# -var request = new LeaderboardsGetRequest { - Offset = 0, - Limit = 10, - Run = "2025-01-15", - UserAttributes = "region_city:southeast_london" -}; - -await trophy.Leaderboards.GetAsync("daily_champions", request); -``` - -```ruby Ruby -result = client.Leaderboards.Get( - :key => "daily_champions", - :offset => 0, - :limit => 10, - :run => "2025-01-15", - :user_attributes => "region_city:southeast_london" -) -``` - - diff --git a/es/snippets/leaderboard-rankings-request-single-attribute.mdx b/es/snippets/leaderboard-rankings-request-single-attribute.mdx deleted file mode 100644 index 67ef588..0000000 --- a/es/snippets/leaderboard-rankings-request-single-attribute.mdx +++ /dev/null @@ -1,83 +0,0 @@ - - -```bash cURL -curl --request GET \ - --url https://api.trophy.so/v1/leaderboards/{key}?userAttributes=city:london \ - --header 'X-API-KEY: ' -``` - -```typescript Node -trophy.leaderboards.get("daily_champions", { - offset: 0, - limit: 10, - run: "2025-01-15", - userAttributes: "city:london" -}); -``` - -```python Python -client.leaderboards.get( - key="daily_champions", - offset=0, - limit=10, - run="2025-01-15", - user_attributes="city:london" -) -``` - -```php PHP -$request = new LeaderboardsGetRequest([ - 'offset' => 0, - 'limit' => 10, - 'run' => "2025-01-15", - 'user_attributes' => "city:london" -]); - -$trophy->leaderboards->get("daily_champions", $request); -``` - -```java Java -LeaderboardsGetRequest request = LeaderboardsGetRequest.builder() - .offset(0) - .limit(10) - .run("2025-01-15") - .userAttributes("city:london") - .build(); - -LeaderboardsGetResponse response = client.leaderboards().get("daily_champions", request); -``` - -```go Go -response, err := client.Leaderboards.Get( - "daily_champions", - &api.LeaderboardsGetRequest{ - Offset: 0, - Limit: 10, - Run: "2025-01-15", - UserAttributes: "city:london", - }, -) -``` - -```csharp C# -var request = new LeaderboardsGetRequest { - Offset = 0, - Limit = 10, - Run = "2025-01-15", - UserAttributes = "city:london" -}; - -await trophy.Leaderboards.GetAsync("daily_champions", request); -``` - -```ruby Ruby -result = client.Leaderboards.Get( - :key => "daily_champions", - :offset => 0, - :limit => 10, - :run => "2025-01-15", - :user_attributes => "city:london" -) -``` - - diff --git a/es/snippets/leaderboard-rankings-request.mdx b/es/snippets/leaderboard-rankings-request.mdx deleted file mode 100644 index 288e1e4..0000000 --- a/es/snippets/leaderboard-rankings-request.mdx +++ /dev/null @@ -1,48 +0,0 @@ - - -```bash cURL -curl --request GET \ - --url https://api.trophy.so/v1/leaderboards/{key} \ - --header 'X-API-KEY: ' -``` - -```typescript Node -trophy.leaderboards.get("daily_champions", { - offset: 0, - limit: 10, - run: "2025-01-15" -}); -``` - -```python Python -client.leaderboards.get( - key="daily_champions", - offset=0, - limit=10, - run="2025-01-15" -) -``` - -```php PHP -$trophy->leaderboards->get("daily_champions"); -``` - -```java Java -client.leaderboards().get("daily_champions"); -``` - -```go Go -response, err := client.Leaderboards.Get("daily_champions") -``` - -```csharp C# -trophy.Leaderboards.GetAsync("daily_champions"); -``` - -```ruby Ruby -result = client.Leaderboards.Get( - :key => "daily_champions" -) -``` - - diff --git a/es/snippets/leaderboard-rankings-response.mdx b/es/snippets/leaderboard-rankings-response.mdx deleted file mode 100644 index a20ca77..0000000 --- a/es/snippets/leaderboard-rankings-response.mdx +++ /dev/null @@ -1,41 +0,0 @@ -{/* vale off */} - -```json Response [expandable] -{ - "id": "5100fe51-6bce-6j44-b0hs-bddc4e123682", - "name": "Weekly Flashcard Challenge", - "key": "weekly-challenge", - "rankBy": "metric", - "metricKey": "flashcards-flipped", - "metricName": "Flashcards Flipped", - "pointsSystemKey": null, - "pointsSystemName": null, - "description": "Compete weekly to see who views the most flashcards", - "status": "active", - "start": "2025-01-01", - "end": null, - "maxParticipants": 100, - "runUnit": "day", - "runInterval": 7, - "rankings": [ - { - "userId": "user-123", - "userName": "Alice Johnson", - "rank": 1, - "value": 5000 - }, - { - "userId": "user-456", - "userName": "Bob Smith", - "rank": 2, - "value": 4500 - }, - { - "userId": "user-789", - "userName": "Charlie Brown", - "rank": 3, - "value": 4200 - } - ] -} -``` \ No newline at end of file diff --git a/es/snippets/metric-change-event-points-level-excerpt-block.mdx b/es/snippets/metric-change-event-points-level-excerpt-block.mdx deleted file mode 100644 index 041be1c..0000000 --- a/es/snippets/metric-change-event-points-level-excerpt-block.mdx +++ /dev/null @@ -1,21 +0,0 @@ -{/* vale off */} - -```json Response [expandable] -{ - ..., - "points": { - "xp": { - ..., - "level": { - "id": "1140fe51-6bce-4b44-b0ad-bddc4e123534", - "key": "bronze", - "name": "Bronze", - "description": "Starting level", - "badgeUrl": null, - "points": 0 - }, - ... - } - } -} -``` diff --git a/es/snippets/metric-change-request-block.mdx b/es/snippets/metric-change-request-block.mdx deleted file mode 100644 index 2fc665a..0000000 --- a/es/snippets/metric-change-request-block.mdx +++ /dev/null @@ -1,106 +0,0 @@ - - -```bash cURL -curl -X POST https://app.trophy.so/api/metrics/flashcards-flipped/event \ - -H "X-API-KEY: " \ - -H "Content-Type: application/json" \ - -d '{ - "user": { - "id": "18", - "email": "user@example.com", - "tz": "Europe/London" - }, - "value": 750 -}' -``` - -```typescript Node -trophy.metrics.event("flashcards-flipped", { - user: { - id: "18", - email: "user@example.com", - tz: "Europe/London", - }, - value: 750, -}); -``` - -```python Python -client.metrics.event( - key="flashcards-flipped", - user=EventRequestUser( - id="18", - email="user@example.com", - tz="Europe/London", - ), - value=750.0, -) -``` - -```php PHP -$user = new EventRequestUser([ - 'id' => '18', - 'email' => 'user@example.com' -]); - -$request = new MetricsEventRequest([ - 'user' => $user, - 'value' => 750 -]); - -$trophy->metrics->event("flashcards-flipped", $request); -``` - -```java Java -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 -response, err := client.Metrics.Event( - "flashcards-flipped", - &api.MetricsEventRequest{ - User: &api.EventRequestUser{ - Id: "18", - Email: "user@example.com", - }, - Value: 750, - }, -) -``` - -```csharp C# -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 -result = client.metrics.event( - :key => 'flashcards-flipped', - :user => { - :id => '18', - :email => 'user@example.com' - }, - :value => 750 -) -``` - - diff --git a/es/snippets/metric-change-response-block.mdx b/es/snippets/metric-change-response-block.mdx deleted file mode 100644 index d787bcb..0000000 --- a/es/snippets/metric-change-response-block.mdx +++ /dev/null @@ -1,83 +0,0 @@ -{/* vale off */} - -```json Response [expandable] -{ - "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", - "eventId": "0040fe51-6bce-4b44-b0ad-bddc4e123534", - "total": 750, - "achievements": [ - { - "id": "5100fe51-6bce-6j44-b0hs-bddc4e123682", - "trigger": "metric", - "metricId": "5100fe51-6bce-6j44-b0hs-bddc4e123682", - "metricName": "Flashcards Flipped", - "metricValue": 500, - "name": "500 Flashcards Flipped", - "description": "Write 500 words in the app.", - "achievedAt": "2020-01-01T00:00:00Z" - } - ], - "currentStreak": { - "length": 1, - "frequency": "daily", - "started": "2025-04-02", - "periodStart": "2025-03-31", - "periodEnd": "2025-04-05", - "expires": "2025-04-12" - }, - "points": { - "xp": { - "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", - "key": "xp", - "name": "XP", - "description": null, - "badgeUrl": null, - "maxPoints": null, - "total": 10, - "level": { - "id": "1140fe51-6bce-4b44-b0ad-bddc4e123534", - "key": "bronze", - "name": "Bronze", - "description": "Starting level", - "badgeUrl": null, - "points": 0 - }, - "added": 10, - "awards": [ - { - "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", - "awarded": 10, - "date": "2021-01-01T00:00:00Z", - "total": 10, - "trigger": { - "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", - "type": "metric", - "metricName": "Flashcards Flipped", - "metricThreshold": 100, - "points": 10 - } - } - ] - } - }, - "leaderboards": { - "daily_champions": { - "id": "0040fe51-6bce-4b44-b0ad-bddc4e123535", - "key": "daily_champions", - "name": "Daily Champions", - "description": null, - "rankBy": "metric", - "runUnit": null, - "runInterval": 0, - "maxParticipants": 100, - "metricName": "Flashcards Flipped", - "metricKey": "flashcards-flipped", - "threshold": 10, - "start": "2025-01-01", - "end": null, - "previousRank": 50, - "rank": 12 - } - } -} -``` diff --git a/es/snippets/metric-event-with-device-tokens-request-block.mdx b/es/snippets/metric-event-with-device-tokens-request-block.mdx deleted file mode 100644 index 6cceab2..0000000 --- a/es/snippets/metric-event-with-device-tokens-request-block.mdx +++ /dev/null @@ -1,116 +0,0 @@ -{/* vale off */} - - - -```bash cURL {10} -curl -X POST https://app.trophy.so/api/metrics/flashcards-flipped/event \ - -H "X-API-KEY: " \ - -H "Content-Type: application/json" \ - -d '{ - "user": { - "id": "18", - "email": "user@example.com", - "tz": "Europe/London", - "deviceTokens": ["token1", "token2"] - }, - "value": 750 -}' -``` - -```typescript Node {6} -trophy.metrics.event("flashcards-flipped", { - user: { - id: "18", - email: "user@example.com", - tz: "Europe/London", - deviceTokens: ["token1", "token2"], - }, - value: 750, -}); -``` - -```python Python {7} -client.metrics.event( - key="flashcards-flipped", - user=EventRequestUser( - id="18", - email="user@example.com", - tz="Europe/London", - deviceTokens=["token1", "token2"], - ), - value=750.0, -) -``` - -```php PHP {6} -$user = new EventRequestUser([ - 'id' => '18', - 'email' => 'user@example.com', - 'deviceTokens' => ['token1', 'token2'], -]); - -$request = new MetricsEventRequest([ - 'user' => $user, - 'value' => 750 -]); - -$trophy->metrics->event("flashcards-flipped", $request); -``` - -```java Java {6} -MetricsEventRequest request = MetricsEventRequest.builder() - .user( - EventRequestUser.builder() - .id("18") - .email("user@example.com") - .deviceTokens(Arrays.asList("token1", "token2")) - .build() - ) - .value(750) - .build(); - -EventResponse response = client.metrics().event("flashcards-flipped", request); -``` - -```go Go {8} -response, err := client.Metrics.Event( - "flashcards-flipped", - &api.MetricsEventRequest{ - User: &api.EventRequestUser{ - Id: "18", - Email: "user@example.com", - DeviceTokens: []string{"token1", "token2"}, - }, - Value: 750, - }, -) -``` - -```csharp C# {6} -var user = new EventRequestUser { - Id = "18", - Email = "user@example.com", - DeviceTokens = new[] { "token1", "token2" } -}; - -var request = new MetricsEventRequest { - User = user, - Value = 750 -}; - -await trophy.Metrics.EventAsync("flashcards-flipped", request); -``` - -```ruby Ruby {7} -result = client.metrics.event( - :key => 'flashcards-flipped', - :user => { - :id => '18', - :email => 'user@example.com', - :deviceTokens => ['token1', 'token2'] - }, - :value => 750 -) -``` - - diff --git a/es/snippets/plan-badge.jsx b/es/snippets/plan-badge.jsx deleted file mode 100644 index 59612aa..0000000 --- a/es/snippets/plan-badge.jsx +++ /dev/null @@ -1,26 +0,0 @@ -export const PlanBadge = ({ plan }) => { - return ( -
- - {plan.charAt(0).toUpperCase() + plan.slice(1)} - -
- ); -}; diff --git a/es/snippets/points-histogram-summary-response-block.mdx b/es/snippets/points-histogram-summary-response-block.mdx deleted file mode 100644 index 5c6943f..0000000 --- a/es/snippets/points-histogram-summary-response-block.mdx +++ /dev/null @@ -1,17 +0,0 @@ -{/* vale off */} - -```json GET /points/{key}/summary — 200 response [expandable] -[ - { "from": 0, "to": 0, "users": 5012 }, - { "from": 1, "to": 100, "users": 1501 }, - { "from": 101, "to": 200, "users": 1007 }, - { "from": 201, "to": 300, "users": 584 }, - { "from": 301, "to": 400, "users": 201 }, - { "from": 401, "to": 500, "users": 102 }, - { "from": 501, "to": 600, "users": 25 }, - { "from": 601, "to": 700, "users": 0 }, - { "from": 701, "to": 800, "users": 0 }, - { "from": 801, "to": 900, "users": 0 }, - { "from": 901, "to": 1000, "users": 0 } -] -``` diff --git a/es/snippets/points-level-summary-response-block.mdx b/es/snippets/points-level-summary-response-block.mdx deleted file mode 100644 index abbcc2f..0000000 --- a/es/snippets/points-level-summary-response-block.mdx +++ /dev/null @@ -1,39 +0,0 @@ -{/* vale off */} - -```json Response [expandable] -[ - { - "level": { - "id": "1140fe51-6bce-4b44-b0ad-bddc4e123534", - "key": "bronze", - "name": "Bronze", - "description": "Starting level", - "badgeUrl": "https://example.com/bronze.png", - "points": 0 - }, - "users": 5012 - }, - { - "level": { - "id": "2240fe51-6bce-4b44-b0ad-bddc4e123534", - "key": "silver", - "name": "Silver", - "description": "Mid-tier level", - "badgeUrl": null, - "points": 50 - }, - "users": 1501 - }, - { - "level": { - "id": "3340fe51-6bce-4b44-b0ad-bddc4e123534", - "key": "gold", - "name": "Gold", - "description": "Top level", - "badgeUrl": "https://example.com/gold.png", - "points": 200 - }, - "users": 102 - } -] -``` diff --git a/es/snippets/points-levels-list-response-block.mdx b/es/snippets/points-levels-list-response-block.mdx deleted file mode 100644 index 0b7319d..0000000 --- a/es/snippets/points-levels-list-response-block.mdx +++ /dev/null @@ -1,30 +0,0 @@ -{/* vale off */} - -```json Response [expandable] -[ - { - "id": "1140fe51-6bce-4b44-b0ad-bddc4e123534", - "key": "bronze", - "name": "Bronze", - "description": "Starting level", - "badgeUrl": "https://example.com/bronze.png", - "points": 0 - }, - { - "id": "2240fe51-6bce-4b44-b0ad-bddc4e123534", - "key": "silver", - "name": "Silver", - "description": "Mid-tier level", - "badgeUrl": null, - "points": 50 - }, - { - "id": "3340fe51-6bce-4b44-b0ad-bddc4e123534", - "key": "gold", - "name": "Gold", - "description": "Top level", - "badgeUrl": "https://example.com/gold.png", - "points": 200 - } -] -``` diff --git a/es/snippets/points-system-response-block.mdx b/es/snippets/points-system-response-block.mdx deleted file mode 100644 index 90eb952..0000000 --- a/es/snippets/points-system-response-block.mdx +++ /dev/null @@ -1,49 +0,0 @@ -{/* vale off */} - -```json GET /points/{key} — 200 response [expandable] -{ - "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", - "name": "XP System", - "description": "Experience points for user engagement", - "badgeUrl": "https://example.com/badge.png", - "maxPoints": null, - "triggers": [ - { - "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", - "type": "metric", - "points": 10, - "status": "active", - "metricId": "0040fe51-6bce-4b44-b0ad-bddc4e123534", - "metricName": "words written", - "metricThreshold": 1000, - "userAttributes": [ - { "key": "plan-type", "value": "premium" }, - { "key": "region", "value": "us-east" } - ], - "eventAttribute": { "key": "source", "value": "mobile-app" }, - "created": "2021-01-01T00:00:00Z", - "updated": "2021-01-01T00:00:00Z" - }, - { - "id": "0040fe51-6bce-4b44-b0ad-bddc4e123536", - "type": "streak", - "points": 10, - "status": "active", - "streakLengthThreshold": 7, - "created": "2021-01-01T00:00:00Z", - "updated": "2021-01-01T00:00:00Z" - }, - { - "id": "0040fe51-6bce-4b44-b0ad-bddc4e123535", - "type": "achievement", - "points": 50, - "status": "active", - "achievementId": "0040fe51-6bce-4b44-b0ad-bddc4e123535", - "achievementName": "finish onboarding", - "userAttributes": [{ "key": "plan-type", "value": "premium" }], - "created": "2021-01-01T00:00:00Z", - "updated": "2021-01-01T00:00:00Z" - } - ] -} -``` diff --git a/es/snippets/rate-limit-badge.jsx b/es/snippets/rate-limit-badge.jsx deleted file mode 100644 index 4ada2b3..0000000 --- a/es/snippets/rate-limit-badge.jsx +++ /dev/null @@ -1,35 +0,0 @@ -export const RateLimitBadge = ({ tier, noTooltip = false }) => { - const getColor = (tier) => { - switch (tier) { - case 1: - return "yellow"; - case 2: - return "green"; - default: - return "gray"; - } - }; - - const getLimit = (tier) => { - switch (tier) { - case 1: - return 10; - case 2: - return 100; - } - }; - - return ( - noTooltip ? ( - Tier {tier} - ) : ( - - Tier {tier} - - ) - ); -}; diff --git a/es/snippets/sdk-install-command.mdx b/es/snippets/sdk-install-command.mdx deleted file mode 100644 index 22b4f6e..0000000 --- a/es/snippets/sdk-install-command.mdx +++ /dev/null @@ -1,46 +0,0 @@ - - -```bash Node -npm install @trophyso/node -``` - -```bash Ruby -gem install trophy_api_client -``` - -```bash Python -pip install trophy -``` - -```bash PHP -composer require trophyso/php -``` - -```bash Java (Gradle) -implementation 'so.trophy:trophy-java:1.0.0' -``` - -```bash Java (Maven) - - so.trophy - trophy-java - 1.0.0 - -``` - -```bash Go -go get github.com/trophy-so/trophy-go -``` - -```bash .NET (C#) -// .NET Core CLI -dotnet add package Trophy - -// Nuget Package Manager -nuget install Trophy - -// Visual Studio -Install-Package Trophy -``` - - diff --git a/es/snippets/snippet-intro.mdx b/es/snippets/snippet-intro.mdx deleted file mode 100644 index 34f0f27..0000000 --- a/es/snippets/snippet-intro.mdx +++ /dev/null @@ -1 +0,0 @@ -Uno de los principios fundamentales del desarrollo de software es DRY (Don't Repeat Yourself, No te repitas). Este es un principio que también se aplica a la documentación. Si te encuentras repitiendo el mismo contenido en varios lugares, deberías considerar crear un snippet personalizado para mantener tu contenido sincronizado. diff --git a/es/snippets/streak-request-block.mdx b/es/snippets/streak-request-block.mdx deleted file mode 100644 index 1d670bc..0000000 --- a/es/snippets/streak-request-block.mdx +++ /dev/null @@ -1,62 +0,0 @@ - - -```bash cURL -curl --request GET \ - --url https://api.trophy.so/v1/users/{id}/streak \ - --header 'X-API-KEY: ' -``` - -```typescript Node -trophy.users.streak("user-id", { - historyPeriods: 14 -}); -``` - -```python Python -client.users.streak( - id="user-id", - history_periods=14, -) -``` - -```php PHP -$request = new UsersStreakRequest([ - 'history_periods' => 14 -]); - -$trophy->users->streak("user-id", $request); -``` - -```java Java -UsersStreakRequest request = UsersStreakRequest.builder() - .historyPeriods(14) - .build(); - -UsersStreakResponse response = client.users().streak("user-id", request); -``` - -```go Go -response, err := client.Users.Streak( - "user-id", - &api.UsersStreakRequest{ - HistoryPeriods: 14, - }, -) -``` - -```csharp C# -var request = new UsersStreakRequest { - HistoryPeriods = 14 -}; - -await trophy.Users.StreaksAsync("user-id", request); -``` - -```ruby Ruby -result = client.users.streak( - :id => 'user-id', - :history_periods => 14 -) -``` - - diff --git a/es/snippets/streak-response-block.mdx b/es/snippets/streak-response-block.mdx deleted file mode 100644 index 320798a..0000000 --- a/es/snippets/streak-response-block.mdx +++ /dev/null @@ -1,49 +0,0 @@ -{/* vale off */} - -```json Response [expandable] -{ - "length": 1, - "frequency": "weekly", - "started": "2025-04-02", - "periodStart": "2025-03-31", - "periodEnd": "2025-04-05", - "expires": "2025-04-12", - "streakHistory": [ - { - "periodStart": "2025-03-30", - "periodEnd": "2025-04-05", - "length": 1 - }, - { - "periodStart": "2025-04-06", - "periodEnd": "2025-04-12", - "length": 2 - }, - { - "periodStart": "2025-04-13", - "periodEnd": "2025-04-19", - "length": 3 - }, - { - "periodStart": "2025-04-20", - "periodEnd": "2025-04-26", - "length": 0 - }, - { - "periodStart": "2025-04-27", - "periodEnd": "2025-05-03", - "length": 1 - }, - { - "periodStart": "2025-05-04", - "periodEnd": "2025-05-10", - "length": 2 - }, - { - "periodStart": "2025-05-11", - "periodEnd": "2025-05-17", - "length": 3 - } - ] -} -``` diff --git a/es/snippets/update-user-notification-preferences-block.mdx b/es/snippets/update-user-notification-preferences-block.mdx deleted file mode 100644 index 15453b1..0000000 --- a/es/snippets/update-user-notification-preferences-block.mdx +++ /dev/null @@ -1,86 +0,0 @@ -{/* vale off */} - - - -```bash cURL -curl --request PATCH \ - --url https://api.trophy.so/v1/users/user-123/preferences \ - --header 'Content-Type: application/json' \ - --header 'X-API-KEY: ' \ - --data '{ - "notifications": { - "recap": ["email"], - "streak_reminder": [] - } -}' -``` - -```typescript Node -const response = await trophy.users.updatePreferences("user-123", { - notifications: { - recap: ["email"], - streak_reminder: [] - } -}); -``` - -```python Python -response = client.users.update_preferences( - id="user-123", - notifications={ - "recap": ["email"], - "streak_reminder": [] - } -) -``` - -```php PHP -$response = $trophy->users()->updatePreferences("user-123", [ - "notifications" => [ - "recap" => ["email"], - "streak_reminder" => [] - ] -]); -``` - -```java Java -UpdateUserPreferencesRequest request = UpdateUserPreferencesRequest.builder() - .notifications(NotificationPreferences.builder() - .recap(Arrays.asList("email")) - .streakReminder(Collections.emptyList()) - .build()) - .build(); -UserPreferencesResponse response = client.users().updatePreferences("user-123", request); -``` - -```go Go -response, err := client.Users.UpdatePreferences("user-123", &UpdateUserPreferencesRequest{ - Notifications: &NotificationPreferences{ - Recap: []string{"email"}, - StreakReminder: []string{}, - }, -}) -``` - -```csharp C# -var response = await trophy.Users.UpdatePreferencesAsync("user-123", new UpdateUserPreferencesRequest -{ - Notifications = new NotificationPreferences - { - Recap = new[] { "email" }, - StreakReminder = Array.Empty() - } -}); -``` - -```ruby Ruby -response = client.users.update_preferences( - id: "user-123", - notifications: { - recap: ["email"], - streak_reminder: [] - } -) -``` - - diff --git a/es/snippets/user-achievements-request-block.mdx b/es/snippets/user-achievements-request-block.mdx deleted file mode 100644 index c3bac36..0000000 --- a/es/snippets/user-achievements-request-block.mdx +++ /dev/null @@ -1,39 +0,0 @@ - - -```bash cURL -curl --request GET \ - --url https://api.trophy.so/v1/users/{id}/achievements \ - --header 'X-API-KEY: ' -``` - -```typescript Node -trophy.users.achievements("user-id"); -``` - -```python Python -client.users.achievements("user-id") -``` - -```php PHP -$trophy->users->achievements("user-id"); -``` - -```java Java -UserAchievementsResponse response = client.users().achievements("user-id"); -``` - -```go Go -response, err := client.Users.Achievements("user-id") -``` - -```csharp C# -await trophy.Users.AchievementsAsync("user-id"); -``` - -```ruby Ruby -result = client.users.achievements( - :id => 'user-id' -) -``` - - diff --git a/es/snippets/user-energy-response-block.mdx b/es/snippets/user-energy-response-block.mdx deleted file mode 100644 index fc9602d..0000000 --- a/es/snippets/user-energy-response-block.mdx +++ /dev/null @@ -1,39 +0,0 @@ -{/* vale off */} - -```json Response [expandable] -{ - "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", - "name": "Energy", - "description": null, - "badgeUrl": null, - "total": 10, - "awards": [ - { - "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", - "awarded": 1, - "date": "2021-01-01T00:00:00Z", - "total": 10, - "trigger": { - "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", - "type": "time", - "points": 1, - "timeUnit": "day", - "timeInterval": 1 - } - }, - { - "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", - "awarded": -1, - "date": "2021-01-01T00:00:00Z", - "total": 9, - "trigger": { - "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", - "type": "metric", - "points": -1, - "metricName": "Flashcards Flipped", - "metricThreshold": 1 - } - } - ] -} -``` diff --git a/es/snippets/user-leaderboard-rankings-request.mdx b/es/snippets/user-leaderboard-rankings-request.mdx deleted file mode 100644 index ee1fc90..0000000 --- a/es/snippets/user-leaderboard-rankings-request.mdx +++ /dev/null @@ -1,42 +0,0 @@ - - -```bash cURL -curl --request GET \ - --url https://api.trophy.so/v1/users/{id}/leaderboards/{key} \ - --header 'X-API-KEY: ' -``` - -```typescript Node -trophy.users.leaderboards("user-id", "daily_champions", { - run: "2025-01-15" -}); -``` - -```python Python -client.users.leaderboards("user-id", "daily_champions", run="2025-01-15") -``` - -```php PHP -$trophy->users->leaderboards("user-id", "daily_champions") -``` - -```java Java -client.users().leaderboards("user-id", "daily_champions") -``` - -```go Go -response, err := client.Users.Leaderboards("user-id", "daily_champions") -``` - -```csharp C# -trophy.Users.Leaderboards("user-id", "daily_champions"); -``` - -```ruby Ruby -result = client.Leaderboards.Get( - :id => "user-id", - :key => "daily_champions" -) -``` - - diff --git a/es/snippets/user-leaderboard-rankings-response.mdx b/es/snippets/user-leaderboard-rankings-response.mdx deleted file mode 100644 index 9765d92..0000000 --- a/es/snippets/user-leaderboard-rankings-response.mdx +++ /dev/null @@ -1,45 +0,0 @@ -{/* vale off */} - -```json Response [expandable] -{ - "id": "5100fe51-6bce-6j44-b0hs-bddc4e123682", - "name": "Weekly Word Count Challenge", - "key": "weekly-words", - "rankBy": "metric", - "metricKey": "flashcards-flipped", - "metricName": "Flashcards Flipped", - "pointsSystemKey": null, - "pointsSystemName": null, - "description": "Compete weekly to see who writes the most words", - "start": "2025-01-01", - "end": null, - "maxParticipants": 100, - "runUnit": "day", - "runInterval": 7, - "rank": 2, - "value": 4500, - "history": [ - { - "timestamp": "2025-01-15T10:30:00Z", - "previousRank": null, - "rank": 5, - "previousValue": null, - "value": 1000 - }, - { - "timestamp": "2025-01-15T14:15:00Z", - "previousRank": 5, - "rank": 3, - "previousValue": 1000, - "value": 3000 - }, - { - "timestamp": "2025-01-15T18:45:00Z", - "previousRank": 3, - "rank": 2, - "previousValue": 3000, - "value": 4500 - } - ] -} -``` \ No newline at end of file diff --git a/es/snippets/user-points-request-block.mdx b/es/snippets/user-points-request-block.mdx deleted file mode 100644 index 0ede579..0000000 --- a/es/snippets/user-points-request-block.mdx +++ /dev/null @@ -1,46 +0,0 @@ - - -```bash cURL -curl --request GET \ - --url https://api.trophy.so/v1/users/{id}/points/{key} \ - --header 'X-API-KEY: ' -``` - -```typescript Node -trophy.users.points("user-id", "points-system-key"); -``` - -```python Python -client.users.points( - id="user-id", - key="points-system-key", -) -``` - -```php PHP -$trophy->users->points("user-id", "points-system-key"); -``` - -```java Java -client.users().points("user-id","points-system-key"); -``` - -```go Go -response, err := client.Users.Points( - "user-id", - "points-system-key" -) -``` - -```csharp C# -trophy.Users.PointsAsync("user-id", "points-system-key"); -``` - -```ruby Ruby -result = client.users.points( - :id => 'user-id', - :key => "points-system-key" -) -``` - - diff --git a/es/snippets/user-points-response-block.mdx b/es/snippets/user-points-response-block.mdx deleted file mode 100644 index 860c7d5..0000000 --- a/es/snippets/user-points-response-block.mdx +++ /dev/null @@ -1,36 +0,0 @@ -{/* vale off */} - -```json Response [expandable] -{ - "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 - } - } - ] -} -``` diff --git a/es/snippets/user-points-summary-request-block.mdx b/es/snippets/user-points-summary-request-block.mdx deleted file mode 100644 index 5eac8ee..0000000 --- a/es/snippets/user-points-summary-request-block.mdx +++ /dev/null @@ -1,53 +0,0 @@ - - -```bash cURL -curl --request GET \ - --url https://api.trophy.so/v1/users/{id}/points/{key}/event-summary \ - --header 'X-API-KEY: ' -``` - -```typescript Node -trophy.users.pointsEventSummary("user-id", "points-system-key", { - aggregation: "daily", - start_date: "2025-01-01", - end_date: "2025-01-07" -}); -``` - -```python Python -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 -$trophy->users->pointsEventSummary("user-id", "points-system-key"); -``` - -```java Java -client.users().pointsEventSummary("user-id","points-system-key"); -``` - -```go Go -response, err := client.Users.PointsEventSummary( - "user-id", - "points-system-key" -) -``` - -```csharp C# -trophy.Users.PointsEventSummaryAsync("user-id", "points-system-key"); -``` - -```ruby Ruby -result = client.users.pointsEventSummary( - :id => 'user-id', - :key => "points-system-key" -) -``` - - diff --git a/es/snippets/user-points-summary-response-block.mdx b/es/snippets/user-points-summary-response-block.mdx deleted file mode 100644 index be8b33a..0000000 --- a/es/snippets/user-points-summary-response-block.mdx +++ /dev/null @@ -1,21 +0,0 @@ -{/* vale off */} - -```json GET /users/{id}/points/{key}/event-summary — 200 response [expandable] -[ - { - "date": "2024-01-01", - "total": 100, - "change": 100 - }, - { - "date": "2024-01-02", - "total": 300, - "change": 200 - }, - { - "date": "2024-01-03", - "total": 600, - "change": 300 - } -] -``` diff --git a/es/snippets/webhook-points-level-changed-payload-block.mdx b/es/snippets/webhook-points-level-changed-payload-block.mdx deleted file mode 100644 index c1d0702..0000000 --- a/es/snippets/webhook-points-level-changed-payload-block.mdx +++ /dev/null @@ -1,44 +0,0 @@ -{/* vale off */} - -```json Webhook points.level_changed — request body [expandable] -{ - "type": "points.level_changed", - "user": { - "id": "user-id", - "email": "user@example.com", - "tz": "Europe/London", - "subscribedToEmails": true, - "created": "2021-01-01T00:00:00Z", - "updated": "2021-01-01T00:00:00Z", - "attributes": { - "department": "engineering", - "role": "developer" - } - }, - "points": { - "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", - "key": "xp", - "name": "XP", - "description": "Experience points", - "badgeUrl": null, - "maxPoints": null, - "total": 100 - }, - "previousLevel": { - "id": "1140fe51-6bce-4b44-b0ad-bddc4e123534", - "key": "bronze", - "name": "Bronze", - "description": "Starting level", - "badgeUrl": "https://example.com/bronze.png", - "points": 0 - }, - "newLevel": { - "id": "2240fe51-6bce-4b44-b0ad-bddc4e123534", - "key": "silver", - "name": "Silver", - "description": "Mid-tier level", - "badgeUrl": null, - "points": 50 - } -} -``` diff --git a/es/webhooks/events/achievements/achievement-completed.mdx b/es/webhooks/events/achievements/achievement-completed.mdx index 375c95d..cbcb51a 100644 --- a/es/webhooks/events/achievements/achievement-completed.mdx +++ b/es/webhooks/events/achievements/achievement-completed.mdx @@ -4,5 +4,5 @@ openapi: webhook achievement.completed Este webhook no se activa para logros que están - [retrodatados](/platform/achievements#backdating-achievements). + [retrodatados](/es/platform/achievements#backdating-achievements). diff --git a/es/webhooks/events/leaderboards/leaderboard-finished.mdx b/es/webhooks/events/leaderboards/leaderboard-finished.mdx index 1466fe1..bc77fe9 100644 --- a/es/webhooks/events/leaderboards/leaderboard-finished.mdx +++ b/es/webhooks/events/leaderboards/leaderboard-finished.mdx @@ -7,5 +7,5 @@ openapi: webhook leaderboard.finished - Los eventos solo incluyen las 10 primeras posiciones de usuarios. Para obtener más, utiliza la [API de clasificación individual](/api-reference/endpoints/leaderboards/get-leaderboard). + Los eventos solo incluyen las 10 primeras posiciones de usuarios. Para obtener más, utiliza la [API de clasificación individual](/es/api-reference/endpoints/leaderboards/get-leaderboard). diff --git a/es/webhooks/events/points/points-changed.mdx b/es/webhooks/events/points/points-changed.mdx index 75d926b..93d4346 100644 --- a/es/webhooks/events/points/points-changed.mdx +++ b/es/webhooks/events/points/points-changed.mdx @@ -4,5 +4,5 @@ openapi: webhook points.changed No aplica al [tipo de activador de identificación de - usuario](/platform/points#types-of-triggers). + usuario](/es/platform/points#types-of-triggers). diff --git a/es/webhooks/events/points/points-level-changed.mdx b/es/webhooks/events/points/points-level-changed.mdx index 57bfc0c..ac8a2df 100644 --- a/es/webhooks/events/points/points-level-changed.mdx +++ b/es/webhooks/events/points/points-level-changed.mdx @@ -3,5 +3,5 @@ openapi: webhook points.level_changed --- - Este evento se activa cuando el **nivel** de un usuario cambia dentro de un sistema de puntos **debido a que ganó o perdió puntos**. Es independiente de [`points.changed`](/webhooks/events/points/points-changed), que refleja cambios en el saldo de puntos de manera más amplia. Utiliza `points.level_changed` cuando te interese específicamente las transiciones de nivel. + Este evento se activa cuando el **nivel** de un usuario cambia dentro de un sistema de puntos **debido a que ganó o perdió puntos**. Es independiente de [`points.changed`](/es/webhooks/events/points/points-changed), que refleja cambios en el saldo de puntos de manera más amplia. Utiliza `points.level_changed` cuando te interese específicamente las transiciones de nivel. diff --git a/es/webhooks/events/streaks/streak-extended.mdx b/es/webhooks/events/streaks/streak-extended.mdx index ffee691..2e2b7df 100644 --- a/es/webhooks/events/streaks/streak-extended.mdx +++ b/es/webhooks/events/streaks/streak-extended.mdx @@ -3,5 +3,5 @@ openapi: webhook racha.extended --- - No se aplica al consumo de [congelación de racha](/platform/streaks#streak-freezes). + No se aplica al consumo de [congelación de racha](/es/platform/streaks#streak-freezes). diff --git a/es/webhooks/idempotency.mdx b/es/webhooks/idempotency.mdx index 4c4addb..ae081a6 100644 --- a/es/webhooks/idempotency.mdx +++ b/es/webhooks/idempotency.mdx @@ -7,15 +7,15 @@ subtitle: Aprenda cómo asegurar que sus endpoints de webhook procesen los icon: repeat --- -## ¿Qué es la Idempotencia? +## ¿Qué es la Idempotencia? {#what-is-idempotency} En el contexto de los webhooks, la [idempotencia](https://en.wikipedia.org/wiki/Idempotence) es la propiedad de un endpoint de asegurar que procese eventos duplicados exactamente una vez. La mayoría de los emisores de webhooks, incluyendo Trophy, operan con una garantía de entrega "al menos una vez". Esto significa que eventualmente puede recibir el mismo webhook múltiples veces y su código necesita manejar esos escenarios sin consecuencias no deseadas. -Por ejemplo, si su manejador de webhook está suscrito a eventos [`leaderboard.finished`](/webhooks/events/leaderboards/leaderboard-finished) y envía tarjetas de regalo a los usuarios si se ubican en el top 10, entonces su código necesita enviar las tarjetas de regalo solo una vez incluso si recibe el mismo evento de Trophy múltiples veces. +Por ejemplo, si su manejador de webhook está suscrito a eventos [`leaderboard.finished`](/es/webhooks/events/leaderboards/leaderboard-finished) y envía tarjetas de regalo a los usuarios si se ubican en el top 10, entonces su código necesita enviar las tarjetas de regalo solo una vez incluso si recibe el mismo evento de Trophy múltiples veces. -## Implementando la Idempotencia de Webhooks +## Implementando la Idempotencia de Webhooks {#implementing-webhook-idempotency} Hacer que sus manejadores de webhook sean más robustos es trivial en la mayoría de los casos. Tomemos el ejemplo de la tarjeta de regalo anterior. @@ -54,6 +54,6 @@ if (!receipt) { Si su código recibe un evento duplicado, la tarjeta de regalo no se enviará nuevamente. -## Obtenga Soporte +## Obtenga Soporte {#get-support} ¿Quiere ponerse en contacto con el equipo de Trophy? Comuníquese con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarlo! diff --git a/es/webhooks/introduction.mdx b/es/webhooks/introduction.mdx index df88492..6c50969 100644 --- a/es/webhooks/introduction.mdx +++ b/es/webhooks/introduction.mdx @@ -7,7 +7,7 @@ subtitle: Aprende sobre webhooks en Trophy y cómo usarlos para potenciar --- - Esta funcionalidad está disponible en el plan [Pro](/account/billing#pro-plan) + Esta funcionalidad está disponible en el plan [Pro](/es/account/billing#pro-plan) Trophy tiene soporte para varios webhooks que se activan según eventos clave como `achievement.completed`, `leaderboard.finished` y `streak.lost`. @@ -19,24 +19,24 @@ Todos los webhooks de Trophy se entregan mediante HTTP, lo que permite a los cli Lee más sobre cómo empezar con webhooks a continuación. - + Comienza a recibir eventos de webhook desde Trophy en menos de 10 minutos. - + Asegura endpoints de webhook usando verificación de firma. - + Aprende cómo Trophy maneja los reintentos de eventos. - + Maneja eventos duplicados y previene efectos secundarios no deseados. - + Aprende cómo monitorear la entrega de webhooks y diagnosticar problemas. -## Casos de Uso Comunes +## Casos de Uso Comunes {#common-use-cases} Los casos de uso comunes incluyen: @@ -50,6 +50,6 @@ Los casos de uso comunes incluyen: de uso y lo desarrollaremos. -## Obtén Soporte +## Obtén Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [email](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/webhooks/observability.mdx b/es/webhooks/observability.mdx index dbdf035..10c6dcc 100644 --- a/es/webhooks/observability.mdx +++ b/es/webhooks/observability.mdx @@ -5,7 +5,7 @@ subtitle: Aprende cómo monitorear y diagnosticar problemas con la entrega de we icon: activity --- -## Monitoreo de la Entrega de Webhooks +## Monitoreo de la Entrega de Webhooks {#monitoring-webhook-delivery} El panel de Trophy cuenta con analíticas integradas para todos los eventos de webhook enviados a tus endpoints, incluyendo la visualización del estado de los eventos enviados y la carga útil. @@ -14,14 +14,14 @@ El panel de Trophy cuenta con analíticas integradas para todos los eventos de w height="200" width="100%" noZoom - src="../assets/webhooks/observability/logs.png" + src="../../assets/webhooks/observability/logs.png" /> -## Retención de Datos +## Retención de Datos {#data-retention} Trophy retiene todos los registros de webhooks durante **7 días**. Si deseas discutir el aumento de tu período de retención de datos, por favor [contáctanos](mailto:support@trophy.so) y con gusto lo hablaremos contigo. -## Obtén Soporte +## Obtén Soporte {#get-support} ¿Quieres comunicarte con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/webhooks/quickstart.mdx b/es/webhooks/quickstart.mdx index 5b347e2..81dd314 100644 --- a/es/webhooks/quickstart.mdx +++ b/es/webhooks/quickstart.mdx @@ -5,11 +5,11 @@ subtitle: Comienza a recibir eventos de webhook de Trophy en menos de 10 minutos icon: circle-play --- -## Primeros pasos +## Primeros pasos {#getting-started} Si aún no tienes una cuenta de Trophy, sigue primero la [guía de inicio rápido - de Trophy](/getting-started/quickstart) para comenzar. + de Trophy](/es/getting-started/quickstart) para comenzar. En Trophy puedes tener múltiples webhooks para manejar uno o más tipos de eventos. Sigue los pasos a continuación para comenzar a recibir eventos de webhook de Trophy. @@ -41,7 +41,7 @@ Luego, configura este endpoint para hacer de proxy con los eventos que recibe ha height="200" width="100%" noZoom - src="../assets/webhooks/quickstart/hookdeck.png" + src="../../assets/webhooks/quickstart/hookdeck.png" /> @@ -64,7 +64,7 @@ Luego, configura este endpoint para hacer de proxy con los eventos que recibe ha height="200" width="100%" noZoom - src="../assets/webhooks/quickstart/dashboard.png" + src="../../assets/webhooks/quickstart/dashboard.png" /> @@ -99,7 +99,7 @@ Trophy enviará solicitudes a tu manejador, indicándole qué `type` de evento e ``` - Asegúrate de que tu manejador de webhook esté configurado para devolver un código de estado `200`. Si Trophy detecta que tu manejador devuelve un código de estado que no es `2XX`, lo tratará como un fallo e [intentará la solicitud nuevamente](/webhooks/retries). + Asegúrate de que tu manejador de webhook esté configurado para devolver un código de estado `200`. Si Trophy detecta que tu manejador devuelve un código de estado que no es `2XX`, lo tratará como un fallo e [intentará la solicitud nuevamente](/es/webhooks/retries). @@ -129,7 +129,7 @@ Obtén tu secreto de webhook seguro desde la página de webhooks en Trophy. loop playsInline className="w-full aspect-15/4" - src="../assets/webhooks/copy_secret.mp4" + src="../../assets/webhooks/copy_secret.mp4" > @@ -176,7 +176,7 @@ Una vez que tengas tu secreto de webhook, estás listo para comenzar a validar e -## Ejemplo Completo de Webhook +## Ejemplo Completo de Webhook {#full-webhook-example} Aquí tienes un ejemplo completo y funcional de un endpoint de webhook en NextJS capaz de recibir de forma segura eventos de webhook desde Trophy. @@ -232,6 +232,6 @@ function handleEvent(payload: WebhookPayload) { } ``` -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Comunícate con nosotros por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/webhooks/retries.mdx b/es/webhooks/retries.mdx index edb9d79..ca0dcf4 100644 --- a/es/webhooks/retries.mdx +++ b/es/webhooks/retries.mdx @@ -18,6 +18,6 @@ Trophy reintentará automáticamente cualquier evento que falle por una de las s con un intervalo de 1 minuto. -## Obtener Soporte +## Obtener Soporte {#get-support} ¿Quieres ponerte en contacto con el equipo de Trophy? Contáctanos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudarte! diff --git a/es/webhooks/security.mdx b/es/webhooks/security.mdx index e544244..f5ff3c0 100644 --- a/es/webhooks/security.mdx +++ b/es/webhooks/security.mdx @@ -7,13 +7,13 @@ subtitle: Cree endpoints de webhook seguros utilizando la verificación de firma icon: shield-check --- -## Firmas de Webhook +## Firmas de Webhook {#webhook-signatures} Para ayudarle a proteger sus controladores de webhook de modo que solo respondan a eventos enviados desde Trophy y no de atacantes maliciosos, Trophy incluye una firma de webhook con cada evento. Esta firma se envía en el encabezado `X-Trophy-Signature` y es un hash codificado en `base64` del payload de la solicitud, hasheado usando un secreto de webhook seguro proporcionado por Trophy. -## Secretos de Webhook +## Secretos de Webhook {#webhook-secrets} Cada webhook que configure en Trophy tiene un secreto de webhook único al que puede acceder desde la [página de webhooks](https://app.trophy.so/integration/webhooks) en el panel de Trophy. @@ -22,7 +22,7 @@ Cada webhook que configure en Trophy tiene un secreto de webhook único al que p y no lo confirme en el control de versiones.
-## Protección de Controladores de Webhook +## Protección de Controladores de Webhook {#securing-webhook-handlers} Para validar que los eventos que recibe su controlador de webhook realmente provienen de Trophy, necesita crear su propio hash usando su [secreto de webhook](#webhook-secrets) seguro y compararlo con la [firma de webhook](#webhook-signatures) en el encabezado `X-Trophy-Signature`. @@ -33,7 +33,7 @@ Para validar que los eventos que recibe su controlador de webhook realmente prov loop playsInline className="w-full aspect-15/4" - src="../assets/webhooks/copy_secret.mp4" + src="../../assets/webhooks/copy_secret.mp4" > @@ -64,6 +64,6 @@ Una vez que tenga su secreto de webhook, estará listo para comenzar a validar e importante rechazar la solicitud lo antes posible con un código de estado `4XX`. -## Obtenga Soporte +## Obtenga Soporte {#get-support} ¿Desea ponerse en contacto con el equipo de Trophy? Contáctenos por [correo electrónico](mailto:support@trophy.so). ¡Estamos aquí para ayudar! diff --git a/i18n.json b/i18n.json index 6b237f9..d10b163 100644 --- a/i18n.json +++ b/i18n.json @@ -75,41 +75,6 @@ "[locale]/platform/push-notifications.mdx", "[locale]/platform/streaks.mdx", "[locale]/platform/users.mdx", - "[locale]/snippets/add-env-var-block.mdx", - "[locale]/snippets/all-achievements-request-block.mdx", - "[locale]/snippets/all-achievements-response-block.mdx", - "[locale]/snippets/control-flag-block.mdx", - "[locale]/snippets/event-attributes-request-block.mdx", - "[locale]/snippets/get-user-notification-preferences-block.mdx", - "[locale]/snippets/idempotent-event-tracking.mdx", - "[locale]/snippets/identify-user-request-block.mdx", - "[locale]/snippets/identify-user-with-device-tokens-request-block.mdx", - "[locale]/snippets/leaderboard-rankings-request-multiple-attributes.mdx", - "[locale]/snippets/leaderboard-rankings-request-single-attribute.mdx", - "[locale]/snippets/leaderboard-rankings-request.mdx", - "[locale]/snippets/leaderboard-rankings-response.mdx", - "[locale]/snippets/metric-change-event-points-level-excerpt-block.mdx", - "[locale]/snippets/metric-change-request-block.mdx", - "[locale]/snippets/metric-change-response-block.mdx", - "[locale]/snippets/metric-event-with-device-tokens-request-block.mdx", - "[locale]/snippets/points-histogram-summary-response-block.mdx", - "[locale]/snippets/points-level-summary-response-block.mdx", - "[locale]/snippets/points-levels-list-response-block.mdx", - "[locale]/snippets/points-system-response-block.mdx", - "[locale]/snippets/sdk-install-command.mdx", - "[locale]/snippets/snippet-intro.mdx", - "[locale]/snippets/streak-request-block.mdx", - "[locale]/snippets/streak-response-block.mdx", - "[locale]/snippets/update-user-notification-preferences-block.mdx", - "[locale]/snippets/user-achievements-request-block.mdx", - "[locale]/snippets/user-energy-response-block.mdx", - "[locale]/snippets/user-leaderboard-rankings-request.mdx", - "[locale]/snippets/user-leaderboard-rankings-response.mdx", - "[locale]/snippets/user-points-request-block.mdx", - "[locale]/snippets/user-points-response-block.mdx", - "[locale]/snippets/user-points-summary-request-block.mdx", - "[locale]/snippets/user-points-summary-response-block.mdx", - "[locale]/snippets/webhook-points-level-changed-payload-block.mdx", "[locale]/webhooks/events/achievements/achievement-completed.mdx", "[locale]/webhooks/events/leaderboards/leaderboard-changed.mdx", "[locale]/webhooks/events/leaderboards/leaderboard-finished.mdx", diff --git a/package.json b/package.json index 9a47e3d..15213eb 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,16 @@ "dev": "mint dev", "translate:generate": "node --env-file=.env scripts/generate-translations.mjs", "translate:docs-json": "node --env-file=.env scripts/translate-docs-json.mjs", + "translate:localize-links": "node scripts/localize-internal-links.mjs", + "translate:localize-links:all": "node scripts/localize-internal-links.mjs --all", + "translate:localize-links:check": "node scripts/localize-internal-links.mjs --all --check", + "translate:sync-anchors": "node scripts/sync-heading-anchors.mjs", + "translate:sync-anchors:check": "node scripts/sync-heading-anchors.mjs --check", "lingo:validate-glossary": "node scripts/validate-glossary-csv.mjs", "translate:verify": "npx lingo.dev@latest run --frozen", - "translate:validate": "node --env-file=.env scripts/validate-translations.mjs" + "translate:validate": "node scripts/validate-translations.mjs", + "mint:validate": "npx mint@latest validate", + "mint:broken-links": "npx mint@latest broken-links --check-anchors --check-snippets" }, "repository": { "type": "git", diff --git a/scripts/generate-translations.mjs b/scripts/generate-translations.mjs index 06dbd4e..08f2c7c 100644 --- a/scripts/generate-translations.mjs +++ b/scripts/generate-translations.mjs @@ -80,8 +80,10 @@ try { const forceFlag = forceRetranslate ? " --force" : ""; execSync(`npx lingo.dev@latest run --target-locale ${target}${forceFlag}`, { stdio: "inherit" }); execSync(`node scripts/translate-docs-json.mjs --target ${target}`, { stdio: "inherit" }); + execSync(`node scripts/localize-internal-links.mjs --target ${target}`, { stdio: "inherit" }); console.log(`Translation generation completed for target locale: ${target}`); } + execSync("node scripts/sync-heading-anchors.mjs", { stdio: "inherit" }); } finally { if (fs.existsSync(backupPath)) { fs.copyFileSync(backupPath, configPath); diff --git a/scripts/localize-internal-links.mjs b/scripts/localize-internal-links.mjs new file mode 100644 index 0000000..b62f189 --- /dev/null +++ b/scripts/localize-internal-links.mjs @@ -0,0 +1,117 @@ +#!/usr/bin/env node + +import fs from "node:fs"; +import path from "node:path"; + +const args = process.argv.slice(2); + +function getArg(name) { + const idx = args.indexOf(name); + if (idx === -1) return null; + return args[idx + 1] ?? null; +} + +function hasFlag(name) { + return args.includes(name); +} + +const config = JSON.parse(fs.readFileSync("i18n.json", "utf8")); +const sourceLocale = config.locale?.source || "en"; +const targetLocales = Array.isArray(config.locale?.targets) ? config.locale.targets : []; +const knownLocales = [sourceLocale, ...targetLocales]; + +const targetArg = getArg("--target"); +const allLocales = hasFlag("--all"); +const checkOnly = hasFlag("--check"); + +const locales = allLocales + ? knownLocales + : (targetArg + ? targetArg.split(",").map((s) => s.trim()).filter(Boolean) + : targetLocales); + +if (locales.length === 0) { + console.error("No locales to process. Use --target or ensure i18n.json locale.targets exists."); + process.exit(1); +} + +const excludedPrefixes = [ + "assets/", + "images/", + "logo/", + "icons/", + "favicon/" +]; + +function shouldPrefix(rawPath, locale) { + if (!rawPath || rawPath.startsWith("#")) return false; + if (rawPath.startsWith("http://") || rawPath.startsWith("https://") || rawPath.startsWith("mailto:")) { + return false; + } + if (excludedPrefixes.some((prefix) => rawPath.startsWith(prefix))) return false; + if (knownLocales.some((l) => rawPath === l || rawPath.startsWith(`${l}/`))) return false; + + const first = rawPath.split("/")[0]; + if (first.includes(".") && !rawPath.includes("/")) return false; + return true; +} + +function rewriteContent(content, locale) { + let next = content; + + // Markdown links: [label](/path) + next = next.replace(/\]\(\/([^)]+)\)/g, (full, p1) => { + const p = p1.trim(); + if (!shouldPrefix(p, locale)) return full; + return `](/${locale}/${p})`; + }); + + // JSX/HTML attrs: href="/path" only (keep relative image/video src unchanged) + next = next.replace(/\bhref=(["'])\/([^"']+)\1/g, (full, quote, p1) => { + const p = p1.trim(); + if (!shouldPrefix(p, locale)) return full; + return `href=${quote}/${locale}/${p}${quote}`; + }); + + return next; +} + +function walkMdx(dir) { + if (!fs.existsSync(dir)) return []; + const out = []; + const stack = [dir]; + while (stack.length) { + const current = stack.pop(); + const entries = fs.readdirSync(current, { withFileTypes: true }); + for (const entry of entries) { + const abs = path.join(current, entry.name); + if (entry.isDirectory()) stack.push(abs); + if (entry.isFile() && abs.endsWith(".mdx")) out.push(abs); + } + } + return out; +} + +let changedFiles = 0; + +for (const locale of locales) { + const files = walkMdx(locale); + for (const file of files) { + const raw = fs.readFileSync(file, "utf8"); + const next = rewriteContent(raw, locale); + if (next !== raw) { + changedFiles += 1; + if (!checkOnly) fs.writeFileSync(file, next); + } + } +} + +if (checkOnly) { + if (changedFiles > 0) { + console.error(`Found ${changedFiles} file(s) with non-localized internal links.`); + process.exit(1); + } + console.log("Internal links are locale-localized."); +} else { + console.log(`Localized internal links in ${changedFiles} file(s).`); +} diff --git a/scripts/sync-heading-anchors.mjs b/scripts/sync-heading-anchors.mjs new file mode 100644 index 0000000..6b77d03 --- /dev/null +++ b/scripts/sync-heading-anchors.mjs @@ -0,0 +1,254 @@ +#!/usr/bin/env node + +/** + * Mintlify custom heading IDs using markdown ATX + `{#slug}` (see Mintlify + * “Custom heading IDs”). Migrates legacy one-line HTML `` from older + * runs back to `## Title {#slug}`. + * + * - en/: slugs from English visible title (Mintlify-style + duplicate suffixes). + * - targets: same `{#slug}` list in document order. + * Skips lines inside fenced code blocks (```). + */ + +import fs from "node:fs"; +import path from "node:path"; + +const args = process.argv.slice(2); + +function getArg(name) { + const idx = args.indexOf(name); + if (idx === -1) return null; + return args[idx + 1] ?? null; +} + +function hasFlag(name) { + return args.includes(name); +} + +const checkOnly = hasFlag("--check"); +const targetArg = getArg("--target"); + +const ROOT = process.cwd(); +const config = JSON.parse(fs.readFileSync(path.join(ROOT, "i18n.json"), "utf8")); +const sourceLocale = config.locale?.source || "en"; +const configuredTargets = Array.isArray(config.locale?.targets) ? config.locale.targets : []; + +const targets = targetArg + ? targetArg.split(",").map((s) => s.trim()).filter(Boolean) + : configuredTargets; + +/** Mintlify: lowercase, spaces → hyphens, remove specials, keep letters/digits. */ +function mintlifySlugBase(raw) { + const s = raw + .toLowerCase() + .trim() + .replace(/\s+/g, "-") + .replace(/[^a-z0-9-]/g, "") + .replace(/-+/g, "-") + .replace(/^-|-$/g, ""); + return s || "heading"; +} + +/** Strip `{#id}` from end of heading text (legacy ATX syntax). */ +function stripTrailingAnchor(title) { + return title.replace(/\s*\{#[^#}]*\}\s*$/, "").trim(); +} + +function stripHtmlTags(s) { + return s.replace(/<[^>]+>/g, ""); +} + +/** Undo minimal escapes we emit in HTML headings so slugs stay stable across runs. */ +function decodeBasicEntities(s) { + return s + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, '"') + .replace(/'/g, "'"); +} + +/** Lightweight markdown strip for slugging (bold/italic/code/link text). */ +function plainForSlug(title) { + let s = stripTrailingAnchor(title); + s = stripHtmlTags(s); + s = decodeBasicEntities(s); + s = s.replace(/\*\*([^*]+)\*\*/g, "$1"); + s = s.replace(/\*([^*]+)\*/g, "$1"); + s = s.replace(/__([^_]+)__/g, "$1"); + s = s.replace(/_([^_]+)_/g, "$1"); + s = s.replace(/`([^`]+)`/g, "$1"); + s = s.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1"); + return s.trim(); +} + +function uniqueSlugsFromTitles(titles) { + const seen = new Map(); + const slugs = []; + for (const title of titles) { + const base = mintlifySlugBase(plainForSlug(title)); + let count = seen.get(base) ?? 0; + seen.set(base, count + 1); + const slug = count === 0 ? base : `${base}-${count}`; + slugs.push(slug); + } + return slugs; +} + +const ATX_HEADING_LINE = /^(#{1,6})\s+(.+)$/; +/** Single-line HTML heading with id (output format). */ +const HTML_HEADING_LINE = /^]*)>(.*)<\/h\1>\s*$/; + +function extractHeadingTitlesFromSource(content) { + const titles = []; + let inFence = false; + const lines = content.split("\n"); + for (const line of lines) { + if (/^\s*```/.test(line)) inFence = !inFence; + if (inFence) continue; + const html = line.match(HTML_HEADING_LINE); + if (html) { + titles.push(html[4]); + continue; + } + const atx = line.match(ATX_HEADING_LINE); + if (atx) titles.push(atx[2].trim()); + } + return titles; +} + +function rewriteHeadings(content, slugs) { + let inFence = false; + let i = 0; + const lines = content.split("\n"); + const out = lines.map((line) => { + if (/^\s*```/.test(line)) inFence = !inFence; + if (inFence) return line; + + const html = line.match(HTML_HEADING_LINE); + if (html) { + const level = parseInt(html[1], 10); + const inner = html[4]; + const slug = slugs[i]; + i += 1; + if (!slug) return line; + const visible = stripHtmlTags(decodeBasicEntities(inner)).trim(); + return `${"#".repeat(level)} ${visible} {#${slug}}`; + } + + const atx = line.match(ATX_HEADING_LINE); + if (atx) { + const hashes = atx[1]; + const rest = atx[2].trim(); + const visible = stripTrailingAnchor(rest); + const slug = slugs[i]; + i += 1; + if (!slug) return line; + return `${hashes} ${visible} {#${slug}}`; + } + + return line; + }); + if (i !== slugs.length) { + throw new Error(`Heading count mismatch: used ${i} headings but have ${slugs.length} slugs`); + } + return out.join("\n"); +} + +function walkMdx(dir) { + if (!fs.existsSync(dir)) return []; + const out = []; + const stack = [dir]; + while (stack.length) { + const cur = stack.pop(); + for (const e of fs.readdirSync(cur, { withFileTypes: true })) { + const abs = path.join(cur, e.name); + if (e.isDirectory()) stack.push(abs); + else if (e.isFile() && e.name.endsWith(".mdx")) out.push(abs); + } + } + return out.sort(); +} + +function relUnderLocale(abs, locale) { + return path.relative(path.join(ROOT, locale), abs).replace(/\\/g, "/"); +} + +function processSourceFile(absPath) { + const raw = fs.readFileSync(absPath, "utf8"); + const titles = extractHeadingTitlesFromSource(raw); + const slugs = uniqueSlugsFromTitles(titles); + const next = rewriteHeadings(raw, slugs); + return { next, changed: next !== raw }; +} + +let errorCount = 0; +let changedFiles = 0; + +const sourceDir = path.join(ROOT, sourceLocale); +const sourceFiles = walkMdx(sourceDir); + +for (const abs of sourceFiles) { + try { + const { next, changed } = processSourceFile(abs); + if (changed) { + changedFiles += 1; + if (!checkOnly) fs.writeFileSync(abs, next); + } + } catch (e) { + console.error(String(e.message || e)); + errorCount += 1; + } +} + +for (const locale of targets) { + if (locale === sourceLocale) continue; + const localeDir = path.join(ROOT, locale); + for (const abs of sourceFiles) { + const rel = relUnderLocale(abs, sourceLocale); + const targetPath = path.join(localeDir, rel); + if (!fs.existsSync(targetPath)) continue; + try { + const raw = fs.readFileSync(targetPath, "utf8"); + const enPath = abs; + const enRaw = fs.readFileSync(enPath, "utf8"); + const titles = extractHeadingTitlesFromSource(enRaw); + const slugs = uniqueSlugsFromTitles(titles); + const targetTitles = extractHeadingTitlesFromSource(raw); + if (targetTitles.length !== titles.length) { + throw new Error( + `Heading count mismatch for ${locale}/${rel}: en ${titles.length} vs ${locale} ${targetTitles.length}` + ); + } + const next = rewriteHeadings(raw, slugs); + const changed = next !== raw; + if (changed) { + changedFiles += 1; + if (!checkOnly) fs.writeFileSync(targetPath, next); + } + } catch (e) { + console.error(String(e.message || e)); + errorCount += 1; + } + } +} + +if (checkOnly) { + if (errorCount > 0) { + console.error(`sync-heading-anchors: ${errorCount} error(s).`); + process.exit(1); + } + if (changedFiles > 0) { + console.error( + `sync-heading-anchors: ${changedFiles} file(s) are out of sync. Run: node scripts/sync-heading-anchors.mjs` + ); + process.exit(1); + } + console.log("Heading anchor sync check passed."); +} else { + if (errorCount > 0) { + console.error(`sync-heading-anchors: ${errorCount} error(s).`); + process.exit(1); + } + console.log(`sync-heading-anchors: updated ${changedFiles} file(s).`); +} diff --git a/en/snippets/add-env-var-block.mdx b/snippets/add-env-var-block.mdx similarity index 100% rename from en/snippets/add-env-var-block.mdx rename to snippets/add-env-var-block.mdx diff --git a/en/snippets/all-achievements-request-block.mdx b/snippets/all-achievements-request-block.mdx similarity index 100% rename from en/snippets/all-achievements-request-block.mdx rename to snippets/all-achievements-request-block.mdx diff --git a/en/snippets/all-achievements-response-block.mdx b/snippets/all-achievements-response-block.mdx similarity index 100% rename from en/snippets/all-achievements-response-block.mdx rename to snippets/all-achievements-response-block.mdx diff --git a/en/snippets/control-flag-block.mdx b/snippets/control-flag-block.mdx similarity index 100% rename from en/snippets/control-flag-block.mdx rename to snippets/control-flag-block.mdx diff --git a/en/snippets/event-attributes-request-block.mdx b/snippets/event-attributes-request-block.mdx similarity index 100% rename from en/snippets/event-attributes-request-block.mdx rename to snippets/event-attributes-request-block.mdx diff --git a/en/snippets/get-user-notification-preferences-block.mdx b/snippets/get-user-notification-preferences-block.mdx similarity index 100% rename from en/snippets/get-user-notification-preferences-block.mdx rename to snippets/get-user-notification-preferences-block.mdx diff --git a/en/snippets/idempotent-event-tracking.mdx b/snippets/idempotent-event-tracking.mdx similarity index 100% rename from en/snippets/idempotent-event-tracking.mdx rename to snippets/idempotent-event-tracking.mdx diff --git a/en/snippets/identify-user-request-block.mdx b/snippets/identify-user-request-block.mdx similarity index 100% rename from en/snippets/identify-user-request-block.mdx rename to snippets/identify-user-request-block.mdx diff --git a/en/snippets/identify-user-with-device-tokens-request-block.mdx b/snippets/identify-user-with-device-tokens-request-block.mdx similarity index 100% rename from en/snippets/identify-user-with-device-tokens-request-block.mdx rename to snippets/identify-user-with-device-tokens-request-block.mdx diff --git a/en/snippets/leaderboard-rankings-request-multiple-attributes.mdx b/snippets/leaderboard-rankings-request-multiple-attributes.mdx similarity index 100% rename from en/snippets/leaderboard-rankings-request-multiple-attributes.mdx rename to snippets/leaderboard-rankings-request-multiple-attributes.mdx diff --git a/en/snippets/leaderboard-rankings-request-single-attribute.mdx b/snippets/leaderboard-rankings-request-single-attribute.mdx similarity index 100% rename from en/snippets/leaderboard-rankings-request-single-attribute.mdx rename to snippets/leaderboard-rankings-request-single-attribute.mdx diff --git a/en/snippets/leaderboard-rankings-request.mdx b/snippets/leaderboard-rankings-request.mdx similarity index 100% rename from en/snippets/leaderboard-rankings-request.mdx rename to snippets/leaderboard-rankings-request.mdx diff --git a/en/snippets/leaderboard-rankings-response.mdx b/snippets/leaderboard-rankings-response.mdx similarity index 100% rename from en/snippets/leaderboard-rankings-response.mdx rename to snippets/leaderboard-rankings-response.mdx diff --git a/en/snippets/metric-change-event-points-level-excerpt-block.mdx b/snippets/metric-change-event-points-level-excerpt-block.mdx similarity index 100% rename from en/snippets/metric-change-event-points-level-excerpt-block.mdx rename to snippets/metric-change-event-points-level-excerpt-block.mdx diff --git a/en/snippets/metric-change-request-block.mdx b/snippets/metric-change-request-block.mdx similarity index 100% rename from en/snippets/metric-change-request-block.mdx rename to snippets/metric-change-request-block.mdx diff --git a/en/snippets/metric-change-response-block.mdx b/snippets/metric-change-response-block.mdx similarity index 100% rename from en/snippets/metric-change-response-block.mdx rename to snippets/metric-change-response-block.mdx diff --git a/en/snippets/metric-event-with-device-tokens-request-block.mdx b/snippets/metric-event-with-device-tokens-request-block.mdx similarity index 100% rename from en/snippets/metric-event-with-device-tokens-request-block.mdx rename to snippets/metric-event-with-device-tokens-request-block.mdx diff --git a/en/snippets/plan-badge.jsx b/snippets/plan-badge.jsx similarity index 100% rename from en/snippets/plan-badge.jsx rename to snippets/plan-badge.jsx diff --git a/en/snippets/points-histogram-summary-response-block.mdx b/snippets/points-histogram-summary-response-block.mdx similarity index 100% rename from en/snippets/points-histogram-summary-response-block.mdx rename to snippets/points-histogram-summary-response-block.mdx diff --git a/en/snippets/points-level-summary-response-block.mdx b/snippets/points-level-summary-response-block.mdx similarity index 100% rename from en/snippets/points-level-summary-response-block.mdx rename to snippets/points-level-summary-response-block.mdx diff --git a/en/snippets/points-levels-list-response-block.mdx b/snippets/points-levels-list-response-block.mdx similarity index 100% rename from en/snippets/points-levels-list-response-block.mdx rename to snippets/points-levels-list-response-block.mdx diff --git a/en/snippets/points-system-response-block.mdx b/snippets/points-system-response-block.mdx similarity index 100% rename from en/snippets/points-system-response-block.mdx rename to snippets/points-system-response-block.mdx diff --git a/en/snippets/rate-limit-badge.jsx b/snippets/rate-limit-badge.jsx similarity index 100% rename from en/snippets/rate-limit-badge.jsx rename to snippets/rate-limit-badge.jsx diff --git a/en/snippets/sdk-install-command.mdx b/snippets/sdk-install-command.mdx similarity index 100% rename from en/snippets/sdk-install-command.mdx rename to snippets/sdk-install-command.mdx diff --git a/en/snippets/snippet-intro.mdx b/snippets/snippet-intro.mdx similarity index 100% rename from en/snippets/snippet-intro.mdx rename to snippets/snippet-intro.mdx diff --git a/en/snippets/streak-request-block.mdx b/snippets/streak-request-block.mdx similarity index 100% rename from en/snippets/streak-request-block.mdx rename to snippets/streak-request-block.mdx diff --git a/en/snippets/streak-response-block.mdx b/snippets/streak-response-block.mdx similarity index 100% rename from en/snippets/streak-response-block.mdx rename to snippets/streak-response-block.mdx diff --git a/en/snippets/update-user-notification-preferences-block.mdx b/snippets/update-user-notification-preferences-block.mdx similarity index 100% rename from en/snippets/update-user-notification-preferences-block.mdx rename to snippets/update-user-notification-preferences-block.mdx diff --git a/en/snippets/user-achievements-request-block.mdx b/snippets/user-achievements-request-block.mdx similarity index 100% rename from en/snippets/user-achievements-request-block.mdx rename to snippets/user-achievements-request-block.mdx diff --git a/en/snippets/user-energy-response-block.mdx b/snippets/user-energy-response-block.mdx similarity index 100% rename from en/snippets/user-energy-response-block.mdx rename to snippets/user-energy-response-block.mdx diff --git a/en/snippets/user-leaderboard-rankings-request.mdx b/snippets/user-leaderboard-rankings-request.mdx similarity index 100% rename from en/snippets/user-leaderboard-rankings-request.mdx rename to snippets/user-leaderboard-rankings-request.mdx diff --git a/en/snippets/user-leaderboard-rankings-response.mdx b/snippets/user-leaderboard-rankings-response.mdx similarity index 100% rename from en/snippets/user-leaderboard-rankings-response.mdx rename to snippets/user-leaderboard-rankings-response.mdx diff --git a/en/snippets/user-points-request-block.mdx b/snippets/user-points-request-block.mdx similarity index 100% rename from en/snippets/user-points-request-block.mdx rename to snippets/user-points-request-block.mdx diff --git a/en/snippets/user-points-response-block.mdx b/snippets/user-points-response-block.mdx similarity index 100% rename from en/snippets/user-points-response-block.mdx rename to snippets/user-points-response-block.mdx diff --git a/en/snippets/user-points-summary-request-block.mdx b/snippets/user-points-summary-request-block.mdx similarity index 100% rename from en/snippets/user-points-summary-request-block.mdx rename to snippets/user-points-summary-request-block.mdx diff --git a/en/snippets/user-points-summary-response-block.mdx b/snippets/user-points-summary-response-block.mdx similarity index 100% rename from en/snippets/user-points-summary-response-block.mdx rename to snippets/user-points-summary-response-block.mdx diff --git a/en/snippets/webhook-points-level-changed-payload-block.mdx b/snippets/webhook-points-level-changed-payload-block.mdx similarity index 100% rename from en/snippets/webhook-points-level-changed-payload-block.mdx rename to snippets/webhook-points-level-changed-payload-block.mdx From 3a71258d4c897d8d184dad15e53a522be900aaaa Mon Sep 17 00:00:00 2001 From: Charlie Hopkins-Brinicombe Date: Tue, 31 Mar 2026 01:39:13 +0100 Subject: [PATCH 06/16] Re-generate translations to ensure api and webhook names are translated and get new lock file --- .cursor/rules/localization-workflow.mdc | 4 +- .github/workflows/translate-on-main.yml | 3 + .github/workflows/validate-translations.yml | 4 + README.md | 10 +- .../endpoints/points/archive-a-boost.mdx | 3 +- .../endpoints/points/archive-boosts-batch.mdx | 3 +- .../endpoints/points/create-boosts.mdx | 3 +- .../endpoints/streaks/grant-freezes.mdx | 3 +- .../endpoints/streaks/restore-streaks.mdx | 3 +- .../achievements/all-achievements.mdx | 3 +- .../mark-an-achievement-as-completed.mdx | 3 +- .../get-all-active-leaderboards.mdx | 3 +- .../leaderboards/get-leaderboard.mdx | 3 +- .../metrics/send-a-metric-change-event.mdx | 3 +- .../endpoints/points/get-points-boosts.mdx | 3 +- .../points/get-points-level-summary.mdx | 3 +- .../endpoints/points/get-points-levels.mdx | 3 +- .../endpoints/points/get-points-summary.mdx | 3 +- .../endpoints/points/get-points.mdx | 3 +- .../endpoints/streaks/get-streak-rankings.mdx | 3 +- .../endpoints/streaks/get-streaks.mdx | 3 +- .../endpoints/users/create-a-user.mdx | 3 +- ...single-metric-event-summary-for-a-user.mdx | 3 +- .../users/get-a-single-metric-for-a-user.mdx | 3 +- .../endpoints/users/get-a-single-user.mdx | 3 +- .../get-a-users-completed-achievements.mdx | 3 +- .../users/get-a-users-leaderboard.mdx | 3 +- .../users/get-a-users-points-boosts.mdx | 3 +- .../users/get-a-users-points-summary.mdx | 3 +- .../endpoints/users/get-a-users-points.mdx | 3 +- .../endpoints/users/get-a-users-streak.mdx | 3 +- .../endpoints/users/get-a-users-wrapped.mdx | 3 +- .../users/get-all-metrics-for-a-user.mdx | 3 +- .../endpoints/users/get-user-preferences.mdx | 3 +- .../endpoints/users/identify-a-user.mdx | 3 +- .../endpoints/users/update-a-user.mdx | 3 +- .../users/update-user-preferences.mdx | 3 +- .../achievements/achievement-completed.mdx | 3 +- .../leaderboards/leaderboard-changed.mdx | 3 +- .../leaderboards/leaderboard-finished.mdx | 3 +- .../leaderboards/leaderboard-rank-changed.mdx | 3 +- .../leaderboards/leaderboard-started.mdx | 3 +- .../events/points/points-boost-finished.mdx | 3 +- .../events/points/points-boost-started.mdx | 3 +- en/webhooks/events/points/points-changed.mdx | 3 +- .../events/points/points-level-changed.mdx | 3 +- .../events/streaks/streak-extended.mdx | 3 +- .../events/streaks/streak-freeze-consumed.mdx | 3 +- .../events/streaks/streak-freeze-earned.mdx | 3 +- en/webhooks/events/streaks/streak-lost.mdx | 3 +- en/webhooks/events/streaks/streak-started.mdx | 3 +- es/account/billing.mdx | 42 +- es/account/branding.mdx | 18 +- es/account/members.mdx | 20 +- es/account/overview.mdx | 11 +- .../endpoints/points/archive-a-boost.mdx | 5 +- .../endpoints/points/archive-boosts-batch.mdx | 3 +- .../endpoints/points/create-boosts.mdx | 5 +- .../endpoints/streaks/grant-freezes.mdx | 5 +- .../endpoints/streaks/restore-streaks.mdx | 3 +- es/admin-api/introduction.mdx | 17 +- es/api-reference/authentication.mdx | 28 +- es/api-reference/client-libraries.mdx | 12 +- .../achievements/all-achievements.mdx | 3 +- .../mark-an-achievement-as-completed.mdx | 3 +- .../get-all-active-leaderboards.mdx | 5 +- .../leaderboards/get-leaderboard.mdx | 3 +- .../metrics/send-a-metric-change-event.mdx | 5 +- .../endpoints/points/get-points-boosts.mdx | 3 +- .../points/get-points-level-summary.mdx | 5 +- .../endpoints/points/get-points-levels.mdx | 5 +- .../endpoints/points/get-points-summary.mdx | 5 +- .../endpoints/points/get-points.mdx | 5 +- .../endpoints/streaks/get-streak-rankings.mdx | 3 +- .../endpoints/streaks/get-streaks.mdx | 5 +- .../endpoints/users/create-a-user.mdx | 5 +- ...single-metric-event-summary-for-a-user.mdx | 5 +- .../users/get-a-single-metric-for-a-user.mdx | 5 +- .../endpoints/users/get-a-single-user.mdx | 5 +- .../get-a-users-completed-achievements.mdx | 5 +- .../users/get-a-users-leaderboard.mdx | 5 +- .../users/get-a-users-points-boosts.mdx | 5 +- .../users/get-a-users-points-summary.mdx | 3 +- .../endpoints/users/get-a-users-points.mdx | 5 +- .../endpoints/users/get-a-users-streak.mdx | 3 +- .../endpoints/users/get-a-users-wrapped.mdx | 5 +- .../users/get-all-metrics-for-a-user.mdx | 3 +- .../endpoints/users/get-user-preferences.mdx | 5 +- .../endpoints/users/identify-a-user.mdx | 5 +- .../endpoints/users/update-a-user.mdx | 3 +- .../users/update-user-preferences.mdx | 3 +- es/api-reference/idempotency.mdx | 38 +- es/api-reference/introduction.mdx | 11 +- es/api-reference/openapi.yml | 6288 ----------------- es/api-reference/rate-limiting.mdx | 14 +- es/experimentation/engagement.mdx | 12 +- es/experimentation/overview.mdx | 34 +- es/experimentation/retention.mdx | 6 +- es/getting-started/introduction.mdx | 22 +- es/getting-started/quickstart.mdx | 20 +- es/guides/gamified-fitness-platform.mdx | 331 +- es/guides/gamified-study-platform.mdx | 246 +- .../how-to-build-a-leaderboards-feature.mdx | 68 +- es/guides/how-to-build-a-streaks-feature.mdx | 58 +- .../how-to-build-an-achievements-feature.mdx | 66 +- es/guides/how-to-build-an-energy-feature.mdx | 50 +- es/guides/how-to-build-an-xp-feature.mdx | 72 +- es/platform/achievements.mdx | 166 +- es/platform/emails.mdx | 254 +- es/platform/events.mdx | 82 +- es/platform/leaderboards.mdx | 134 +- es/platform/metrics.mdx | 54 +- es/platform/overview.mdx | 30 +- es/platform/points.mdx | 264 +- es/platform/push-notifications.mdx | 78 +- es/platform/streaks.mdx | 61 +- es/platform/users.mdx | 174 +- .../achievements/achievement-completed.mdx | 5 +- .../leaderboards/leaderboard-changed.mdx | 3 +- .../leaderboards/leaderboard-finished.mdx | 11 +- .../leaderboards/leaderboard-rank-changed.mdx | 3 +- .../leaderboards/leaderboard-started.mdx | 5 +- .../events/points/points-boost-finished.mdx | 7 +- .../events/points/points-boost-started.mdx | 11 +- es/webhooks/events/points/points-changed.mdx | 5 +- .../events/points/points-level-changed.mdx | 5 +- .../events/streaks/streak-extended.mdx | 5 +- .../events/streaks/streak-freeze-consumed.mdx | 3 +- .../events/streaks/streak-freeze-earned.mdx | 3 +- es/webhooks/events/streaks/streak-lost.mdx | 3 +- es/webhooks/events/streaks/streak-started.mdx | 3 +- es/webhooks/idempotency.mdx | 24 +- es/webhooks/introduction.mdx | 22 +- es/webhooks/observability.mdx | 10 +- es/webhooks/quickstart.mdx | 48 +- es/webhooks/retries.mdx | 5 +- es/webhooks/security.mdx | 26 +- i18n.lock | 1895 +++-- en/api-reference/openapi.yml => openapi.yml | 0 package-lock.json | 21 +- package.json | 7 +- scripts/generate-translations.mjs | 15 +- scripts/localize-internal-links.mjs | 78 +- scripts/sync-openapi-titles.mjs | 237 + 144 files changed, 2746 insertions(+), 8798 deletions(-) delete mode 100644 es/api-reference/openapi.yml rename en/api-reference/openapi.yml => openapi.yml (100%) create mode 100644 scripts/sync-openapi-titles.mjs diff --git a/.cursor/rules/localization-workflow.mdc b/.cursor/rules/localization-workflow.mdc index 79e1536..2232544 100644 --- a/.cursor/rules/localization-workflow.mdc +++ b/.cursor/rules/localization-workflow.mdc @@ -9,8 +9,10 @@ alwaysApply: true - 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/`. Locale pages are nested under `en/` or `es/` (typically `locale/
/.mdx`), so relative `src` paths must account for that depth (for example `../../assets/...`). `scripts/localize-internal-links.mjs` only localizes absolute `href` paths, not media `src` or snippet imports. +- Shared media lives under repo-root `assets/`. Locale pages are nested under `en/` or `es/` (typically `locale/
/.mdx`), so relative `src` paths must account for that depth (for example `../../assets/...`). `scripts/localize-internal-links.mjs` rewrites markdown `](/...)` and JSX `href="/..."` only: for whichever **`locale` is being processed**, bare paths get that prefix, and paths prefixed with **any other** locale from `i18n.json` are re-prefixed to that same active locale; it does not change media `src` or snippet imports. - Shared MDX/JSX snippets live under repo-root `snippets/` (not per-locale). Import with relative paths from each page; do not add `snippets/` to Lingo `i18n.json` buckets unless you intend to translate them. +- Keep a **single** OpenAPI document at repo-root `openapi.yml` (not under `en/` or `es/`). Endpoint and webhook MDX must use Mintlify’s form `openapi: openapi.yml ` or `openapi: openapi.yml webhook `. Do not translate the `openapi:` line. +- 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 via `scripts/sync-heading-anchors.mjs`. Never translate or alter `{#…}`; sync runs after Lingo in PIT/CI. Do not merge heading-count drift between `en/` and target locales without fixing structure first. - 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. diff --git a/.github/workflows/translate-on-main.yml b/.github/workflows/translate-on-main.yml index ea63e29..601b5e6 100644 --- a/.github/workflows/translate-on-main.yml +++ b/.github/workflows/translate-on-main.yml @@ -35,6 +35,9 @@ jobs: fi node -e 'const fs=require("fs");const p="i18n.json";const j=JSON.parse(fs.readFileSync(p,"utf8"));j.engineId=process.env.LINGO_ENGINE_ID;fs.writeFileSync(p,JSON.stringify(j,null,2)+"\n");' + - name: Sync API and webhook titles from OpenAPI + run: node scripts/sync-openapi-titles.mjs + - name: Run Lingo translation env: LINGO_API_KEY: ${{ secrets.LINGO_API_KEY }} diff --git a/.github/workflows/validate-translations.yml b/.github/workflows/validate-translations.yml index aea34d0..112561f 100644 --- a/.github/workflows/validate-translations.yml +++ b/.github/workflows/validate-translations.yml @@ -5,6 +5,7 @@ on: paths: - "docs.json" - "i18n.json" + - "openapi.yml" - "package.json" - "en/**" - "es/**" @@ -47,6 +48,9 @@ jobs: - name: Ensure heading anchors match English slugs run: npm run translate:sync-anchors:check + - name: Ensure API and webhook titles match OpenAPI summaries + run: npm run translate:sync-openapi-titles:check + - name: Mintlify validate run: npx mint@latest validate diff --git a/README.md b/README.md index 2436a13..060ae1f 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,11 @@ Notes: - `lingo/glossary.csv`: Terms that must stay fixed or use specific translations. - `lingo/brand-voice.md`: Single brand voice used for all locales. - `scripts/translate-docs-json.mjs`: Translates language-specific `docs.json` navigation labels directly in the source-of-truth `docs.json`. Prefer `npm run translate:docs-json -- --target `. -- `scripts/localize-internal-links.mjs`: Rewrites internal absolute **navigation** links in locale content (for example `/getting-started/quickstart` -> `/es/getting-started/quickstart`). It does **not** change relative image or video `src` paths, MDX/JSX **import** paths, or shared **snippets**; those must be correct in source so all locales stay aligned. Prefer `npm run translate:localize-links --` with `--target`, `--all`, or `--all --check`. +- `scripts/localize-internal-links.mjs`: Rewrites internal absolute **navigation** links in each locale’s MDX for whatever locale is being processed (`--target`, `--all`, or default targets): bare paths get that locale’s prefix (e.g. `/platform/points` → `/es/platform/points` when processing `es/`), and any path already prefixed with **another** locale from `i18n.json` (`en`, `es`, future `fr`, etc.) is re-prefixed to the active locale (so `/en/...` or stale `/es/...` on a `fr/` page become `/fr/...`). Longer codes are matched first (e.g. `en-US` before `en`). It does **not** change relative image or video `src` paths, MDX/JSX **import** paths, or shared **snippets**; those must be correct in source so all locales stay aligned. Prefer `npm run translate:localize-links --` with `--target`, `--all`, or `--all --check`. - **Shared `snippets/`**: Reusable MDX and JSX live in repo-root `snippets/` (not under `en/` or `es/`). Import them with paths relative to each page (for example `../../snippets/foo.mdx` from `locale/
/.mdx`, or more `../` segments for deeper pages). They are excluded from Lingo buckets in `i18n.json` so they stay English and identical everywhere. - **Media paths**: Pages live under `en/
/...` or `es/
/...`, while shared files sit in repo-root `assets/`. Use a path like `../../assets/...` from a typical `locale/
/.mdx` file. +- **`openapi.yml`**: One OpenAPI 3.1 spec at the **repository root** (alongside `docs.json`). API and webhook pages reference it explicitly in frontmatter, for example `openapi: openapi.yml get /users/{id}` or `openapi: openapi.yml webhook points.changed`. Do not duplicate the YAML under locale folders; Lingo must not alter `openapi:` lines. +- **`scripts/sync-openapi-titles.mjs`**: Copies each operation/webhook **`summary`** from `openapi.yml` into the English page’s **`title:`** frontmatter (Mintlify’s default when `title` is omitted). Target locales get an English `title` only if missing (bootstrap); run **`npm run translate:generate`** so Lingo translates those titles. Runs automatically at the start of `translate:generate` and in translate-on-main before Lingo. - `scripts/sync-heading-anchors.mjs`: Writes Mintlify [custom heading IDs](https://www.mintlify.com/docs/create/text#custom-heading-ids) as **`## Title {#slug}`** markdown. Slugs match Mintlify’s auto rules from the **English** title so hashes like `#pro-plan` stay stable across locales. Run `npm run translate:sync-anchors` after bulk heading edits; translation pipelines run it automatically (see **Heading anchors and Lingo** below). The script can also migrate one-line **`

`** left from older tooling back to `{#slug}` syntax. - `scripts/validate-glossary-csv.mjs`: Validates glossary schema and duplicate canonical keys. Prefer `npm run lingo:validate-glossary`. @@ -85,6 +87,8 @@ Use `npm run