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.
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:
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/sdkprocessmethod, 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
eventTypevalues.
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.
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.
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.
Make Wix webhooks production-ready
Hookdeck verifies the Wix JWT, deduplicates, and durably queues every event
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 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 for free and handle Wix webhooks reliably in minutes.