Streaks
Track daily check-in streaks and reward consistent engagement
Streaks reward members for returning day after day. The platform tracks current streak, longest streak, and check-in deadlines automatically.
Get Streak Info
Retrieve detailed streak state for a member.
GET /api/v1/members/:externalId/streakSDK
const streak = await useLoyalty.streaks.get('user_123');Response
{
"memberId": "clx123abc",
"externalId": "user_123",
"currentStreak": 7,
"longestStreak": 14,
"lastCheckIn": "2024-01-20T08:15:00Z",
"nextCheckInDeadline": "2024-01-21T23:59:59Z",
"checkedInToday": true
}| Field | Description |
|---|---|
currentStreak | Consecutive days with a check-in |
longestStreak | All-time best streak |
lastCheckIn | ISO 8601 timestamp of last check-in |
nextCheckInDeadline | Check in before this time or streak resets |
checkedInToday | Whether member has already checked in today |
Record a Check-In
Mark today as checked in for a member. Safe to call multiple times — duplicates within the same day return alreadyCheckedIn: true without double-awarding points.
POST /api/v1/members/:externalId/streak/check-inSDK
const result = await useLoyalty.streaks.checkIn('user_123');Response
{
"memberId": "clx123abc",
"currentStreak": 8,
"longestStreak": 14,
"pointsAwarded": 25,
"streakExtended": true,
"alreadyCheckedIn": false,
"newBalance": 1625
}| Field | Description |
|---|---|
streakExtended | true when this check-in extended an existing streak; false if it started a new one |
alreadyCheckedIn | true when member already checked in today — no points awarded again |
pointsAwarded | Points awarded for this check-in (0 if duplicate) |
Award Streak Days (Admin)
Manually add streak days to a member — useful for rewarding long-term members or recovering from a platform outage.
POST /api/v1/members/:externalId/streak/awardRequest Body
{
"days": 3,
"reason": "Platform outage compensation"
}SDK
const streak = await useLoyalty.streaks.award('user_123', {
days: 3,
reason: 'Platform outage compensation',
});Reset Streak (Admin)
Reset a member's current streak to zero. Does not affect longestStreak.
POST /api/v1/members/:externalId/streak/resetSDK
const streak = await useLoyalty.streaks.reset('user_123');Code Examples
Daily Check-In Button
// Next.js API route — POST /api/checkin
import { UseLoyaltyClient } from '@useloyalty/sdk';
const useLoyalty = new UseLoyaltyClient({
publicKey: process.env.USELOYALTY_PUBLIC_KEY!,
privateKey: process.env.USELOYALTY_PRIVATE_KEY!,
});
export async function POST(req: Request) {
const { userId } = await req.json();
const result = await useLoyalty.streaks.checkIn(userId);
if (result.alreadyCheckedIn) {
return Response.json({ message: 'Already checked in today', streak: result.currentStreak });
}
return Response.json({
message: `🔥 ${result.currentStreak} day streak!`,
pointsAwarded: result.pointsAwarded,
streak: result.currentStreak,
});
}Show Streak Status in UI
import { useLoyaltyMember } from '@useloyalty/sdk/react';
function StreakBadge({ userId }: { userId: string }) {
const { member } = useLoyaltyMember({
fetcher: () => fetch(`/api/member/${userId}`).then(r => r.json()),
});
if (!member) return null;
return (
<div>
<span>🔥 {member.currentStreak} day streak</span>
{member.currentStreak === member.longestStreak && (
<span> (personal best!)</span>
)}
</div>
);
}Streak-Gated Reward
async function claimStreakReward(userId: string, requiredStreak: number) {
const streak = await useLoyalty.streaks.get(userId);
if (streak.currentStreak < requiredStreak) {
throw new Error(
`Need a ${requiredStreak}-day streak. Current: ${streak.currentStreak}`
);
}
return useLoyalty.rewards.redeem(userId, 'reward_streak_bonus');
}Best Practices
Check checkedInToday before showing the button
Avoid user confusion by hiding or disabling the check-in button when already done today:
const streak = await useLoyalty.streaks.get(userId);
if (streak.checkedInToday) {
// Show "Come back tomorrow" with nextCheckInDeadline countdown
} else {
// Show check-in button
}Show the deadline, not just the streak
nextCheckInDeadline lets you render a countdown so members don't accidentally miss their streak:
const hoursLeft = Math.floor(
(new Date(streak.nextCheckInDeadline).getTime() - Date.now()) / 3_600_000
);
// "Check in within 4 hours to keep your streak!"