Perks Service API Reference
The Perks Service manages perk browsing and redemption across all platform integrations. It provides a unified interface for viewing available perks and processing redemptions.
Base URL
https://services.loyalteez.app
Endpoints
Get Perks
Retrieves available perks for a brand, optionally filtered by category.
Endpoint: GET /perks/:brandId?category=all
URL Parameters:
brandId(path, required): The brand's wallet addresscategory(query, optional): Category filter. Default:"all"all: All categories- Specific category name (e.g.,
"discount","exclusive","merch")
Response:
{
"success": true,
"perks": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "10% Off First Purchase",
"description": "Get 10% off your first purchase with this code",
"cost": 100,
"available": 50,
"category": "discount",
"image": "https://example.com/perk-image.jpg",
"metadata": {
"discount_code": "WELCOME10",
"valid_until": "2024-12-31"
}
},
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Exclusive NFT",
"description": "Claim an exclusive NFT",
"cost": 500,
"available": 10,
"category": "exclusive",
"image": null,
"metadata": {}
}
]
}
Response Fields:
success: Whether the operation succeededperks: Array of available perksid: Unique perk identifier (UUID)name: Perk namedescription: Perk descriptioncost: Cost in LTZ tokensavailable: Available quantity remainingcategory: Perk categoryimage: Image URL (if available)metadata: Additional perk data (discount codes, etc.)
Example:
const brandId = encodeURIComponent('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');
// Get all perks
const response = await fetch(
`https://services.loyalteez.app/perks/${brandId}?category=all`
);
const result = await response.json();
if (result.success) {
result.perks.forEach(perk => {
console.log(`${perk.name}: ${perk.cost} LTZ (${perk.available} available)`);
});
}
// Get perks by category
const discountResponse = await fetch(
`https://services.loyalteez.app/perks/${brandId}?category=discount`
);
Redeem Perk
Redeems a perk for a user. This creates a redemption record and decrements the available quantity.
Endpoint: POST /perks/redeem
Request Body:
{
"brandId": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"userIdentifier": "[email protected]",
"platform": "discord",
"perkId": "550e8400-e29b-41d4-a716-446655440000"
}
Parameters:
brandId(required): The brand's wallet addressuserIdentifier(required): User identifierplatform(required): Platform nameperkId(required): Perk ID (UUID)
Response:
{
"success": true,
"perk": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "10% Off First Purchase",
"description": "Get 10% off your first purchase with this code"
},
"redemptionId": "770e8400-e29b-41d4-a716-446655440002",
"transactionHash": null,
"confirmationCode": "ABC12345"
}
Response Fields:
success: Whether the operation succeededperk: Perk detailsid: Perk IDname: Perk namedescription: Perk description
redemptionId: Redemption record ID (for tracking)transactionHash: Blockchain transaction hash (if applicable)confirmationCode: Confirmation code for the redemption
Error Response:
{
"success": false,
"error": "Perk not found or inactive"
}
Common Errors:
Perk not found or inactive: Perk doesn't exist or is inactivePerk is out of stock: Available quantity is 0Insufficient balance: User doesn't have enough LTZ (handled by your application)
Example:
async function redeemPerk(userIdentifier, platform, brandId, perkId) {
// First, verify user has sufficient balance (via your balance check)
const userBalance = await getUserBalance(userIdentifier, brandId);
const perk = await getPerkDetails(brandId, perkId);
if (userBalance < perk.cost) {
return { success: false, error: 'Insufficient balance' };
}
// Redeem the perk
const response = await fetch('https://services.loyalteez.app/perks/redeem', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
brandId,
userIdentifier,
platform,
perkId
})
});
const result = await response.json();
if (result.success) {
// Deduct LTZ from user balance (via Event Handler API or blockchain)
await deductLTZ(userIdentifier, brandId, perk.cost);
// Return redemption details
return result;
}
return result;
}
Perk Categories
Perks can be organized into categories for better organization:
discount: Discount codes and couponsexclusive: Exclusive items or accessmerch: Physical merchandisedigital: Digital items (NFTs, codes, etc.)experience: Experiences or eventsgeneral: General perks
Categories are configurable per brand through the Partner Portal.
Redemption Flow
- Browse Perks: User views available perks via
/perks/:brandId - Check Balance: Your application checks if user has sufficient LTZ
- Redeem: Call
/perks/redeemto create redemption record - Deduct LTZ: Deduct LTZ from user's balance (via Event Handler API or blockchain)
- Deliver Perk: Use
confirmationCodeormetadatato deliver the perk (e.g., send discount code)
Integration Examples
Discord Bot - Browse Perks
async function handlePerks(brandId) {
const response = await fetch(
`https://services.loyalteez.app/perks/${encodeURIComponent(brandId)}?category=all`
);
const result = await response.json();
if (!result.success) {
return 'Error fetching perks';
}
if (result.perks.length === 0) {
return 'No perks available at this time.';
}
let message = '🎁 Available Perks:\n\n';
result.perks.forEach((perk, index) => {
message += `${index + 1}. **${perk.name}**\n`;
message += ` ${perk.description}\n`;
message += ` 💰 ${perk.cost} LTZ`;
if (perk.available > 0) {
message += ` (${perk.available} available)`;
} else {
message += ' (Out of stock)';
}
message += '\n\n';
});
message += 'Use `/claim <perk_number>` to redeem a perk!';
return message;
}
Telegram Bot - Redeem Perk
async function handleClaim(message, perkId, brandId) {
const userId = message.from.id;
const userIdentifier = `telegram_${userId}@loyalteez.app`;
// Check balance first
const balance = await getBalance(userIdentifier, brandId);
const perk = await getPerkDetails(brandId, perkId);
if (balance < perk.cost) {
bot.sendMessage(message.chat.id,
`❌ Insufficient balance. You need ${perk.cost} LTZ, but you have ${balance} LTZ.`
);
return;
}
// Redeem perk
const response = await fetch('https://services.loyalteez.app/perks/redeem', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
brandId,
userIdentifier,
platform: 'telegram',
perkId
})
});
const result = await response.json();
if (result.success) {
// Deduct LTZ
await deductLTZ(userIdentifier, brandId, perk.cost);
// Send confirmation
bot.sendMessage(message.chat.id,
`✅ Redeemed: ${result.perk.name}\n\n` +
`Confirmation Code: ${result.confirmationCode}\n\n` +
(perk.metadata.discount_code
? `Discount Code: ${perk.metadata.discount_code}\n`
: '')
);
} else {
bot.sendMessage(message.chat.id, `❌ Error: ${result.error}`);
}
}
Web Application - Perk Catalog
async function loadPerks(brandId, category = 'all') {
const response = await fetch(
`https://services.loyalteez.app/perks/${encodeURIComponent(brandId)}?category=${category}`
);
const result = await response.json();
if (result.success) {
displayPerks(result.perks);
}
}
async function redeemPerkFromWeb(perkId, userEmail, brandId) {
// Check balance
const balance = await getUserBalance(userEmail, brandId);
const perk = await getPerkDetails(brandId, perkId);
if (balance < perk.cost) {
showError('Insufficient balance');
return;
}
// Redeem
const response = await fetch('https://services.loyalteez.app/perks/redeem', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
brandId,
userIdentifier: userEmail,
platform: 'web',
perkId
})
});
const result = await response.json();
if (result.success) {
// Deduct LTZ
await deductLTZ(userEmail, brandId, perk.cost);
// Show success
showSuccess(`Redeemed: ${result.perk.name}`);
showConfirmationCode(result.confirmationCode);
// If discount code, display it
if (perk.metadata.discount_code) {
showDiscountCode(perk.metadata.discount_code);
}
} else {
showError(result.error);
}
}
Best Practices
- Check Balance First: Always verify user has sufficient LTZ before calling
/perks/redeem - Handle Out of Stock: Check
availablequantity before allowing redemption - Use Confirmation Codes: Use confirmation codes to track and verify redemptions
- Deliver Perks Promptly: Deliver perks (discount codes, etc.) immediately after redemption
- Display Categories: Use categories to organize and filter perks
- Show Availability: Display available quantity to users
- Handle Errors: Always handle errors gracefully and provide clear error messages
Perk Metadata
Perks can include metadata for additional information:
{
"metadata": {
"discount_code": "WELCOME10",
"valid_until": "2024-12-31",
"min_purchase": 50,
"max_discount": 100,
"terms": "Valid for first-time customers only"
}
}
Use metadata to store:
- Discount codes
- Expiration dates
- Terms and conditions
- Additional redemption instructions
- Custom data
Error Handling
All endpoints return standard error responses:
{
"success": false,
"error": "Error message"
}
Common Errors:
400 Bad Request: Missing required fields or invalid parameters404 Not Found: Perk not found or inactive500 Internal Server Error: Server error