Progression Hooks
Data hooks for points, tiers, badges, streaks, challenges, leaderboards, rewards, and the tenant plan. Field lists below are exhaustive and generated from the shipped SDK source.
engagementId / participantId from BricqsProvider unless noted. refreshInterval is milliseconds, 0 disables polling; per-hook defaults are listed on each hook. For push updates instead of polling, pair any of these with useBricqsStream and call refresh() on relevant events.useParticipantState
The one-call snapshot backed by GET /gamify/state/{id}. Prefer this when a screen shows several primitives at once.
const { state, isLoading, error, refresh } = useParticipantState({
// participantId?: string inherited from provider/token
// include?: ('points'|'tier'|'badges'|'streaks'|'challenges'|'contests'|'rewards')[]
// autoLoad?: boolean default true
// refreshInterval?: number default 0 (disabled)
});
state.participantId // string
state.points // { balance, lifetime, redeemed } | undefined
state.tier // { code, name, level, color?, achievedAt?, nextTier? } | undefined
state.tier.nextTier // { code, name, pointsRequired, pointsRemaining } | undefined
state.badges // { code, name, icon?, rarity, earned, earnedAt? }[] | undefined
state.streaks // { current, longest, lastActivityAt? } | undefined
state.challenges // { activeCount, completedCount, active[] } | undefined
state.contests // { enteredCount, entries[] } | undefined
state.rewards // { totalClaimed? } | undefinedusePoints
Store-backed points state. Note: autoLoad defaults to false; pass autoLoad: true or call loadBalance() yourself. Status is exposed as a status string, not isLoading.
const points = usePoints({ engagementId: 'YOUR_UUID', autoLoad: true });
points.status // 'idle' | 'loading' | 'ready' | 'error'
points.balance // number, current spendable balance
points.lifetime // number, total ever earned
points.redeemed // number, total ever spent
points.currentTier // CurrentTier | null
points.transactions // PointsTransaction[]
points.error // Error | null
points.loadBalance() // Promise<void>
points.loadTransactions(limit?) // Promise<void>
points.refresh() // Promise<void>useTier
const tier = useTier({ /* engagementId inherited; autoLoad default true */ });
tier.currentTier // { tierCode, tierName, tierLevel, tierColor?, pointsToNext?, nextTierName? } | null
tier.tierName // string | null
tier.tierLevel // number
tier.tierColor // string | null
tier.pointsToNext // number | null
tier.nextTierName // string | null
tier.isLoading // boolean
tier.error // Error | null
tier.refresh() // Promise<void>useBadgesHeadless
const badges = useBadgesHeadless({
// engagementId inherited
badgeCodes: ['first_quiz', 'streak_7'], // optional filter
refreshInterval: 30000, // default 30000
});
badges.badges // BadgeStatusEntry[]
badges.earned // earned only
badges.unearned // not yet earned
badges.isLoading // boolean
badges.error // Error | null
badges.refresh() // Promise<void>
badges.isEarned('code') // booleanuseStreaks / useStreak
Multi-streak first-class: useStreaks() returns every streak for the participant; useStreak(code) is sugar for one.
const { streaks, getStreak, isLoading, error, refresh } = useStreaks({
// participantId inherited from provider/token
refreshInterval: 30000, // default 30000
});
// Each StreakStatus:
// { code, name, currentCount, longestCount, lastRecordedAt?,
// isAtRisk, period, freezesTotal, freezesUsed, freezesAvailable }
const { streak } = useStreak('daily_login'); // StreakStatus | nulluseChallenge / useChallenges
const challenge = useChallenge({
// engagementId inherited
autoEnroll: true, // default false
refreshInterval: 30000, // default 30000
});
challenge.challenge // ChallengeDetail | null
challenge.objectives // ChallengeObjective[]
challenge.milestones // ChallengeMilestone[]
challenge.isEnrolled // boolean
challenge.enroll() // Promise<void>
challenge.progress // ChallengeProgress | null
challenge.progressPercentage // number (0-100)
challenge.objectiveProgress // ObjectiveProgress[]
challenge.completedObjectives // number
challenge.totalObjectives // number
challenge.milestonesReached // string[]
challenge.leaderboard // LeaderboardEntry[]
challenge.myRank // number | null
challenge.isLoading // boolean
challenge.error // Error | null
challenge.refresh() // Promise<void>
// List variant:
const { challenges } = useChallenges({ status: 'active', limit: 10 });useLeaderboard
Progression boards (by code) or challenge boards (by challengeId). Board reads are served through a short server-side cache; treat entries as up to ~30 seconds stale.
const lb = useLeaderboard({
code: 'main_leaderboard', // or challengeId: 'YOUR_CHALLENGE_UUID'
limit: 20, // default 10
refreshInterval: 30000, // default 30000
});
lb.entries // LeaderboardEntry[]
lb.myRank // number | null
lb.totalParticipants // number
lb.isLoading // boolean
lb.error // Error | null
lb.refresh() // Promise<void>useRewardsHeadless
Claimed rewards for the participant. No polling by default; pass refreshInterval or call refresh() after a claim.
const rewards = useRewardsHeadless({
// engagementId inherited
refreshInterval: 0, // default 0 (disabled)
});
rewards.rewards // ClaimedReward[]
rewards.totalClaimed // number
rewards.isLoading // boolean
rewards.error // Error | null
rewards.refresh() // Promise<void>useTenantPlan
Tenant plan, feature flags, and quota usage (backed by GET /gamify/plan). Needs no ids.
const { plan, isLoading, error, isFeatureEnabled, refresh } = useTenantPlan({
refreshInterval: 0, // default 0 (disabled)
});
plan.tier // string, e.g. "growth"
plan.features // Record<string, boolean>
plan.quotas // Record<string, { used, limit, resetAt? }>
plan.nextTier // { code, name, unlocks[] } | null
isFeatureEnabled('contests_enabled') // booleanLive updates: useBricqsStream
Server-Sent Events for the current participant (backed by GET /gamify/stream/{id}, participant token only). Use it to trigger targeted refresh() calls instead of aggressive polling. Event names are the dot-named catalog events (badge.earned.v1, ...).
const { connected, error } = useBricqsStream({
// participantId inherited from provider/token
enabled: true, // default true
onEvent: (evt) => {
if (evt.event === 'points.awarded.v1') refreshPointsWidget();
if (evt.event === 'badge.earned.v1') refreshBadgesWidget();
},
});