Webhooks

Get real-time notifications about events in your affiliate program

Overview

Refgrow's webhook system allows you to receive HTTP POST notifications about important events in your affiliate program. This enables you to integrate Refgrow with your internal systems for automated conversion processing, notifications, and analytics.

Security: All webhooks are signed with HMAC-SHA256 signatures for authenticity verification.

Supported Events

Event Description When it triggers
referral_signed_up New user signed up via referral link When a user registers through a referral link
referral_converted Referral converted to paying customer When a referred user makes a purchase
referral_canceled Conversion canceled or refunded When a payment is refunded or canceled
referral_updated Conversion updated or modified When a conversion is created or updated via API

Payout Preferences in Webhooks

Webhook payloads automatically include PayPal and Wise payout preferences when available. This allows finance systems to process payouts directly from webhook data without requiring additional API calls.

Payout Preferences Structure

{
  "referrer": {
    "id": "456",
    "email": "affiliate@example.com",
    "payout_preferences": {
      "paypal_email": "affiliate@example.com",
      "wise_details": {
        "accountId": "wise_account_123",
        "email": "affiliate@example.com"
      }
    }
  }
}
Benefits:
  • No need to call GET /api/v1/affiliates/:email to enrich webhook data
  • Automatic payout processing directly from webhook payloads
  • Reduced API calls and faster integration setup
  • Payout preferences are retrieved from both client_payment_settings and custom attributes

Note: The payout_preferences field is only included when an affiliate has configured PayPal email or Wise payment details. If no payout preferences are configured, this field will be omitted from the payload.

Setting up Webhooks

1. Creating a Webhook

  1. Go to Project Settings → "Webhooks" tab
  2. Click "Add Webhook" button
  3. Enter your endpoint URL (e.g.: https://yourapp.com/webhooks/refgrow)
  4. Select the events you want to receive
  5. Optional: Generate a secret key for signature verification
  6. Save the settings

2. Testing Your Webhook

After creating a webhook, you can test it:

  1. In the webhooks table, click the test button next to your webhook
  2. Select the event type to test
  3. Click "Send Test" - the system will send test data
  4. Check the delivery status and server response
Important: Make sure your endpoint responds with HTTP status 200-299 for successful delivery.

Payload Format

All webhooks are sent as HTTP POST requests with JSON payload in the following format:

Payout Preferences: Starting November 17, 2025, webhook payloads automatically include PayPal and Wise payout preferences in the referrer.payout_preferences field when available. This eliminates the need for manual API enrichment steps.

Request Headers

Content-Type: application/json
User-Agent: Refgrow-Webhooks/1.0
X-Refgrow-Event: referral_converted
X-Refgrow-Signature: sha256=abc123... (if signature is configured)

Payload Structure

referral_signed_up

{
  "event": "referral_signed_up",
  "timestamp": 1703123456,
  "project_id": "123",
  "referrer": {
    "id": "456",
    "email": "affiliate@example.com",
    "payout_preferences": {
      "paypal_email": "affiliate@example.com",
      "wise_details": {
        "accountId": "wise_account_123",
        "email": "affiliate@example.com"
      }
    }
  },
  "referred": {
    "id": "789",
    "email": "customer@example.com"
  },
  "data": {
    "referral_code": "REF123",
    "signup_date": "2024-01-15T10:00:00Z",
    "user_agent": "Mozilla/5.0...",
    "ip_address": "192.168.1.1"
  }
}

Note: The payout_preferences field is only included if the affiliate has configured PayPal email or Wise payment details. If no payout preferences are configured, this field will be omitted.

referral_converted

{
  "event": "referral_converted",
  "timestamp": 1703123456,
  "project_id": "123",
  "referrer": {
    "id": "456",
    "email": "affiliate@example.com",
    "payout_preferences": {
      "paypal_email": "affiliate@example.com",
      "wise_details": {
        "accountId": "wise_account_123",
        "email": "affiliate@example.com"
      }
    }
  },
  "referred": {
    "id": "789",
    "email": "customer@example.com"
  },
  "conversion": {
    "id": "conv_123",
    "amount": 99.99,
    "currency": "USD",
    "commission_amount": 19.99,
    "commission_type": "percentage",
    "commission_rate": 20,
    "payment_processor": "stripe",
    "product_id": "prod_abc123",
    "order_id": "order_456",
    "conversion_date": "2024-01-15T10:30:00Z"
  }
}

Note: The payout_preferences field is only included if the affiliate has configured PayPal email or Wise payment details. This allows finance systems to automatically process payouts directly from webhook data without requiring additional API calls.

referral_canceled

{
  "event": "referral_canceled",
  "timestamp": 1703123456,
  "project_id": "123",
  "referrer": {
    "id": "456",
    "email": "affiliate@example.com",
    "payout_preferences": {
      "paypal_email": "affiliate@example.com",
      "wise_details": {
        "accountId": "wise_account_123",
        "email": "affiliate@example.com"
      }
    }
  },
  "referred": {
    "id": "789",
    "email": "customer@example.com"
  },
  "conversion": {
    "id": "conv_123",
    "amount": 99.99,
    "commission_amount": 19.99,
    "refund_amount": 99.99,
    "reason": "Customer requested refund",
    "canceled_date": "2024-01-16T14:20:00Z"
  }
}

Note: The payout_preferences field is only included if the affiliate has configured PayPal email or Wise payment details.

referral_updated

{
  "event": "referral_updated",
  "timestamp": 1703123456,
  "project_id": "123",
  "referrer": {
    "id": "456",
    "email": "affiliate@example.com",
    "payout_preferences": {
      "paypal_email": "affiliate@example.com",
      "wise_details": {
        "accountId": "wise_account_123",
        "email": "affiliate@example.com"
      }
    }
  },
  "referred": {
    "id": "789",
    "email": "customer@example.com"
  },
  "conversion": {
    "id": "conv_123",
    "amount": 99.99,
    "amount_usd": 19.99,
    "commission_amount": 19.99,
    "base_value": 99.99,
    "conversion_date": "2024-01-15T10:30:00Z",
    "reference": "order_456"
  }
}

Note: This event is triggered when a conversion is created or updated via API. The payout_preferences field includes PayPal email and Wise account details when configured. Fields include: referrer email, referred email, conversion amount, commission amount, PayPal email, Wise account ID, Wise email, and conversion date.

Signature Verification

If you configured a secret key, each webhook will contain an HMAC-SHA256 signature in the X-Refgrow-Signature header.

Signature Verification (PHP)

function verifySignature($payload, $signature, $secret) {
    $expectedSignature = 'sha256=' . hash_hmac('sha256', $payload, $secret);
    return hash_equals($expectedSignature, $signature);
}

// Usage
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_REFGROW_SIGNATURE'] ?? '';
$secret = 'your_webhook_secret';

if (!verifySignature($payload, $signature, $secret)) {
    http_response_code(401);
    exit('Invalid signature');
}

Signature Verification (Node.js)

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
    const expectedSignature = 'sha256=' + crypto
        .createHmac('sha256', secret)
        .update(payload, 'utf8')
        .digest('hex');
    
    return crypto.timingSafeEqual(
        Buffer.from(expectedSignature),
        Buffer.from(signature)
    );
}

// Express middleware
app.use('/webhooks/refgrow', express.raw({type: 'application/json'}), (req, res) => {
    const signature = req.headers['x-refgrow-signature'];
    const secret = process.env.REFGROW_WEBHOOK_SECRET;
    
    if (!verifySignature(req.body, signature, secret)) {
        return res.status(401).send('Invalid signature');
    }
    
    // Process webhook
    const event = JSON.parse(req.body);
    console.log('Received event:', event.event);
    
    res.status(200).send('OK');
});

Implementation Examples

Basic PHP Handler

<?php
header('Content-Type: application/json');

$payload = file_get_contents('php://input');
$event = json_decode($payload, true);

// Signature verification (if using secret)
$signature = $_SERVER['HTTP_X_REFGROW_SIGNATURE'] ?? '';
if ($signature && !verifySignature($payload, $signature, 'your_secret')) {
    http_response_code(401);
    exit('Invalid signature');
}

switch ($event['event']) {
    case 'referral_signed_up':
        // New referral signed up
        $referrerEmail = $event['referrer']['email'];
        $referredEmail = $event['referred']['email'];
        
        // Send notification to partner
        sendEmailNotification($referrerEmail, "New referral: $referredEmail");
        break;
        
    case 'referral_converted':
        // Referral made a purchase
        $commissionAmount = $event['conversion']['commission_amount'];
        $referrerEmail = $event['referrer']['email'];
        
        // Get payout preferences if available
        $payoutPreferences = $event['referrer']['payout_preferences'] ?? null;
        if ($payoutPreferences) {
            $paypalEmail = $payoutPreferences['paypal_email'] ?? null;
            $wiseDetails = $payoutPreferences['wise_details'] ?? null;
            
            // Automatically process payout using PayPal or Wise details
            if ($paypalEmail) {
                processPayPalPayout($referrerEmail, $commissionAmount, $paypalEmail);
            } elseif ($wiseDetails) {
                processWisePayout($referrerEmail, $commissionAmount, $wiseDetails);
            }
        }
        
        // Update internal system
        updatePartnerCommission($referrerEmail, $commissionAmount);
        break;
        
    case 'referral_updated':
        // Conversion created or updated via API
        $conversionId = $event['conversion']['id'];
        $amount = $event['conversion']['amount'];
        $commissionAmount = $event['conversion']['commission_amount'];
        $referrerEmail = $event['referrer']['email'];
        $referredEmail = $event['referred']['email'];
        $conversionDate = $event['conversion']['conversion_date'];
        
        // Get payout preferences if available
        $payoutPreferences = $event['referrer']['payout_preferences'] ?? null;
        $paypalEmail = $payoutPreferences['paypal_email'] ?? null;
        $wiseAccountId = $payoutPreferences['wise_details']['accountId'] ?? null;
        $wiseEmail = $payoutPreferences['wise_details']['email'] ?? null;
        
        // Update internal system with conversion data
        updateConversionRecord($conversionId, [
            'amount' => $amount,
            'commission' => $commissionAmount,
            'referrer_email' => $referrerEmail,
            'referred_email' => $referredEmail,
            'conversion_date' => $conversionDate,
            'paypal_email' => $paypalEmail,
            'wise_account_id' => $wiseAccountId,
            'wise_email' => $wiseEmail
        ]);
        break;
        
    case 'referral_canceled':
        // Conversion canceled
        $refundAmount = $event['conversion']['refund_amount'];
        $referrerEmail = $event['referrer']['email'];
        
        // Adjust commission
        adjustPartnerCommission($referrerEmail, -$refundAmount);
        break;
}

http_response_code(200);
echo json_encode(['status' => 'success']);
?>

Express.js Handler

const express = require('express');
const app = express();

app.post('/webhooks/refgrow', express.raw({type: 'application/json'}), async (req, res) => {
    try {
        const event = JSON.parse(req.body);
        
        switch (event.event) {
            case 'referral_signed_up':
                await handleReferralSignup(event);
                break;
                
            case 'referral_converted':
                await handleReferralConversion(event);
                break;
                
            case 'referral_updated':
                await handleReferralUpdate(event);
                break;
                
            case 'referral_canceled':
                await handleReferralCancellation(event);
                break;
                
            default:
                console.log('Unknown event type:', event.event);
        }
        
        res.status(200).json({ received: true });
    } catch (error) {
        console.error('Webhook error:', error);
        res.status(400).send('Webhook Error');
    }
});

async function handleReferralConversion(event) {
    const { referrer, conversion } = event;
    
    // Get payout preferences if available
    const payoutPreferences = referrer.payout_preferences;
    if (payoutPreferences) {
        const { paypal_email, wise_details } = payoutPreferences;
        
        // Automatically process payout using PayPal or Wise details
        if (paypal_email) {
            await processPayPalPayout(referrer.email, conversion.commission_amount, paypal_email);
        } else if (wise_details) {
            await processWisePayout(referrer.email, conversion.commission_amount, wise_details);
        }
    }
    
    // Update stats in database
    await db.updatePartnerStats(referrer.id, {
        totalCommissions: conversion.commission_amount,
        lastConversionDate: new Date()
    });
    
    // Send push notification
    await sendPushNotification(referrer.email, {
        title: 'New conversion!',
        body: `You earned $${conversion.commission_amount}`
    });
}

async function handleReferralUpdate(event) {
    const { referrer, referred, conversion } = event;
    
    // Extract all fields from conversion
    const {
        id: conversionId,
        amount,
        commission_amount,
        base_value,
        conversion_date,
        reference
    } = conversion;
    
    // Get payout preferences if available
    const payoutPreferences = referrer.payout_preferences;
    const paypalEmail = payoutPreferences?.paypal_email || null;
    const wiseAccountId = payoutPreferences?.wise_details?.accountId || null;
    const wiseEmail = payoutPreferences?.wise_details?.email || null;
    
    // Update conversion record in your system
    await db.updateConversion({
        conversionId,
        referrerEmail: referrer.email,
        referredEmail: referred.email,
        amount,
        commissionAmount: commission_amount,
        baseValue: base_value,
        conversionDate: conversion_date,
        reference,
        paypalEmail,
        wiseAccountId,
        wiseEmail
    });
    
    console.log(`Updated conversion ${conversionId} for referrer ${referrer.email}`);
}

Monitoring and Logs

Viewing Delivery Logs

In the webhook settings you can:

  • View delivery history for each webhook
  • See HTTP status codes and response times
  • Analyze delivery errors
  • Retry failed webhooks
Successful Delivery

HTTP Status: 200-299
Response Time: <5 seconds

Delivery Error

HTTP Status: 400+ or timeout
Automatic Retries: 3 attempts

Best Practices

Security
  • Always verify webhook signatures
  • Use HTTPS endpoints
  • Store secret keys securely
  • Restrict endpoint access
Performance
  • Respond quickly (within 10 seconds)
  • Return HTTP 200 on success
  • Handle duplicate events
  • Use queues for heavy operations

Troubleshooting

  1. Check that the URL is publicly accessible
  2. Ensure your server responds with HTTP 200
  3. Check delivery logs in the admin panel
  4. Use the test webhook for diagnostics
  5. Check firewall and security settings

  1. Make sure you're using the correct secret key
  2. Verify you're reading the raw request body
  3. Compare your signature algorithm with examples
  4. Check encoding (should be UTF-8)

Webhooks may be delivered multiple times in case of errors. Implement idempotency:

  • Use event IDs for deduplication
  • Store processed events in database
  • Check for existing records before processing

Testing Endpoint

We provide a simple testing endpoint that you can use to test your webhook implementation:

Test Webhook Endpoint

URL: https://yourdomain.com/webhook/test

This endpoint accepts POST requests and logs the received payload for testing purposes.

Example Usage:
curl -X POST https://yourdomain.com/webhook/test \
  -H "Content-Type: application/json" \
  -H "X-Refgrow-Signature: sha256=test_signature" \
  -d '{
    "event": "referral_converted",
    "timestamp": 1703123456,
    "project_id": "test",
    "referrer": {"id": "123", "email": "test@example.com"},
    "referred": {"id": "456", "email": "customer@example.com"},
    "conversion": {"amount": 99.99, "commission_amount": 19.99}
  }'
Response:
{
  "success": true,
  "message": "Webhook received successfully",
  "timestamp": "2024-01-15T10:30:00Z",
  "headers": {
    "content-type": "application/json",
    "x-refgrow-signature": "sha256=test_signature"
  },
  "payload": { ... }
}

API Reference

Test Webhook Endpoint

POST /api/project/{projectId}/webhooks/{webhookId}/test

Sends a test webhook with generated test data.

Parameters:
{
  "eventType": "referral_converted"
}
Response:
{
  "success": true,
  "status": 200,
  "response": "OK"
}

Need Help?

If you have questions about setting up webhooks, contact support or check our other guides.

Something missing? Suggest a feature