> For the complete documentation index, see [llms.txt](https://docs.rafflex.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rafflex.io/additional-settings/settings/integrations.md).

# n8n Webhooks (Legacy)

Send orders, new customer sign ups and marketing consent changes from your site to an n8n workflow. This is a legacy feature for sites that already use it.

***

Rafflex can send a message to an [n8n](https://n8n.io) workflow whenever certain things happen on your site, such as a completed order or a new customer registering. n8n is an automation tool: it can take that message and do something with it, like adding the customer to a spreadsheet, posting in a Slack channel or updating another system.

These messages are called **webhooks**. A webhook is simply your site sending a small bundle of information to a web address you choose, the moment something happens.

{% hint style="warning" %}
**This is a legacy feature.** It keeps working for sites that already rely on it, but it is no longer being developed and may be retired in the future. If you are planning something new, contact Rafflex support first to ask about the options available for your site.
{% endhint %}

{% hint style="info" %}
**This feature is self serve.** It is aimed at operators who are comfortable building n8n workflows. Rafflex support cannot build or troubleshoot your n8n workflows for you. This page explains what your site sends so that you, or whoever builds your automations, can use it.
{% endhint %}

***

### Where to find the settings

The webhook settings live in the **Legacy webhooks** section of the **Advanced** settings page.

1. In the **admin panel**, find the **Content** group in the left-hand menu.
2. Click **Settings**.
3. In the Settings side menu, click **Advanced**.
4. Scroll down to the **Legacy webhooks** section.

| Field                                           | What it does                                                                                                                                                       |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **n8n Webhook URL**                             | The web address of the Webhook node in your n8n workflow. Leave it empty to stop sending everything.                                                               |
| **Webhook Secret (Optional)**                   | A private word or phrase of your choosing. If you fill it in, it is included with every webhook so your workflow can check the request really came from your site. |
| **Send Order Completed Events**                 | Sends a webhook each time an order is completed.                                                                                                                   |
| **Send User Created Events**                    | Sends a webhook each time a new customer registers an account.                                                                                                     |
| **Send Marketing Subscribe/Unsubscribe Events** | Sends a webhook each time a customer opts in to, or is unsubscribed from, your marketing.                                                                          |

### How to set it up

1. In n8n, create a new workflow and add a **Webhook** node. Set its HTTP method to **POST**.
2. Copy the webhook's **Production URL** from n8n. Make sure your workflow is published (older versions of n8n call this **active**), otherwise n8n will not accept the webhooks.
3. In your Rafflex admin panel, open **Settings**, then **Advanced**, and scroll to **Legacy webhooks**.
4. Paste the address into **n8n Webhook URL**. It must be a full web address, such as one starting with `https://`. If it is not, the page will not save and a red message appears under the field.
5. If you want to use a secret, type it into **Webhook Secret (Optional)**. The description under the field tells you how the secret is sent, so you can set up your n8n workflow to check for it.
6. Switch on the events you want to receive.
7. Click **Save Settings**.

Each toggle works on its own. Nothing is sent for an event while its toggle is off, and nothing is sent at all while **n8n Webhook URL** is empty.

{% hint style="info" %}
**Testing your workflow?** n8n gives each Webhook node a separate test address that only listens while you have clicked **Listen for Test Event**. If you paste the test address into Rafflex, webhooks will only arrive while n8n is listening. Switch to the production address once your workflow is ready.
{% endhint %}

{% hint style="info" %}
The webhooks are sent as standard JSON to the address you enter, so other automation tools that accept incoming webhooks may also be able to receive them. The feature was built for n8n, though, and other tools are not supported.
{% endhint %}

***

### What every webhook looks like

Every webhook has the same outer layout, which we call the envelope. The `event` field tells you what happened, and the `data` field holds the details.

```json
{
  "event": "order.completed",
  "timestamp": "2026-03-20T10:30:00+00:00",
  "tenant": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "domain": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  },
  "data": {
    // Event specific details, shown below
  }
}
```

| Field           | Meaning                                                                             |
| --------------- | ----------------------------------------------------------------------------------- |
| `event`         | The name of the event, for example `order.completed`                                |
| `timestamp`     | When the webhook was created                                                        |
| `tenant.id`     | Your site's unique Rafflex account reference                                        |
| `tenant.domain` | Currently holds the same account reference as `tenant.id`, **not** your web address |
| `data`          | The details of the event                                                            |

{% hint style="warning" %}
Despite its name, `tenant.domain` does not contain your website address. If you send webhooks from more than one Rafflex site into the same workflow, use `tenant.id` to tell them apart.
{% endhint %}

***

### Event reference

There are four events in total.

| Event                    | Sent when                                    | Toggle                                          |
| ------------------------ | -------------------------------------------- | ----------------------------------------------- |
| `order.completed`        | A customer completes a purchase              | **Send Order Completed Events**                 |
| `user.created`           | A customer registers an account on your site | **Send User Created Events**                    |
| `marketing.subscribed`   | A customer opts in to marketing              | **Send Marketing Subscribe/Unsubscribe Events** |
| `marketing.unsubscribed` | A customer is unsubscribed from marketing    | **Send Marketing Subscribe/Unsubscribe Events** |

{% hint style="info" %}
Money values are sent as decimal text in your store currency, for example `"10.00"` means ten pounds. They are not pennies. The `currency` field tells you which currency the amounts are in.
{% endhint %}

#### 1. Order Completed (`order.completed`)

Sent when a customer completes a purchase. This fires for completed orders whatever payment method was used, including card, PayPal, wallet credit and free orders. The `payment_gateway` field tells you which method was used.

The `customer` and `billing` details are the ones entered at checkout for that order. The `status` of a completed order is always `complete`.

**Data:**

```json
{
  "order": {
    "id": 1024,
    "status": "complete",
    "subtotal": "10.00",
    "discount": "0.00",
    "credit_used": "0.00",
    "total": "10.00",
    "payment_gateway": "cashflows",
    "discount_code": null,
    "created_at": "2026-03-20T10:30:00+00:00",
    "updated_at": "2026-03-20T10:30:00+00:00"
  },
  "customer": {
    "id": 55,
    "email": "customer@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "phone": "+447700900000"
  },
  "billing": {
    "street": "123 Raffle Lane",
    "city": "London",
    "county": "Greater London",
    "postcode": "SW1A 1AA",
    "country": "GB"
  },
  "items": [
    {
      "product_title": "Win a PS5",
      "product_slug": "win-a-ps5",
      "quantity": 2,
      "unit_price": "5.00",
      "total": "10.00"
    }
  ],
  "ticket_count": 2,
  "currency": "GBP"
}
```

#### 2. User Created (`user.created`)

Sent when a customer registers an account using the sign up form on your site.

It is **not** sent for:

* Accounts created when a customer signs in with Google or Facebook for the first time.
* Accounts you create yourself from the admin panel.
* Accounts brought in by an import.

The `consent` block shows the customer's marketing status at the moment they registered. If they ticked the marketing opt in box while signing up, `marketing_opted_in_at` is already filled in, as in the example below. If they did not, both fields are `null`.

**Data:**

```json
{
  "user": {
    "id": 55,
    "email": "customer@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "display_name": "johndoe",
    "created_at": "2026-03-20T10:30:00+00:00"
  },
  "consent": {
    "marketing_opted_in_at": "2026-03-20T10:30:00+00:00",
    "marketing_unsubscribed_at": null
  }
}
```

#### 3. Marketing Subscribed (`marketing.subscribed`)

Sent when a customer opts in to your marketing. This happens when they:

* Tick the marketing consent box at checkout.
* Tick the opt in box while registering an account.
* Enter their email address into a newsletter or email sign up block on your site.

This event is only sent when the email address belongs to a customer account on your site. If someone who has never registered enters their email into a newsletter sign up block, no webhook is sent for them.

The event can be sent again for a customer who is already subscribed, for example if they tick the consent box on a later order. Build your workflow so that a repeat does no harm.

Opting in clears any earlier unsubscribe, so `marketing_unsubscribed_at` is always `null` on this event.

**Data:**

```json
{
  "user": {
    "id": 55,
    "email": "customer@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "display_name": "johndoe",
    "created_at": "2026-03-20T10:30:00+00:00"
  },
  "consent": {
    "marketing_opted_in_at": "2026-03-20T10:30:00+00:00",
    "marketing_unsubscribed_at": null
  }
}
```

#### 4. Marketing Unsubscribed (`marketing.unsubscribed`)

Sent when a customer is unsubscribed from your marketing. This happens when:

* They unsubscribe themselves in **My Account**.
* You unsubscribe them from their page in the **Users** area of your admin panel, using the **Unsubscribe Email Marketing** action.
* They take a timeout from your site, or you apply a timeout to their account for them.

`marketing_opted_in_at` keeps its earlier value on this event, because unsubscribing does not erase the date they opted in. See [How consent is shown](#how-consent-is-shown) below, because this catches people out.

{% hint style="warning" %}
Deleting a customer's account does not send this event. The account is removed first, so there is no customer left to describe by the time the event would be sent. If you use n8n to remove customers from another system, handle account deletions separately rather than relying on this webhook.
{% endhint %}

**Data:**

```json
{
  "user": {
    "id": 55,
    "email": "customer@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "display_name": "johndoe",
    "created_at": "2026-03-20T10:30:00+00:00"
  },
  "consent": {
    "marketing_opted_in_at": "2026-03-20T10:30:00+00:00",
    "marketing_unsubscribed_at": "2026-04-02T16:05:00+00:00"
  }
}
```

***

### How consent is shown

Every event that describes a customer account includes a `consent` block, so you never have to guess their marketing status. It holds two dates:

| Field                       | Meaning                                                                |
| --------------------------- | ---------------------------------------------------------------------- |
| `marketing_opted_in_at`     | When the customer opted in, or `null` if they never have               |
| `marketing_unsubscribed_at` | When the customer unsubscribed, or `null` if they are not unsubscribed |

**Always check `marketing_unsubscribed_at` first.** Opting in and unsubscribing are recorded separately, and unsubscribing does not erase the opt in date. So a customer who opted in during March and unsubscribed in April arrives with **both fields filled in**. If you only check `marketing_opted_in_at`, you will treat an unsubscribed customer as subscribed, and email someone who asked you not to.

Follow this rule:

1. If `marketing_unsubscribed_at` is filled in, the customer is **unsubscribed**, whatever `marketing_opted_in_at` says.
2. Otherwise, if `marketing_opted_in_at` is filled in, the customer is **subscribed**.
3. If both are `null`, the customer has never opted in.

When a customer opts in again after unsubscribing, `marketing_unsubscribed_at` is cleared back to `null` and `marketing_opted_in_at` is updated to the new date. Ticking the marketing consent box on a later order also updates `marketing_opted_in_at` to that moment, even if the customer was already subscribed. If an already subscribed customer enters their email into a newsletter sign up block, their existing opt in date is kept.

The `event` field tells you what just happened, which is handy for deciding what your workflow does next. The `consent` block tells you where the customer stands now.

Here is a complete `marketing.unsubscribed` webhook, envelope included:

```json
{
  "event": "marketing.unsubscribed",
  "timestamp": "2026-04-02T16:05:00+00:00",
  "tenant": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "domain": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  },
  "data": {
    "user": {
      "id": 55,
      "email": "customer@example.com",
      "first_name": "John",
      "last_name": "Doe",
      "display_name": "johndoe",
      "created_at": "2026-03-20T10:30:00+00:00"
    },
    "consent": {
      "marketing_opted_in_at": "2026-03-20T10:30:00+00:00",
      "marketing_unsubscribed_at": "2026-04-02T16:05:00+00:00"
    }
  }
}
```

{% hint style="success" %}
**A simple n8n layout:** n8n places everything Rafflex sends inside `body`. Add a **Switch** node straight after your Webhook node and branch on `{{ $json.body.event }}` to send each event type down its own path. Use the customer's email address (`{{ $json.body.data.user.email }}` for the account and marketing events, `{{ $json.body.data.customer.email }}` for orders) to find the customer in the system you are updating, and read `{{ $json.body.data.consent }}` to set their marketing status there.
{% endhint %}

{% hint style="info" %}
These dates show the customer's current status, not a full history. Rafflex keeps their opt in date and their unsubscribe date, so it will not tell you that someone opted in and out four times last year. If you need a complete record, store each webhook as it arrives. See [Klaviyo Integration](/marketing/sms-and-email/klaviyo-integration.md) for more on how marketing consent is handled.
{% endhint %}

***

### Delivery

Webhooks are sent shortly after the event happens. If your n8n address cannot be reached, Rafflex may try again a few times before giving up.

Because of these retries, your workflow can occasionally receive the same event more than once, so build it to cope with repeats. For `order.completed`, use `order.id` to spot an order you have already handled. For the account and marketing events, use the customer's email address.

{% hint style="warning" %}
Webhooks are not saved up while your n8n workflow is unavailable. If it cannot receive a webhook, that event is lost and is not sent again later. Keep your n8n workflow published and reachable, and do not rely on these webhooks as your only record of anything important. Your orders and customers are always available in your admin panel.
{% endhint %}

***

### Turning webhooks off

To stop all webhooks, open **Settings**, then **Advanced**, clear the **n8n Webhook URL** box and click **Save Settings**. To stop just one type, switch off its toggle and save.

***

### Related pages

{% content-ref url="/pages/861hLq1tB4p0nhlnqRL1" %}
[Advanced Settings](/additional-settings/settings/advanced-settings.md)
{% endcontent-ref %}
