# Webhooks

Webhooks are used to send events from ReferralHero to your server. You can decide which events to send to your server.

To enable webhooks:

* go to your campaign dashboard > *Edit Campaign > Integrations > Webhooks*
* Click on the **+ New Webhook** button
* In the popup, add your endpoint URL and toggle the events you want to receive
* Click on **Create Webhook**

<figure><img src="https://363135598-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LsuqexOLPOWiUrWg_Ko%2Fuploads%2FTt0HX9U9W0D8mn5CH0la%2FScreenshot%202025-09-10%20at%206.56.15%20PM.png?alt=media&#x26;token=ff658f1d-67c1-4412-882e-ea455061898d" alt=""><figcaption></figcaption></figure>

***

### Webhook Payload Verification

To ensure that your webhook payloads are authentic and have not been tampered with, ReferralHero includes a signature header in every webhook request. You can use this signature to validate requests.

⚠️ **Important:** Make sure to **enable the “Payload Verification” toggle**. Once enabled, a secret key will be generated. This key is required to decode and validate the signature header in your application that receives the webhook.

<figure><img src="https://363135598-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LsuqexOLPOWiUrWg_Ko%2Fuploads%2FlcyQszQldF5JZFjCCubS%2FScreenshot%202025-09-10%20at%206.56.26%20PM.png?alt=media&#x26;token=e6473ede-b1b0-42dd-a98a-415178976a6b" alt=""><figcaption></figcaption></figure>

***

### Where to Find Your Webhook Secret Key

You can find your Webhook Secret Key in two places:

**Option 1: From Profile Menu**

1. Log in to your **ReferralHero dashboard**.
2. Click on your **profile button** (top-right corner).
3. In the popup, select **Webhook Secret**.
4. You will see your **Webhook Secret Key** along with an option to **regenerate** it if needed.

<figure><img src="https://363135598-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LsuqexOLPOWiUrWg_Ko%2Fuploads%2Ftq1Drkp2MiNuCnPawA0t%2FScreenshot%202025-09-13%20at%201.11.40%E2%80%AFam.png?alt=media&#x26;token=1d73c6f8-21f8-42f6-9503-71df923f8b37" alt=""><figcaption></figcaption></figure>

**Option 2: From Campaign Settings**

1. Log in to your ReferralHero dashboard.
2. Click **Edit Campaign** for the campaign you want.
3. Go to the **Integration** tab.
4. Click on **Webhook**, and you will see the Webhook Secret Key.

<figure><img src="https://363135598-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LsuqexOLPOWiUrWg_Ko%2Fuploads%2FV0bmzni9H9GGQAJKOvjx%2FScreenshot%202025-09-12%20at%2010.29.54%20PM.png?alt=media&#x26;token=7e63c893-2f5c-4bca-a71a-ca2b805e1bd5" alt=""><figcaption></figcaption></figure>

Use this secret key in your server code to verify incoming webhooks.

***

### Steps to Verify a Webhook

1. **Read the raw request body** (e.g., `request.raw_post` in Rails).
2. **Retrieve the signature** from the `X-ReferralHero-Signature` header.
3. **Recompute the HMAC-SHA256 hash** of the raw body using your Webhook Secret Key as the secret.
4. **Compare** your computed value with the signature header. If they match, the webhook is valid.

***

Code Examples

{% tabs %}
{% tab title="Ruby" %}

```ruby
require 'openssl'
require 'base64'

class WebhooksController < ActionController::API
  skip_before_action :verify_authenticity_token

  def receive
    raw_payload = request.raw_post
    signature   = request.headers['X-ReferralHero-Signature']
    secret      = ENV['REFERRALHERO_API_KEY']

    computed_signature = Base64.strict_encode64(
      OpenSSL::HMAC.digest('sha256', secret, raw_payload)
    )

    unless ActiveSupport::SecurityUtils.secure_compare(signature.to_s, computed_signature)
      render json: { error: 'Invalid signature' }, status: :unauthorized and return
    end

    data = JSON.parse(raw_payload)
    # handle data...
    head :ok
  end
end

```

{% endtab %}

{% tab title="Python (Flask)" %}

```python
from flask import Flask, request, abort
import hmac, hashlib, base64, os

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    raw = request.get_data()
    signature = request.headers.get('X-ReferralHero-Signature', '')
    secret = os.environ['REFERRALHERO_API_KEY'].encode()

    digest = hmac.new(secret, raw, hashlib.sha256).digest()
    computed = base64.b64encode(digest).decode()

    if not hmac.compare_digest(signature, computed):
        abort(401)
    data = request.get_json()
    # handle data...
    return '', 200
```

{% endtab %}

{% tab title="Node.js (Express)" %}

```javascript
const express = require('express');
const crypto  = require('crypto');

const app = express();
app.use(express.raw({ type: 'application/json' }));

app.post('/webhook', (req, res) => {
  const rawBody = req.body; // Buffer
  const signature = req.header('X-ReferralHero-Signature') || '';
  const secret = process.env.REFERRALHERO_API_KEY;

  const computed = crypto.createHmac('sha256', secret).update(rawBody).digest('base64');

  if (!timingSafeEqual(signature, computed)) {
    return res.status(401).send('Invalid signature');
  }

  const data = JSON.parse(rawBody.toString('utf8'));
  // process data...
  res.sendStatus(200);
});

function timingSafeEqual(a, b) {
  const bufA = Buffer.from(a || '');
  const bufB = Buffer.from(b || '');
  if (bufA.length !== bufB.length) return false;
  return crypto.timingSafeEqual(bufA, bufB);
}

```

{% endtab %}
{% endtabs %}

***

✅ **Backward Compatible**: If you do not check this header, your existing webhooks will continue to work without any changes.\
🔒 **Recommended**: Implement signature validation to ensure authenticity and security of incoming webhooks.

### Events

ReferralHero sends a `POST HTTP` request with a `JSON` payload when specific events occur.

There are 6 types of events:

#### **new\_registration**

{% tabs %}
{% tab title="Description" %}
Sent when a new person subscribes to your list. If you the confirmation email is disabled,  the event is sent as soon as the person is subscribed to the list.
{% endtab %}

{% tab title="Response" %}

```yaml
{
  response: "new_registration",
  list_uuid: "MFXXXX", //The UUID of your list
  subscriber_id: "sub_123ABC", // Subscriber's ID
  name: "John Doe", //Subscriber's name
  first_name: "John",
  last_name: "Doe",
  email: "john.doe@email.com", //Subscriber's email
  extra_field: "+1 2348891123", // Subscriber's extra field's value
  extra_field_2: "USA", // Subscriber's second extra field's value
  code: "2hg36dvs", //Subscriber's unique referral code
  source: "facebook", //Subscriber's source. If the subscriber doesn't have a source the value will be "direct_visit"
  referred: true,
  referrer: {
    subscriber_id: "sub_123CCD",
    people_referred: 2,
    referral_link: "http://mywebsite.com/LINK_PLAIN",
    points: 3,
    last_referral_at: 1702017953,
    name: "Mark Doe",
    first_name: "Mark",
    last_name: "Doe",
    email: "mark@yahoo.com"
    extra_field: "+1 2348894454",
    extra_field_2: "USA",
    option_field: "Los Angeles",
    code: "fd336dff",
    phone_number: "+1 2348894454",
    crypto_wallet_address: "0x0000000000000000000000000000000000000011"
  }, //This property can have 3 possible values: an empty string (if the subscriber has not been referred), an object containing data of the referral (if the subscriber has been referred) or "subscriber_deleted" (if the subscriber has been referred but the user has been deleted.)
  referral_link: "http://mywebsite.com/LINK_PLAIN",
  created_at: 1234567889 // Timestamp of the subscriber's sign up
}
```

{% endtab %}
{% endtabs %}

#### **subscriber\_promoted**

{% tabs %}
{% tab title="Description" %}
Sent when a subscriber is promoted.
{% endtab %}

{% tab title="Response" %}

```yaml
{
  response: "subscriber_promoted",
  list_uuid: "MFXXXX",
  subscriber_id: "sub_123ABC",
  source: "facebook",
  referred: false,
  referral: {
    subscriber_id: "sub_123CCD",
    people_referred: 2,
    referral_link: "http://mywebsite.com/LINK_PLAIN",
    points: 3,
    last_referral_at: 1702017953,
    name: "Mark Doe",
    first_name: "Mark",
    last_name: "Doe",
    email: "mark@yahoo.com"
    extra_field: "+1 2348894454",
    extra_field_2: "USA",
    option_field: "Los Angeles",
    code: "fd336dff",
    phone_number: "+1 2348894454",
    crypto_wallet_address: "0x0000000000000000000000000000000000000011"
  },
  people_referred: 3, //Number of people referred
  referral_link: "http://mywebsite.com/LINK_PLAIN",
  created_at: 1234567889 // Timestamp of the subscriber's promotion
  name: "John Doe", //Subscriber's name
  first_name: "John",
  last_name: "Doe",
  email: "john.doe@email.com" //Subscriber's email
  extra_field: "+1 2348891123", // Subscriber's extra field's value
  extra_field_2: "USA", // Subscriber's second extra field's value
  option_field: "Florida", // Subscriber's option field's value
  code: "2hg36dvs",
  phone_number: "+1 2348891123",
  crypto_wallet_address: "0x0000000000000000000000000000000000000000"
}
```

{% endtab %}
{% endtabs %}

#### **subscriber\_updated**

{% tabs %}
{% tab title="Description" %}
Sent when a subscriber field is updated.
{% endtab %}

{% tab title="Response" %}

```yaml
{
"response": "subscriber_fields_updated",
"list_uuid": "MF833ac6ee2d", // Unique list identifier
"subscriber_id": "sub_61ad4723e3a9", // Unique ID of the subscriber
"source": "referral", // Source of the subscriber (e.g., referral, direct_visit)
"referred": false, // Whether the subscriber was referred
"referral": null,
"referral_link": "https://campaign.referralhero.com/MF833ac6ee2d/signup?mwr=1296cda7", // Personalized referral URL
"people_referred": 0,
"created_at": 1746795375, // Timestamp of subscriber creation
"last_referral_at": null,
"name": "Jane Doe", // Full name
"first_name": "Jane", // First name
"last_name": "Doe", // Last name
"email": "jane.doe@example.com", // Email address
"extra_field": "Company ABC",
"extra_field_2": "Product Manager",
"extra_field_3": "New York",
"extra_field_4": "Referral Campaign A",
"option_field": "Option 1",
"code": "1296cda7",
"phone_number": "+1234567890", // Phone number
"crypto_wallet_address": "0xABCDEF1234567890"
}
```

{% endtab %}
{% endtabs %}

#### **subscriber\_deleted**

{% tabs %}
{% tab title="Description" %}
Sent when a subscriber is deleted.
{% endtab %}

{% tab title="Response" %}

```yaml
{
  response: "subscriber_deleted",
  list_uuid: "MFXXXX",
  subscriber_id: "sub_123ABC",
  last_referral_at: 1702017953,
  name: "John Doe", //Subscriber's name
  first_name: "John",
  last_name: "Doe",
  email: "john.doe@email.com" //Subscriber's email
  extra_field: "+1 2348891123", // Subscriber's extra field's value
  extra_field_2: "USA", // Subscriber's second extra field's value
  option_field: "Florida", // Subscriber's option field's value
  code: "2hg36dvs",
  phone_number: "+1 2348891123",
  crypto_wallet_address: "0x0000000000000000000000000000000000000000"
}
```

{% endtab %}
{% endtabs %}

#### **reward\_unlocked**

{% tabs %}
{% tab title="Description" %}
Sent immediately when a subscriber qualifies for and unlocks a reward.
{% endtab %}

{% tab title="Response" %}

```yaml
{
"response": "reward_unlocked",
"list_uuid": "MFABC123",
"subscriber_id": "sub_ABC123",
"bonus_id": 178, // Unique ID of the reward unlocked
"reward_name": "$100 Giftcard", // Name of reward
"reward_value": 100, // Value of the reward if set
"name": "John Smith", // Subscriber's full name
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@email.com", // Subscriber's email
"extra_field": null, // Custom field #1 (optional)
"extra_field_2": null, // Custom field #2 (optional)
"extra_field_3": null, // Custom field #3 (optional)
"extra_field_4": null, // Custom field #4 (optional)
"code": "YYY999", // Unique referral code
"people_referred": 5, // Number of successful referrals
"referral_link": "ReferralHero | Referral Program Software for B2B & B2C Brands ", // Personalized referral URL
"phone_number": "", // Subscriber's phone number
"crypto_wallet_address": "" // Wallet address if used
}
```

{% endtab %}
{% endtabs %}

#### **reward\_sent**

{% tabs %}
{% tab title="Description" %}
Sent when a reward is actually delivered to the subscriber. This happens only after conditions like 'Hold until manually reviewed' or 'Hold for X days' are fulfilled or Reward delivery set to\
'Unlock and send reward immediately'.
{% endtab %}

{% tab title="Response" %}

```yaml
{
"response": "reward_sent",
"list_uuid": "MFABC123",
"subscriber_id": "sub_ABC123",
"bonus_id": 178, // Unique ID of the reward sent
"reward_name": "$100 Giftcard", // Name of reward
"reward_value": 100, // Value of the reward if set
"name": "John Smith", // Subscriber's full name
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@email.com", // Subscriber's email
"extra_field": null, // Custom field #1 (optional)
"extra_field_2": null, // Custom field #2 (optional)
"extra_field_3": null, // Custom field #3 (optional)
"extra_field_4": null, // Custom field #4 (optional)
"code": "YYY999", // Unique referral code
"people_referred": 5, // Number of successful referrals
"referral_link": "https://referralhero.com/?mwr=YYY999", // Personalized referral URL
"phone_number": "", // Subscriber's phone number
"crypto_wallet_address": "" // Wallet address if used
}
```

{% endtab %}
{% endtabs %}

### Errors

Please send back a blank response with a status code of `200`. \
All not-200 responses will be considered errors. After 10 consecutive bad responses, the webhook will be disabled.

If a webhook fails, we will try to deliver it 3 times over a period of 5 minutes.

### How to test a webhook

To test a webhook just click on the **Test** button next to the webhook URL you want to test.\
We will ping your webhook URL with a JSON file containing fake data.
