AWorld Lab
English
  • English
  • Italiano
About
API ReferenceAWorld.orgAWorld Trust
About
API ReferenceAWorld.orgAWorld Trust
AWorld.org
English
  • English
  • Italiano
English
  • English
  • Italiano
  1. Domain Deep-Dives
  • Gamification Fundamentals
    • Engagement for Businesses and Organizations
    • API-first for Gamification
    • Activities, Learning, and Content
    • Missions, Rewards, and Progression
    • Leaderboards and Social Mechanics
  • Engagement Scenarios
    • Employee Engagement and Training
    • Customer Loyalty Program
    • Education Platform
    • Community and App Engagement
  • Domain Deep-Dives
    • Mission Domain
    • Learning Content Domain
    • Reward and Currency Domain
    • Badge Domain
    • Leaderboard Domain
    • Streak Domain
    • Cross-Cutting Patterns
  • Infrastructure & Security
    • Cloud Infrastructure and Architecture
    • Security and Cybersecurity
    • Compliance and Certifications
    • Disaster Recovery and Business Continuity
    • Performance and Scalability
    • Access Methods and Integration
    • Technical Glossary
  1. Domain Deep-Dives

Streak Domain

The streak system tracks user engagement over time. It answers questions like "Has this user been active every day for the past 30 days?" or "How many weeks in a row has this user completed at least one quiz?"
This section covers the full streak model: configuration and matching, cadence and metric dimensions, goal tracking, freeze mechanics, and the record system. It is relevant for anyone configuring engagement streaks or integrating them via API. For a high-level overview, see the Gamification Fundamentals. For shared patterns (JsonLogic expressions, timeframes, entity matching), see Cross-Cutting Patterns.
The domain is built around three entities that form a clear hierarchy:
StreakConfiguration  (what counts)
      │
      └──▶ StreakRule  (the rules of the game)
                │
                └──▶ Streak  (per-user tracking records)

Streak Configuration#

A Streak Configuration defines what user actions count toward a streak. It is the matching layer that connects domain events (completing an activity, finishing a quiz, etc.) to the streak system.

Fields#

FieldTypeDescription
streakConfigurationIdnanoidUnique identifier
matchTypeINSTANCE | ENTITY | TAGHow to match incoming events
matchEntityMission | Activity | Quiz | TagWhich entity type to watch
matchEntityIdstring?Specific entity or tag ID (required for INSTANCE and TAG)
matchConditionJsonLogicAdditional matching logic evaluated at runtime

Match Types#

INSTANCE: Matches a specific entity. Example: completing activity abc123. Requires matchEntityId.
ENTITY: Matches any entity of that type. Example: completing any quiz.
TAG: Matches any entity tagged with the given tag. Example: completing any activity tagged christmas. Requires matchEntityId (the tag ID).
This is where the flexibility lives. By combining TAG matching with matchCondition JsonLogic expressions, you can create targeted streaks like "only Christmas-themed activities performed in December" or "quizzes with difficulty >= 3".

Streak Rule#

A Streak Rule defines the rules of the game: how often the user must act, for how long, what goals to track, and what happens when they miss a period. Every rule references a Streak Configuration via streakConfigurationId.

Core Fields#

FieldTypeDescription
streakRuleIdnanoidUnique identifier
streakConfigurationIdstringReferences the Streak Configuration
namestringHuman-readable name
statePENDING | ACTIVE | ENDEDLifecycle state
usersMatchConditionJsonLogicWhich users this rule applies to (evaluated against user profile + tags)

Cadence and Metric#

These two fields define how the streak counts progress:
FieldTypeDefaultDescription
cadenceDAY | WEEKrequiredHow often the user must be active to maintain the streak
metricDAYS | WEEKSDAYSWhat each increment of the counter represents
Cadence determines the frequency requirement:
DAY = the user must act every calendar day to keep the streak alive.
WEEK = the user must act at least once per ISO calendar week.
Metric determines what gets counted in ITERATION and GOAL records:
DAYS = each increment of count represents one active day.
WEEKS = each increment of count represents one active week.
These are orthogonal dimensions. The most common combinations:
CadenceMetricMeaning
DAYDAYSUser must act daily; counters track active days
WEEKDAYS (default)User must act weekly; counters track active days within the streak
WEEKWEEKSUser must act weekly; counters track consecutive active weeks
Constraint: DAY cadence can only have DAYS metric (or undefined). WEEKS metric only makes sense with WEEK cadence.

Timeframe#

FieldTypeDescription
timeframeTypePERMANENT | RANGEWhether the rule runs indefinitely or has a fixed end date
timeframeStartsAtISO datetimeWhen the rule begins
timeframeEndsAtISO datetime?When the rule ends (required for RANGE)
timeframeTimezoneTypeFIXED | USERWhether to use a fixed timezone or each user's own timezone
timeframeTimezonetimezone?The fixed timezone (required when timeframeTimezoneType is FIXED)
The timezone setting is critical for determining period boundaries. With USER timezone, a user in Tokyo and a user in London will have different "day" boundaries, ensuring fairness regardless of location.

Goal Targets#

FieldTypeDescription
goalTargetsnumber[]?Milestones the user can reach (e.g., [7, 30, 100, 365])
Goal targets define milestone thresholds. When a user's cumulative count reaches a target, the corresponding GOAL record is marked COMPLETED. Multiple targets create a tiered system: reach 7 days in a row, then 30, then 100, etc. Once all targets for a cycle are completed, a new goal cycle begins with goalId incremented.

Perfect Period Tracking#

FieldTypeDescription
perfectWeekEnabledbooleanTrack whether the user was active every day of a week
perfectMonthEnabledbooleanTrack whether the user was active every day/week of a month
perfectYearEnabledbooleanTrack whether the user was active every day/week of a year
These booleans enable additional tracking on calendar records (WEEK, MONTH, YEAR). A "perfect week" means the user was active on all 7 days (for DAY cadence) or didn't miss any required period.

Freeze Settings#

FieldTypeDescription
freezeEnabledbooleanWhether users can spend virtual currency to preserve a streak
freezeVirtualCurrencyIdstring?Which virtual currency to deduct (required when freeze is enabled)
freezeCostExpressionJsonLogic?Expression to calculate freeze cost (receives { user, streak } context)
When a streak is about to break (the maintenance scheduler detects a missed period), the system can automatically deduct virtual currency to "freeze" the streak instead of breaking it. The cost is dynamic via JsonLogic — for example, the cost could increase with longer streaks.

Streak (Records)#

A Streak is a DynamoDB record that tracks a specific counter for a specific user under a specific rule. The key insight is that a single user action generates multiple streak records simultaneously — each tracking a different dimension of the same streak.

Record Fields#

FieldTypeDescription
streakIdnanoidUnique record identifier (auto-generated on first write)
userIdstringThe user this record belongs to
streakRuleIdstringThe rule this record belongs to
periodTypeenumThe time window or tracking dimension (see below)
periodIdstring?Calendar period identifier (e.g., 2025-09-02, 2025-W37)
cadenceDAY | WEEKInherited from the rule
metricDAYS | WEEKSWhat count represents
countnumberAccumulated counter (atomically incremented)
statusACTIVE | COMPLETED | BROKEN | ENDEDLifecycle state
kindREGULAR | FREEZE | ANYHow this period was kept alive
iterationIdnumber?Which consecutive run this belongs to (for ITERATION records)
goalIdnumber?Which goal cycle this belongs to (for GOAL records)
targetnumber?The milestone threshold (for GOAL records)
timezonestringThe effective timezone used for period calculation

Period Types#

The periodType field is what makes streak records versatile. There are two categories:

Calendar Records (log/historical)#

These record what happened in a specific time window. They serve as a calendar view and historical log.
periodTypeperiodId examplecountDescription
DAY2025-09-02Always 1Binary marker: the user was active on this day
WEEK2025-W37Days or weeks activeHow many times the user was active this week
MONTH2025-09Days or weeks activeHow many times the user was active this month
YEAR2025Days or weeks activeHow many times the user was active this year
Calendar records are always written regardless of the metric setting. They are what the frontend calendar view renders.
A DAY record has status: COMPLETED and count: 1 — it is a simple "this day happened" marker.
WEEK, MONTH, YEAR records have status: ACTIVE and their count is atomically incremented each time the user acts within that period. They accumulate.

Counter Records (progress tracking)#

These track cumulative progress and streaks. They are conditionally written based on the rule's metric setting.
periodTypeUsescountDescription
ITERATIONiterationIdConsecutive days or weeksTracks a single unbroken streak run. When the streak breaks and restarts, iterationId increments.
GOALgoalId + targetProgress toward targetTracks progress toward a specific milestone. One record per target per goal cycle.
An ITERATION record answers: "How long is the current unbroken streak?" When count reaches high values, the user has maintained a long streak. When the streak breaks (status changes to BROKEN), a new iteration begins with an incremented iterationId.
A GOAL record answers: "How close is the user to reaching X days/weeks?" For goalTargets: [7, 30, 100], three GOAL records are maintained per goal cycle. When count reaches the target, status becomes COMPLETED. When all targets are completed, a new cycle starts with goalId incremented.

Status Lifecycle#

              user acts
                 │
    ┌────────────▼────────────┐
    │         ACTIVE          │◄────── initial state (ITERATION, GOAL, calendar accumulators)
    └────┬──────────────┬─────┘
         │              │
    count >= target   missed period
         │              │
         ▼              ▼
    COMPLETED        BROKEN ◄─── (unless freeze kicks in)

    Rule ends → ENDED (any status can transition to ENDED)
ACTIVE: The record is being tracked and can be incremented.
COMPLETED: The record reached its goal target (GOAL) or represents a completed calendar day (DAY). Cannot be further incremented.
BROKEN: The user missed a required period and the streak was broken. Terminal state for ITERATION records.
ENDED: The streak rule has ended (state: ENDED). Terminal state.
Only ACTIVE records can be updated — the system enforces this via DynamoDB conditions.

Kind#

KindDescription
REGULARThe period was completed by real user activity
FREEZEThe period was preserved by spending virtual currency
ANYAggregate — used for ITERATION and GOAL records which don't distinguish between regular and frozen periods

How Records Are Written#

When a user completes an action that matches a streak configuration, the system writes multiple DynamoDB records in a single transaction. The exact set depends on the cadence and metric.

DAY Cadence#

A single code path (updateDayCadenceStreakCounters) handles everything. On each qualifying action (max once per calendar day):
RecordperiodTypemetricAlways written?
Day entryDAYDAYSYes (calendar)
Week accumulatorWEEKDAYSYes (calendar)
Month accumulatorMONTHDAYSYes (calendar)
Year accumulatorYEARDAYSYes (calendar)
Iteration counterITERATIONDAYSYes
Goal countersGOALDAYSYes (one per target)
For DAY cadence, metric is always DAYS — there's no ambiguity.

WEEK Cadence#

Two sub-paths run independently with their own deduplication guards:
updateDayMetrics — runs per calendar day (first action of the day):
RecordperiodTypemetricWritten when
Day entryDAYDAYSAlways (calendar)
Iteration counterITERATIONDAYSOnly if rule.metric is DAYS (default)
Goal countersGOALDAYSOnly if rule.metric is DAYS (default)
updateWeekMetrics — runs per calendar week (first action of the week):
RecordperiodTypemetricWritten when
Week entryWEEKWEEKSAlways (calendar)
Month accumulatorMONTHWEEKSAlways (calendar)
Year accumulatorYEARWEEKSAlways (calendar)
Iteration counterITERATIONWEEKSOnly if rule.metric is WEEKS
Goal countersGOALWEEKSOnly if rule.metric is WEEKS
This split ensures that ITERATION and GOAL records are written exactly once per the appropriate time granularity:
With metric: DAYS (default): iteration counts active days, goals track days of progress, and the day-level dedup guard ensures at most one increment per day.
With metric: WEEKS: iteration counts active weeks, goals track weeks of progress, and the week-level dedup guard ensures at most one increment per week.

Deduplication#

Each code path has a dedup guard that prevents double-counting:
Day dedup: Before writing day metrics, the system queries for an existing DAY record with today's periodId. If found, the entire function returns early (no records written).
Week dedup: Before writing week metrics, the system queries for an existing WEEK record with this week's periodId. If found, the entire function returns early.
Additionally, the prepareStreakUpdate function adds DynamoDB conditions to ensure records are only written/updated when status is ACTIVE (or the record doesn't exist yet).

DynamoDB Access Patterns#

Partition Key#

All streak records for a user share the same partition key:
pk = workspaceId#<workspaceId>#userId#<userId>

Sort Key (sk)#

The primary sort key encodes the full record identity:
periodType#DAY#periodId#2025-09-02#streakRuleId#ID1#cadence#DAY#metric#DAYS#kind#REGULAR
periodType#ITERATION#iterationId#000001#streakRuleId#ID1#cadence#WEEK#metric#DAYS#kind#ANY
periodType#GOAL#goalId#000001#target#000007#streakRuleId#ID1#cadence#DAY#metric#DAYS#kind#ANY
This structure enables efficient range queries by periodType and periodId — for example, fetching all DAY records between two dates.

Secondary Sort Key (sk2)#

A GSI (bySk2) reorders the sort key with streakRuleId first:
#streakRuleId#ID1periodType#DAY#periodId#2025-09-02#cadence#WEEK#metric#DAYS#kind#REGULAR
This enables efficient queries filtered by a specific streak rule — used internally by the counter update logic to check dedup guards and fetch the latest iteration/goal state.

Streak Lifecycle Management#

A scheduled maintenance process runs for each active streak to detect missed periods:
1.
When a streak record is written, a StreakSchedule entry is created that fires after the current period ends.
2.
The scheduler invokes updateStreakStatus, which:
Fetches the streak rule and user.
If the rule is ENDED, marks the streak as ENDED.
If the rule is ACTIVE, queries streak records in the expected time range.
If the user was active (records found for the expected period), the streak continues — a FREEZE-kind counter update is written to advance the streak.
If the user was not active, the system checks whether the user can afford a freeze (virtual currency deduction). If yes, the streak is preserved. If not, the streak is marked as BROKEN.

Multiple Streaks Per User#

A user can have multiple active streaks simultaneously, each tracked independently:
Different rules: A workspace might have a "Daily Activity Streak" (DAY cadence) and a "Weekly Quiz Streak" (WEEK cadence). Each generates its own set of records under a different streakRuleId.
Targeted streaks: Using TAG match type on the configuration, you can create seasonal streaks like "Christmas Activity Streak" that only counts activities tagged with a specific tag. The same user might have a general daily streak and a seasonal December streak running in parallel.
Different goal tiers: A single rule with goalTargets: [7, 30, 100, 365] creates multiple GOAL records per cycle, but they all belong to the same rule.

Example: Full Record Set#

Consider a user on day 15 of a WEEK-cadence streak with metric: DAYS and goalTargets: [7, 30]:
# Calendar records (always written)
periodType#DAY#periodId#2025-09-15#streakRuleId#R1#cadence#WEEK#metric#DAYS#kind#REGULAR     count=1
periodType#WEEK#periodId#2025-W38#streakRuleId#R1#cadence#WEEK#metric#WEEKS#kind#REGULAR     count=3  (3rd day active this week)
periodType#MONTH#periodId#2025-09#streakRuleId#R1#cadence#WEEK#metric#WEEKS#kind#REGULAR     count=11
periodType#YEAR#periodId#2025#streakRuleId#R1#cadence#WEEK#metric#WEEKS#kind#REGULAR         count=190

# Counter records (written because metric=DAYS)
periodType#ITERATION#iterationId#000002#streakRuleId#R1#cadence#WEEK#metric#DAYS#kind#ANY    count=15  (15 days in current streak run)
periodType#GOAL#goalId#000003#target#000007#streakRuleId#R1#cadence#WEEK#metric#DAYS#kind#ANY count=7  status=COMPLETED
periodType#GOAL#goalId#000003#target#000030#streakRuleId#R1#cadence#WEEK#metric#DAYS#kind#ANY count=15 status=ACTIVE
In this example:
The user is on iteration #2 (their first streak broke at some point, this is their second run).
They are on goal cycle #3 (they completed two full cycles of all targets previously).
The 7-day goal for this cycle is COMPLETED; the 30-day goal is in progress at count 15.
Calendar records show 3 active days this week, 11 this month, 190 this year.
The WEEK, MONTH, YEAR calendar records use metric: WEEKS because they are written by updateWeekMetrics (which always uses WEEKS metric for its calendar entries).

List API#

The list API (GET /streaks) supports querying by periodType with optional date ranges:
Calendar view: periodType=DAY&from=2025-09-01&to=2025-09-30 returns all daily markers for September.
Iteration history: periodType=ITERATION returns all streak runs (each with their iterationId and count).
Goal progress: periodType=GOAL returns all goal records across all cycles.
On the last page of results, the API also synthesizes empty counters for any active rules that have no streak records yet. This ensures the frontend always shows all active rules, even if the user hasn't started them.
Optional filters: streakRuleId (uses the bySk2 GSI for efficient lookups), iterationId, goalId, target.

Summary of Key Concepts#

ConceptPurpose
StreakConfigurationDefines what events count (entity matching + JsonLogic conditions)
StreakRuleDefines the game rules (cadence, metric, goals, freeze, timeframe)
StreakPer-user DynamoDB records that track every dimension of progress
CadenceHow often the user must act (DAY = daily, WEEK = weekly)
MetricWhat counters represent (DAYS = active days, WEEKS = active weeks)
periodTypeThe dimension being tracked (calendar: DAY/WEEK/MONTH/YEAR; progress: ITERATION/GOAL)
IterationA single unbroken streak run; increments when the streak breaks and restarts
GoalA milestone target; cycles through goalId increments as targets are completed
KindWhether a period was kept by real activity (REGULAR) or virtual currency (FREEZE)
FreezeAutomatic virtual currency deduction to prevent streak breakage

Related Domains#

Reward & Currency Domain: the freeze mechanism deducts virtual currency to preserve streaks; reward rules can also trigger on streak-related events.
Mission Domain: mission completion and activity events are common triggers for streak configurations.
Learning Content Domain: learning path and quiz completions can feed streak counters through entity matching.
Leaderboard Domain: streak-based engagement contributes to leaderboard scores indirectly through virtual currency accumulation.
Cross-Cutting Patterns: JsonLogic expressions, entity matching (INSTANCE/ENTITY/TAG), timeframes, and state lifecycle patterns used throughout this domain.
Modified at 2026-02-24 16:05:04
Previous
Leaderboard Domain
Next
Cross-Cutting Patterns
Built with