The reward system is the core of your referral program. Configure what rewards users earn, when they're fulfilled, and how to prevent abuse.
Available Reward Types
Sylphx supports three primary reward types that cover most referral program needs:
premium_trialFree premium period for new users
e.g., 14-day free trialdiscountPercentage or fixed amount off
e.g., 20% off or $10 offpointsIn-app currency credits
e.g., 500 bonus pointspremium_trial
Grant free premium access for a specified duration. Ideal for SaaS products with tiered pricing.
// Dashboard configuration
{
"rewards": {
"referee": {
"type": "premium_trial",
"duration": 14, // Days of free premium
"tier": "pro", // Which plan to grant
"stackable": false // Can combine with existing trial?
},
"referrer": {
"type": "premium_trial",
"duration": 7,
"tier": "pro"
}
}
}
// Server-side handling
const result = await platform.referrals.redeem({
code: referralCode,
newUserId: user.id,
})
// result.reward = {
// type: 'premium_trial',
// duration: 14,
// tier: 'pro',
// expiresAt: '2024-02-15T00:00:00Z'
// }discount
Apply percentage or fixed-amount discounts to purchases. Can be one-time or recurring.
{
"rewards": {
"referee": {
"type": "discount",
"discountType": "percentage",
"amount": 20, // 20% off
"maxUses": 1, // One-time use
"appliesTo": "first_purchase"
}
}
}
// Creates a discount code automatically
// result.reward.discountCode = "REF20-ABC123"points
Credit in-app currency or points to user accounts. Perfect for apps with virtual currency systems.
{
"rewards": {
"referee": {
"type": "points",
"amount": 500, // 500 points
"pointsType": "credits", // Your currency name
"expiresIn": 90 // Points expire in 90 days (optional)
},
"referrer": {
"type": "points",
"amount": 1000, // Referrer gets more
"pointsType": "credits",
"triggerOn": "conversion" // Only when referee converts to paid
}
}
}
// In your app, check user's points balance
const balance = await platform.users.getPoints(userId)
// { credits: 1500, expiresAt: '2024-04-15T00:00:00Z' }Combining Reward Types
Configuring Reward Rules
Reward rules determine when and how rewards are distributed. Configure these in your dashboard or via the API.
| Property | Type | Description |
|---|---|---|
triggerOn | "signup" | "conversion" | "purchase"= "signup" | When the reward is granted |
delayDays | number= 0 | Days to wait before granting (for verification) |
maxRewardsPerUser | number= 50 | Maximum rewards a referrer can earn |
requireVerifiedEmail | boolean= true | Referee must verify email before reward |
minPurchaseAmount | number | Minimum purchase for conversion trigger |
expirationDays | number | Days until reward expires after granting |
// Full configuration example
const referralConfig = {
enabled: true,
rewards: {
referrer: {
type: 'points',
amount: 1000,
pointsType: 'credits',
triggerOn: 'conversion', // Wait for paid conversion
delayDays: 7, // 7-day hold for verification
maxRewardsPerUser: 50, // Cap at 50 referrals
},
referee: {
type: 'premium_trial',
duration: 14,
tier: 'pro',
triggerOn: 'signup', // Immediate on signup
requireVerifiedEmail: true,
},
},
// Global settings
codeExpiration: 30, // Referral codes expire in 30 days
attributionWindow: 7, // 7-day attribution window
// Advanced rules
rules: {
sameCompanyAllowed: false, // Block same-domain emails
requireNewAccount: true, // Must be new user
geofencing: ['US', 'CA', 'UK'], // Limit to specific countries
},
}Reward Fulfillment Callbacks
Fulfillment callbacks let you execute custom logic when rewards are granted. Use webhooks or server-side handlers.
Webhook Handler
import { platform } from '@/lib/platform'
import { headers } from 'next/headers'
import { NextResponse } from 'next/server'
export async function POST(req: Request) {
const body = await req.text()
const signature = headers().get('x-sylphx-signature')
// Verify webhook signature
const event = platform.webhooks.verify(body, signature!)
switch (event.type) {
case 'referral.reward.pending':
// Reward is pending (waiting for delay or verification)
await notifyPendingReward(event.data)
break
case 'referral.reward.granted':
// Reward has been granted
await fulfillReward(event.data)
break
case 'referral.reward.expired':
// Reward expired before being used
await handleExpiredReward(event.data)
break
case 'referral.reward.revoked':
// Reward was revoked (fraud detected)
await handleRevokedReward(event.data)
break
}
return NextResponse.json({ received: true })
}
async function fulfillReward(data: RewardEventData) {
const { userId, reward, referralId } = data
switch (reward.type) {
case 'premium_trial':
// Upgrade user's subscription
await db.subscriptions.update({
where: { userId },
data: {
tier: reward.tier,
trialEndsAt: reward.expiresAt,
},
})
break
case 'points':
// Credit user's account
await db.users.update({
where: { id: userId },
data: {
points: { increment: reward.amount },
},
})
break
case 'discount':
// Create discount code in billing system
await stripe.coupons.create({
id: reward.discountCode,
percent_off: reward.amount,
max_redemptions: reward.maxUses,
})
break
}
// Send notification
await sendEmail(userId, 'reward-granted', { reward })
}Server-Side Handler
import { platform } from '@/lib/platform'
// Configure fulfillment handlers
platform.referrals.onRewardGranted(async (event) => {
const { userId, reward, referralId } = event
console.log(`Granting ${reward.type} to user ${userId}`)
// Your custom fulfillment logic
if (reward.type === 'points') {
await creditPoints(userId, reward.amount)
}
// Track in analytics
await analytics.track('reward_granted', {
userId,
rewardType: reward.type,
rewardValue: reward.amount,
referralId,
})
})
// Listen for revocations
platform.referrals.onRewardRevoked(async (event) => {
const { userId, reward, reason } = event
// Reverse the reward
if (reward.type === 'points') {
await deductPoints(userId, reward.amount)
}
// Log for audit
await auditLog.write({
action: 'reward_revoked',
userId,
reason,
})
})Handling Reward Edge Cases
Handle these common edge cases to ensure a smooth reward experience:
Expired Referral Code
Gracefully handle when a referral code has expired
Maxed Out Referrer
Handle when referrer has reached their reward limit
Already Referred User
User was already referred by someone else
Reward Fulfillment Failure
External service failure during reward grant
import { platform, ReferralError } from '@/lib/platform'
async function redeemReferral(code: string, userId: string) {
try {
const result = await platform.referrals.redeem({
code,
newUserId: userId,
})
return { success: true, reward: result.reward }
} catch (error) {
if (error instanceof ReferralError) {
switch (error.code) {
case 'CODE_EXPIRED':
// Code has expired
return {
success: false,
message: 'This referral link has expired.',
showAlternative: true,
}
case 'REFERRER_MAXED_OUT':
// Referrer hit their limit - still allow signup
return {
success: false,
message: 'Referral limit reached, but you can still sign up!',
continueSignup: true,
}
case 'USER_ALREADY_REFERRED':
// User was already referred
return {
success: false,
message: 'You already used a referral link.',
}
case 'SELF_REFERRAL':
// Attempting to refer themselves
return {
success: false,
message: 'You cannot refer yourself.',
}
case 'INVALID_CODE':
// Code doesn't exist
return {
success: false,
message: 'Invalid referral code.',
}
default:
throw error
}
}
throw error
}
}Always Allow Signup
Fraud Prevention
Protect your referral program from abuse with built-in and configurable fraud prevention mechanisms.
Built-in Protections
| Property | Type | Description |
|---|---|---|
Self-referral detection | Automatic | Blocks users from referring themselves using multiple accounts |
Device fingerprinting | Automatic | Identifies suspicious signups from the same device |
IP rate limiting | Automatic | Limits referrals from the same IP address (default: 5/day) |
Email pattern detection | Automatic | Detects email aliases and plus-addressing patterns |
Velocity checks | Automatic | Flags unusually high referral rates for review |
Configurable Protections
// Dashboard: App Settings → Growth → Referrals → Fraud Prevention
{
"fraudPrevention": {
// Email restrictions
"blockDisposableEmails": true,
"blockEmailAliases": true, // Block user+tag@example.com
"allowedEmailDomains": [], // Whitelist (empty = all allowed)
"blockedEmailDomains": [ // Blacklist
"tempmail.com",
"throwaway.email"
],
// Rate limits
"maxReferralsPerIP": 5, // Per day
"maxReferralsPerDevice": 3, // Per day
"maxReferralsPerReferrer": 50, // Total
// Verification requirements
"requireEmailVerification": true,
"requirePhoneVerification": false,
"minimumAccountAge": 0, // Days before earning rewards
// Delayed rewards
"rewardHoldPeriod": 7, // Days to hold rewards
"releaseOnConversion": true, // Release when referee converts
// Manual review thresholds
"flagForReviewThreshold": 10, // Flag referrer after 10 referrals
"autoApproveThreshold": 5, // Auto-approve first 5 referrals
}
}Manual Review Queue
Suspicious referrals are flagged for manual review. Access the review queue in your dashboard.
// Get flagged referrals
const flagged = await platform.referrals.getFlaggedReferrals({
status: 'pending_review',
limit: 50,
})
// Review a flagged referral
await platform.referrals.reviewReferral({
referralId: 'ref_123',
decision: 'approve', // or 'reject'
reason: 'Verified legitimate user',
})
// Bulk actions
await platform.referrals.bulkReview({
referralIds: ['ref_123', 'ref_456'],
decision: 'approve',
})
// Revoke rewards for fraudulent referrals
await platform.referrals.revokeReward({
referralId: 'ref_789',
reason: 'Fraudulent activity detected',
revokeFromBothParties: true,
})Reward Revocation
Fraud Detection Webhooks
// Listen for fraud detection events
platform.referrals.onFraudDetected(async (event) => {
const { referralId, userId, indicators, riskScore } = event
console.log(`Fraud detected: ${indicators.join(', ')}`)
// indicators: ['same_device', 'velocity_spike', 'disposable_email']
if (riskScore > 0.8) {
// High risk - auto-reject
await platform.referrals.reviewReferral({
referralId,
decision: 'reject',
reason: 'Auto-rejected: High fraud risk score',
})
// Notify security team
await slack.notify('#security', {
text: `High-risk referral detected: ${referralId}`,
riskScore,
indicators,
})
} else {
// Medium risk - flag for review
await slack.notify('#referrals', {
text: `Referral flagged for review: ${referralId}`,
})
}
})Best Practices
Start Conservative
Begin with smaller rewards and stricter rules. It's easier to increase rewards than to reduce them without upsetting users.
Use Delayed Rewards
Hold rewards for 7-14 days to verify legitimate referrals. Release early if the referee makes a purchase.
Monitor Actively
Set up alerts for unusual referral patterns. Review the fraud queue regularly during campaign launches.
Clear Communication
Be transparent about reward rules and timelines. Users should know when and why rewards might be delayed.
Ready to track referral attribution?
Learn how to track referral links, configure attribution windows, and debug tracking issues.
Referral Tracking