From 45effeabda19b1322b5670329a3c0945a81f74e6 Mon Sep 17 00:00:00 2001 From: Charlie Hopkins-Brinicombe Date: Thu, 14 May 2026 16:13:23 +0100 Subject: [PATCH 1/5] Update packge name --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 354f37a..856a8e7 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "shadcn", + "name": "ui", "version": "0.0.1", "private": true, "license": "MIT", From 3ac610501d0be9c4c2d1ec5c54e94a32b9007ac6 Mon Sep 17 00:00:00 2001 From: Charlie Hopkins-Brinicombe Date: Thu, 14 May 2026 16:41:04 +0100 Subject: [PATCH 2/5] Fetch OpenAPI specs from public URLs for docs code samples Replace vendored YAML with JSON from api.trophy.so and admin.trophy.so OpenAPI endpoints. Run openapi:build before www production builds instead of postinstall so installs stay offline-friendly. Remove the direct yaml dependency from www. Add a GitHub Actions workflow that regenerates the index and fails if the committed openapi-code-samples.json is out of date. Co-authored-by: Cursor --- .github/workflows/openapi-drift.yml | 29 + .../lib/generated/openapi-code-samples.json | 51 + apps/www/openapi/admin.yml | 5427 ---------------- apps/www/openapi/application.yml | 5531 ----------------- apps/www/package.json | 6 +- .../scripts/build-openapi-code-samples.mts | 65 +- pnpm-lock.yaml | 3 - 7 files changed, 135 insertions(+), 10977 deletions(-) create mode 100644 .github/workflows/openapi-drift.yml delete mode 100644 apps/www/openapi/admin.yml delete mode 100644 apps/www/openapi/application.yml diff --git a/.github/workflows/openapi-drift.yml b/.github/workflows/openapi-drift.yml new file mode 100644 index 0000000..c339fce --- /dev/null +++ b/.github/workflows/openapi-drift.yml @@ -0,0 +1,29 @@ +name: OpenAPI code samples + +on: + pull_request: + push: + branches: + - main + +jobs: + drift: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 9 + + - uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "pnpm" + + - run: pnpm install --frozen-lockfile + + - run: pnpm --filter www openapi:build + + - name: Ensure generated OpenAPI index matches commit + run: git diff --exit-code -- apps/www/lib/generated/openapi-code-samples.json diff --git a/apps/www/lib/generated/openapi-code-samples.json b/apps/www/lib/generated/openapi-code-samples.json index bae2d7d..c0f8895 100644 --- a/apps/www/lib/generated/openapi-code-samples.json +++ b/apps/www/lib/generated/openapi-code-samples.json @@ -526,6 +526,57 @@ ] } }, + "admin.yml post /leaderboards": { + "spec": "admin.yml", + "method": "post", + "path": "/leaderboards", + "samplesByLanguage": { + "javascript": [ + { + "source": "import { TrophyApiClient } from '@trophyso/node';\n\nconst trophy = new TrophyApiClient({\n apiKey: 'YOUR_API_KEY'\n});\n\nconst response = await trophy.admin.leaderboards.create([\n {\n name: 'Revenue Champions',\n key: 'revenue-champions',\n status: 'inactive',\n rankBy: 'metric',\n metricId: '550e8400-e29b-41d4-a716-446655440000',\n maxParticipants: 100,\n start: '2026-04-20'\n },\n {\n name: 'Streak Legends',\n key: 'streak-legends',\n status: 'scheduled',\n rankBy: 'streak',\n start: '2026-04-27'\n }\n]);\n" + } + ], + "python": [ + { + "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.admin.leaderboards.create([\n {\n \"name\": \"Revenue Champions\",\n \"key\": \"revenue-champions\",\n \"status\": \"inactive\",\n \"rankBy\": \"metric\",\n \"metricId\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"maxParticipants\": 100,\n \"start\": \"2026-04-20\"\n },\n {\n \"name\": \"Streak Legends\",\n \"key\": \"streak-legends\",\n \"status\": \"scheduled\",\n \"rankBy\": \"streak\",\n \"start\": \"2026-04-27\"\n }\n])\n" + } + ] + } + }, + "admin.yml patch /leaderboards": { + "spec": "admin.yml", + "method": "patch", + "path": "/leaderboards", + "samplesByLanguage": { + "javascript": [ + { + "source": "import { TrophyApiClient } from '@trophyso/node';\n\nconst trophy = new TrophyApiClient({\n apiKey: 'YOUR_API_KEY'\n});\n\nconst response = await trophy.admin.leaderboards.update([\n {\n id: '550e8400-e29b-41d4-a716-446655440100',\n name: 'Monthly Revenue Champions',\n description: 'Ranked by monthly revenue',\n status: 'active'\n },\n {\n id: '550e8400-e29b-41d4-a716-446655440101',\n status: 'finished'\n }\n]);\n" + } + ], + "python": [ + { + "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.admin.leaderboards.update([\n {\n \"id\": \"550e8400-e29b-41d4-a716-446655440100\",\n \"name\": \"Monthly Revenue Champions\",\n \"description\": \"Ranked by monthly revenue\",\n \"status\": \"active\"\n },\n {\n \"id\": \"550e8400-e29b-41d4-a716-446655440101\",\n \"status\": \"finished\"\n }\n])\n" + } + ] + } + }, + "admin.yml delete /leaderboards": { + "spec": "admin.yml", + "method": "delete", + "path": "/leaderboards", + "samplesByLanguage": { + "javascript": [ + { + "source": "import { TrophyApiClient } from '@trophyso/node';\n\nconst trophy = new TrophyApiClient({\n apiKey: 'YOUR_API_KEY'\n});\n\nconst response = await trophy.admin.leaderboards.delete({\n ids: [\n '550e8400-e29b-41d4-a716-446655440100',\n '550e8400-e29b-41d4-a716-446655440101'\n ]\n});\n" + } + ], + "python": [ + { + "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.admin.leaderboards.delete(\n ids=[\n '550e8400-e29b-41d4-a716-446655440100',\n '550e8400-e29b-41d4-a716-446655440101'\n ]\n)\n" + } + ] + } + }, "admin.yml get /leaderboards/{id}": { "spec": "admin.yml", "method": "get", diff --git a/apps/www/openapi/admin.yml b/apps/www/openapi/admin.yml deleted file mode 100644 index 0e5fc2e..0000000 --- a/apps/www/openapi/admin.yml +++ /dev/null @@ -1,5427 +0,0 @@ -openapi: 3.1.0 -info: - title: Trophy - version: '1.7.0' -paths: - /attributes: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - get: - description: List attributes. - operationId: admin_attributes_list - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - attributes - x-fern-sdk-method-name: list - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const attributes = await trophy.admin.attributes.list({ - limit: 10, - skip: 0 - }); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - attributes = client.admin.attributes.list( - limit=10, - skip=0 - ) - parameters: - - name: limit - in: query - description: Number of records to return. - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 10 - example: 10 - - name: skip - in: query - description: Number of records to skip from the start of the list. - required: false - schema: - type: integer - minimum: 0 - default: 0 - example: 0 - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ListAttributesResponse' - examples: - Attributes list: - value: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: Plan - key: plan - type: user - - id: '550e8400-e29b-41d4-a716-446655440001' - name: Device - key: device - type: event - '401': - description: 'Unauthorized' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - '422': - description: 'Unprocessible Entity' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: List attributes - security: - - ApiKeyAuth: [] - post: - description: Create attributes. - operationId: admin_attributes_create - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - attributes - 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.attributes.create([ - { - name: 'Plan', - key: 'plan', - type: 'user' - }, - { - name: 'Device', - key: 'device', - type: 'event' - } - ]); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - response = client.admin.attributes.create([ - { - "name": "Plan", - "key": "plan", - "type": "user" - }, - { - "name": "Device", - "key": "device", - "type": "event" - } - ]) - requestBody: - description: Array of attributes to create. Maximum 100 attributes per request. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateAttributesRequest' - examples: - Create user and event attributes: - value: - - name: Plan - key: plan - type: user - - name: Device - key: device - type: event - responses: - '200': - description: Successful operation (no attributes created) - content: - application/json: - schema: - $ref: '#/components/schemas/CreateAttributesResponse' - examples: - All requests had errors: - value: - created: [] - issues: - - index: 0 - severity: error - message: Name not a string - - index: 1 - severity: error - message: Key already in use by another attribute of this type - '201': - description: Created (at least one attribute created) - content: - application/json: - schema: - $ref: '#/components/schemas/CreateAttributesResponse' - examples: - Success with no issues: - value: - created: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: Plan - key: plan - type: user - - id: '550e8400-e29b-41d4-a716-446655440001' - name: Device - key: device - type: event - issues: [] - '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 attributes - security: - - ApiKeyAuth: [] - patch: - description: Update attributes by ID. - operationId: admin_attributes_update - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - attributes - x-fern-sdk-method-name: update - 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.attributes.update([ - { - id: '550e8400-e29b-41d4-a716-446655440000', - name: 'Subscription Plan' - } - ]); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - response = client.admin.attributes.update([ - { - "id": "550e8400-e29b-41d4-a716-446655440000", - "name": "Subscription Plan" - } - ]) - requestBody: - description: Array of attributes to update. Each item must include an ID and may include `name`. `key` and `type` cannot be changed. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateAttributesRequest' - examples: - Rename attributes: - value: - - id: 550e8400-e29b-41d4-a716-446655440000 - name: Subscription Plan - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateAttributesResponse' - examples: - Success with no issues: - value: - updated: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: Subscription Plan - key: plan - type: user - issues: [] - Mixed success and errors: - value: - updated: - - id: '550e8400-e29b-41d4-a716-446655440001' - name: Device - key: device - type: event - issues: - - id: '550e8400-e29b-41d4-a716-446655440999' - severity: error - message: Attribute not found by ID - '401': - description: 'Unauthorized' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - '422': - description: 'Unprocessible Entity' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: Update attributes - security: - - ApiKeyAuth: [] - delete: - description: Delete attributes by ID. - operationId: admin_attributes_delete - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - attributes - x-fern-sdk-method-name: delete - 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.attributes.delete({ - ids: [ - '550e8400-e29b-41d4-a716-446655440000', - '550e8400-e29b-41d4-a716-446655440001' - ] - }); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - response = client.admin.attributes.delete( - ids=[ - '550e8400-e29b-41d4-a716-446655440000', - '550e8400-e29b-41d4-a716-446655440001' - ] - ) - parameters: - - name: ids - in: query - description: Attribute IDs to delete. Repeat the query param or provide a comma-separated list. - required: true - schema: - type: array - items: - type: string - format: uuid - minItems: 1 - maxItems: 100 - style: form - explode: true - examples: - Multiple query params: - value: - - 550e8400-e29b-41d4-a716-446655440000 - - 550e8400-e29b-41d4-a716-446655440001 - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteAttributesResponse' - examples: - Success with no issues: - value: - deleted: - - id: '550e8400-e29b-41d4-a716-446655440000' - issues: [] - Mixed success and errors: - value: - deleted: - - id: '550e8400-e29b-41d4-a716-446655440000' - issues: - - id: not-a-uuid - severity: error - message: id must be a valid UUID - - id: '550e8400-e29b-41d4-a716-446655440999' - severity: error - message: Attribute not found by ID - '401': - description: 'Unauthorized' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - '422': - description: 'Unprocessible Entity' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: Delete attributes - security: - - ApiKeyAuth: [] - - /attributes/{id}: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - get: - description: Get an attribute by ID. - operationId: admin_attributes_get - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - attributes - x-fern-sdk-method-name: get - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const attribute = await trophy.admin.attributes.get( - '550e8400-e29b-41d4-a716-446655440000' - ); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - attribute = client.admin.attributes.get( - "550e8400-e29b-41d4-a716-446655440000" - ) - parameters: - - name: id - in: path - description: The UUID of the attribute to retrieve. - required: true - schema: - type: string - format: uuid - example: 550e8400-e29b-41d4-a716-446655440000 - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/AdminAttribute' - examples: - User attribute: - value: - id: '550e8400-e29b-41d4-a716-446655440000' - name: Plan - key: plan - type: user - Event attribute: - value: - id: '550e8400-e29b-41d4-a716-446655440001' - name: Device - key: device - type: event - '401': - description: 'Unauthorized' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - '404': - description: 'Attribute Not Found' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - '422': - description: 'Unprocessible Entity' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: Get an attribute - security: - - ApiKeyAuth: [] - - /metrics: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - get: - description: List metrics. - operationId: admin_metrics_list - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - metrics - x-fern-sdk-method-name: list - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const metrics = await trophy.admin.metrics.list({ - limit: 10, - skip: 0 - }); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - metrics = client.admin.metrics.list( - limit=10, - skip=0 - ) - parameters: - - name: limit - in: query - description: Number of records to return. - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 10 - example: 10 - - name: skip - in: query - description: Number of records to skip from the start of the list. - required: false - schema: - type: integer - minimum: 0 - default: 0 - example: 0 - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ListMetricsResponse' - examples: - Metrics list: - value: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: Oldest Metric - key: oldest-metric - unitType: number - units: '' - - id: '550e8400-e29b-41d4-a716-446655440001' - name: Revenue - key: revenue - unitType: currency - units: USD - '401': - description: 'Unauthorized' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - '422': - description: 'Unprocessible Entity' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: List metrics - security: - - ApiKeyAuth: [] - post: - description: Create metrics. - operationId: admin_metrics_create - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - metrics - 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.metrics.create([ - { - name: 'Invites Sent', - key: 'invites-sent' - }, - { - name: 'Revenue', - key: 'revenue', - unitType: 'currency', - units: 'USD' - } - ]); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - response = client.admin.metrics.create([ - { - "name": "Invites Sent", - "key": "invites-sent" - }, - { - "name": "Revenue", - "key": "revenue", - "unitType": "currency", - "units": "USD" - } - ]) - requestBody: - description: Array of metrics to create. Maximum 100 metrics per request. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateMetricsRequest' - examples: - Create number and currency metrics: - value: - - name: Invites Sent - key: invites-sent - - name: Revenue - key: revenue - unitType: currency - units: USD - responses: - '200': - description: Successful operation (no metrics created) - content: - application/json: - schema: - $ref: '#/components/schemas/CreateMetricsResponse' - examples: - All requests had errors: - value: - created: [] - issues: - - index: 0 - severity: error - message: Name not a string - - index: 1 - severity: error - message: Key already in use by another metric - '201': - description: Created (at least one metric created) - content: - application/json: - schema: - $ref: '#/components/schemas/CreateMetricsResponse' - examples: - Success with no issues: - value: - created: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: Invites Sent - key: invites-sent - unitType: number - units: '' - - id: '550e8400-e29b-41d4-a716-446655440001' - name: Revenue - key: revenue - unitType: currency - units: USD - issues: [] - Mixed success and errors: - value: - created: - - id: '550e8400-e29b-41d4-a716-446655440002' - name: Signups - key: signups - unitType: number - units: '' - issues: - - index: 0 - severity: error - message: Key already in use by another metric - '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 metrics - security: - - ApiKeyAuth: [] - patch: - description: Update metrics by ID. - operationId: admin_metrics_update - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - metrics - x-fern-sdk-method-name: update - 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.metrics.update([ - { - id: '550e8400-e29b-41d4-a716-446655440000', - name: 'Invites Completed', - units: 'invites' - }, - { - id: '550e8400-e29b-41d4-a716-446655440001', - unitType: 'number', - units: 'dollars' - } - ]); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - response = client.admin.metrics.update([ - { - "id": "550e8400-e29b-41d4-a716-446655440000", - "name": "Invites Completed", - "units": "invites" - }, - { - "id": "550e8400-e29b-41d4-a716-446655440001", - "unitType": "number", - "units": "dollars" - } - ]) - requestBody: - description: Array of metrics to update. Each item must include an ID and may include any subset of mutable metric fields except `key`. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateMetricsRequest' - examples: - Update metric fields: - value: - - id: 550e8400-e29b-41d4-a716-446655440000 - name: Invites Completed - units: invites - - id: 550e8400-e29b-41d4-a716-446655440001 - unitType: number - units: dollars - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateMetricsResponse' - examples: - Success with no issues: - value: - updated: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: Invites Completed - key: invites-sent - unitType: number - units: invites - issues: [] - Mixed success and errors: - value: - updated: - - id: '550e8400-e29b-41d4-a716-446655440001' - name: Revenue - key: revenue - unitType: number - units: dollars - issues: - - id: '550e8400-e29b-41d4-a716-446655440999' - severity: error - message: Metric not found by ID - '401': - description: 'Unauthorized' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - '422': - description: 'Unprocessible Entity' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: Update metrics - security: - - ApiKeyAuth: [] - delete: - description: Delete metrics by ID. - operationId: admin_metrics_delete - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - metrics - x-fern-sdk-method-name: delete - 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.metrics.delete({ - ids: [ - '550e8400-e29b-41d4-a716-446655440000', - '550e8400-e29b-41d4-a716-446655440001' - ] - }); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - response = client.admin.metrics.delete( - ids=[ - '550e8400-e29b-41d4-a716-446655440000', - '550e8400-e29b-41d4-a716-446655440001' - ] - ) - parameters: - - name: ids - in: query - description: Metric IDs to delete. Repeat the query param or provide a comma-separated list. - required: true - schema: - type: array - items: - type: string - format: uuid - minItems: 1 - maxItems: 100 - style: form - explode: true - examples: - Multiple query params: - value: - - 550e8400-e29b-41d4-a716-446655440000 - - 550e8400-e29b-41d4-a716-446655440001 - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteMetricsResponse' - examples: - Success with no issues: - value: - deleted: - - id: '550e8400-e29b-41d4-a716-446655440000' - issues: [] - Mixed success and errors: - value: - deleted: - - id: '550e8400-e29b-41d4-a716-446655440000' - issues: - - id: not-a-uuid - severity: error - message: id must be a valid UUID - - id: '550e8400-e29b-41d4-a716-446655440999' - severity: error - message: Metric not found by ID - '401': - description: 'Unauthorized' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - '422': - description: 'Unprocessible Entity' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: Delete metrics - security: - - ApiKeyAuth: [] - - /metrics/{id}: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - get: - description: Get a metric by ID. - operationId: admin_metrics_get - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - metrics - x-fern-sdk-method-name: get - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const metric = await trophy.admin.metrics.get( - '550e8400-e29b-41d4-a716-446655440000' - ); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - metric = client.admin.metrics.get( - "550e8400-e29b-41d4-a716-446655440000" - ) - parameters: - - name: id - in: path - description: The UUID of the metric to retrieve. - required: true - schema: - type: string - format: uuid - example: 550e8400-e29b-41d4-a716-446655440000 - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/CreatedMetric' - examples: - Number metric: - value: - id: '550e8400-e29b-41d4-a716-446655440000' - name: Invites Sent - key: invites-sent - unitType: number - units: '' - Currency metric: - value: - id: '550e8400-e29b-41d4-a716-446655440001' - name: Revenue - key: revenue - unitType: currency - units: USD - '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 metric by ID - security: - - ApiKeyAuth: [] - - /points: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - get: - description: List points systems. - operationId: admin_points_systems_list - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - systems - x-fern-sdk-method-name: list - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const systems = await trophy.admin.points.systems.list({ - limit: 10, - skip: 0 - }); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - systems = client.admin.points.systems.list( - limit=10, - skip=0 - ) - parameters: - - name: limit - in: query - description: Number of records to return. - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 10 - example: 10 - - name: skip - in: query - description: Number of records to skip from the start of the list. - required: false - schema: - type: integer - minimum: 0 - default: 0 - example: 0 - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ListPointsSystemsResponse' - examples: - Systems list: - value: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: XP - key: xp - description: Experience points - status: active - badge: null - maxPoints: 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: List points systems - security: - - ApiKeyAuth: [] - post: - description: Create points systems. Optionally include sub-entities (levels, boosts, triggers) in each system payload to create them alongside the system. - operationId: admin_points_systems_create - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - systems - 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 result = await trophy.admin.points.systems.create([ - { - name: 'XP', - key: 'xp', - description: 'Experience points', - levels: [ - { name: 'Bronze', key: 'bronze', points: 100 }, - { name: 'Silver', key: 'silver', points: 500 } - ] - } - ]); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - result = client.admin.points.systems.create( - request=[{ - "name": "XP", - "key": "xp", - "description": "Experience points", - "levels": [ - {"name": "Bronze", "key": "bronze", "points": 100}, - {"name": "Silver", "key": "silver", "points": 500} - ] - }] - ) - requestBody: - description: Array of points systems to create. Maximum 100 systems per request. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreatePointsSystemsRequest' - examples: - Create a system with levels: - value: - - name: XP - key: xp - description: Experience points - levels: - - name: Bronze - key: bronze - points: 100 - - name: Silver - key: silver - points: 500 - responses: - '200': - description: Successful operation (no systems created) - content: - application/json: - schema: - $ref: '#/components/schemas/CreatePointsSystemsResponse' - examples: - All requests had errors: - value: - created: [] - issues: - - index: 0 - severity: error - message: name must be a string - '201': - description: Created (at least one system created) - content: - application/json: - schema: - $ref: '#/components/schemas/CreatePointsSystemsResponse' - examples: - Success with no issues: - value: - created: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: XP - key: xp - description: Experience points - status: active - badge: null - maxPoints: null - levels: - - id: '550e8400-e29b-41d4-a716-446655440001' - name: Bronze - key: bronze - points: 100 - description: '' - badge: null - issues: [] - '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 points systems - security: - - ApiKeyAuth: [] - patch: - description: Update points systems by ID. - operationId: admin_points_systems_update - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - systems - x-fern-sdk-method-name: update - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const result = await trophy.admin.points.systems.update([ - { - id: 'system-uuid', - name: 'New Name', - description: 'Updated description' - } - ]); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - result = client.admin.points.systems.update( - request=[{ - "id": "system-uuid", - "name": "New Name", - "description": "Updated description" - }] - ) - requestBody: - description: Array of points system updates. Maximum 100 systems per request. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdatePointsSystemsRequest' - examples: - Update a system name: - value: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: New Name - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/UpdatePointsSystemsResponse' - examples: - Success: - value: - updated: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: New Name - key: xp - description: '' - status: active - badge: null - maxPoints: null - issues: [] - '401': - description: 'Unauthorized' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - '422': - description: 'Unprocessible Entity' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: Update points systems - security: - - ApiKeyAuth: [] - delete: - description: Delete (archive) points systems by ID. - operationId: admin_points_systems_delete - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - systems - x-fern-sdk-method-name: delete - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const result = await trophy.admin.points.systems.delete({ - ids: ['system-uuid-1', 'system-uuid-2'] - }); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - result = client.admin.points.systems.delete( - ids=['system-uuid-1', 'system-uuid-2'] - ) - parameters: - - name: ids - in: query - description: The IDs of the points systems to delete. - required: true - schema: - type: array - items: - type: string - format: uuid - example: - - 550e8400-e29b-41d4-a716-446655440000 - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/DeletePointsSystemsResponse' - examples: - Success: - value: - deleted: - - id: '550e8400-e29b-41d4-a716-446655440000' - issues: [] - '401': - description: 'Unauthorized' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - '422': - description: 'Unprocessible Entity' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: Delete points systems - security: - - ApiKeyAuth: [] - - /points/{id}: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - get: - description: Get a points system by ID. - operationId: admin_points_systems_get - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - systems - x-fern-sdk-method-name: get - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const system = await trophy.admin.points.systems.get('system-uuid'); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - system = client.admin.points.systems.get(id='system-uuid') - parameters: - - name: id - in: path - required: true - schema: - type: string - format: uuid - description: The ID of the points system. - example: 550e8400-e29b-41d4-a716-446655440000 - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/AdminPointsSystem' - examples: - Points system: - value: - id: '550e8400-e29b-41d4-a716-446655440000' - name: XP - key: xp - description: Experience points - status: active - badge: null - maxPoints: null - '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 points system by ID - security: - - ApiKeyAuth: [] - - /points/{systemId}/boosts: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - get: - description: List points boosts for a system. - operationId: admin_points_boosts_list - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - boosts - x-fern-sdk-method-name: list - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const boosts = await trophy.admin.points.boosts.list('system-uuid', { - limit: 10, - skip: 0 - }); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - boosts = client.admin.points.boosts.list( - system_id='system-uuid', - limit=10, - skip=0 - ) - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - - name: limit - in: query - description: Maximum number of results to return (1-100, default 10). - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 10 - - name: skip - in: query - description: Number of results to skip for pagination (default 0). - required: false - schema: - type: integer - minimum: 0 - default: 0 - responses: - '200': - description: A paginated list of points boosts. - content: - application/json: - schema: - $ref: '#/components/schemas/ListPointsBoostsResponse' - examples: - Boosts list: - value: - - 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 - '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: List points boosts - security: - - ApiKeyAuth: [] - post: - description: Create points boosts. - 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('system-uuid', [ - { - userId: 'user-123', - name: 'Double XP Weekend', - start: '2024-01-01', - end: '2024-01-03', - multiplier: 2 - }, - { - name: 'Global Holiday Bonus', - start: '2024-12-25', - multiplier: 1.5, - rounding: 'up' - }, - { - name: 'Premium User Boost', - start: '2024-01-01', - multiplier: 2, - userAttributes: [ - { attributeId: 'attr-uuid', attributeValue: 'premium' } - ] - } - ]); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - response = client.admin.points.boosts.create( - system_id='system-uuid', - boosts=[ - { - "userId": "user-123", - "name": "Double XP Weekend", - "start": "2024-01-01", - "end": "2024-01-03", - "multiplier": 2 - }, - { - "name": "Global Holiday Bonus", - "start": "2024-12-25", - "multiplier": 1.5, - "rounding": "up" - }, - { - "name": "Premium User Boost", - "start": "2024-01-01", - "multiplier": 2, - "userAttributes": [ - {"attributeId": "attr-uuid", "attributeValue": "premium"} - ] - } - ] - ) - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - requestBody: - description: Array of boosts to create. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreatePointsBoostsRequest' - examples: - Create user-specific boost: - value: - - userId: user-123 - name: Double XP Weekend - start: '2024-01-01' - end: '2024-01-03' - multiplier: 2 - Create global boost: - value: - - name: Global Holiday Bonus - start: '2024-12-25' - multiplier: 1.5 - rounding: up - Create boost with user attributes: - value: - - name: Premium User Boost - start: '2024-01-01' - multiplier: 2 - userAttributes: - - attributeId: '550e8400-e29b-41d4-a716-446655440000' - attributeValue: premium - responses: - '200': - description: Successful operation (no boosts created) - content: - application/json: - schema: - $ref: '#/components/schemas/CreatePointsBoostsResponse' - examples: - All requests had errors: - value: - created: [] - issues: - - index: 0 - severity: error - message: 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: [] - Global boost created: - value: - created: - - id: '550e8400-e29b-41d4-a716-446655440001' - name: Global Holiday Bonus - status: active - start: '2024-01-15' - end: null - multiplier: 1.5 - rounding: down - userId: null - userAttributes: [] - 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: null - userAttributes: - - attributeId: '550e8400-e29b-41d4-a716-446655440000' - attributeValue: premium - issues: - - index: 1 - severity: error - message: User does not exist - '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 - security: - - ApiKeyAuth: [] - patch: - description: Update multiple points boosts. - operationId: admin_points_boosts_update - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - boosts - x-fern-sdk-method-name: update - 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.update('system-uuid', [ - { - id: 'boost-uuid-1', - name: 'Updated Boost Name', - multiplier: 3 - } - ]); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - response = client.admin.points.boosts.update( - system_id='system-uuid', - boosts=[ - { - "id": "boost-uuid-1", - "name": "Updated Boost Name", - "multiplier": 3 - } - ] - ) - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - requestBody: - description: Array of boost patches. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PatchPointsBoostsRequest' - examples: - Update boost names and multipliers: - value: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: Updated Boost Name - multiplier: 3 - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/PatchPointsBoostsResponse' - examples: - Success: - value: - updated: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: Updated Boost Name - status: active - start: '2024-01-01' - end: '2024-01-03' - multiplier: 3 - rounding: down - userId: user-123 - issues: [] - '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: Update multiple points boosts - security: - - ApiKeyAuth: [] - delete: - description: Delete multiple points boosts by ID. - operationId: admin_points_boosts_delete - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - boosts - x-fern-sdk-method-name: delete - 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.delete('system-uuid', { - ids: ['boost-uuid-1', 'boost-uuid-2', 'boost-uuid-3'] - }); - - console.log(`Deleted ${response.deleted.length} boosts`); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - response = client.admin.points.boosts.delete( - system_id='system-uuid', - ids=['boost-uuid-1', 'boost-uuid-2', 'boost-uuid-3'] - ) - - print(f"Deleted {len(response.deleted)} boosts") - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - - 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/DeletePointsBoostsResponse' - examples: - All boosts deleted: - value: - deleted: - - id: '550e8400-e29b-41d4-a716-446655440000' - - id: '550e8400-e29b-41d4-a716-446655440001' - - id: '550e8400-e29b-41d4-a716-446655440002' - issues: [] - Some boosts not found: - value: - deleted: - - id: '550e8400-e29b-41d4-a716-446655440000' - issues: - - id: '550e8400-e29b-41d4-a716-446655440001' - severity: error - message: Points boost not found by ID - '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 (no IDs provided or too many IDs)' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: Delete multiple points boosts - security: - - ApiKeyAuth: [] - /points/{systemId}/boosts/{id}: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - get: - description: Get a single points boost by ID. - operationId: admin_points_boosts_get - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - boosts - x-fern-sdk-method-name: get - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const boost = await trophy.admin.points.boosts.get('system-uuid', 'boost-uuid'); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - boost = client.admin.points.boosts.get( - system_id='system-uuid', - boost_id='boost-uuid' - ) - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - - name: id - in: path - description: The UUID of the points boost. - required: true - schema: - type: string - format: uuid - example: '660f9500-f30c-42e5-b827-557766550001' - responses: - '200': - description: The points boost. - content: - application/json: - schema: - $ref: '#/components/schemas/AdminPointsBoost' - examples: - A single boost: - value: - 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 - '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 points boost by ID - security: - - ApiKeyAuth: [] - /points/{systemId}/levels: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - get: - description: List points levels for a system. - operationId: admin_points_levels_list - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - levels - x-fern-sdk-method-name: list - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const levels = await trophy.admin.points.levels.list('system-uuid', { - limit: 10, - skip: 0 - }); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - levels = client.admin.points.levels.list( - system_id='system-uuid', - limit=10, - skip=0 - ) - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - - name: limit - in: query - description: Number of records to return. - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 10 - example: 10 - - name: skip - in: query - description: Number of records to skip from the start of the list. - required: false - schema: - type: integer - minimum: 0 - default: 0 - example: 0 - responses: - '200': - description: A paginated list of points levels. - content: - application/json: - schema: - $ref: '#/components/schemas/ListPointsLevelsResponse' - examples: - Two levels: - value: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: Bronze - key: bronze - points: 100 - description: Starting level - badge: null - - id: '660f9500-f30c-42e5-b827-557766550001' - name: Silver - key: silver - points: 500 - description: Intermediate level - badge: - url: 'https://example.com/silver.png' - '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: List points levels - security: - - ApiKeyAuth: [] - post: - description: Create points levels. Maximum 100 levels per request. - operationId: admin_points_levels_create - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - levels - 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 result = await trophy.admin.points.levels.create('system-uuid', [ - { - name: 'Bronze', - key: 'bronze', - points: 100, - description: 'Starting level', - badge: { url: 'https://example.com/bronze.png' } - } - ]); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - result = client.admin.points.levels.create( - system_id='system-uuid', - request=[{ - "name": "Bronze", - "key": "bronze", - "points": 100, - "description": "Starting level", - "badge": {"url": "https://example.com/bronze.png"} - }] - ) - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - requestBody: - description: Array of levels to create. Maximum 100 levels per request. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreatePointsLevelsRequest' - responses: - '200': - description: Successful operation (no levels created) - content: - application/json: - schema: - $ref: '#/components/schemas/CreatePointsLevelsResponse' - examples: - No levels created: - value: - created: [] - issues: - - index: 0 - severity: error - message: Key already in use by another level - '201': - description: Created (at least one level created) - content: - application/json: - schema: - $ref: '#/components/schemas/CreatePointsLevelsResponse' - examples: - Created successfully: - value: - created: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: Bronze - key: bronze - points: 100 - description: Starting level - badge: - url: 'https://example.com/bronze.png' - issues: [] - '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 levels - security: - - ApiKeyAuth: [] - patch: - description: Update multiple points levels. Each item must include an ID. `key` cannot be changed. - operationId: admin_points_levels_update - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - levels - x-fern-sdk-method-name: update - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const result = await trophy.admin.points.levels.update('system-uuid', [ - { - id: '550e8400-e29b-41d4-a716-446655440000', - name: 'Bronze Tier', - points: 150 - } - ]); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - result = client.admin.points.levels.update( - system_id='system-uuid', - request=[{ - "id": "550e8400-e29b-41d4-a716-446655440000", - "name": "Bronze Tier", - "points": 150 - }] - ) - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - requestBody: - description: Array of level patches. Each item must include an ID. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PatchPointsLevelsRequest' - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/PatchPointsLevelsResponse' - examples: - Updated successfully: - value: - updated: - - id: '550e8400-e29b-41d4-a716-446655440000' - name: Bronze Tier - key: bronze - points: 150 - description: Starting level - badge: null - issues: [] - '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: Update multiple points levels - security: - - ApiKeyAuth: [] - delete: - description: Delete multiple points levels by ID. - operationId: admin_points_levels_delete - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - levels - x-fern-sdk-method-name: delete - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const result = await trophy.admin.points.levels.delete('system-uuid', { - ids: ['550e8400-e29b-41d4-a716-446655440000'] - }); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - result = client.admin.points.levels.delete( - system_id='system-uuid', - ids=['550e8400-e29b-41d4-a716-446655440000'] - ) - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - - name: ids - in: query - description: Comma-separated list of level UUIDs to delete. - required: true - schema: - type: array - items: - type: string - format: uuid - style: form - explode: true - example: 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11,b1ffcd00-0d1c-4ef9-cc7e-7cc0ce491b22' - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/DeletePointsLevelsResponse' - examples: - Deleted successfully: - value: - deleted: - - id: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 - issues: [] - Partial success: - value: - deleted: - - id: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 - issues: - - id: b1ffcd00-0d1c-4ef9-cc7e-7cc0ce491b22 - severity: error - message: Level not found by ID - '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 (no IDs provided or too many IDs)' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: Delete multiple points levels - security: - - ApiKeyAuth: [] - /points/{systemId}/levels/{id}: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - get: - description: Get a single points level by ID. - operationId: admin_points_levels_get - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - levels - x-fern-sdk-method-name: get - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const level = await trophy.admin.points.levels.get('system-uuid', 'level-uuid'); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - level = client.admin.points.levels.get( - system_id='system-uuid', - level_id='level-uuid' - ) - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - - name: id - in: path - description: The UUID of the points level. - required: true - schema: - type: string - format: uuid - example: '660f9500-f30c-42e5-b827-557766550001' - responses: - '200': - description: The points level. - content: - application/json: - schema: - $ref: '#/components/schemas/AdminPointsLevel' - examples: - A single level: - value: - id: '550e8400-e29b-41d4-a716-446655440000' - name: Bronze - key: bronze - points: 100 - description: Starting level - badge: - url: 'https://example.com/bronze.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 points level by ID - security: - - ApiKeyAuth: [] - /points/{systemId}/triggers: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - get: - description: List points triggers for a system. - operationId: admin_points_triggers_list - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - triggers - x-fern-sdk-method-name: list - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const triggers = await trophy.admin.points.triggers.list('system-uuid', { - limit: 10, - skip: 0 - }); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - triggers = client.admin.points.triggers.list( - system_id='system-uuid', - limit=10, - skip=0 - ) - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - - name: limit - in: query - description: Maximum number of results to return (1-100, default 10). - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 10 - - name: skip - in: query - description: Number of results to skip for pagination (default 0). - required: false - schema: - type: integer - minimum: 0 - default: 0 - responses: - '200': - description: A paginated list of points triggers. - content: - application/json: - schema: - $ref: '#/components/schemas/ListPointsTriggersResponse' - examples: - Triggers list: - value: - - id: '550e8400-e29b-41d4-a716-446655440000' - type: metric - points: 10 - status: active - userAttributes: [] - metricId: '660f9500-f30c-42e5-b827-557766550001' - metricThreshold: 5 - eventAttributes: [] - blockIfOutOfPoints: false - '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: List points triggers - security: - - ApiKeyAuth: [] - post: - description: Create points triggers in bulk. Maximum 100 triggers per request. - operationId: admin_points_triggers_create - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - triggers - 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 result = await trophy.admin.points.triggers.create('system-uuid', [ - { - type: 'metric', - points: 10, - metricId: 'metric-uuid', - metricThreshold: 5 - } - ]); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - result = client.admin.points.triggers.create( - system_id='system-uuid', - request=[{ - 'type': 'metric', - 'points': 10, - 'metric_id': 'metric-uuid', - 'metric_threshold': 5 - }] - ) - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreatePointsTriggersRequest' - responses: - '200': - description: Request was valid but no triggers were created (all items had issues). - content: - application/json: - schema: - $ref: '#/components/schemas/CreatePointsTriggersResponse' - '201': - description: At least one trigger was created. - content: - application/json: - schema: - $ref: '#/components/schemas/CreatePointsTriggersResponse' - examples: - Created triggers: - value: - created: - - id: '550e8400-e29b-41d4-a716-446655440000' - type: metric - points: 10 - status: active - userAttributes: [] - metricId: '660f9500-f30c-42e5-b827-557766550001' - metricThreshold: 5 - eventAttributes: [] - blockIfOutOfPoints: false - issues: [] - '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 triggers - security: - - ApiKeyAuth: [] - patch: - description: Update points triggers in bulk. Maximum 100 triggers per request. Only provided fields are updated; omitted fields are preserved. - operationId: admin_points_triggers_update - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - triggers - x-fern-sdk-method-name: update - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const result = await trophy.admin.points.triggers.update('system-uuid', [ - { id: 'trigger-uuid', points: 20 } - ]); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - result = client.admin.points.triggers.update( - system_id='system-uuid', - request=[{'id': 'trigger-uuid', 'points': 20}] - ) - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PatchPointsTriggersRequest' - responses: - '200': - description: Successfully processed the update request. - content: - application/json: - schema: - $ref: '#/components/schemas/PatchPointsTriggersResponse' - examples: - Updated triggers: - value: - updated: - - id: '550e8400-e29b-41d4-a716-446655440000' - type: metric - points: 20 - status: active - userAttributes: [] - metricId: '660f9500-f30c-42e5-b827-557766550001' - metricThreshold: 5 - eventAttributes: [] - blockIfOutOfPoints: false - issues: [] - '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: Update points triggers - security: - - ApiKeyAuth: [] - delete: - description: Delete (archive) points triggers by ID. Maximum 100 trigger IDs per request. - operationId: admin_points_triggers_delete - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - triggers - x-fern-sdk-method-name: delete - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const result = await trophy.admin.points.triggers.delete('system-uuid', { - ids: ['trigger-uuid-1', 'trigger-uuid-2'] - }); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - result = client.admin.points.triggers.delete( - system_id='system-uuid', - ids=['trigger-uuid-1', 'trigger-uuid-2'] - ) - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - - name: ids - in: query - description: Trigger IDs to delete. Can be repeated or comma-separated. - required: true - schema: - type: array - items: - type: string - format: uuid - style: form - explode: true - example: - - '550e8400-e29b-41d4-a716-446655440000' - responses: - '200': - description: Successfully processed the delete request. - content: - application/json: - schema: - $ref: '#/components/schemas/DeletePointsTriggersResponse' - examples: - Deleted triggers: - value: - deleted: - - id: '550e8400-e29b-41d4-a716-446655440000' - issues: [] - '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: Delete points triggers - security: - - ApiKeyAuth: [] - /points/{systemId}/triggers/{id}: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - get: - description: Get a single points trigger by ID. - operationId: admin_points_triggers_get - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - points - - triggers - x-fern-sdk-method-name: get - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const trigger = await trophy.admin.points.triggers.get('system-uuid', 'trigger-uuid'); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - trigger = client.admin.points.triggers.get( - system_id='system-uuid', - id='trigger-uuid' - ) - parameters: - - name: systemId - in: path - description: The UUID of the points system. - required: true - schema: - type: string - format: uuid - example: '550e8400-e29b-41d4-a716-446655440000' - - name: id - in: path - description: The UUID of the points trigger. - required: true - schema: - type: string - format: uuid - example: '660f9500-f30c-42e5-b827-557766550001' - responses: - '200': - description: The points trigger. - content: - application/json: - schema: - $ref: '#/components/schemas/AdminPointsTrigger' - examples: - A single trigger: - value: - id: '550e8400-e29b-41d4-a716-446655440000' - type: metric - points: 10 - status: active - userAttributes: [] - metricId: '660f9500-f30c-42e5-b827-557766550001' - metricThreshold: 5 - eventAttributes: [] - blockIfOutOfPoints: false - '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 points trigger by ID - security: - - ApiKeyAuth: [] - /leaderboards: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - get: - description: List leaderboards. - operationId: admin_leaderboards_list - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - leaderboards - x-fern-sdk-method-name: list - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const leaderboards = await trophy.admin.leaderboards.list({ - limit: 10, - skip: 0 - }); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - leaderboards = client.admin.leaderboards.list( - limit=10, - skip=0 - ) - parameters: - - name: limit - in: query - description: Number of records to return. - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 10 - example: 10 - - name: skip - in: query - description: Number of records to skip from the start of the list. - required: false - schema: - type: integer - minimum: 0 - default: 0 - example: 0 - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ListLeaderboardsResponse' - examples: - Leaderboards list: - value: - - id: '550e8400-e29b-41d4-a716-446655440100' - name: Revenue Champions - key: revenue-champions - description: Ranked by monthly revenue - status: active - rankBy: metric - metricId: '550e8400-e29b-41d4-a716-446655440000' - maxParticipants: 100 - start: '2026-04-20' - end: '2026-05-20' - breakdownAttributes: - - '550e8400-e29b-41d4-a716-446655440010' - runUnit: month - runInterval: 1 - - id: '550e8400-e29b-41d4-a716-446655440101' - name: Streak Legends - key: streak-legends - status: scheduled - rankBy: streak - maxParticipants: 1000 - start: '2026-04-27' - breakdownAttributes: [] - '401': - description: 'Unauthorized' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - '422': - description: 'Unprocessible Entity' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: List leaderboards - security: - - ApiKeyAuth: [] - post: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - description: Create leaderboards. Maximum 100 leaderboards per request. - operationId: admin_leaderboards_create - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - leaderboards - x-fern-sdk-method-name: create - tags: - - Admin - requestBody: - description: Array of leaderboards to create. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateLeaderboardsRequest' - examples: - Create leaderboards: - value: - - name: Revenue Champions - key: revenue-champions - status: inactive - rankBy: metric - metricId: 550e8400-e29b-41d4-a716-446655440000 - maxParticipants: 100 - start: '2026-04-20' - breakdownAttributes: - - 550e8400-e29b-41d4-a716-446655440010 - runUnit: month - runInterval: 1 - - name: Streak Legends - key: streak-legends - status: scheduled - rankBy: streak - start: '2026-04-27' - responses: - '200': - description: Successful operation (no leaderboards created) - content: - application/json: - schema: - $ref: '#/components/schemas/CreateLeaderboardsResponse' - '201': - description: Created (at least one leaderboard created) - content: - application/json: - schema: - $ref: '#/components/schemas/CreateLeaderboardsResponse' - '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 leaderboards - security: - - ApiKeyAuth: [] - patch: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - description: Update leaderboards by ID. Updating `status` behaves the same as activating, scheduling, deactivating, or finishing a leaderboard in the dashboard. - operationId: admin_leaderboards_update - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - leaderboards - x-fern-sdk-method-name: update - tags: - - Admin - requestBody: - description: Array of leaderboards to update. Each item must include an ID. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateLeaderboardsRequest' - examples: - Update leaderboards: - value: - - id: 550e8400-e29b-41d4-a716-446655440100 - name: Monthly Revenue Champions - description: Ranked by monthly revenue - status: active - - id: 550e8400-e29b-41d4-a716-446655440101 - status: finished - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateLeaderboardsResponse' - '401': - description: 'Unauthorized' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - '422': - description: 'Unprocessible Entity' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: Update leaderboards - security: - - ApiKeyAuth: [] - delete: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - description: Delete leaderboards by ID. - operationId: admin_leaderboards_delete - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - leaderboards - x-fern-sdk-method-name: delete - tags: - - Admin - parameters: - - name: ids - in: query - description: Leaderboard IDs to delete. Repeat the query param or provide a comma-separated list. - required: true - schema: - type: array - items: - type: string - format: uuid - minItems: 1 - maxItems: 100 - style: form - explode: true - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteLeaderboardsResponse' - '401': - description: 'Unauthorized' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - '422': - description: 'Unprocessible Entity' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: Delete leaderboards - security: - - ApiKeyAuth: [] - - /leaderboards/{id}: - servers: - - url: https://admin.trophy.so/v1 - description: Admin API - get: - description: Get a leaderboard by ID. - operationId: admin_leaderboards_get - x-fern-server-name: admin - x-fern-sdk-group-name: - - admin - - leaderboards - x-fern-sdk-method-name: get - tags: - - Admin - x-codeSamples: - - lang: javascript - source: | - import { TrophyApiClient } from '@trophyso/node'; - - const trophy = new TrophyApiClient({ - apiKey: 'YOUR_API_KEY' - }); - - const leaderboard = await trophy.admin.leaderboards.get( - '550e8400-e29b-41d4-a716-446655440100' - ); - - lang: python - source: | - from trophy import TrophyApi - - client = TrophyApi(api_key='YOUR_API_KEY') - - leaderboard = client.admin.leaderboards.get( - "550e8400-e29b-41d4-a716-446655440100" - ) - parameters: - - name: id - in: path - description: The UUID of the leaderboard to retrieve. - required: true - schema: - type: string - format: uuid - example: 550e8400-e29b-41d4-a716-446655440100 - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/AdminLeaderboard' - examples: - Metric leaderboard: - value: - id: '550e8400-e29b-41d4-a716-446655440100' - name: Revenue Champions - key: revenue-champions - description: Ranked by monthly revenue - status: active - rankBy: metric - metricId: '550e8400-e29b-41d4-a716-446655440000' - maxParticipants: 100 - start: '2026-04-20' - end: '2026-05-20' - breakdownAttributes: - - '550e8400-e29b-41d4-a716-446655440010' - runUnit: month - runInterval: 1 - Streak leaderboard: - value: - id: '550e8400-e29b-41d4-a716-446655440101' - name: Streak Legends - key: streak-legends - status: active - rankBy: streak - maxParticipants: 1000 - start: '2026-04-27' - breakdownAttributes: [] - '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 leaderboard - security: - - ApiKeyAuth: [] - /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 (no freezes created) - content: - application/json: - schema: - $ref: '#/components/schemas/CreateStreakFreezesResponse' - examples: - Success with warnings: - value: - issues: - - userId: user-789 - severity: warning - message: Would exceed maximum freeze limit - Mixed success and errors: - value: - issues: - - userId: non-existent-user - severity: error - message: User does not exist - - userId: user-456 - severity: warning - message: Would exceed maximum freeze limit - '201': - description: Created (at least one freeze created) - content: - application/json: - schema: - $ref: '#/components/schemas/CreateStreakFreezesResponse' - examples: - Success with no issues: - value: - issues: [] - Mixed success and warnings: - value: - issues: - - userId: user-456 - severity: warning - message: 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 previously achieved streak length found within the current restore window: the last 90 days for daily streaks, weekly periods starting with the week containing the start of the current calendar year for weekly streaks, and monthly periods starting at the beginning of the previous calendar year for 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 (no streaks restored) - content: - application/json: - schema: - $ref: '#/components/schemas/RestoreStreaksResponse' - examples: - Success with warnings: - value: - restoredUsers: [] - issues: - - userId: user-123 - severity: warning - message: No streak to restore - Mixed success and errors: - value: - restoredUsers: [] - issues: - - userId: non-existent-user - severity: error - message: User does not exist - - userId: user-789 - severity: warning - message: No streak to restore - '201': - description: Created (at least one streak restored) - content: - application/json: - schema: - $ref: '#/components/schemas/RestoreStreaksResponse' - examples: - Success with no issues: - value: - restoredUsers: - - user-123 - - user-456 - issues: [] - Mixed success and errors: - value: - restoredUsers: - - user-123 - issues: - - userId: non-existent-user - severity: error - message: 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' - '422': - description: 'Unprocessible Entity' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorBody' - summary: Restore streaks for multiple users - security: - - ApiKeyAuth: [] -components: - schemas: - ErrorBody: - title: ErrorBody - type: object - properties: - error: - type: string - required: - - error - 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 100 freezes per request. - maxItems: 100 - 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/AdminIssue' - description: Array of issues encountered during freeze creation. - required: - - issues - CreateAttributesRequest: - title: CreateAttributesRequest - type: array - description: Request body for creating attributes. - items: - $ref: '#/components/schemas/CreateAttributeRequestItem' - minItems: 1 - maxItems: 100 - CreateAttributeRequestItem: - title: CreateAttributeRequestItem - type: object - description: An attribute to create. - properties: - name: - type: string - description: The attribute name. - example: Plan - key: - type: string - pattern: '^[a-zA-Z\d-_]+$' - description: The attribute key. Only alphanumeric characters, hyphens, and underscores are permitted. - example: plan - type: - type: string - enum: - - user - - event - description: The attribute type. - example: user - required: - - name - - key - - type - AdminAttribute: - title: AdminAttribute - type: object - description: An attribute returned from the admin attributes endpoints. - properties: - id: - type: string - format: uuid - description: The UUID of the attribute. - name: - type: string - description: The attribute name. - key: - type: string - description: The attribute key. - type: - type: string - enum: - - user - - event - description: The attribute type. - required: - - id - - name - - key - - type - CreateAttributesResponse: - title: CreateAttributesResponse - type: object - description: Response containing created attributes and any per-item issues. - properties: - created: - type: array - items: - $ref: '#/components/schemas/AdminAttribute' - description: Array of successfully created attributes. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during attribute creation. - required: - - created - - issues - UpdateAttributesRequest: - title: UpdateAttributesRequest - type: array - description: Request body for updating attributes. - items: - $ref: '#/components/schemas/UpdateAttributeRequestItem' - minItems: 1 - maxItems: 100 - UpdateAttributeRequestItem: - title: UpdateAttributeRequestItem - type: object - description: 'An attribute update object. `id` is required and `name` is optional. `key` and `type` cannot be changed through this endpoint.' - properties: - id: - type: string - format: uuid - description: The UUID of the attribute to update. - example: 550e8400-e29b-41d4-a716-446655440000 - name: - type: string - description: The updated attribute name. - example: Subscription Plan - required: - - id - UpdateAttributesResponse: - title: UpdateAttributesResponse - type: object - description: Response containing updated attributes and any per-item issues identified by attribute ID. - properties: - updated: - type: array - items: - $ref: '#/components/schemas/AdminAttribute' - description: Array of successfully updated attributes. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during attribute update. - required: - - updated - - issues - DeleteAttributesResponse: - title: DeleteAttributesResponse - type: object - description: Response containing deleted attributes represented by ID and any per-item issues, including invalid or missing attribute IDs. - properties: - deleted: - type: array - items: - $ref: '#/components/schemas/DeletedResource' - description: Array of deleted attributes represented by ID. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during attribute deletion. - required: - - deleted - - issues - ListAttributesResponse: - title: ListAttributesResponse - type: array - description: A paginated list of attributes. - items: - $ref: '#/components/schemas/AdminAttribute' - CreateLeaderboardsRequest: - title: CreateLeaderboardsRequest - type: array - description: Request body for creating leaderboards. - items: - $ref: '#/components/schemas/CreateLeaderboardRequestItem' - minItems: 1 - maxItems: 100 - CreateLeaderboardRequestItem: - title: CreateLeaderboardRequestItem - type: object - description: A leaderboard to create. - properties: - name: - type: string - description: The leaderboard name. - example: Revenue Champions - key: - type: string - pattern: '^[a-zA-Z\d-_]+$' - description: The leaderboard key. Only alphanumeric characters, hyphens, and underscores are permitted. - example: revenue-champions - description: - type: string - description: The leaderboard description. - example: Ranked by monthly revenue - status: - type: string - enum: - - inactive - - active - - scheduled - - finished - default: inactive - description: The initial user-facing status. Defaults to `inactive`. Use `scheduled` for leaderboards that should be active in the future and `finished` only when creating a leaderboard with an end date in the past. - example: inactive - rankBy: - type: string - enum: - - metric - - streak - - points - description: What the leaderboard ranks by. - example: metric - metricId: - type: string - format: uuid - description: The metric ID to rank by when `rankBy` is `metric`. - pointsSystemId: - type: string - format: uuid - description: The points system ID to rank by when `rankBy` is `points`. - maxParticipants: - type: integer - minimum: 1 - maximum: 1000 - default: 1000 - description: The maximum number of participants. Defaults to `1000`. - example: 100 - start: - type: string - format: date - description: The leaderboard start date in YYYY-MM-DD format. Defaults to today when omitted. - example: '2026-04-20' - end: - type: string - format: date - description: The optional leaderboard end date in YYYY-MM-DD format. - example: '2026-05-20' - breakdownAttributes: - type: array - description: The UUIDs of the active user attributes to break rankings down by. - items: - type: string - format: uuid - runUnit: - type: string - enum: - - day - - month - - year - description: How often the leaderboard repeats. Omit for a non-recurring leaderboard. Streak leaderboards cannot repeat. - example: month - runInterval: - type: integer - minimum: 1 - description: The number of `runUnit`s between repeats. Required when `runUnit` is set. - example: 1 - required: - - name - - key - - rankBy - AdminLeaderboard: - title: AdminLeaderboard - type: object - description: A leaderboard returned from the admin leaderboards endpoints. - properties: - id: - type: string - format: uuid - description: The UUID of the leaderboard. - name: - type: string - description: The leaderboard name. - key: - type: string - description: The leaderboard key. - description: - type: string - description: The leaderboard description. - status: - type: string - enum: - - inactive - - active - - scheduled - - finished - description: The current user-facing status of the leaderboard. - rankBy: - type: string - enum: - - metric - - streak - - points - description: What the leaderboard ranks by. - metricId: - type: string - format: uuid - description: The metric ID used when `rankBy` is `metric`. - pointsSystemId: - type: string - format: uuid - description: The points system ID used when `rankBy` is `points`. - maxParticipants: - type: integer - description: The maximum number of participants. - start: - type: string - format: date - description: The leaderboard start date in YYYY-MM-DD format. - end: - type: string - format: date - description: The optional leaderboard end date in YYYY-MM-DD format. - breakdownAttributes: - type: array - description: The UUIDs of the user attributes used for ranking breakdowns. - items: - type: string - format: uuid - runUnit: - type: string - enum: - - day - - month - - year - description: The recurrence unit when the leaderboard repeats. - runInterval: - type: integer - description: The number of recurrence units between leaderboard runs. - required: - - id - - name - - key - - status - - rankBy - - maxParticipants - - start - - breakdownAttributes - CreateLeaderboardsResponse: - title: CreateLeaderboardsResponse - type: object - description: Response containing created leaderboards and any per-item issues. - properties: - created: - type: array - items: - $ref: '#/components/schemas/AdminLeaderboard' - description: Array of successfully created leaderboards. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during leaderboard creation. - required: - - created - - issues - UpdateLeaderboardsRequest: - title: UpdateLeaderboardsRequest - type: array - description: Request body for updating leaderboards. - items: - $ref: '#/components/schemas/UpdateLeaderboardRequestItem' - minItems: 1 - maxItems: 100 - UpdateLeaderboardRequestItem: - title: UpdateLeaderboardRequestItem - type: object - description: 'A leaderboard update object. `id` is required. Once a leaderboard has been activated, the dashboard-imposed restrictions on ranking configuration and scheduling changes still apply.' - properties: - id: - type: string - format: uuid - description: The UUID of the leaderboard to update. - example: 550e8400-e29b-41d4-a716-446655440100 - name: - type: string - description: The updated leaderboard name. - key: - type: string - pattern: '^[a-zA-Z\d-_]+$' - description: The updated leaderboard key. This can only be changed while the leaderboard is inactive. - description: - type: string - description: The updated leaderboard description. - status: - type: string - enum: - - inactive - - active - - scheduled - - finished - description: The target user-facing status. `scheduled` activates a leaderboard whose start date is in the future. `finished` behaves like the dashboard finish action. - rankBy: - type: string - enum: - - metric - - streak - - points - description: The updated ranking criterion. This can only be changed while the leaderboard is inactive. - metricId: - type: string - format: uuid - description: The metric ID to use when `rankBy` is `metric`. - pointsSystemId: - type: string - format: uuid - description: The points system ID to use when `rankBy` is `points`. - maxParticipants: - type: integer - minimum: 1 - maximum: 1000 - description: The updated maximum number of participants. - start: - type: string - format: date - description: The updated start date in YYYY-MM-DD format. - end: - oneOf: - - type: string - format: date - - type: 'null' - description: The updated end date in YYYY-MM-DD format, or `null` to clear it. - breakdownAttributes: - type: array - description: The updated breakdown attribute UUIDs. - items: - type: string - format: uuid - runUnit: - oneOf: - - type: string - enum: - - day - - month - - year - - type: 'null' - description: The updated recurrence unit. - runInterval: - type: integer - minimum: 1 - description: The updated recurrence interval. - required: - - id - UpdateLeaderboardsResponse: - title: UpdateLeaderboardsResponse - type: object - description: Response containing updated leaderboards and any per-item issues identified by leaderboard ID. - properties: - updated: - type: array - items: - $ref: '#/components/schemas/AdminLeaderboard' - description: Array of successfully updated leaderboards. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during leaderboard update. - required: - - updated - - issues - DeleteLeaderboardsResponse: - title: DeleteLeaderboardsResponse - type: object - description: Response containing deleted leaderboards represented by ID and any per-item issues. - properties: - deleted: - type: array - items: - $ref: '#/components/schemas/DeletedResource' - description: Array of deleted leaderboards represented by ID. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during leaderboard deletion. - required: - - deleted - - issues - ListLeaderboardsResponse: - title: ListLeaderboardsResponse - type: array - description: A paginated list of leaderboards. - items: - $ref: '#/components/schemas/AdminLeaderboard' - CreateMetricsRequest: - title: CreateMetricsRequest - type: array - description: Request body for creating metrics. - items: - $ref: '#/components/schemas/CreateMetricRequestItem' - minItems: 1 - maxItems: 100 - CreateMetricRequestItem: - title: CreateMetricRequestItem - type: object - description: A metric to create. - properties: - name: - type: string - description: The metric name. - example: Invites Sent - key: - type: string - pattern: '^[a-zA-Z\d-_]+$' - description: The metric key. Only alphanumeric characters, hyphens, and underscores are permitted. - example: invites-sent - unitType: - type: string - enum: - - number - - currency - default: number - description: The metric unit type. Defaults to `number`. - example: currency - units: - type: string - description: 'For `unitType: currency`, this must be a supported `MetricCurrency` code such as `USD`. For `number`, this is an optional freeform unit label.' - example: USD - required: - - name - - key - CreatedMetric: - title: CreatedMetric - type: object - description: A successfully created metric returned from the create endpoint. - properties: - id: - type: string - format: uuid - description: The UUID of the created metric. - name: - type: string - description: The metric name. - key: - type: string - description: The metric key. - unitType: - type: string - enum: - - number - - currency - description: The metric unit type. - units: - type: string - description: The stored units value for the metric. - required: - - id - - name - - key - - unitType - - units - CreateMetricsResponse: - title: CreateMetricsResponse - type: object - description: Response containing created metrics and any per-item issues. - properties: - created: - type: array - items: - $ref: '#/components/schemas/CreatedMetric' - description: Array of successfully created metrics. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during metric creation. - required: - - created - - issues - UpdateMetricsRequest: - title: UpdateMetricsRequest - type: array - description: Request body for updating metrics. - items: - $ref: '#/components/schemas/UpdateMetricRequestItem' - minItems: 1 - maxItems: 100 - UpdateMetricRequestItem: - title: UpdateMetricRequestItem - type: object - description: 'A metric update object. `id` is required; `name`, `unitType`, and `units` are optional. `key` cannot be changed through this endpoint.' - properties: - id: - type: string - format: uuid - description: The UUID of the metric to update. - example: 550e8400-e29b-41d4-a716-446655440000 - name: - type: string - description: The updated metric name. - example: Invites Completed - unitType: - type: string - enum: - - number - - currency - description: The updated metric unit type. - example: currency - units: - type: string - description: 'The updated units value. For `unitType: currency`, this must be a supported `MetricCurrency` code such as `USD`.' - example: USD - required: - - id - UpdateMetricsResponse: - title: UpdateMetricsResponse - type: object - description: Response containing updated metrics and any per-item issues identified by metric ID. - properties: - updated: - type: array - items: - $ref: '#/components/schemas/CreatedMetric' - description: Array of successfully updated metrics. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during metric update. - required: - - updated - - issues - DeleteMetricsResponse: - title: DeleteMetricsResponse - type: object - description: Response containing deleted metrics represented by ID and any per-item issues, including invalid or missing metric IDs. - properties: - deleted: - type: array - items: - $ref: '#/components/schemas/DeletedResource' - description: Array of deleted metrics represented by ID. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during metric deletion. - required: - - deleted - - issues - ListMetricsResponse: - title: ListMetricsResponse - type: array - description: A paginated list of metrics. - items: - $ref: '#/components/schemas/CreatedMetric' - CreatePointsBoostRequestItem: - title: CreatePointsBoostRequestItem - type: object - description: A points boost to create. May optionally target a specific user via `userId` or filter by user attributes via `userAttributes`. These two fields are mutually exclusive. - properties: - userId: - type: - - string - - 'null' - description: The ID of the user to create a boost for. Mutually exclusive with `userAttributes` — providing `userAttributes` when `userId` is set will result in an error. Omit for a global boost. - 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 - userAttributes: - type: - - array - - 'null' - description: User attribute filters for the boost. Cannot be provided when `userId` is set. - items: - type: object - properties: - attributeId: - type: string - format: uuid - description: The UUID of the user attribute. - attributeValue: - type: string - description: The value to match. - required: - - attributeId - - attributeValue - example: - - attributeId: '550e8400-e29b-41d4-a716-446655440000' - attributeValue: premium - required: - - name - - start - - multiplier - CreatePointsBoostsRequest: - title: CreatePointsBoostsRequest - type: array - description: Array of points boosts to create. Maximum 100 boosts per request. Each boost may optionally target a specific user via `userId` or filter by user attributes via `userAttributes`. These two fields are mutually exclusive — providing `userAttributes` when `userId` is set will result in an error. Omitting both creates a global boost. - items: - $ref: '#/components/schemas/CreatePointsBoostRequestItem' - maxItems: 100 - minItems: 1 - AdminPointsBoost: - title: AdminPointsBoost - type: object - description: A points boost as returned from admin endpoints. - properties: - id: - type: string - format: uuid - description: The UUID of the 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 - - 'null' - description: The customer ID of the user the boost was created for, or null for global/attribute-filtered boosts. - userAttributes: - type: array - description: User attribute filters applied to the boost. Only present for non-user-specific boosts (i.e. when `userId` is null). Empty array if no filters are set. - items: - type: object - properties: - attributeId: - type: string - format: uuid - description: The UUID of the user attribute. - attributeValue: - type: string - description: The matched attribute value. - required: - - attributeId - - attributeValue - required: - - id - - name - - status - - start - - end - - multiplier - - rounding - - userId - ListPointsBoostsResponse: - title: ListPointsBoostsResponse - type: array - description: A paginated list of points boosts. - items: - $ref: '#/components/schemas/AdminPointsBoost' - 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/AdminPointsBoost' - description: Array of successfully created boosts. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during boost creation. - required: - - created - - issues - PatchPointsBoostsRequest: - title: PatchPointsBoostsRequest - type: array - description: Array of points boost patches. Maximum 100 per request. - items: - type: object - properties: - id: - type: string - format: uuid - description: The UUID of the boost to update. - name: - type: string - description: Updated name for the boost. - maxLength: 255 - start: - type: string - format: date - description: Updated start date (YYYY-MM-DD). - end: - type: - - string - - 'null' - format: date - description: Updated end date (YYYY-MM-DD) or null to remove end date. - multiplier: - type: number - description: Updated points multiplier. - exclusiveMinimum: 0 - exclusiveMaximum: 100 - rounding: - type: string - enum: - - down - - up - - nearest - description: Updated rounding strategy. - userAttributes: - type: - - array - - 'null' - description: Updated user attribute filters. Cannot be set on user-specific boosts. Set to null to clear. - items: - type: object - properties: - attributeId: - type: string - format: uuid - description: The UUID of the user attribute. - attributeValue: - type: string - description: The value to match. - required: - - attributeId - - attributeValue - required: - - id - maxItems: 100 - minItems: 1 - PatchPointsBoostsResponse: - title: PatchPointsBoostsResponse - type: object - description: Response containing updated boosts and any issues encountered. - properties: - updated: - type: array - items: - $ref: '#/components/schemas/AdminPointsBoost' - description: Array of successfully updated boosts. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during boost updates. - required: - - updated - - issues - DeletePointsBoostsResponse: - title: DeletePointsBoostsResponse - type: object - description: Response containing the points boosts that were deleted and any per-item issues. - properties: - deleted: - type: array - items: - $ref: '#/components/schemas/DeletedResource' - description: Array of deleted points boosts represented by ID. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during boost deletion. - required: - - deleted - - issues - CreatePointsLevelRequestItem: - title: CreatePointsLevelRequestItem - type: object - description: A points level to create. - properties: - name: - type: string - description: The name of the level. - example: Bronze - key: - type: string - description: A unique key for the level. Only alphanumeric characters, hyphens, and underscores are permitted. - pattern: '^[a-zA-Z\d-_]+$' - example: bronze - points: - type: integer - minimum: 0 - description: The threshold points value for the level. - example: 100 - description: - type: string - description: An optional description of the level. - example: Starting level - badge: - type: object - description: An optional badge for the level. - properties: - url: - type: string - description: The URL of the badge image. - example: 'https://example.com/bronze.png' - required: - - url - required: - - name - - key - - points - CreatePointsLevelsRequest: - title: CreatePointsLevelsRequest - type: array - description: Array of points levels to create. Maximum 100 levels per request. - items: - $ref: '#/components/schemas/CreatePointsLevelRequestItem' - maxItems: 100 - minItems: 1 - AdminPointsLevel: - title: AdminPointsLevel - type: object - description: A points level as returned from admin endpoints. - properties: - id: - type: string - format: uuid - description: The UUID of the level. - name: - type: string - description: The name of the level. - key: - type: string - description: The level key. - points: - type: integer - description: The threshold points value for the level. - description: - type: string - description: The level description. - badge: - oneOf: - - type: object - properties: - url: - type: string - description: The URL of the badge image. - required: - - url - - type: 'null' - description: The badge for the level, or null if no badge is set. - required: - - id - - name - - key - - points - - description - - badge - CreatePointsLevelsResponse: - title: CreatePointsLevelsResponse - type: object - description: Response containing created levels and any per-item issues. - properties: - created: - type: array - items: - $ref: '#/components/schemas/AdminPointsLevel' - description: Array of successfully created levels. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during level creation. - required: - - created - - issues - ListPointsLevelsResponse: - title: ListPointsLevelsResponse - type: array - description: A paginated list of points levels. - items: - $ref: '#/components/schemas/AdminPointsLevel' - PatchPointsLevelsRequest: - title: PatchPointsLevelsRequest - type: array - description: Array of points level patches. Maximum 100 per request. - items: - type: object - properties: - id: - type: string - format: uuid - description: The UUID of the level to update. - example: '550e8400-e29b-41d4-a716-446655440000' - name: - type: string - description: The updated level name. - points: - type: integer - minimum: 0 - description: The updated threshold points value. - description: - type: string - description: The updated level description. - badge: - oneOf: - - type: object - properties: - url: - type: string - description: The URL of the badge image. - required: - - url - - type: 'null' - description: The updated badge, or `null` to clear it. - required: - - id - maxItems: 100 - minItems: 1 - PatchPointsLevelsResponse: - title: PatchPointsLevelsResponse - type: object - description: Response containing updated levels and any issues encountered. - properties: - updated: - type: array - items: - $ref: '#/components/schemas/AdminPointsLevel' - description: Array of successfully updated levels. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during level updates. - required: - - updated - - issues - DeletePointsLevelsResponse: - title: DeletePointsLevelsResponse - type: object - description: Response containing the points levels that were deleted and any per-item issues. - properties: - deleted: - type: array - items: - $ref: '#/components/schemas/DeletedResource' - description: Array of deleted points levels represented by ID. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during level deletion. - required: - - deleted - - issues - CreatePointsTriggerRequestItem: - title: CreatePointsTriggerRequestItem - type: object - description: A points trigger to create. - properties: - type: - type: string - enum: - - metric - - achievement - - streak - - time - - user_creation - description: The type of trigger. - example: metric - points: - type: integer - description: The number of points to award or deduct when the trigger fires. Cannot be zero. - example: 10 - status: - type: string - enum: - - active - - inactive - default: inactive - description: The status of the trigger. Defaults to 'inactive'. - example: inactive - userAttributes: - type: - - array - - 'null' - description: Optional user attribute filters for the trigger. - items: - type: object - properties: - attributeId: - type: string - format: uuid - description: The UUID of the user attribute. - attributeValue: - type: string - description: The value to match. - required: - - attributeId - - attributeValue - metricId: - type: string - format: uuid - description: Required if type is `metric`. The UUID of the metric. - metricThreshold: - type: integer - minimum: 1 - description: Required if type is `metric`. The metric increment that triggers the points. - eventAttributes: - type: - - array - - 'null' - description: Optional event attribute filters. Only permitted if type is `metric`. - items: - type: object - properties: - attributeId: - type: string - format: uuid - description: The UUID of the event attribute. - attributeValue: - type: string - description: The value to match. - required: - - attributeId - - attributeValue - achievementId: - type: string - format: uuid - description: Required if type is `achievement`. The UUID of the achievement. - streakLength: - type: integer - minimum: 1 - description: Required if type is `streak`. The number of streak periods that triggers the points. - timeUnit: - type: string - enum: - - hours - - days - description: Required if type is `time`. The unit for the time interval. - timeInterval: - type: integer - minimum: 1 - description: Required if type is `time`. The number of time units between recurring awards. - blockIfOutOfPoints: - type: boolean - default: false - description: Whether to block metric events that would reduce the user's points below zero. Defaults to false. - required: - - type - - points - CreatePointsTriggersRequest: - title: CreatePointsTriggersRequest - type: array - description: Array of points triggers to create. Maximum 100 triggers per request. - items: - $ref: '#/components/schemas/CreatePointsTriggerRequestItem' - maxItems: 100 - minItems: 1 - AdminPointsTrigger: - title: AdminPointsTrigger - type: object - description: A points trigger as returned from admin endpoints. - properties: - id: - type: string - format: uuid - description: The UUID of the trigger. - type: - type: string - enum: - - metric - - achievement - - streak - - time - - user_creation - description: The type of trigger. - points: - type: integer - description: The number of points awarded or deducted when the trigger fires. - status: - type: string - enum: - - active - - inactive - description: The status of the trigger. - userAttributes: - type: array - description: User attribute filters applied to the trigger. - items: - type: object - properties: - attributeId: - type: string - format: uuid - attributeValue: - type: string - required: - - attributeId - - attributeValue - metricId: - type: string - format: uuid - description: The UUID of the metric. Only present for metric triggers. - metricThreshold: - type: integer - description: The metric threshold. Only present for metric triggers. - eventAttributes: - type: array - description: Event attribute filters applied to the trigger. Only present for metric triggers. - items: - type: object - properties: - attributeId: - type: string - format: uuid - attributeValue: - type: string - required: - - attributeId - - attributeValue - achievementId: - type: string - format: uuid - description: The UUID of the achievement. Only present for achievement triggers. - streakLength: - type: integer - description: The streak length. Only present for streak triggers. - timeUnit: - type: string - enum: - - hours - - days - description: The time unit. Only present for time triggers. - timeInterval: - type: integer - description: The time interval. Only present for time triggers. - blockIfOutOfPoints: - type: boolean - description: Whether metric events that would reduce the user's points below zero are blocked. - required: - - id - - type - - points - - status - - userAttributes - - blockIfOutOfPoints - ListPointsTriggersResponse: - title: ListPointsTriggersResponse - type: array - description: A paginated list of points triggers. - items: - $ref: '#/components/schemas/AdminPointsTrigger' - CreatePointsTriggersResponse: - title: CreatePointsTriggersResponse - type: object - description: Response containing created triggers and any per-item issues. - properties: - created: - type: array - items: - $ref: '#/components/schemas/AdminPointsTrigger' - description: Array of successfully created triggers. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during trigger creation. - required: - - created - - issues - PatchPointsTriggersRequest: - title: PatchPointsTriggersRequest - type: array - description: Array of points trigger patches. Maximum 100 per request. - items: - type: object - properties: - id: - type: string - format: uuid - description: The UUID of the trigger to update. - type: - type: string - enum: - - metric - - achievement - - streak - - time - - user_creation - description: Updated trigger type. Can only be changed when the trigger is inactive. Required fields for the new type must be provided. - points: - type: integer - description: Updated points value. - status: - type: string - enum: - - active - - inactive - description: Updated status. - userAttributes: - type: - - array - - 'null' - description: Updated user attribute filters. Set to null to clear. - items: - type: object - properties: - attributeId: - type: string - format: uuid - attributeValue: - type: string - required: - - attributeId - - attributeValue - metricId: - type: string - format: uuid - description: Updated metric ID. Only permitted for metric triggers. - metricThreshold: - type: integer - minimum: 1 - description: Updated metric threshold. Only permitted for metric triggers. - eventAttributes: - type: - - array - - 'null' - description: Updated event attribute filters. Only permitted for metric triggers. Set to null to clear. - items: - type: object - properties: - attributeId: - type: string - format: uuid - attributeValue: - type: string - required: - - attributeId - - attributeValue - achievementId: - type: string - format: uuid - description: Updated achievement ID. Only permitted for achievement triggers. - streakLength: - type: integer - minimum: 1 - description: Updated streak length. Only permitted for streak triggers. - timeUnit: - type: string - enum: - - hours - - days - description: Updated time unit. Only permitted for time triggers. - timeInterval: - type: integer - minimum: 1 - description: Updated time interval. Only permitted for time triggers. - blockIfOutOfPoints: - type: boolean - description: Updated block-if-out-of-points setting. - required: - - id - maxItems: 100 - minItems: 1 - PatchPointsTriggersResponse: - title: PatchPointsTriggersResponse - type: object - description: Response containing updated triggers and any issues encountered. - properties: - updated: - type: array - items: - $ref: '#/components/schemas/AdminPointsTrigger' - description: Array of successfully updated triggers. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during trigger updates. - required: - - updated - - issues - DeletePointsTriggersResponse: - title: DeletePointsTriggersResponse - type: object - description: Response containing the points triggers that were deleted and any per-item issues. - properties: - deleted: - type: array - items: - $ref: '#/components/schemas/DeletedResource' - description: Array of deleted points triggers represented by ID. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during trigger deletion. - required: - - deleted - - issues - 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/AdminIssue' - description: Array of issues encountered during streak restoration. - required: - - restoredUsers - - issues - AdminIssue: - title: AdminIssue - type: object - description: An issue encountered while processing an item in an admin API request. - properties: - userId: - type: string - description: The ID of the user the issue relates to, when applicable. - example: user-123 - boostId: - type: string - description: The ID of the points boost the issue relates to, when applicable. - example: 550e8400-e29b-41d4-a716-446655440000 - index: - type: integer - description: The zero-based index of the item the issue relates to, when no resource ID exists yet. - example: 0 - severity: - type: string - enum: - - error - - warning - description: The severity level of the issue. - example: warning - message: - type: string - description: A human-readable description of the issue. - example: Would exceed maximum freeze limit - required: - - severity - - message - DeletedResource: - title: DeletedResource - type: object - description: A deleted resource represented by ID. - properties: - id: - type: string - format: uuid - description: The ID of the deleted resource. - required: - - id - CreatePointsSystemsRequest: - title: CreatePointsSystemsRequest - type: array - description: Request body for creating points systems. - items: - $ref: '#/components/schemas/CreatePointsSystemRequestItem' - minItems: 1 - maxItems: 100 - CreatePointsSystemRequestItem: - title: CreatePointsSystemRequestItem - type: object - description: A points system to create. Optionally include sub-entities. - properties: - name: - type: string - description: The points system name. - example: XP - key: - type: string - pattern: '^[a-zA-Z\d-_]+$' - description: The points system key. Only alphanumeric characters, hyphens, and underscores are permitted. - example: xp - description: - type: string - description: A short description of the points system. - example: Experience points - badge: - oneOf: - - type: object - properties: - url: - type: string - description: Badge image URL. - required: - - url - - type: 'null' - description: An optional badge for the points system. - maxPoints: - type: - - integer - - 'null' - minimum: 1 - description: Optional maximum points a user can earn. - levels: - type: array - description: Optional array of levels to create alongside the system. - items: - $ref: '#/components/schemas/CreatePointsLevelRequestItem' - boosts: - type: array - description: Optional array of boosts to create alongside the system. - items: - $ref: '#/components/schemas/CreatePointsBoostRequestItem' - triggers: - type: array - description: Optional array of triggers to create alongside the system. - items: - $ref: '#/components/schemas/CreatePointsTriggerRequestItem' - required: - - name - - key - AdminPointsSystem: - title: AdminPointsSystem - type: object - description: A points system returned from the admin points systems endpoints. - properties: - id: - type: string - format: uuid - description: The UUID of the points system. - name: - type: string - description: The points system name. - key: - type: string - description: The points system key. - description: - type: string - description: The points system description. - status: - type: string - enum: - - active - - archived - description: The points system status. - badge: - oneOf: - - type: object - properties: - url: - type: string - required: - - url - - type: 'null' - description: The badge for the points system. - maxPoints: - type: - - integer - - 'null' - description: The maximum points a user can earn. - required: - - id - - name - - key - - description - - status - - badge - - maxPoints - CreatedAdminPointsSystem: - title: CreatedAdminPointsSystem - description: A points system returned from the creation endpoint. Extends AdminPointsSystem with optional sub-entity arrays that are present when those sub-entities were included in the creation request. - allOf: - - $ref: '#/components/schemas/AdminPointsSystem' - - type: object - properties: - levels: - type: array - items: - $ref: '#/components/schemas/AdminPointsLevel' - description: Levels created alongside the system. Present when levels were provided in the request. - boosts: - type: array - items: - $ref: '#/components/schemas/AdminPointsBoost' - description: Boosts created alongside the system. Present when boosts were provided in the request. - triggers: - type: array - items: - $ref: '#/components/schemas/AdminPointsTrigger' - description: Triggers created alongside the system. Present when triggers were provided in the request. - ListPointsSystemsResponse: - title: ListPointsSystemsResponse - type: array - description: Response containing a list of points systems. - items: - $ref: '#/components/schemas/AdminPointsSystem' - CreatePointsSystemsResponse: - title: CreatePointsSystemsResponse - type: object - description: Response containing created points systems and any per-item issues. - properties: - created: - type: array - items: - $ref: '#/components/schemas/CreatedAdminPointsSystem' - description: Array of successfully created points systems. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during creation. - required: - - created - - issues - UpdatePointsSystemsRequest: - title: UpdatePointsSystemsRequest - type: array - description: Request body for updating points systems. - items: - $ref: '#/components/schemas/UpdatePointsSystemRequestItem' - minItems: 1 - maxItems: 100 - UpdatePointsSystemRequestItem: - title: UpdatePointsSystemRequestItem - type: object - description: A points system update object. Only id is required; all other fields are optional. - properties: - id: - type: string - format: uuid - description: The UUID of the points system to update. - example: 550e8400-e29b-41d4-a716-446655440000 - name: - type: string - description: Updated name. - description: - type: string - description: Updated description. - badge: - oneOf: - - type: object - properties: - url: - type: string - required: - - url - - type: 'null' - description: Updated badge. Set to null to remove. - maxPoints: - type: - - integer - - 'null' - minimum: 1 - description: Updated max points. Set to null to remove. - required: - - id - UpdatePointsSystemsResponse: - title: UpdatePointsSystemsResponse - type: object - description: Response containing updated points systems and any per-item issues. - properties: - updated: - type: array - items: - $ref: '#/components/schemas/AdminPointsSystem' - description: Array of successfully updated points systems. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during update. - required: - - updated - - issues - DeletePointsSystemsResponse: - title: DeletePointsSystemsResponse - type: object - description: Response containing deleted points system IDs and any per-item issues. - properties: - deleted: - type: array - items: - $ref: '#/components/schemas/DeletedResource' - description: Array of deleted points system IDs. - issues: - type: array - items: - $ref: '#/components/schemas/AdminIssue' - description: Array of issues encountered during deletion. - required: - - deleted - - issues - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: X-API-KEY -servers: - - x-fern-server-name: Admin API - url: https://admin.trophy.so/v1 - description: Admin API diff --git a/apps/www/openapi/application.yml b/apps/www/openapi/application.yml deleted file mode 100644 index aed5101..0000000 --- a/apps/www/openapi/application.yml +++ /dev/null @@ -1,5531 +0,0 @@ -openapi: 3.1.0 -info: - title: Trophy - version: '1.7.0' -paths: - /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 - eventAttributes: - - key: source - value: mobile-app - - key: plan - value: premium - - 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: - $ref: '#/components/schemas/UpsertedUser' - 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' - '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' - '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 - 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 - 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: - $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 - eventAttributes: - - key: source - value: mobile-app - - key: plan - value: premium - 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 - breakdownAttributes: - - country - - plan - 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 - breakdownAttributes: [] - 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: [] -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: - 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: Deprecated. The user's rank across all users. Null if the user has no active streak. - deprecated: true - 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. - status: - type: string - enum: ['active', 'inactive', '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. - 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 - userAttributes: - type: array - description: User attribute filters that must be met for this trigger to award points. Empty when the trigger has no 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 required value of the user attribute. - example: premium - required: - - key - - value - eventAttribute: - type: object - description: Deprecated. Event attribute filter that must be met for this trigger to award points. Only present if the trigger has an event filter configured. - deprecated: true - 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 - eventAttributes: - type: array - description: If the trigger has type 'metric', the event attributes that must match for the trigger to award points. Empty when the trigger is metric-based and has no event attribute filters. Omitted for non-metric triggers. - items: - type: object - 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 - - userAttributes - - created - - updated - 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: Deprecated. The key of the attribute to break down this leaderboard by. - deprecated: true - example: country - breakdownAttributes: - type: array - description: The user attribute keys that this leaderboard is broken down by. - items: - type: string - description: The key of a user attribute in this leaderboard breakdown. - 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 - - breakdownAttributes - - 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: Deprecated. For leaderboards with a single breakdown attribute, the value of that attribute for the user. - deprecated: true - example: USA - breakdownAttributeValues: - type: array - description: For leaderboards with breakdown attributes, the user's values for each breakdown attribute. - items: - type: object - properties: - key: - type: string - description: The key of the breakdown attribute. - example: country - value: - type: string - description: The user's value for the breakdown attribute. - example: USA - required: - - key - - value - 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. - 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: Deprecated. Event attribute filter that must be met for this achievement to be completed. Only present if the achievement has an event filter configured. - deprecated: true - 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 - eventAttributes: - type: array - description: Event attribute filters that must be met for this achievement to be completed. Omitted for non-metric achievements. - items: - type: object - 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 - - userAttributes - 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 - 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 - - 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. - 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' - 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/PointsTrigger' - 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 - 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 diff --git a/apps/www/package.json b/apps/www/package.json index 529a9dc..9d45dc3 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -13,8 +13,9 @@ "format:write": "prettier --write \"**/*.{ts,tsx,mdx}\" --cache", "format:check": "prettier --check \"**/*.{ts,tsx,mdx}\" --cache", "openapi:build": "tsx --tsconfig ./tsconfig.scripts.json ./scripts/build-openapi-code-samples.mts", + "prebuild": "pnpm openapi:build", "registry:build": "tsx --tsconfig ./tsconfig.scripts.json ./scripts/build-registry.mts && prettier --log-level silent --write \"registry/**/*.{ts,tsx,json,mdx}\" --cache", - "postinstall": "fumadocs-mdx && pnpm openapi:build" + "postinstall": "fumadocs-mdx" }, "dependencies": { "@radix-ui/react-avatar": "^1.1.2", @@ -44,8 +45,7 @@ "react-syntax-highlighter": "^16.1.0", "recharts": "^3.8.1", "shiki": "3.22.0", - "tailwind-merge": "^3.0.1", - "yaml": "^2.8.2" + "tailwind-merge": "^3.0.1" }, "prettier": { "endOfLine": "lf", diff --git a/apps/www/scripts/build-openapi-code-samples.mts b/apps/www/scripts/build-openapi-code-samples.mts index 471c36c..d349ab2 100644 --- a/apps/www/scripts/build-openapi-code-samples.mts +++ b/apps/www/scripts/build-openapi-code-samples.mts @@ -2,18 +2,29 @@ import * as fs from "fs" import * as path from "path" import { fileURLToPath } from "url" -import { parse } from "yaml" - const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) -const OPENAPI_DIR = path.join(__dirname, "../openapi") const OUTPUT_PATH = path.join( __dirname, "../lib/generated/openapi-code-samples.json" ) -const SPEC_FILES = ["admin.yml", "application.yml"] as const +const SPECS = [ + { + specKey: "admin.yml" as const, + url: + process.env.TROPHY_OPENAPI_ADMIN_URL ?? + "https://admin.trophy.so/v1/openapi", + }, + { + specKey: "application.yml" as const, + url: + process.env.TROPHY_OPENAPI_APPLICATION_URL ?? + "https://api.trophy.so/v1/openapi", + }, +] as const + const HTTP_METHODS = ["get", "post", "put", "patch", "delete"] as const interface RawCodeSample { @@ -95,20 +106,48 @@ function normalizeOperationSamples( } } -function readSpec(specFile: (typeof SPEC_FILES)[number]) { - const specPath = path.join(OPENAPI_DIR, specFile) - const raw = fs.readFileSync(specPath, "utf8") - return parse(raw) as { - paths?: Record>> +async function fetchOpenApiDocument( + specKey: string, + url: string +): Promise<{ + paths?: Record>> +}> { + const response = await fetch(url, { + headers: { + Accept: "application/json", + "User-Agent": "trophy-ui-docs-openapi-build", + }, + }) + + if (!response.ok) { + throw new Error( + `OpenAPI fetch failed for ${specKey}: ${response.status} ${response.statusText} (${url})` + ) + } + + const text = await response.text() + try { + return JSON.parse(text) as { + paths?: Record>> + } + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + throw new Error(`OpenAPI JSON parse failed for ${specKey} (${url}): ${message}`) } } async function buildOpenApiCodeSamples() { + const documents = await Promise.all( + SPECS.map(async ({ specKey, url }) => ({ + specKey, + document: await fetchOpenApiDocument(specKey, url), + })) + ) + const entries: Record = {} let sampleCount = 0 - for (const specFile of SPEC_FILES) { - const document = readSpec(specFile) + for (const { specKey, document } of documents) { const paths = document.paths ?? {} for (const [endpointPath, pathItem] of Object.entries(paths)) { @@ -119,7 +158,7 @@ async function buildOpenApiCodeSamples() { } const normalized = normalizeOperationSamples( - specFile, + specKey, method, endpointPath, operation @@ -129,7 +168,7 @@ async function buildOpenApiCodeSamples() { continue } - const key = `${specFile} ${method} ${endpointPath}` + const key = `${specKey} ${method} ${endpointPath}` entries[key] = normalized sampleCount += Object.values(normalized.samplesByLanguage).reduce( (total, snippets) => total + snippets.length, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 618613c..ced110b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,9 +162,6 @@ importers: tailwind-merge: specifier: ^3.0.1 version: 3.5.0 - yaml: - specifier: ^2.8.2 - version: 2.8.2 devDependencies: '@eslint/eslintrc': specifier: ^3 From dccefb1e80294e7924ced1f0f87e5d63a794035a Mon Sep 17 00:00:00 2001 From: Charlie Hopkins-Brinicombe Date: Thu, 14 May 2026 16:46:51 +0100 Subject: [PATCH 3/5] Add profound logging --- apps/www/.env.example | 4 + apps/www/lib/profound/customLogs.ts | 180 ++++++++++++++++++++++++++++ apps/www/middleware.ts | 67 +++++++++++ 3 files changed, 251 insertions(+) create mode 100644 apps/www/lib/profound/customLogs.ts create mode 100644 apps/www/middleware.ts diff --git a/apps/www/.env.example b/apps/www/.env.example index 5e461d5..ee5541f 100644 --- a/apps/www/.env.example +++ b/apps/www/.env.example @@ -1 +1,5 @@ NEXT_PUBLIC_SITE_URL=https://ui.trophy.so + +PROFOUND_CUSTOM_LOGGING_ENABLED=false +PROFOUND_API_KEY= +PROFOUND_LOG_QUERY_PARAM_ALLOWLIST=utm_source,utm_medium,utm_campaign,utm_term,utm_content,utm_id,utm_source_platform,utm_creative_format,utm_marketing_tactic diff --git a/apps/www/lib/profound/customLogs.ts b/apps/www/lib/profound/customLogs.ts new file mode 100644 index 0000000..363de52 --- /dev/null +++ b/apps/www/lib/profound/customLogs.ts @@ -0,0 +1,180 @@ +const PROFOUND_CUSTOM_LOGS_ENDPOINT = + "https://artemis.api.tryprofound.com/v1/logs/custom" +const MAX_BATCH_SIZE = 1000 + +type QueryParams = Record + +export type ProFoundCustomLogEntry = { + timestamp: string | number + method: string + host: string + path: string + status_code: number + ip: string + user_agent: string + query_params?: QueryParams + referer?: string + bytes_sent?: number + duration_ms?: number +} + +type ProfoundSendOptions = { + apiKey?: string + enabled?: boolean + timeoutMs?: number +} + +const DEFAULT_TIMEOUT_MS = 1200 + +type ProcessEnv = Record + +function getProcessEnv(): ProcessEnv | undefined { + const globalWithProcess = globalThis as typeof globalThis & { + process?: { env?: ProcessEnv } + } + return globalWithProcess.process?.env +} + +function truncate(value: string, maxLength: number): string { + if (value.length <= maxLength) return value + return value.slice(0, maxLength) +} + +function toIsoTimestamp(value: string | number): string | number | null { + if (typeof value === "number") { + if (!Number.isFinite(value)) return null + return value + } + + const date = new Date(value) + if (Number.isNaN(date.getTime())) return null + return date.toISOString() +} + +function sanitizeQueryParams( + value: ProFoundCustomLogEntry["query_params"] +): QueryParams | undefined { + if (!value || typeof value !== "object") return undefined + + const entries = Object.entries(value) + .slice(0, 50) + .map(([key, val]) => [ + truncate(String(key), 100), + truncate(String(val), 1000), + ]) + + if (entries.length === 0) return undefined + return Object.fromEntries(entries) +} + +export function sanitizeCustomLogEntry( + entry: ProFoundCustomLogEntry +): ProFoundCustomLogEntry | null { + const timestamp = toIsoTimestamp(entry.timestamp) + if (!timestamp) return null + + const statusCode = Number(entry.status_code) + if (!Number.isInteger(statusCode) || statusCode < 100 || statusCode > 599) { + return null + } + + const method = truncate(String(entry.method || ""), 10) + const host = truncate(String(entry.host || ""), 255) + const path = truncate(String(entry.path || ""), 2048) + const ip = truncate(String(entry.ip || ""), 45) + const userAgent = truncate(String(entry.user_agent || ""), 1024) + + if (!method || !host || !path || !ip || !userAgent) return null + + const sanitized: ProFoundCustomLogEntry = { + timestamp, + method, + host, + path, + status_code: statusCode, + ip, + user_agent: userAgent, + } + + const queryParams = sanitizeQueryParams(entry.query_params) + if (queryParams) sanitized.query_params = queryParams + + if (typeof entry.referer === "string" && entry.referer) { + sanitized.referer = truncate(entry.referer, 2048) + } + + if (typeof entry.bytes_sent === "number" && entry.bytes_sent >= 0) { + sanitized.bytes_sent = entry.bytes_sent + } + + if (typeof entry.duration_ms === "number" && entry.duration_ms >= 0) { + sanitized.duration_ms = entry.duration_ms + } + + return sanitized +} + +function chunkLogs( + entries: ProFoundCustomLogEntry[], + chunkSize: number +): ProFoundCustomLogEntry[][] { + const chunks: ProFoundCustomLogEntry[][] = [] + + for (let index = 0; index < entries.length; index += chunkSize) { + chunks.push(entries.slice(index, index + chunkSize)) + } + + return chunks +} + +export async function sendProfoundCustomLogs( + payload: ProFoundCustomLogEntry | ProFoundCustomLogEntry[], + options: ProfoundSendOptions = {} +): Promise { + const env = getProcessEnv() + const enabled = + options.enabled ?? env?.PROFOUND_CUSTOM_LOGGING_ENABLED === "true" + const apiKey = options.apiKey ?? env?.PROFOUND_API_KEY + + if (!enabled || !apiKey) return + + const entries = Array.isArray(payload) ? payload : [payload] + const sanitizedEntries = entries + .map((entry) => sanitizeCustomLogEntry(entry)) + .filter((entry): entry is ProFoundCustomLogEntry => entry !== null) + + if (sanitizedEntries.length === 0) return + + const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS + const batches = chunkLogs(sanitizedEntries, MAX_BATCH_SIZE) + + await Promise.all( + batches.map(async (batch) => { + const controller = new AbortController() + const timeout = setTimeout(() => controller.abort(), timeoutMs) + + try { + const response = await fetch(PROFOUND_CUSTOM_LOGS_ENDPOINT, { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-api-key": apiKey, + }, + body: JSON.stringify(batch), + signal: controller.signal, + }) + + if (!response.ok) { + console.error("Profound custom logs request failed", { + status: response.status, + statusText: response.statusText, + }) + } + } catch (error) { + console.error("Profound custom logs request error", error) + } finally { + clearTimeout(timeout) + } + }) + ) +} diff --git a/apps/www/middleware.ts b/apps/www/middleware.ts new file mode 100644 index 0000000..808e53d --- /dev/null +++ b/apps/www/middleware.ts @@ -0,0 +1,67 @@ +import { NextRequest, NextResponse, type NextFetchEvent } from "next/server" + +import { sendProfoundCustomLogs } from "@/lib/profound/customLogs" + +const PROFOUND_LOG_HOST = "ui.trophy.so" + +const SAFE_QUERY_PARAMS = new Set( + (process.env.PROFOUND_LOG_QUERY_PARAM_ALLOWLIST ?? "") + .split(",") + .map((value) => value.trim().toLowerCase()) + .filter(Boolean) +) + +function isSafeQueryParam(key: string): boolean { + return SAFE_QUERY_PARAMS.has(key.toLowerCase()) +} + +function getRequestIp(request: NextRequest): string { + const forwardedFor = request.headers.get("x-forwarded-for") + if (forwardedFor) { + const firstIp = forwardedFor.split(",")[0]?.trim() + if (firstIp) return firstIp + } + + const realIp = request.headers.get("x-real-ip") + if (realIp) return realIp + + return "0.0.0.0" +} + +function getSafeQueryParams(url: URL): Record | undefined { + const safeEntries = Array.from(url.searchParams.entries()).filter(([key]) => + isSafeQueryParam(key) + ) + + if (safeEntries.length === 0) return undefined + return Object.fromEntries(safeEntries) +} + +export function middleware(request: NextRequest, event: NextFetchEvent) { + const response = NextResponse.next() + + const referer = request.headers.get("referer") || undefined + const queryParams = getSafeQueryParams(request.nextUrl) + + event.waitUntil( + sendProfoundCustomLogs({ + timestamp: new Date().toISOString(), + method: request.method, + host: PROFOUND_LOG_HOST, + path: request.nextUrl.pathname, + status_code: response.status, + ip: getRequestIp(request), + user_agent: request.headers.get("user-agent") || "unknown-user-agent", + referer, + query_params: queryParams, + }) + ) + + return response +} + +export const config = { + matcher: [ + "/((?!_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico|css|js|map)$).*)", + ], +} From 72146f50f732218d74d13ca5983745e523790664 Mon Sep 17 00:00:00 2001 From: Charlie Hopkins-Brinicombe Date: Thu, 14 May 2026 16:52:58 +0100 Subject: [PATCH 4/5] Migrate openapi snippets to read from live spec files --- .../docs/components/achievement-badge.mdx | 2 +- .../docs/components/achievement-card.mdx | 2 +- .../docs/components/achievement-grid.mdx | 2 +- .../docs/components/achievement-list.mdx | 2 +- .../docs/components/achievement-unlocked.mdx | 2 +- .../docs/components/leaderboard-card.mdx | 2 +- .../docs/components/leaderboard-podium.mdx | 2 +- .../docs/components/leaderboard-rankings.mdx | 2 +- .../content/docs/components/points-awards.mdx | 2 +- .../content/docs/components/points-badge.mdx | 2 +- .../content/docs/components/points-boost.mdx | 2 +- .../content/docs/components/points-chart.mdx | 2 +- .../docs/components/points-levels-list.mdx | 4 +- .../components/points-levels-timeline.mdx | 4 +- .../content/docs/components/streak-badge.mdx | 2 +- .../docs/components/streak-calendar.mdx | 2 +- .../content/docs/components/streak-card.mdx | 2 +- .../lib/generated/openapi-code-samples.json | 260 +++++++++--------- .../scripts/build-openapi-code-samples.mts | 4 +- 19 files changed, 151 insertions(+), 151 deletions(-) diff --git a/apps/www/content/docs/components/achievement-badge.mdx b/apps/www/content/docs/components/achievement-badge.mdx index 2f8cbe9..aab5056 100644 --- a/apps/www/content/docs/components/achievement-badge.mdx +++ b/apps/www/content/docs/components/achievement-badge.mdx @@ -202,7 +202,7 @@ When `achievement.achievedAt` is `null`, the badge is shown in a locked state. Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch a user's achievements server-side: diff --git a/apps/www/content/docs/components/achievement-card.mdx b/apps/www/content/docs/components/achievement-card.mdx index 930af7b..ddaec87 100644 --- a/apps/www/content/docs/components/achievement-card.mdx +++ b/apps/www/content/docs/components/achievement-card.mdx @@ -147,7 +147,7 @@ import { AchievementCard } from "@/components/ui/achievement-card" Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch a user's achievements server-side: diff --git a/apps/www/content/docs/components/achievement-grid.mdx b/apps/www/content/docs/components/achievement-grid.mdx index 8cfae3a..2fb60a7 100644 --- a/apps/www/content/docs/components/achievement-grid.mdx +++ b/apps/www/content/docs/components/achievement-grid.mdx @@ -222,7 +222,7 @@ import { AchievementGrid } from "@/components/ui/achievement-grid" Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch a user's achievements server-side: diff --git a/apps/www/content/docs/components/achievement-list.mdx b/apps/www/content/docs/components/achievement-list.mdx index 981abc9..25d9e56 100644 --- a/apps/www/content/docs/components/achievement-list.mdx +++ b/apps/www/content/docs/components/achievement-list.mdx @@ -101,7 +101,7 @@ import { AchievementList } from "@/components/ui/achievement-list" Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch a user's achievements server-side: diff --git a/apps/www/content/docs/components/achievement-unlocked.mdx b/apps/www/content/docs/components/achievement-unlocked.mdx index 5f42928..d7f4c9d 100644 --- a/apps/www/content/docs/components/achievement-unlocked.mdx +++ b/apps/www/content/docs/components/achievement-unlocked.mdx @@ -109,7 +109,7 @@ const [open, setOpen] = useState(false) Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to send a metric event server-side: diff --git a/apps/www/content/docs/components/leaderboard-card.mdx b/apps/www/content/docs/components/leaderboard-card.mdx index 03bb72c..db5629e 100644 --- a/apps/www/content/docs/components/leaderboard-card.mdx +++ b/apps/www/content/docs/components/leaderboard-card.mdx @@ -194,7 +194,7 @@ const selectedRun = runs[selectedRunId] ?? runs["this-week"] Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch leaderboard data server-side: diff --git a/apps/www/content/docs/components/leaderboard-podium.mdx b/apps/www/content/docs/components/leaderboard-podium.mdx index 9fef32b..7b260c0 100644 --- a/apps/www/content/docs/components/leaderboard-podium.mdx +++ b/apps/www/content/docs/components/leaderboard-podium.mdx @@ -113,7 +113,7 @@ Works with fewer than 3 users: Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch leaderboard data server-side: diff --git a/apps/www/content/docs/components/leaderboard-rankings.mdx b/apps/www/content/docs/components/leaderboard-rankings.mdx index 8cedb4a..89a2ad0 100644 --- a/apps/www/content/docs/components/leaderboard-rankings.mdx +++ b/apps/www/content/docs/components/leaderboard-rankings.mdx @@ -285,7 +285,7 @@ import { LeaderboardRankings } from "@/components/ui/leaderboard-rankings" Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch leaderboard data server-side: diff --git a/apps/www/content/docs/components/points-awards.mdx b/apps/www/content/docs/components/points-awards.mdx index 1eecba3..1a484de 100644 --- a/apps/www/content/docs/components/points-awards.mdx +++ b/apps/www/content/docs/components/points-awards.mdx @@ -121,7 +121,7 @@ import { PointsAwards } from "@/components/ui/points-awards" Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch the user's points data server-side: diff --git a/apps/www/content/docs/components/points-badge.mdx b/apps/www/content/docs/components/points-badge.mdx index a787404..f62cff9 100644 --- a/apps/www/content/docs/components/points-badge.mdx +++ b/apps/www/content/docs/components/points-badge.mdx @@ -73,7 +73,7 @@ Available in three sizes: `sm`, `default`, and `lg`. Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch the user's points total server-side: diff --git a/apps/www/content/docs/components/points-boost.mdx b/apps/www/content/docs/components/points-boost.mdx index a85ac3d..6aabc62 100644 --- a/apps/www/content/docs/components/points-boost.mdx +++ b/apps/www/content/docs/components/points-boost.mdx @@ -111,7 +111,7 @@ Pass `endDate` to enable the countdown label. Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch boosts for a points system server-side: diff --git a/apps/www/content/docs/components/points-chart.mdx b/apps/www/content/docs/components/points-chart.mdx index 9783651..58747ad 100644 --- a/apps/www/content/docs/components/points-chart.mdx +++ b/apps/www/content/docs/components/points-chart.mdx @@ -122,7 +122,7 @@ const [period, setPeriod] = useState("last-7-days") Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch points time-series data server-side: diff --git a/apps/www/content/docs/components/points-levels-list.mdx b/apps/www/content/docs/components/points-levels-list.mdx index d9df420..cf85837 100644 --- a/apps/www/content/docs/components/points-levels-list.mdx +++ b/apps/www/content/docs/components/points-levels-list.mdx @@ -206,7 +206,7 @@ import { PointsLevelsList } from "@/components/ui/points-levels-list" Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch user progress and system levels server-side: @@ -214,7 +214,7 @@ Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to f Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch points system level definitions server-side: diff --git a/apps/www/content/docs/components/points-levels-timeline.mdx b/apps/www/content/docs/components/points-levels-timeline.mdx index 645f6a4..ef56b79 100644 --- a/apps/www/content/docs/components/points-levels-timeline.mdx +++ b/apps/www/content/docs/components/points-levels-timeline.mdx @@ -131,7 +131,7 @@ import { PointsLevelsTimeline } from "@/components/ui/points-levels-timeline" Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch user progress server-side: @@ -139,7 +139,7 @@ Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to f Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch points system level definitions server-side: diff --git a/apps/www/content/docs/components/streak-badge.mdx b/apps/www/content/docs/components/streak-badge.mdx index fd1ea58..de25a95 100644 --- a/apps/www/content/docs/components/streak-badge.mdx +++ b/apps/www/content/docs/components/streak-badge.mdx @@ -71,7 +71,7 @@ Available in three sizes: `sm`, `default`, and `lg`. Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch streak data for a particular user server-side: diff --git a/apps/www/content/docs/components/streak-calendar.mdx b/apps/www/content/docs/components/streak-calendar.mdx index efd8ef0..d36c398 100644 --- a/apps/www/content/docs/components/streak-calendar.mdx +++ b/apps/www/content/docs/components/streak-calendar.mdx @@ -130,7 +130,7 @@ const streak = [ Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch streak data server-side: diff --git a/apps/www/content/docs/components/streak-card.mdx b/apps/www/content/docs/components/streak-card.mdx index 6a2b2b1..7066712 100644 --- a/apps/www/content/docs/components/streak-card.mdx +++ b/apps/www/content/docs/components/streak-card.mdx @@ -73,7 +73,7 @@ const streak = [ Use the [Trophy SDK](https://docs.trophy.so/api-reference/client-libraries) to fetch streak data server-side: diff --git a/apps/www/lib/generated/openapi-code-samples.json b/apps/www/lib/generated/openapi-code-samples.json index c0f8895..d58d02d 100644 --- a/apps/www/lib/generated/openapi-code-samples.json +++ b/apps/www/lib/generated/openapi-code-samples.json @@ -1,6 +1,6 @@ { - "admin.yml get /attributes": { - "spec": "admin.yml", + "admin get /attributes": { + "spec": "admin", "method": "get", "path": "/attributes", "samplesByLanguage": { @@ -16,8 +16,8 @@ ] } }, - "admin.yml post /attributes": { - "spec": "admin.yml", + "admin post /attributes": { + "spec": "admin", "method": "post", "path": "/attributes", "samplesByLanguage": { @@ -33,8 +33,8 @@ ] } }, - "admin.yml patch /attributes": { - "spec": "admin.yml", + "admin patch /attributes": { + "spec": "admin", "method": "patch", "path": "/attributes", "samplesByLanguage": { @@ -50,8 +50,8 @@ ] } }, - "admin.yml delete /attributes": { - "spec": "admin.yml", + "admin delete /attributes": { + "spec": "admin", "method": "delete", "path": "/attributes", "samplesByLanguage": { @@ -67,8 +67,8 @@ ] } }, - "admin.yml get /attributes/{id}": { - "spec": "admin.yml", + "admin get /attributes/{id}": { + "spec": "admin", "method": "get", "path": "/attributes/{id}", "samplesByLanguage": { @@ -84,8 +84,8 @@ ] } }, - "admin.yml get /metrics": { - "spec": "admin.yml", + "admin get /metrics": { + "spec": "admin", "method": "get", "path": "/metrics", "samplesByLanguage": { @@ -101,8 +101,8 @@ ] } }, - "admin.yml post /metrics": { - "spec": "admin.yml", + "admin post /metrics": { + "spec": "admin", "method": "post", "path": "/metrics", "samplesByLanguage": { @@ -118,8 +118,8 @@ ] } }, - "admin.yml patch /metrics": { - "spec": "admin.yml", + "admin patch /metrics": { + "spec": "admin", "method": "patch", "path": "/metrics", "samplesByLanguage": { @@ -135,8 +135,8 @@ ] } }, - "admin.yml delete /metrics": { - "spec": "admin.yml", + "admin delete /metrics": { + "spec": "admin", "method": "delete", "path": "/metrics", "samplesByLanguage": { @@ -152,8 +152,8 @@ ] } }, - "admin.yml get /metrics/{id}": { - "spec": "admin.yml", + "admin get /metrics/{id}": { + "spec": "admin", "method": "get", "path": "/metrics/{id}", "samplesByLanguage": { @@ -169,8 +169,8 @@ ] } }, - "admin.yml get /points": { - "spec": "admin.yml", + "admin get /points": { + "spec": "admin", "method": "get", "path": "/points", "samplesByLanguage": { @@ -186,8 +186,8 @@ ] } }, - "admin.yml post /points": { - "spec": "admin.yml", + "admin post /points": { + "spec": "admin", "method": "post", "path": "/points", "samplesByLanguage": { @@ -203,8 +203,8 @@ ] } }, - "admin.yml patch /points": { - "spec": "admin.yml", + "admin patch /points": { + "spec": "admin", "method": "patch", "path": "/points", "samplesByLanguage": { @@ -220,8 +220,8 @@ ] } }, - "admin.yml delete /points": { - "spec": "admin.yml", + "admin delete /points": { + "spec": "admin", "method": "delete", "path": "/points", "samplesByLanguage": { @@ -237,8 +237,8 @@ ] } }, - "admin.yml get /points/{id}": { - "spec": "admin.yml", + "admin get /points/{id}": { + "spec": "admin", "method": "get", "path": "/points/{id}", "samplesByLanguage": { @@ -254,8 +254,8 @@ ] } }, - "admin.yml get /points/{systemId}/boosts": { - "spec": "admin.yml", + "admin get /points/{systemId}/boosts": { + "spec": "admin", "method": "get", "path": "/points/{systemId}/boosts", "samplesByLanguage": { @@ -271,8 +271,8 @@ ] } }, - "admin.yml post /points/{systemId}/boosts": { - "spec": "admin.yml", + "admin post /points/{systemId}/boosts": { + "spec": "admin", "method": "post", "path": "/points/{systemId}/boosts", "samplesByLanguage": { @@ -288,8 +288,8 @@ ] } }, - "admin.yml patch /points/{systemId}/boosts": { - "spec": "admin.yml", + "admin patch /points/{systemId}/boosts": { + "spec": "admin", "method": "patch", "path": "/points/{systemId}/boosts", "samplesByLanguage": { @@ -305,8 +305,8 @@ ] } }, - "admin.yml delete /points/{systemId}/boosts": { - "spec": "admin.yml", + "admin delete /points/{systemId}/boosts": { + "spec": "admin", "method": "delete", "path": "/points/{systemId}/boosts", "samplesByLanguage": { @@ -322,8 +322,8 @@ ] } }, - "admin.yml get /points/{systemId}/boosts/{id}": { - "spec": "admin.yml", + "admin get /points/{systemId}/boosts/{id}": { + "spec": "admin", "method": "get", "path": "/points/{systemId}/boosts/{id}", "samplesByLanguage": { @@ -339,8 +339,8 @@ ] } }, - "admin.yml get /points/{systemId}/levels": { - "spec": "admin.yml", + "admin get /points/{systemId}/levels": { + "spec": "admin", "method": "get", "path": "/points/{systemId}/levels", "samplesByLanguage": { @@ -356,8 +356,8 @@ ] } }, - "admin.yml post /points/{systemId}/levels": { - "spec": "admin.yml", + "admin post /points/{systemId}/levels": { + "spec": "admin", "method": "post", "path": "/points/{systemId}/levels", "samplesByLanguage": { @@ -373,8 +373,8 @@ ] } }, - "admin.yml patch /points/{systemId}/levels": { - "spec": "admin.yml", + "admin patch /points/{systemId}/levels": { + "spec": "admin", "method": "patch", "path": "/points/{systemId}/levels", "samplesByLanguage": { @@ -390,8 +390,8 @@ ] } }, - "admin.yml delete /points/{systemId}/levels": { - "spec": "admin.yml", + "admin delete /points/{systemId}/levels": { + "spec": "admin", "method": "delete", "path": "/points/{systemId}/levels", "samplesByLanguage": { @@ -407,8 +407,8 @@ ] } }, - "admin.yml get /points/{systemId}/levels/{id}": { - "spec": "admin.yml", + "admin get /points/{systemId}/levels/{id}": { + "spec": "admin", "method": "get", "path": "/points/{systemId}/levels/{id}", "samplesByLanguage": { @@ -424,8 +424,8 @@ ] } }, - "admin.yml get /points/{systemId}/triggers": { - "spec": "admin.yml", + "admin get /points/{systemId}/triggers": { + "spec": "admin", "method": "get", "path": "/points/{systemId}/triggers", "samplesByLanguage": { @@ -441,8 +441,8 @@ ] } }, - "admin.yml post /points/{systemId}/triggers": { - "spec": "admin.yml", + "admin post /points/{systemId}/triggers": { + "spec": "admin", "method": "post", "path": "/points/{systemId}/triggers", "samplesByLanguage": { @@ -458,8 +458,8 @@ ] } }, - "admin.yml patch /points/{systemId}/triggers": { - "spec": "admin.yml", + "admin patch /points/{systemId}/triggers": { + "spec": "admin", "method": "patch", "path": "/points/{systemId}/triggers", "samplesByLanguage": { @@ -475,8 +475,8 @@ ] } }, - "admin.yml delete /points/{systemId}/triggers": { - "spec": "admin.yml", + "admin delete /points/{systemId}/triggers": { + "spec": "admin", "method": "delete", "path": "/points/{systemId}/triggers", "samplesByLanguage": { @@ -492,8 +492,8 @@ ] } }, - "admin.yml get /points/{systemId}/triggers/{id}": { - "spec": "admin.yml", + "admin get /points/{systemId}/triggers/{id}": { + "spec": "admin", "method": "get", "path": "/points/{systemId}/triggers/{id}", "samplesByLanguage": { @@ -509,8 +509,8 @@ ] } }, - "admin.yml get /leaderboards": { - "spec": "admin.yml", + "admin get /leaderboards": { + "spec": "admin", "method": "get", "path": "/leaderboards", "samplesByLanguage": { @@ -526,8 +526,8 @@ ] } }, - "admin.yml post /leaderboards": { - "spec": "admin.yml", + "admin post /leaderboards": { + "spec": "admin", "method": "post", "path": "/leaderboards", "samplesByLanguage": { @@ -543,8 +543,8 @@ ] } }, - "admin.yml patch /leaderboards": { - "spec": "admin.yml", + "admin patch /leaderboards": { + "spec": "admin", "method": "patch", "path": "/leaderboards", "samplesByLanguage": { @@ -560,8 +560,8 @@ ] } }, - "admin.yml delete /leaderboards": { - "spec": "admin.yml", + "admin delete /leaderboards": { + "spec": "admin", "method": "delete", "path": "/leaderboards", "samplesByLanguage": { @@ -577,8 +577,8 @@ ] } }, - "admin.yml get /leaderboards/{id}": { - "spec": "admin.yml", + "admin get /leaderboards/{id}": { + "spec": "admin", "method": "get", "path": "/leaderboards/{id}", "samplesByLanguage": { @@ -594,8 +594,8 @@ ] } }, - "admin.yml post /streaks/freezes": { - "spec": "admin.yml", + "admin post /streaks/freezes": { + "spec": "admin", "method": "post", "path": "/streaks/freezes", "samplesByLanguage": { @@ -611,8 +611,8 @@ ] } }, - "admin.yml post /streaks/restore": { - "spec": "admin.yml", + "admin post /streaks/restore": { + "spec": "admin", "method": "post", "path": "/streaks/restore", "samplesByLanguage": { @@ -628,8 +628,8 @@ ] } }, - "application.yml get /achievements": { - "spec": "application.yml", + "application get /achievements": { + "spec": "application", "method": "get", "path": "/achievements", "samplesByLanguage": { @@ -645,8 +645,8 @@ ] } }, - "application.yml post /achievements/{key}/complete": { - "spec": "application.yml", + "application post /achievements/{key}/complete": { + "spec": "application", "method": "post", "path": "/achievements/{key}/complete", "samplesByLanguage": { @@ -662,8 +662,8 @@ ] } }, - "application.yml post /metrics/{key}/event": { - "spec": "application.yml", + "application post /metrics/{key}/event": { + "spec": "application", "method": "post", "path": "/metrics/{key}/event", "samplesByLanguage": { @@ -679,8 +679,8 @@ ] } }, - "application.yml post /users": { - "spec": "application.yml", + "application post /users": { + "spec": "application", "method": "post", "path": "/users", "samplesByLanguage": { @@ -696,8 +696,8 @@ ] } }, - "application.yml get /users/{id}": { - "spec": "application.yml", + "application get /users/{id}": { + "spec": "application", "method": "get", "path": "/users/{id}", "samplesByLanguage": { @@ -713,8 +713,8 @@ ] } }, - "application.yml put /users/{id}": { - "spec": "application.yml", + "application put /users/{id}": { + "spec": "application", "method": "put", "path": "/users/{id}", "samplesByLanguage": { @@ -730,8 +730,8 @@ ] } }, - "application.yml patch /users/{id}": { - "spec": "application.yml", + "application patch /users/{id}": { + "spec": "application", "method": "patch", "path": "/users/{id}", "samplesByLanguage": { @@ -747,8 +747,8 @@ ] } }, - "application.yml get /users/{id}/preferences": { - "spec": "application.yml", + "application get /users/{id}/preferences": { + "spec": "application", "method": "get", "path": "/users/{id}/preferences", "samplesByLanguage": { @@ -764,8 +764,8 @@ ] } }, - "application.yml patch /users/{id}/preferences": { - "spec": "application.yml", + "application patch /users/{id}/preferences": { + "spec": "application", "method": "patch", "path": "/users/{id}/preferences", "samplesByLanguage": { @@ -781,8 +781,8 @@ ] } }, - "application.yml get /users/{id}/metrics": { - "spec": "application.yml", + "application get /users/{id}/metrics": { + "spec": "application", "method": "get", "path": "/users/{id}/metrics", "samplesByLanguage": { @@ -798,8 +798,8 @@ ] } }, - "application.yml get /users/{id}/metrics/{key}": { - "spec": "application.yml", + "application get /users/{id}/metrics/{key}": { + "spec": "application", "method": "get", "path": "/users/{id}/metrics/{key}", "samplesByLanguage": { @@ -815,8 +815,8 @@ ] } }, - "application.yml get /users/{id}/metrics/{key}/event-summary": { - "spec": "application.yml", + "application get /users/{id}/metrics/{key}/event-summary": { + "spec": "application", "method": "get", "path": "/users/{id}/metrics/{key}/event-summary", "samplesByLanguage": { @@ -832,8 +832,8 @@ ] } }, - "application.yml get /users/{id}/achievements": { - "spec": "application.yml", + "application get /users/{id}/achievements": { + "spec": "application", "method": "get", "path": "/users/{id}/achievements", "samplesByLanguage": { @@ -859,8 +859,8 @@ ] } }, - "application.yml get /users/{id}/streak": { - "spec": "application.yml", + "application get /users/{id}/streak": { + "spec": "application", "method": "get", "path": "/users/{id}/streak", "samplesByLanguage": { @@ -876,8 +876,8 @@ ] } }, - "application.yml get /streaks": { - "spec": "application.yml", + "application get /streaks": { + "spec": "application", "method": "get", "path": "/streaks", "samplesByLanguage": { @@ -893,8 +893,8 @@ ] } }, - "application.yml get /streaks/rankings": { - "spec": "application.yml", + "application get /streaks/rankings": { + "spec": "application", "method": "get", "path": "/streaks/rankings", "samplesByLanguage": { @@ -910,8 +910,8 @@ ] } }, - "application.yml get /users/{id}/points/{key}": { - "spec": "application.yml", + "application get /users/{id}/points/{key}": { + "spec": "application", "method": "get", "path": "/users/{id}/points/{key}", "samplesByLanguage": { @@ -927,8 +927,8 @@ ] } }, - "application.yml get /users/{id}/points/{key}/boosts": { - "spec": "application.yml", + "application get /users/{id}/points/{key}/boosts": { + "spec": "application", "method": "get", "path": "/users/{id}/points/{key}/boosts", "samplesByLanguage": { @@ -944,8 +944,8 @@ ] } }, - "application.yml get /users/{id}/points/{key}/event-summary": { - "spec": "application.yml", + "application get /users/{id}/points/{key}/event-summary": { + "spec": "application", "method": "get", "path": "/users/{id}/points/{key}/event-summary", "samplesByLanguage": { @@ -961,8 +961,8 @@ ] } }, - "application.yml get /users/{id}/leaderboards/{key}": { - "spec": "application.yml", + "application get /users/{id}/leaderboards/{key}": { + "spec": "application", "method": "get", "path": "/users/{id}/leaderboards/{key}", "samplesByLanguage": { @@ -978,8 +978,8 @@ ] } }, - "application.yml get /users/{id}/wrapped": { - "spec": "application.yml", + "application get /users/{id}/wrapped": { + "spec": "application", "method": "get", "path": "/users/{id}/wrapped", "samplesByLanguage": { @@ -995,8 +995,8 @@ ] } }, - "application.yml get /points/{key}/summary": { - "spec": "application.yml", + "application get /points/{key}/summary": { + "spec": "application", "method": "get", "path": "/points/{key}/summary", "samplesByLanguage": { @@ -1012,8 +1012,8 @@ ] } }, - "application.yml get /points/{key}": { - "spec": "application.yml", + "application get /points/{key}": { + "spec": "application", "method": "get", "path": "/points/{key}", "samplesByLanguage": { @@ -1029,8 +1029,8 @@ ] } }, - "application.yml get /points/{key}/boosts": { - "spec": "application.yml", + "application get /points/{key}/boosts": { + "spec": "application", "method": "get", "path": "/points/{key}/boosts", "samplesByLanguage": { @@ -1046,8 +1046,8 @@ ] } }, - "application.yml get /points/{key}/levels": { - "spec": "application.yml", + "application get /points/{key}/levels": { + "spec": "application", "method": "get", "path": "/points/{key}/levels", "samplesByLanguage": { @@ -1063,8 +1063,8 @@ ] } }, - "application.yml get /points/{key}/level-summary": { - "spec": "application.yml", + "application get /points/{key}/level-summary": { + "spec": "application", "method": "get", "path": "/points/{key}/level-summary", "samplesByLanguage": { @@ -1080,8 +1080,8 @@ ] } }, - "application.yml get /leaderboards": { - "spec": "application.yml", + "application get /leaderboards": { + "spec": "application", "method": "get", "path": "/leaderboards", "samplesByLanguage": { @@ -1097,8 +1097,8 @@ ] } }, - "application.yml get /leaderboards/{key}": { - "spec": "application.yml", + "application get /leaderboards/{key}": { + "spec": "application", "method": "get", "path": "/leaderboards/{key}", "samplesByLanguage": { diff --git a/apps/www/scripts/build-openapi-code-samples.mts b/apps/www/scripts/build-openapi-code-samples.mts index d349ab2..0643ce6 100644 --- a/apps/www/scripts/build-openapi-code-samples.mts +++ b/apps/www/scripts/build-openapi-code-samples.mts @@ -12,13 +12,13 @@ const OUTPUT_PATH = path.join( const SPECS = [ { - specKey: "admin.yml" as const, + specKey: "admin" as const, url: process.env.TROPHY_OPENAPI_ADMIN_URL ?? "https://admin.trophy.so/v1/openapi", }, { - specKey: "application.yml" as const, + specKey: "application" as const, url: process.env.TROPHY_OPENAPI_APPLICATION_URL ?? "https://api.trophy.so/v1/openapi", From f35dcbca484d2114e385edc679464d4be43d873b Mon Sep 17 00:00:00 2001 From: Charlie Hopkins-Brinicombe Date: Thu, 14 May 2026 16:54:16 +0100 Subject: [PATCH 5/5] Move to proxy file convention --- apps/www/{middleware.ts => proxy.ts} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename apps/www/{middleware.ts => proxy.ts} (93%) diff --git a/apps/www/middleware.ts b/apps/www/proxy.ts similarity index 93% rename from apps/www/middleware.ts rename to apps/www/proxy.ts index 808e53d..12561a6 100644 --- a/apps/www/middleware.ts +++ b/apps/www/proxy.ts @@ -2,7 +2,7 @@ import { NextRequest, NextResponse, type NextFetchEvent } from "next/server" import { sendProfoundCustomLogs } from "@/lib/profound/customLogs" -const PROFOUND_LOG_HOST = "ui.trophy.so" +const PROFOUND_LOG_HOST = "ui.trophy.so"; const SAFE_QUERY_PARAMS = new Set( (process.env.PROFOUND_LOG_QUERY_PARAM_ALLOWLIST ?? "") @@ -37,7 +37,7 @@ function getSafeQueryParams(url: URL): Record | undefined { return Object.fromEntries(safeEntries) } -export function middleware(request: NextRequest, event: NextFetchEvent) { +export function proxy(request: NextRequest, event: NextFetchEvent) { const response = NextResponse.next() const referer = request.headers.get("referer") || undefined