# Challenge Progress

Challenge Progress records the **overall state** of a single end user's engagement with a specific challenge. It tracks how much cumulative progress the user has accumulated so far, whether the challenge is still winnable, and key lifecycle timestamps.

:::info
Challenge Progress records are **system-managed** — they are created and updated automatically as `Challenge Log` entries are processed. They cannot be created, updated, or deleted via the API.
:::

The **Challenge** defines the rules (`completionTargetValue`, aggregation method, period). The **Challenge Progress** is the running summary of a specific user's journey against those rules.

---

## How it works

1. **First log entry arrives** — when a `Challenge Log` is created for a user on a challenge for the first time, a Challenge Progress record is automatically created with `challengeProgressStatus: in_progress` and `challengeStartedAt` stamped to `activityAt` field of the Challenge Log API.
2. **Progress accumulates** — each `Challenge Log` entry adds to `currentProgressValue` according to the challenge's `aggregationMethod` (`sum` or `count`).
3. **Challenge completed** — when `currentProgressValue` reaches the challenge's `completionTargetValue`, the status transitions to `completed` and `challengeCompletedAt` is stamped with the `activityAt` field of the Challenge Log API.
4. **Challenge failed** — when the challenge's aggregation period ends and `currentProgressValue` has not reached `completionTargetValue`, the status transitions to `failed` and `challengeFailedAt` is stamped.
5. **Task progress surfaced** — when listing Challenge Progress records, the response also includes the current **Task Progress** (`taskProgress`) inline, giving a full picture of this-period activity without a separate request.

---

## Fields

### Identity & linking

| Field | Type | Description |
|---|---|---|
| `challengeId` | UUID | ID of the parent challenge this progress record belongs to. Indexed. |
| `uniqueIdentifier` | string | Auto-generated composite key: `challengeId + ownedById`. It is unique when `challengeProgressStatus` value is `in_progress`. |

### Progress & status

| Field | Type | Default | Description |
|---|---|---|---|
| `currentProgressValue` | number | `0` | The user's cumulative progress value so far — the running total of all `Challenge Log` contributions. Compared against the challenge's `completionTargetValue` to determine completion. |
| `challengeProgressStatus` | enum | `in_progress` | Current status of the challenge for this user. See [statuses](#status-values) below. |

### Timestamps

| Field | Type | Description |
|---|---|---|
| `challengeStartedAt` | datetime | When the user first engaged with this challenge. Defaults to the creation time. |
| `challengeCompletedAt` | datetime | Stamped when `challengeProgressStatus` transitions to `completed`. |
| `challengeFailedAt` | datetime | Stamped when `challengeProgressStatus` transitions to `failed`. |

### Ownership

| Field | Type | Description |
|---|---|---|
| `ownedById` | UUID | ID of the end user this progress record belongs to. |
| `ownedBy` | object | Read-only joined user object. |

---

## Status values

| Status | Meaning |
|---|---|
| `in_progress` | The challenge is active — the user has not yet hit the target, and the challenge window has not closed. |
| `completed` | The user's `currentProgressValue` reached the challenge's `completionTargetValue` — the challenge is won. |
| `failed` | The challenge window ended before the user reached the target — the challenge is lost. |

:::tip
Unlike streaks (which break when a single period is missed), challenges only fail when the **entire challenge window closes** without the target being met. Users can still complete a challenge right up to the last moment of the period.
:::

---

## The `taskProgress` field

When you list Challenge Progress records, the response automatically includes a `taskProgress` object inline. This gives a real-time view of the user's activity in the **current period** without a separate API call.

```json
{
  "id": "...",
  "challengeId": "a1b2c3d4-...",
  "currentProgressValue": 3,
  "challengeProgressStatus": "in_progress",
  "challengeStartedAt": "2026-04-01T00:00:00Z",
  "challengeCompletedAt": null,
  "challengeFailedAt": null,
  "taskProgress": {
    "periodKey": "monthly_2026-04",
    "currentProgressValue": 3,
    "taskProgressStatus": "in_progress"
  },
  "ownedById": "user-uuid-here"
}
```

> For a monthly challenge requiring 8 visits, `currentProgressValue: 3` means the user has visited 3 times this month. `taskProgress` reflects the same value since challenge progress accumulates within the single challenge period.

---

## Real-world examples

**🛍️ E-commerce — monthly spend challenge (in progress)**

A user is working toward a monthly challenge requiring $500 total spend (`aggregationMethod: sum`, `completionTargetValue: 500`):

```json
{
  "challengeId": "a1b2c3d4-0000-0000-0000-000000000000",
  "currentProgressValue": 187.50,
  "challengeProgressStatus": "in_progress",
  "challengeStartedAt": "2026-04-01T09:12:00Z",
  "challengeCompletedAt": null,
  "challengeFailedAt": null,
  "taskProgress": {
    "periodKey": "monthly_2026-04",
    "currentProgressValue": 187.50,
    "taskProgressStatus": "in_progress"
  },
  "ownedById": "user-uuid-here"
}
```

The user needs $312.50 more to complete the challenge before 30 April.

---

**☕ Food & Beverage — monthly visit challenge (completed)**

A user completes a challenge requiring 8 café visits in a month (`aggregationMethod: count`, `completionTargetValue: 8`):

```json
{
  "challengeId": "b2c3d4e5-0000-0000-0000-000000000000",
  "currentProgressValue": 8,
  "challengeProgressStatus": "completed",
  "challengeStartedAt": "2026-04-01T10:00:00Z",
  "challengeCompletedAt": "2026-04-22T14:35:00Z",
  "challengeFailedAt": null,
  "taskProgress": {
    "periodKey": "monthly_2026-04",
    "currentProgressValue": 8,
    "taskProgressStatus": "completed"
  },
  "ownedById": "user-uuid-here"
}
```

---

**🏋️ Fitness — weekly step challenge (failed)**

A user fails to accumulate 100,000 steps in a week (`aggregationMethod: sum`, `completionTargetValue: 100000`):

```json
{
  "challengeId": "c3d4e5f6-0000-0000-0000-000000000000",
  "currentProgressValue": 61420,
  "challengeProgressStatus": "failed",
  "challengeStartedAt": "2026-04-07T00:00:00Z",
  "challengeCompletedAt": null,
  "challengeFailedAt": "2026-04-14T00:00:00Z",
  "taskProgress": {
    "periodKey": "weekly_2026-15",
    "currentProgressValue": 61420,
    "taskProgressStatus": "failed"
  },
  "ownedById": "user-uuid-here"
}
```

> The user accumulated 61,420 steps — 38,580 short of the 100,000 target when the weekly window closed.

---

## Fetching a user's challenge progress

**As an end user — get your progress on a specific challenge:**

```http
GET /api/v1.0/schema/challenge_progress/record?filters={"operator": "and", "conditions": [{"operator": "and", "conditions": [{"fieldSlug": "challengeId", "operator": "equals", "fieldValue": "<Challenge Id>"}]}]}
x-enduser-access-token: <token>
x-account-id: <account-id>
```

**As an admin — list all progress records for a challenge:**

```http
GET /api/v1.0/schema/challenge_progress/record?filters={"operator": "and", "conditions": [{"operator": "and", "conditions": [{"fieldSlug": "challengeId", "operator": "equals", "fieldValue": "<Challenge Id>"}]}]}
x-api-key: <your-api-key>
x-api-secret: <your-api-secret>
```

---

## Access & permissions

| Caller | Allowed operations | Notes |
|---|---|---|
| Admin | GET · LIST | Read-only — no CREATE, UPDATE, or DELETE |
| End user | GET · LIST | Can only read their own progress records |
| Guest user | *(none)* | Not accessible to unauthenticated callers |

:::warning
Challenge Progress records **cannot be created, updated, or deleted** via the API. They are managed exclusively by the platform as `Challenge Log` entries are processed.
:::

---

## Related resources

| Resource | Description |
|---|---|
| [Challenge](/challenge) | The parent challenge configuration that this progress record belongs to |
| [Challenge Log](/challenge_log) | Individual activity log entries that drive progress updates |
| [Milestone](/milestone) | Reward checkpoints linked to the parent challenge |
| [Reward Log](/reward_log) | Reward entries issued when a milestone threshold is crossed |
| [Streak Progress](/streak_progress) | Equivalent progress tracking for streaks |

---

## API reference

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

- `GET /schema/challenge_progress/record` — list challenge progress records
- `GET /schema/challenge_progress/record/{id}` — get a challenge progress record by ID
