useLoyalty
Server SDK

Rewards

Let members redeem their points for real-world and digital rewards

The Rewards API lets members spend their points on items from your reward catalog — discount codes, physical goods, digital downloads, raffle entries, or custom rewards.

List Rewards

Fetch all active rewards available for redemption.

GET /api/v1/rewards

Query Parameters

ParameterTypeDefaultDescription
limitnumber50Results per page (max 100)
offsetnumber0Pagination offset
cursorstringCursor for cursor-based pagination

SDK

const { data: rewards, hasMore, nextCursor } = await useLoyalty.rewards.list({ limit: 20 });

Response

{
  "data": [
    {
      "id": "reward_discount_10",
      "name": "10% Off Coupon",
      "description": "Get 10% off your next order",
      "type": "COUPON",
      "pointsCost": 500,
      "stock": 100,
      "maxClaimsPerUser": 3,
      "minLevel": null,
      "imageUrl": "https://cdn.example.com/rewards/coupon.png",
      "expiresAt": null,
      "isActive": true
    }
  ],
  "total": 12,
  "hasMore": false
}

Reward Types

TypeDescription
COUPONGenerates a discount code (Shopify / WooCommerce)
PHYSICALPhysical item — triggers fulfillment
DIGITALDigital download or access code
RAFFLEEntry into a raffle draw
CUSTOMAny custom reward type

Get Reward

Fetch a single reward by ID.

GET /api/v1/rewards/:rewardId

SDK

const reward = await useLoyalty.rewards.get('reward_discount_10');

Redeem a Reward

Spend a member's points to claim a reward.

POST /api/v1/rewards/:rewardId/redeem

Request Body

{
  "externalId": "user_123"
}

SDK

const result = await useLoyalty.rewards.redeem('user_123', 'reward_discount_10');

Response

{
  "redemptionId": "rdm_abc123",
  "reward": { "id": "reward_discount_10", "name": "10% Off Coupon", "type": "COUPON" },
  "pointsSpent": 500,
  "newBalance": 1100,
  "couponCode": "JOHN-10OFF-X7K2"
}
FieldDescription
couponCodePopulated for COUPON type rewards; null for others
pointsSpentPoints deducted from member's balance
newBalanceMember's balance after redemption

For COUPON rewards, the code is auto-generated and synced to your Shopify or WooCommerce store. Pass it directly to the user.


Get Member Redemptions

List all rewards a member has redeemed.

GET /api/v1/members/:externalId/reward-redemptions

SDK

const { data: redemptions } = await useLoyalty.rewards.getRedemptions('user_123', {
  limit: 10,
});

Response

{
  "data": [
    {
      "id": "rdm_abc123",
      "rewardId": "reward_discount_10",
      "reward": { "name": "10% Off Coupon", "type": "COUPON" },
      "memberId": "clx123abc",
      "pointsSpent": 500,
      "couponCode": "JOHN-10OFF-X7K2",
      "createdAt": "2024-01-20T11:00:00Z"
    }
  ],
  "total": 1,
  "hasMore": false
}

Code Examples

Reward Catalog Page

// Fetch rewards with member's balance to show affordability
async function getRewardCatalog(userId: string) {
  const [{ data: rewards }, balance] = await Promise.all([
    useLoyalty.rewards.list({ limit: 50 }),
    useLoyalty.members.getPoints(userId),
  ]);

  return rewards.map(reward => ({
    ...reward,
    canAfford: balance.points >= reward.pointsCost,
    pointsNeeded: Math.max(0, reward.pointsCost - balance.points),
  }));
}

Handle Redemption Errors

import { UseLoyaltyError } from '@useloyalty/sdk';

async function redeemReward(userId: string, rewardId: string) {
  try {
    return await useLoyalty.rewards.redeem(userId, rewardId);
  } catch (err) {
    if (err instanceof UseLoyaltyError) {
      if (err.code === 'INSUFFICIENT_POINTS') {
        throw new Error('Not enough points for this reward');
      }
      if (err.code === 'REWARD_OUT_OF_STOCK') {
        throw new Error('This reward is no longer available');
      }
      if (err.code === 'REWARD_ALREADY_CLAIMED') {
        throw new Error('You have already claimed the maximum for this reward');
      }
    }
    throw err;
  }
}

Show Coupon After Redemption

function RewardRedeemButton({ reward, userId }: { reward: Reward; userId: string }) {
  const [couponCode, setCouponCode] = useState<string | null>(null);

  async function handleRedeem() {
    const result = await fetch('/api/rewards/redeem', {
      method: 'POST',
      body: JSON.stringify({ userId, rewardId: reward.id }),
    }).then(r => r.json());

    if (result.couponCode) {
      setCouponCode(result.couponCode);
    }
  }

  if (couponCode) {
    return <code>{couponCode}</code>;
  }

  return (
    <button onClick={handleRedeem}>
      Redeem for {reward.pointsCost} pts
    </button>
  );
}

On this page