Skip to main content

Reward System

Growth

Configure reward types, set up fulfillment hooks, and implement fraud prevention for your referral program.

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_trial

Free premium period for new users

e.g., 14-day free trial
discount

Percentage or fixed amount off

e.g., 20% off or $10 off
points

In-app currency credits

e.g., 500 bonus points

premium_trial

Grant free premium access for a specified duration. Ideal for SaaS products with tiered pricing.

Premium Trial Configuration
// 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.

Points Configuration
{
  "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

You can configure different reward types for referrers and referees. For example, give referees a premium trial while giving referrers points.

Configuring Reward Rules

Reward rules determine when and how rewards are distributed. Configure these in your dashboard or via the API.

PropertyTypeDescription
triggerOn"signup" | "conversion" | "purchase"= "signup"When the reward is granted
delayDaysnumber= 0Days to wait before granting (for verification)
maxRewardsPerUsernumber= 50Maximum rewards a referrer can earn
requireVerifiedEmailboolean= trueReferee must verify email before reward
minPurchaseAmountnumberMinimum purchase for conversion trigger
expirationDaysnumberDays until reward expires after granting
Complete Reward Configuration
// 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

app/api/webhooks/referral/route.ts
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

Inline fulfillment
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

Edge case handling
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

Never block user signup due to referral issues. If a referral code is invalid or expired, continue with normal signup and log the issue for review.

Fraud Prevention

Protect your referral program from abuse with built-in and configurable fraud prevention mechanisms.

Built-in Protections

PropertyTypeDescription
Self-referral detectionAutomaticBlocks users from referring themselves using multiple accounts
Device fingerprintingAutomaticIdentifies suspicious signups from the same device
IP rate limitingAutomaticLimits referrals from the same IP address (default: 5/day)
Email pattern detectionAutomaticDetects email aliases and plus-addressing patterns
Velocity checksAutomaticFlags unusually high referral rates for review

Configurable Protections

Fraud prevention configuration
// 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.

API access to review queue
// 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

When revoking rewards, users will be notified. Ensure you have clear terms of service that explain reward revocation policies.

Fraud Detection Webhooks

Fraud detection events
// 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

1

Start Conservative

Begin with smaller rewards and stricter rules. It's easier to increase rewards than to reduce them without upsetting users.

2

Use Delayed Rewards

Hold rewards for 7-14 days to verify legitimate referrals. Release early if the referee makes a purchase.

3

Monitor Actively

Set up alerts for unusual referral patterns. Review the fraud queue regularly during campaign launches.

4

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