---
title: Badges & Achievements
feature: true
featureGroup: core
featureState: beta
---

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`:

```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:

```ts
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:

```ts
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.
