Badges & Achievements

The Business Platform includes a badge system that rewards users for reaching key milestones. Badges are awarded automatically when a user completes a specific action for the first time.

Available Badges

Badge Name Description
πŸ† Project Created projectCreated Awarded when a user creates their first project
⚑ First Action Created firstActionCreated Awarded when a user creates their first action within a project
πŸ“± First Application Created firstApplicationCreated Awarded when a user creates their first application within a project
πŸ‘₯ First Project Member Invite firstProjectMemberInvite Awarded when a user invites a member to a project for the first time
πŸ—„οΈ First Data Type Created firstDataTypeCreated Awarded when a user creates their first data type within a project

How Badges Are Earned

Badges are awarded automatically on the server side when a qualifying action is successfully completed. Each badge is awarded only once per user β€” if a user has already earned a badge, subsequent qualifying actions do not re-award it.

When a badge is newly awarded, the user receives an in-app system notification confirming the achievement.

Trigger Points

Badge Trigger
projectCreated POST /api/projects β€” after a project is successfully created
firstActionCreated POST /api/projects/[projectId]/actions β€” after an action is successfully created
firstApplicationCreated POST /api/projects/[projectId]/applications β€” after an application is successfully created
firstProjectMemberInvite Awarded when a project member invitation is sent for the first time
firstDataTypeCreated Awarded when the first data type is created within a project

Viewing Your Badges

Users can view their earned and locked badges through the profile menu:

  1. Open the User Profile panel (top-right avatar or profile button).
  2. Click the Badges button (trophy icon).
  3. The badges modal displays:
    • Earned badges β€” shown with a green border, the badge icon in full colour, and the date the badge was earned.
    • Locked badges β€” shown with a grey border and a greyscale icon, indicating they have not yet been earned.

The modal also shows a summary count, e.g. Earned Badges: 2 / 5.

Technical Reference

Shared Package

Badge names are defined as constants in packages/shared/src/utils/badge.utils.ts:

import { BADGE_NAMES } from "@smallstack/shared";

BADGE_NAMES.PROJECT_CREATED             // "projectCreated"
BADGE_NAMES.FIRST_ACTION_CREATED        // "firstActionCreated"
BADGE_NAMES.FIRST_APPLICATION_CREATED   // "firstApplicationCreated"
BADGE_NAMES.FIRST_PROJECT_MEMBER_INVITE // "firstProjectMemberInvite"
BADGE_NAMES.FIRST_DATA_TYPE_CREATED     // "firstDataTypeCreated"

The BadgeDefinition interface and helper functions getAllBadgeDefinitions() and getBadgeDefinition(name) are also exported from @smallstack/shared.

Earned badges are stored on the User object as an array of EarnedBadge entries:

interface EarnedBadge {
  name: string;      // badge identifier
  earnedAt: number;  // Unix timestamp (ms)
}

Server Package

The checkBadge utility in packages/server/src/utils/badge.server.ts handles badge awarding:

import { checkBadge } from "@smallstack/server";
import { BADGE_NAMES } from "@smallstack/shared";

await checkBadge(BADGE_NAMES.PROJECT_CREATED, userId);

checkBadge performs the following steps atomically within a transaction:

  1. Fetches the latest user record from the database.
  2. Checks whether the badge has already been earned.
  3. If not, appends the new EarnedBadge entry and persists the update via a patch operation.
  4. Sends a system notification message to the user.
  5. Returns true if the badge was newly awarded, or false if it was already present.

Errors during badge awarding are caught and logged, and do not interrupt the parent request.

Adding a New Badge

  1. Add a new key to BADGE_NAMES in packages/shared/src/utils/badge.utils.ts.
  2. Add a corresponding BadgeDefinition entry to the BADGE_DEFINITIONS array (including an icon URL, and i18n label/description keys).
  3. Add the i18n translation strings for the new labelKey and descriptionKey.
  4. Call checkBadge(BADGE_NAMES.YOUR_NEW_BADGE, userId) at the appropriate server-side endpoint after the qualifying action succeeds.