# Streak

Streaks inspire repeat visits and brand loyalty by challenging users to meet activity targets over consecutive time periods. When users hit milestones along the way, rewards are automatically issued.

:::tip
Customizable streak durations, milestone configurations, and engaging end-user displays make streaks one of the most powerful engagement tools in Gamopanda.
:::

## How it works

1. **Create a streak** — define a name, aggregation method, aggregation period, aggregation target value per period, and an overall completion target.
2. **Attach milestones** — link `Milestone` records to the streak. Each milestone fires a reward when the user reaches that progress threshold.
3. **Log activity** — send `Streak Log` entries as users perform qualifying actions. The platform aggregates logs per period automatically.
4. **Track progress** — `Streak Progress` records are maintained per user, tracking their current value, status, and timestamps.
5. **Earn rewards** — when a milestone is hit, a `Reward Log` entry is created with a coupon code for the user.

---

## Fields

### Basic information

| Field | Type | Required | Max length | Description |
|---|---|---|---|---|
| `name` | string | ✅ | 256 | The name of the streak |
| `description` | string | ✅ | 1 024 | A brief description of the streak |
| `status` | enum | ✅ | — | `draft` · `live` · `paused` |
| `completionTargetValue` | integer | ✅ | — | Total number of periods the user must complete to finish the streak |

### Aggregation settings

Aggregation controls how the platform measures per-period progress from incoming `Streak Log` entries.

| Field | Type | Required | Default | Options | Description |
|---|---|---|---|---|---|
| `aggregationMethod` | enum | ✅ | `sum` | `sum` · `count` | How log values are combined within a period — `sum` adds `activityValue`, `count` counts the number of log entries |
| `aggregationPeriod` | enum | ✅ | `daily` | `daily` · `weekly` · `monthly` | The time window over which progress is measured |
| `aggregationTargetValue` | number | ✅ | — | — | The value a user must reach within a single period to count that period as complete |

### Real-world examples

**🛍️ E-commerce — "7-Day Shopper" streak**

Reward customers who spend $50 every day for 7 consecutive days. Milestone at day 3 gives 5% off; milestone at day 7 gives free shipping for a month.

```json
{
  "name": "7-Day Shopper",
  "description": "Spend $50 or more every day for 7 days to unlock exclusive rewards.",
  "status": "live",
  "aggregationMethod": "sum",
  "aggregationPeriod": "daily",
  "aggregationTargetValue": 50,
  "completionTargetValue": 7
}
```

---

**☕ Food & Beverage — "Weekly Regular" streak**

Reward café customers who make at least 2 purchases every week for 4 consecutive weeks. Each week counts as one completed period.

```json
{
  "name": "Weekly Regular",
  "description": "Visit us at least twice a week for 4 weeks and earn a free drink.",
  "status": "live",
  "aggregationMethod": "count",
  "aggregationPeriod": "weekly",
  "aggregationTargetValue": 2,
  "completionTargetValue": 4
}
```

> `aggregationMethod: count` — each `Streak Log` entry counts as 1 visit regardless of its `activityValue`.

---

**🏋️ Fitness app — "Monthly Mover" streak**

Reward users who log at least 1 500 activity minutes every month for 3 months. Milestone at month 1 gives a discount on premium; completing all 3 months gives free membership.

```json
{
  "name": "Monthly Mover",
  "description": "Log 1,500 activity minutes every month for 3 months.",
  "status": "live",
  "aggregationMethod": "sum",
  "aggregationPeriod": "monthly",
  "aggregationTargetValue": 1500,
  "completionTargetValue": 3
}
```

---

**📚 EdTech — "Daily Learner" streak**

Reward students who complete at least 1 lesson per day for 30 consecutive days.

```json
{
  "name": "Daily Learner",
  "description": "Complete at least one lesson every day for 30 days.",
  "status": "live",
  "aggregationMethod": "count",
  "aggregationPeriod": "daily",
  "aggregationTargetValue": 1,
  "completionTargetValue": 30
}
```

> Milestones at day 7, 14, and 30 progressively unlock course discounts.

### Milestones

| Field | Type | Description |
|---|---|---|
| `milestones` | Milestone[] | Read-only. Milestones linked to this streak via the Milestone module. Manage them from the **Milestones** tab. |

---

## End-user display (EUD)

All EUD fields are **localisable** — supply a `languageCode` query parameter when reading to receive the translated value.

### Labels

| Field | Default | Max length | Description |
|---|---|---|---|
| `eudTitle` | — | 48 | Title shown to the end user |
| `eudDescription` | — | 1 024 | Description shown to the end user |
| `eudIcon` | *(default streak icon)* | — | Icon image. Allowed: PNG, JPEG, JPG, GIF, SVG · Max size: 100 KB |
| `eudInProgressLabel` | `In Progress` | 32 | Status label while streak is active |
| `eudCompletedLabel` | `Completed` | 32 | Status label when streak is finished |
| `eudNotStartedLabel` | `Not Started` | 32 | Status label before any activity |
| `eudBrokenLabel` | `Broken` | 32 | Status label when streak is broken |
| `eudViewDetailsButtonText` | `View Details` | 32 | CTA button label |

### Messages

Messages support **template variables** that are resolved at runtime. Available variables:

| Variable | Description | Example value |
|---|---|---|
| `{{$helpers.periodText}}` | Human-readable period name derived from `aggregationPeriod` | `daily` → `day` · `weekly` → `week` · `monthly` → `month` |
| `{{$helpers.remainingValue}}` | Amount still needed in the **current** period to mark it complete. It is usually equal to either `{{$helpers.nextMilestoneRemainingValue}}` or `{{$helpers.nextNonMilestoneRemainingValue}}` based on streak progress | `20.50` (user has spent $29.50, needs $50 total → $20.50 remaining) |
| `{{$helpers.nextMilestoneRemainingValue}}` | Steps/spend remaining to reach the next milestone | `20.50` (user has spent $29.50, needs $50 total → $20.50 remaining) |
| `{{$helpers.nextNonMilestoneRemainingValue}}` | Steps/spend remaining to reach the next non-milestone | `20.50` (user has spent $29.50, needs $50 total → $20.50 remaining) |
| `{{$helpers.currentProgressValue}}` | Number of periods the user has successfully completed so far | `3` (user has completed 3 out of 7 days) |
| `{{$helpers.nextMilestoneTargetValue}}` | Target value for the next milestone | `5` (User has completed 4 out of 7 days and next milestone is on 5th day) |
| `{{$helpers.nextNonMilestoneTargetValue}}` | Target value for the next non-milestone | `3` (user has completed 2 out of 7 days) |
| `{{$helpers.completionTargetValue}}` | Target value for the current period | `7` (User needs to complete 7 days to complete the streak) |
| `{{$helpers.formattedStreakToBeBrokenAt}}` | Expiration time for active period | `2026-07-20 23:59:59` |

**Rendered example** — for a daily 7 day $50 spend streak where the user is on day 3, has spent $29.50 today, and the next milestone is at day 5:

| Variable | Resolved value | Remarks
|---|---|---|
| `{{$helpers.periodText}}` | `day` |
| `{{$helpers.remainingValue}}` | `20.50` |
| `{{$helpers.nextMilestoneRemainingValue}}` | undefined | Since the day is a non-milestone day, it will be undefined
| `{{$helpers.nextNonMilestoneRemainingValue}}` | `20.50` |
| `{{$helpers.currentProgressValue}}` | `2` |
| `{{$helpers.nextMilestoneTargetValue}}` | undefined | Since the day is a non-milestone day, it will be undefined
| `{{$helpers.nextNonMilestoneTargetValue}}` | `3` |
| `{{$helpers.completionTargetValue}}` | `7` |
| `{{$helpers.formattedStreakToBeBrokenAt}}` | `DateTime of the streak end date in ISO format` |

So the default `eudNonMilestoneNextToBeAchievedMessage` would render as:

> *Shop for $20.50 or more to complete the day 3 streak!*

And `eudMilestoneNextToBeAchievedMessage` would render on day 5 as:

> *Shop for $20.50 or more to complete the next milestone!*

| Field | Default | Max length |
|---|---|---|
| `eudNonMilestoneNextToBeAchievedMessage` | ```Shop for ${{$helpers.nextNonMilestoneRemainingValue}} or more to complete the {{$helpers.periodText}} {{$helpers.nextNonMilestoneTargetValue}} streak!``` | 256 |
| `eudNonMilestoneAchievedMessage` | `You have completed {{$helpers.periodText}} {{$helpers.currentProgressValue}} streak! Come back next {{$helpers.periodText}} to shop more to maintain your streak and reach the next milestone!` | 256 |
| `eudCompletedStreakMessage` | `Congratulations! You have completed this streak! Come back next {{$helpers.periodText}} to shop more and start your next streak!` | 256 |
| `eudBrokenStreakMessage` | `Your streak has been broken. Don\'t worry, you can start a new streak today! Shop more and reach your next milestone!` | 256 |
| `eudMilestoneNextToBeAchievedMessage` | `Shop for ${{$helpers.nextMilestoneRemainingValue}} or more to complete the next milestone!` | 256 |
| `eudMilestoneAchievedMessage` | `You have completed {{$helpers.periodText}} {{$helpers.currentProgressValue}} streak! Come back next {{$helpers.periodText}} to shop more to maintain your streak and reach the next milestone!` | 256 |
| `eudToBeBrokenStreakMessage` | `Your streak will break if you don\'t shop for ${{$helpers.remainingValue}} or more by {{$helpers.formattedStreakToBeBrokenAt}}.` | 256
---

## Access & permissions

| Caller | Allowed operations |
|---|---|
| Admin | CREATE · GET · LIST · UPDATE · DELETE |
| End user | GET · LIST |
| Guest (unauthenticated) | GET · LIST |

---

## Related resources

A streak is the parent record that ties together several sub-resources:

| Resource | Description |
|---|---|
| [Milestone](/milestone) | Reward checkpoints attached to a streak |
| [Streak Log](/streak_log) | Individual activity events logged against a streak |
| [Streak Progress](/streak_progress) | Per-user progress state for a streak |
| [Reward Log](/reward_log) | Rewards issued when a milestone is reached |

---

## API reference

See the [API Reference](/api/streak) for full request/response schemas and interactive examples for:

- `GET /schema/streak/record` — list streaks
- `POST /schema/streak/record` — create a streak
- `GET /schema/streak/record/{id}` — get a streak by ID
- `PATCH /schema/streak/record/{id}` — update a streak
- `DELETE /schema/streak/record/{id}` — delete a streak
