useLoyalty
Server SDK

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/streak

SDK

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
}
FieldDescription
currentStreakConsecutive days with a check-in
longestStreakAll-time best streak
lastCheckInISO 8601 timestamp of last check-in
nextCheckInDeadlineCheck in before this time or streak resets
checkedInTodayWhether 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-in

SDK

const result = await useLoyalty.streaks.checkIn('user_123');

Response

{
  "memberId": "clx123abc",
  "currentStreak": 8,
  "longestStreak": 14,
  "pointsAwarded": 25,
  "streakExtended": true,
  "alreadyCheckedIn": false,
  "newBalance": 1625
}
FieldDescription
streakExtendedtrue when this check-in extended an existing streak; false if it started a new one
alreadyCheckedIntrue when member already checked in today — no points awarded again
pointsAwardedPoints 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/award

Request 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/reset

SDK

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!"

On this page