Webhooks

Receive real-time notifications when referral events occur.

Webhooks allow you to receive real-time HTTP notifications when events occur in your referral program. This is the recommended way to handle reward fulfillment.

Setting Up Webhooks

  1. Go to Settings → Webhooks in your dashboard
  2. Click Add Endpoint
  3. Enter your webhook URL (must be HTTPS)
  4. Select the events you want to receive
  5. Click Create Webhook
Your webhook endpoint must respond with a 2xx status code within 30 seconds. We'll retry failed deliveries up to 5 times with exponential backoff.

Webhook Payload

All webhook payloads follow this structure:

{
  "id": "evt_xxxxx",
  "type": "referral.converted",
  "created": "2024-01-15T10:30:00Z",
  "data": {
    // Event-specific data
  }
}

Event Types

referral.clicked

Sent when someone clicks a referral link.

{
  "id": "evt_xxxxx",
  "type": "referral.clicked",
  "created": "2024-01-15T10:30:00Z",
  "data": {
    "referralCode": "abc123",
    "referrerId": "user_123",
    "campaignId": "camp_xxxxx",
    "clickId": "click_xxxxx",
    "metadata": {
      "userAgent": "Mozilla/5.0...",
      "country": "US",
      "device": "mobile"
    }
  }
}

referral.converted

Sent when a referred user completes the reward trigger action.

{
  "id": "evt_xxxxx",
  "type": "referral.converted",
  "created": "2024-01-15T10:30:00Z",
  "data": {
    "referralCode": "abc123",
    "referrerId": "user_123",
    "referredUserId": "user_456",
    "campaignId": "camp_xxxxx",
    "conversionType": "signup",
    "metadata": {
      "email": "newuser@example.com"
    }
  }
}

reward.earned

Sent when a reward is earned (for either referrer or referred user).

{
  "id": "evt_xxxxx",
  "type": "reward.earned",
  "created": "2024-01-15T10:30:00Z",
  "data": {
    "rewardId": "reward_xxxxx",
    "userId": "user_123",
    "type": "referrer",  // or "referred"
    "amount": 10,
    "currency": "USD",
    "rewardType": "credit",
    "referredUserId": "user_456",
    "campaignId": "camp_xxxxx"
  }
}

Verifying Signatures

All webhooks include a signature in the X-Referral-Signature header. Always verify this signature to ensure the request came from Referral Engine.

app/api/webhooks/referral/route.tstypescript
import crypto from 'crypto'

function verifyWebhook(payload: string, signature: string, secret: string): boolean {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex')
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  )
}

// In your webhook handler
export async function POST(request: Request) {
  const payload = await request.text()
  const signature = request.headers.get('X-Referral-Signature')
  
  if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
    return new Response('Invalid signature', { status: 401 })
  }
  
  const event = JSON.parse(payload)
  // Process the event...
}

Handling Webhooks

Best practices for webhook handlers:

  • Respond quickly — Return a 2xx response as soon as possible, then process asynchronously
  • Handle duplicates — Use the event ID to deduplicate (we may retry on network issues)
  • Verify signatures — Always verify the webhook signature
  • Log everything — Store raw payloads for debugging
app/api/webhooks/referral/route.tstypescript
export async function POST(request: Request) {
  const payload = await request.text()
  const signature = request.headers.get('X-Referral-Signature')
  
  // Verify signature
  if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
    return new Response('Invalid signature', { status: 401 })
  }
  
  const event = JSON.parse(payload)
  
  // Check for duplicate (idempotency)
  const processed = await db.webhookEvents.findUnique({
    where: { eventId: event.id }
  })
  if (processed) {
    return Response.json({ received: true })
  }
  
  // Store the event
  await db.webhookEvents.create({
    data: { eventId: event.id, payload: event }
  })
  
  // Process asynchronously
  await queue.add('process-webhook', event)
  
  // Respond immediately
  return Response.json({ received: true })
}

Testing Webhooks

Use the Test button in your webhook settings to send a test event. You can also use tools like ngrok to expose your local development server.

Retry Policy

If your endpoint fails to respond with a 2xx status, we'll retry:

  • Attempt 1: Immediately
  • Attempt 2: 1 minute later
  • Attempt 3: 5 minutes later
  • Attempt 4: 30 minutes later
  • Attempt 5: 2 hours later

After 5 failed attempts, the webhook is marked as failed. You can manually retry from the webhook logs in your dashboard.