← View All Guides
Webhooks logo
Integration Guide

How to Handle GrowSurf Referral Webhook Events

Build a webhook handler that processes GrowSurf referral events for custom integrations and automation.

Webhooks are the most flexible way to connect GrowSurf to your custom tech stack. Every time a referral event occurs β€” a participant joins, makes a referral, or earns a reward β€” GrowSurf can send a POST request to your server with all the event details. From there, you can trigger any custom logic: update your database, send notifications, process rewards, sync to CRMs, or kick off complex workflows.

This guide is the foundational reference for working with GrowSurf webhooks. You'll learn how to set up webhook endpoints, validate incoming requests, parse event payloads, handle different event types, implement error handling and retry logic, and build a robust webhook processing architecture.

Integration Steps

Step 1: Configure Webhooks in GrowSurf

Set up your GrowSurf campaign to send webhook events to your server.

  • Go to Campaign > Settings > Webhooks
  • Enter your webhook endpoint URL (e.g., https://api.yourapp.com/webhooks/growsurf)
  • Select the events you want to receive:
    • PARTICIPANT_CREATED β€” new participant signed up
    • PARTICIPANT_REFERRED β€” someone was referred
    • CAMPAIGN_REFERRAL_CONVERTED β€” referral converted
    • PARTICIPANT_REACHED_REWARD β€” participant earned a reward
  • Note the webhook signing secret for verification

Step 2: Build the Webhook Endpoint

Create a server endpoint that receives and processes GrowSurf webhook events.

  • Create a POST endpoint at your chosen URL
  • Accept JSON request bodies
  • Return 200 status quickly (within 5 seconds) to prevent retries
  • Process the event asynchronously if your logic takes time

Step 3: Validate Webhook Signatures

Verify that incoming webhooks actually come from GrowSurf to prevent spoofing.

  • GrowSurf includes a signature header with each webhook request
  • Compute an HMAC-SHA256 hash of the request body using your webhook secret
  • Compare your computed signature with the header value
  • Reject requests with invalid signatures (return 401)

Step 4: Parse and Route Event Types

Handle different event types with dedicated processing functions.

  • Extract the event field from the request body
  • Route to event-specific handlers:
    • PARTICIPANT_CREATED β†’ create user record, send welcome email
    • PARTICIPANT_REFERRED β†’ update CRM, send notifications
    • CAMPAIGN_REFERRAL_CONVERTED β†’ process rewards, update metrics
  • Log unrecognized events for debugging

Step 5: Implement Idempotency and Error Handling

Build resilient webhook processing that handles retries and failures gracefully.

  • Use the event ID as an idempotency key β€” check if you've already processed this event
  • Store processed event IDs in a database or cache (Redis) with a TTL
  • If processing fails, return a 500 error so GrowSurf retries
  • If the event was already processed, return 200 to prevent duplicate processing

Step 6: Monitor and Debug Webhooks

Set up monitoring to ensure your webhook handler stays healthy.

  • Log all incoming webhooks with timestamp, event type, and processing result
  • Set up alerts for webhook failures (5xx responses or processing errors)
  • Use GrowSurf's webhook delivery log to check for failed deliveries
  • Create a health check endpoint that returns handler status and recent event counts

Code Snippets

// Complete GrowSurf Webhook Handler
const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.json());

// Processed event cache (use Redis in production)
const processedEvents = new Set();

// Verify webhook signature
function verifySignature(payload, signature, secret) {
  const computed = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(computed)
  );
}

// Event handlers
const eventHandlers = {
  PARTICIPANT_CREATED: async (data) => {
    console.log(`New participant: ${data.participant.email}`);
    // Add to your database, CRM, email list, etc.
  },

  PARTICIPANT_REFERRED: async (data) => {
    console.log(`${data.participant.email} referred by ${data.referrer.email}`);
    // Notify referrer, update CRM, track analytics
  },

  CAMPAIGN_REFERRAL_CONVERTED: async (data) => {
    console.log(`Referral converted: ${data.participant.email}`);
    // Process rewards, update metrics, celebrate
  },

  PARTICIPANT_REACHED_REWARD: async (data) => {
    console.log(`Reward earned: ${data.participant.email}`);
    // Fulfill reward, send confirmation
  }
};

// Main webhook endpoint
app.post('/webhooks/growsurf', async (req, res) => {
  // 1. Verify signature
  const signature = req.headers['x-growsurf-signature'];
  if (!verifySignature(req.body, signature, process.env.GROWSURF_WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // 2. Check idempotency
  const eventId = req.body.id || `${req.body.event}-${req.body.participant?.id}-${Date.now()}`;
  if (processedEvents.has(eventId)) {
    return res.status(200).json({ message: 'Already processed' });
  }

  // 3. Route to handler
  const handler = eventHandlers[req.body.event];
  if (!handler) {
    console.warn(`Unknown event: ${req.body.event}`);
    return res.status(200).json({ message: 'Unrecognized event' });
  }

  // 4. Process asynchronously
  res.status(200).json({ received: true });

  try {
    await handler(req.body);
    processedEvents.add(eventId);
  } catch (error) {
    console.error(`Error processing ${req.body.event}:`, error);
    // Alert your monitoring system
  }
});

app.listen(3000);

Tips

Return 200 Immediately, Process Asynchronously

GrowSurf expects a response within 5 seconds. If your processing logic takes longer (database queries, API calls to third parties), return 200 immediately and process the event asynchronously using a job queue or background worker. This prevents webhook timeouts and unnecessary retries.

Use Timing-Safe Comparison for Signatures

Always use crypto.timingSafeEqual() when comparing webhook signatures. Regular string comparison is vulnerable to timing attacks. This security practice prevents attackers from incrementally guessing your webhook secret.

Implement Dead Letter Queues for Failed Events

When event processing fails, don't just log and forget. Push failed events to a dead letter queue (DLQ) for manual review and replay. This ensures no referral events are permanently lost, even during outages or bugs in your handler code.

Version Your Webhook Handler

Use a versioned endpoint (e.g., /webhooks/growsurf/v1) so you can deploy a new version alongside the old one during migrations. This prevents downtime when you change your webhook processing logic.

FAQ

How often does GrowSurf retry failed webhook deliveries?

GrowSurf retries failed webhooks (non-2xx responses) with exponential backoff. Typically, retries happen at 1 minute, 5 minutes, 30 minutes, and 2 hours after the initial failure. After several failed retries, the webhook is marked as failed in GrowSurf's delivery log. Always return 2xx for successfully received events to prevent unnecessary retries.

What data does the GrowSurf webhook payload contain?

The payload includes: the event type, the participant object (email, name, referral code, referral count, metadata), the referrer object (if applicable), and campaign details. The exact fields vary by event type β€” for example, PARTICIPANT_REFERRED includes both participant and referrer objects, while PARTICIPANT_CREATED only includes the participant.

Can I receive webhooks for specific events only?

Yes. In GrowSurf's webhook settings, you can select which event types to receive. Only enable the events you actually process β€” this reduces unnecessary traffic and simplifies your handler code. You can add more event types later as your integration expands.

Set up your refer a friend program with customer referral and affiliate program software that lowers your acquisition costs, increases customer loyalty, and saves you gobs of time.

Trusted by marketing and product teams at fast-growing B2C, fintech, and SaaS companies