# Guide to Wix Webhooks: Features and Best Practices

Wix webhooks notify your app when something happens in a connected Wix site: an order is created, an order is approved, an app instance is installed. If you're building a Wix app or integrating Wix commerce with another system, webhooks are how you react without polling.

This guide covers how Wix webhooks work, the events you'll handle, how to verify the request (the whole body is a signed JWT, not an HMAC), the delivery behavior, and the best practices for production.

## What are Wix webhooks?

A Wix webhook is an HTTP POST whose entire request body is a JWT (RS256) signed by Wix. There's no separate signature header and no HMAC: you verify the JWT with your app's public key (from the app dashboard), and the decoded token contains the event envelope (`instanceId`, `eventType`, and the entity data).

This is unusual, so the securing section below is the part to get right. It also means verification is asymmetric: you hold a public key, Wix holds the private key.

## Wix webhook features

| Feature | Details |
| --- | --- |
| Configuration | App dashboard (Custom Apps > your app > Webhooks), per event |
| Transport | Entire request body is a signed JWT |
| Signature scheme | RS256 JWT, signed by Wix |
| Verification key | Your app's public key (Custom Apps > View ID & keys / Get Public Key) |
| Key distribution | Per-app public key; no global JWKS |
| Claims | JWT carries `exp`/`iat`; envelope has `instanceId`, `eventType`, entity data |
| Delivery | ~1,250 ms initial timeout, then retries over hours |
| Ordering | Not guaranteed; duplicates possible |
| SDK | `@wix/sdk` verifies and decodes via the `process` method |

## Common events

Wix event names use a fully-qualified form on the wire. Ecommerce order events are the common ones:

| Event | Fires when |
| --- | --- |
| `wix.ecom.v1.order_created` | A new order is created |
| `wix.ecom.v1.order_approved` | An order is approved (payment captured) |
| `wix.ecom.v1.order_canceled` | An order is canceled |

A note on encoding: Wix event names vary across API generations. The current FQDN form is `wix.ecom.v1.order_*`; older APIs used a dotted form like `orders.order.created`, and the dashboard shows human titles ("Order Created"). Match on the exact wire string your integration receives. App-instance events (App Instance Installed, and similar) also exist.

## Setting up Wix webhooks

Configure webhooks per event in the app dashboard under Custom Apps > your app > Webhooks. To verify deliveries you need your app's public key: open the app, then More Actions > View ID & keys (or "Get Public Key" under Webhooks), and copy it into your environment.

## Securing Wix webhooks

Every delivery's body is a JWT signed with RS256. Verify it with your app's public key, then read the event from the decoded payload. The official `@wix/sdk` does this for you via its `process` method (with `AppStrategy` configured with your public key); it verifies the signature and returns the decoded event.

```javascript
const { createClient, AppStrategy } = require("@wix/sdk");

const client = createClient({
  auth: AppStrategy({ publicKey: process.env.WIX_PUBLIC_KEY }),
});

app.post("/webhook", express.text({ type: "*/*" }), async (req, res) => {
  try {
    // req.body is the raw JWT string; process() verifies + decodes it
    const event = await client.webhooks.process(req.body);
    res.sendStatus(200); // acknowledge fast
    processQueue.add(event); // process asynchronously
  } catch {
    res.sendStatus(401); // signature verification failed
  }
});

```

If you verify manually instead of using the SDK, treat the raw body as the JWT and verify it with a standard library, keyed with the app public key and pinned to RS256:

```javascript
const jwt = require("jsonwebtoken");

app.post("/webhook", express.text({ type: "*/*" }), (req, res) => {
  try {
    // The entire raw body is the JWT
    const decoded = jwt.verify(req.body, process.env.WIX_PUBLIC_KEY, {
      algorithms: ["RS256"],
    });
    res.sendStatus(200);
    processQueue.add(decoded);
  } catch {
    res.sendStatus(401);
  }
});

```

Two things to get right: verify against the raw, unparsed body (the JWT is the body, so any re-serialization breaks it), and pin the algorithm to `RS256` so a malformed token can't downgrade verification.

## Wix webhook limitations and pain points

### The whole body is a JWT, not an HMAC

The Problem: There's no `webhook-signature` header to check. The body itself is a JWT, verified with an asymmetric public key. Developers expecting an HMAC header look for a signature that isn't there and skip verification, or try to HMAC the body and fail.

Why It Happens: Wix uses RS256 JWTs signed with its private key rather than a shared-secret HMAC.

Workarounds:

* Treat the raw body as a JWT and verify it with your app's public key, pinned to RS256.
* Prefer the `@wix/sdk` `process` method, which handles verification and decoding.

How Hookdeck Can Help: Hookdeck can verify the Wix JWT at the edge, so your application receives pre-verified, decoded events without embedding JWT verification in every handler.

### Event-name encoding varies

The Problem: The same logical event appears as `wix.ecom.v1.order_created`, a legacy `orders.order.created`, or the human title "Order Created" depending on the API generation. Code matching the wrong form silently ignores events.

Why It Happens: Wix's APIs evolved across generations without a single canonical event-name form.

Workarounds:

* Match on the exact wire string your subscription delivers, and log unrecognized `eventType` values.

How Hookdeck Can Help: Hookdeck's filters route on the fields you actually receive, so you can normalize event names in one place instead of scattering string checks through your app.

### Out-of-order and duplicate delivery

The Problem: The initial delivery timeout is short (~1,250 ms) and Wix retries over hours, so events arrive out of order and more than once. Some legacy events also send only changed fields.

Why It Happens: Aggressive retries favor eventual delivery over ordering or exactly-once semantics.

Workarounds:

* Return 200 fast so retries don't pile up, and dedupe on the event/entity ID.
* Order by the entity's own timestamp, not receipt order, and re-fetch full state when a payload is partial.

How Hookdeck Can Help: Hookdeck deduplicates and durably queues events, and its replay lets you re-process in a controlled order during recovery. See our [guide to webhook idempotency](/webhooks/guides/implement-webhook-idempotency).

### Short acknowledgement window

The Problem: The ~1,250 ms initial timeout leaves almost no room for synchronous work before you respond.

Why It Happens: Wix expects a near-immediate acknowledgement.

Workarounds:

* Verify, enqueue, and return 200 immediately; do everything else off a queue.

How Hookdeck Can Help: Hookdeck acknowledges Wix within the window and durably queues events, decoupling the tight timeout from your processing time.

## Best practices

### Verify the JWT with your public key

Treat the raw body as an RS256 JWT and verify it with your app's public key (via `@wix/sdk` `process` or a JWT library pinned to `RS256`) before acting on the event.

### Acknowledge fast, process asynchronously

Return 200 within the short window and defer work to a queue. See [why to process webhooks asynchronously](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

### Match the exact event-name form and dedupe

Branch on the wire string you receive, dedupe on the event/entity ID, and order by the entity timestamp.

### Re-fetch when payloads are partial

Some legacy events carry only changed fields; re-fetch the full entity from the Wix API when you need complete state.

## Conclusion

Wix webhooks are distinctive: the entire request body is an RS256 JWT you verify with your app's public key, not an HMAC over a header. Get that right (ideally via the `@wix/sdk` `process` method), match the exact event-name encoding your integration receives, and plan for a tight acknowledgement window with out-of-order, duplicated delivery.

[Hookdeck](https://hookdeck.com) verifies the Wix JWT, deduplicates, and durably queues every event at the edge, so your app only ever processes verified, unique events without embedding JWT verification everywhere.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle Wix webhooks reliably in minutes.