---
title: "Webhooks"
description: "Receive real-time order status updates from SparkCRM via webhooks."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.sparkcrm.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

**Navigation**: Spark CRM > Webhooks

Webhooks enable real-time communication from SparkCRM back to your WooCommerce store. When an order status changes in SparkCRM, a webhook event is sent to your WordPress site to update the corresponding WooCommerce order.

---

## Webhook Endpoint

Your site's webhook URL is displayed on the **Spark CRM > Webhooks** page:

```
https://yourstore.com/wp-json/spark/v1/webhook
```

Copy this URL and configure it as a webhook endpoint in your SparkCRM account under **Settings > Webhooks**.

---

## Webhook Secret

The plugin generates a secret on the Webhooks settings page, where you can also regenerate it. Despite its name it is used as the **Basic Authentication password** for the Spark CRM webhook - see the note below.

**To configure in SparkCRM:**

1. Copy the **Webhook Secret** and the **Webhook URL** from the plugin's Webhooks page
2. In SparkCRM, go to **Settings > Webhooks** and click **Add Webhook**
3. Paste the plugin's Webhook URL into **Endpoint URL**
4. Set **Authentication** to **Basic Authentication** and paste the Webhook Secret into the password field
5. Under **Events**, select the triggers you want to send
6. Add the **JSON Payload Parameters** the plugin expects for that event (see below)
7. Tick **Active** and save

> **Use Basic Authentication, not the signature.** Spark CRM signs every delivery
> with HMAC-SHA256 and sends the digest in `X-Webhook-Signature` (raw lowercase hex, no
> `sha256=` prefix), alongside a delivery id in `X-Webhook-ID`. This plugin version reads a
> differently named header, `X-Spark-Signature`, so it cannot verify Spark CRM's
> signature. Because the plugin generates a webhook secret when it is activated, it rejects
> any request that arrives without `X-Spark-Signature` — so a webhook configured for
> signature verification returns **401** on every delivery. Basic Authentication is the
> path the plugin actually supports, and is what its own setup card instructs.

---

## Supported Events

Two names matter here, and they are not the same:

- The **trigger** you select under **Events** in Spark CRM. These are snake_case
  (`order_complete`, `refund_created`) and never dot-separated.
- The **`event_type` value the plugin dispatches on**. These are dot-separated
  (`order.approved`), and the plugin acts on nothing else — any other value returns
  `Event type not handled` and the order is untouched.

The plugin does not translate between them. You bridge the two by adding a **JSON Payload
Parameter** named `event_type` whose value is the dotted string, and nesting the order
fields under a `data` object — the plugin reads its fields from `body.data`, not from the
flat payload Spark CRM sends by default.

| Select this trigger | Set `event_type` to | WooCommerce action |
|---------------------|---------------------|--------------------|
| `order_complete` | `order.approved` | Order status set to **Processing** |
| `order_declined` | `order.declined` | Order status set to **Failed** |
| `refund_created` | `order.refunded` | WooCommerce refund created |
| `order_canceled` | `order.cancelled` | Order cancelled, stock restored |
| `fulfillment_created` or `tracking_updated` | `order.shipped` | Order set to **Completed**; tracking number, tracking URL and carrier stored as order meta |
| `delivered` | `order.delivered` | Delivery recorded in order meta (`_spark_delivered_date`) plus an order note |
| `subscription_created` | `subscription.created` | Subscription data stored in order meta |
| `subscription_canceled` | `subscription.cancelled` | Subscription status updated |

Note the spelling difference across the boundary: the Spark CRM triggers use the
American single-l `canceled`, while the plugin's `event_type` values use `cancelled`.

**Notes on these events:**

- Subscription rebills emit `subscription_rebill_complete` rather than `order_complete`, and rebill declines deliberately emit `subscription_rebill_declined` instead of `order_declined`. Use `transaction_approved` if you need per-transaction granularity.
- A queued `order_declined` delivery is skipped if the order is no longer declined when it is sent, and a queued `subscription_canceled` delivery is skipped if the subscription is no longer cancelled.
- `refund_created` carries `refund_amount`, `refund_currency`, `refund_is_partial`, `refund_transaction_number`, `refunded_transaction_number` and `refunded_products[]`, which is what the plugin needs to mirror a partial refund.
- All fulfillment events share the same payload shape (`fulfillment_number`, `tracking_number`, `carrier`, `status`, `shipping_method`); `tracking_number` is populated once tracking exists, so `fulfillment_created` may carry an empty one.
- `delivered` requires Enhanced Shipping Notifications on your team before it can be selected. Its payload is fulfillment-scoped (`fulfillment_number`, `tracking_number`, `carrier`, `status`) and the delivery time comes from the payload's `timestamp` field. The plugin looks for a `delivered_at` field, so map one in your payload parameters if you want the real delivery date recorded rather than the time the webhook arrived.
- `subscription_created` payload fields include `subscription_number`, `status`, `amount`, `currency`, `billing_interval`, `next_billing_at`, `trial_days`, `trial_ends_at` and `products[]`. Note that `order_id` holds an order *number* for the customer's most recent order, which is not guaranteed to be the order that created the subscription — match on `subscription_number` instead.

---

## Order Matching

Webhooks match SparkCRM orders to WooCommerce orders using the `_spark_order_number` meta key, which is stored when an order is first synced to SparkCRM.

---

## Testing

A test endpoint is available to verify your webhook configuration:

```
GET https://yourstore.com/wp-json/spark/v1/webhook/test
```

This returns a success response without processing any data.

---

## Troubleshooting

### Webhooks Not Arriving

- Verify the endpoint URL is correct in SparkCRM
- Check that your site is accessible from the internet (not localhost)
- Ensure no firewall or security plugin is blocking the REST API endpoint
- Test the endpoint manually using the test URL above

### Every Delivery Returns 401

This is what happens when the webhook is configured for signature verification. The plugin
verifies `X-Spark-Signature`, which Spark CRM never sends — it sends
`X-Webhook-Signature` — and because the plugin generates a secret when it is activated, it
refuses any request that arrives without its own header.

- Set **Authentication** to **Basic Authentication** on the Spark CRM webhook and use the plugin's Webhook Secret as the password
- Do not use the plugin secret as a signing secret; the two schemes are not interoperable in this plugin version
- Check that no proxy or CDN is stripping the `Authorization` header

### Deliveries Return 200 but Nothing Changes

The plugin answers `Event type not handled` and returns 200 when it does not recognise the
`event_type` value. Add a **JSON Payload Parameter** named `event_type` set to the dotted
value for that trigger (see [Supported Events](#supported-events)), and nest the order
fields under a `data` object.

### Order Not Updating

- Confirm the order was originally synced from this WooCommerce site
- Check that the `_spark_order_number` meta exists on the WooCommerce order
- Enable debug mode to see detailed webhook processing logs

Source: https://docs.sparkcrm.io/wordpress-plugin/webhooks/index.mdx
