Journeys & Streaks
The Journeys system tracks user activity streaks and daily activity logs using Cloudflare Workers KV storage. This service provides fast, distributed access to user streak data without requiring Mantle2 database queries.
Architecture
- Storage: Cloudflare Workers KV (key-value storage)
- Key Format:
journey:{user_id}:{date}(e.g.,journey:000000000000000123456789:2025-01-10) - TTL: Journey records persist indefinitely to maintain historical streak data
- Caching: KV provides automatic edge caching
Data Model
Each journey record is stored as JSON in KV:
{
"user_id": "000000000000000123456789",
"date": "2025-01-10",
"streak": 7,
"activities": ["000000000000001234567890", "000000000000001234567891"],
"created_at": "2025-01-10T08:00:00Z",
"updated_at": "2025-01-10T18:30:00Z"
}Journey Fields
| Field | Type | Description |
|---|---|---|
| user_id | string | User ID (24-digit zero-padded) |
| date | string | Journey date (YYYY-MM-DD) |
| streak | number | Current consecutive day streak |
| activities | array | Activity IDs logged for this day (24-digit strings) |
| created_at | string | ISO 8601 timestamp when journey started |
| updated_at | string | ISO 8601 timestamp of last update |
Streak Calculation
Streaks are calculated by checking consecutive days of activity:
- User completes an activity on Day N
- Check if previous day (N-1) has a journey record
- If yes, increment streak:
previous_streak + 1 - If no, reset streak to:
1
Streak Rules
- Minimum 1 activity per day to maintain streak
- Midnight UTC is the day boundary
- Missing a single day resets streak to 0
- Streaks can be manually reset via API
Endpoints
GET /users/:id/journey
Retrieve current journey and streak information for a user.
Path Parameters
| Name | Type | Description |
|---|---|---|
| id | string | User ID (24-digit string) |
Query Parameters
| Name | Type | Description |
|---|---|---|
| date | string | Specific date (YYYY-MM-DD), defaults to today |
Responses
200 OK:
{
"user_id": "000000000000000123456789",
"date": "2025-01-10",
"streak": 7,
"activities": [
"000000000000001234567890",
"000000000000001234567891",
"000000000000001234567892"
],
"milestones": {
"longest_streak": 14,
"total_days": 42,
"current_month_days": 10
},
"created_at": "2025-01-10T08:00:00Z",
"updated_at": "2025-01-10T19:45:00Z"
}404 Not Found — no journey found for date
401 Unauthorized — authentication required
POST /users/:id/journey/increment
Increment the daily streak. Idempotent per day — calling multiple times on same day does not increase streak.
Path Parameters
| Name | Type | Description |
|---|---|---|
| id | string | User ID (24-digit string) |
Request Body
{
"date": "2025-01-10"
}If date is omitted, uses current UTC date.
Responses
200 OK:
{
"user_id": "000000000000000123456789",
"date": "2025-01-10",
"streak": 8,
"activities": [],
"created_at": "2025-01-10T08:00:00Z",
"updated_at": "2025-01-10T08:00:00Z"
}401 Unauthorized
POST /users/:id/journey/reset
Reset user's streak to zero. Clears current streak counter but preserves historical journey records.
Path Parameters
| Name | Type | Description |
|---|---|---|
| id | string | User ID (24-digit string) |
Responses
200 OK:
{
"user_id": "000000000000000123456789",
"date": "2025-01-10",
"streak": 0,
"activities": [],
"message": "Streak reset successfully"
}401 Unauthorized
404 Not Found — user has no active journey
POST /users/:id/journey/activities/:activity_id
Add an activity to today's journey. Ensures uniqueness — adding the same activity multiple times on the same day has no effect.
Path Parameters
| Name | Type | Description |
|---|---|---|
| id | string | User ID (24-digit string) |
| activity_id | string | Activity ID (24-digit string) |
Request Body
Optional:
{
"date": "2025-01-10"
}If date is omitted, uses current UTC date.
Responses
200 OK:
{
"user_id": "000000000000000123456789",
"date": "2025-01-10",
"streak": 7,
"activities": [
"000000000000001234567890",
"000000000000001234567891",
"000000000000001234567892",
"000000000000001234567893"
],
"created_at": "2025-01-10T08:00:00Z",
"updated_at": "2025-01-10T20:15:00Z"
}401 Unauthorized
404 Not Found — user or activity not found
409 Conflict — activity already added to this day's journey
GET /users/:id/journey/history
Retrieve journey history for a user over a date range.
Path Parameters
| Name | Type | Description |
|---|---|---|
| id | string | User ID (24-digit string) |
Query Parameters
| Name | Type | Description |
|---|---|---|
| start_date | string | Start date (YYYY-MM-DD), defaults to 30 days ago |
| end_date | string | End date (YYYY-MM-DD), defaults to today |
Responses
200 OK:
{
"user_id": "000000000000000123456789",
"start_date": "2024-12-11",
"end_date": "2025-01-10",
"journeys": [
{
"date": "2025-01-10",
"streak": 7,
"activities": ["000000000000001234567890", "000000000000001234567891"],
"created_at": "2025-01-10T08:00:00Z",
"updated_at": "2025-01-10T20:15:00Z"
},
{
"date": "2025-01-09",
"streak": 6,
"activities": ["000000000000001234567890"],
"created_at": "2025-01-09T09:30:00Z",
"updated_at": "2025-01-09T09:30:00Z"
}
],
"total_days": 2
}401 Unauthorized
Milestones
The system tracks several milestone metrics:
| Milestone | Description |
|---|---|
| longest_streak | Highest consecutive day streak achieved |
| total_days | Total number of days with any activity |
| current_month_days | Days active in current calendar month |
| total_activities | Total activities logged across all days |
Semantics
Increment Idempotency
Calling POST /journey/increment multiple times on the same day is safe:
- First call: creates journey record or increments streak
- Subsequent calls: returns existing journey, no streak change
Activity Uniqueness
Adding the same activity multiple times to the same day's journey:
- First call: adds activity to array
- Subsequent calls: returns 409 Conflict, no duplicate added
Streak Preservation
Journey history is preserved even after streak resets, allowing users to view their complete activity history.
Integration
- Mantle2: Journey service reads activity data from Mantle2 API to validate activity IDs
- Crust: Frontend displays journey calendar and streak badges
- Scheduled Tasks: Daily job checks for broken streaks and sends notifications
Errors
Standard error response:
{
"error": {
"code": "E404",
"message": "Journey not found for date"
}
}Common status codes:
- 200 OK
- 401 Unauthorized — authentication required
- 404 Not Found — user or activity not found
- 409 Conflict — duplicate activity on same day
- 429 Too Many Requests — rate limit exceeded