BricqsBricqs
Documentation
← Headless SDK

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.

Conventions. Hooks inherit 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? } | undefined

usePoints

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') // boolean

useStreaks / 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 | null

useChallenge / 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') // boolean

Live 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();
  },
});