# Guide to Mews POS Webhooks: Features and Best Practices

Mews is a cloud platform for hotels and hospitality groups, and its POS API covers the restaurant and bar side: table bookings, orders, payments and menu products. Integrations such as kitchen displays, delivery platforms and accounting tools use POS API webhooks to learn when a party is seated, an order moves to the kitchen, a payment is added or a dish sells out. Only the POS API signs its webhooks. The Mews Connector API, used for the property management system, is a separate product with its own webhook scheme.

This guide explains how POS webhooks are registered, the six events and their status values, verifying the base64 `X-Signature`, and the parts of delivery that Mews leaves undocumented.

## What are Mews POS webhooks?

You register a webhook endpoint with the POS API's **Create webhook endpoint** operation, and choose the entities and event types you want through a webhook subscription. Mews then posts JSON to your endpoint whenever a subscribed booking, order or product changes.

Each event identifies the entity and its new state, not the full record. Mews's guidance is to use the entity's ID with the relevant API operation when you need more detail. Mews also describes "unified event delivery": one message can contain all the simultaneous events for entities you've subscribed to, such as several bookings changing status at once.

## Mews POS webhook features

| Feature | Details |
| --- | --- |
| Signature | base64 HMAC-SHA256 |
| Signature header | `x-signature` |
| Body | JSON |
| Event routing | Body field `meta.eventType` |

Not covered by the [POS API webhook documentation](https://docs.mews.com/pos-api/events/webhooks): where the secret key comes from, how failed deliveries are retried, the timeout, which status codes count as success, and source IP addresses.

## Events and payloads

| Event routing | Body field `meta.eventType` |

Status values are fixed lists. Bookings move through `confirmed`, `seated`, `completed`, `cancelled` and `no_show`. Order state covers `draft`, `sent`, `paid`, `discarded`, `cart`, `pending_payment`, `open` and `open_web`, while order status tracks fulfilment from `received` and `confirmed` through `preparing`, `ready_for_delivery`, `dispatched`, `in_transit` and `delivered`, or `rejected`.

This is the `booking.status.updated` sample from the [webhook samples library](https://github.com/hookdeck/webhook-samples/blob/main/providers/mews-pos/latest/booking.status.updated.json):

```json
{
  "data": {
    "id": "98656c4a-950d-4b19-b7ef-ad1744e92b53",
    "type": "bookings",
    "attributes": {
      "status": "seated",
      "updatedAt": "2025-02-06T09:41:30.465Z"
    }
  },
  "meta": {
    "eventType": "booking.status.updated"
  }
}

```

`meta.eventType` names the event. `data.type` names the entity, which several events share, so `orders` alone doesn't tell you whether the state, status, total or payments changed. `data.attributes` depends on the event: a new `status`, `state` or `isAvailable` value with `updatedAt`, tip and correction amounts for `order.total.updated`, or `orderId` and `paymentId` for `order.payments.added`.

## Setting up Mews POS webhooks

1. Call the POS API's **Create webhook endpoint** operation with your URL, for example `https://example.com/webhooks/mews-pos`.
2. Set up the webhook subscription for the entities and events you need, such as `booking.status.updated` and `order.status.updated`.
3. Get the secret key used for `X-Signature`. Mews's security page calls it "your secret key" without saying where it's issued, so check the endpoint registration response or ask your Mews contact. Store it as `MEWS_POS_WEBHOOK_SECRET`.

To use Hookdeck, create a Mews POS source with that key as its verification secret and register the source URL as the endpoint. For local testing:

```bash
npx hookdeck-cli listen 3000 mews-pos --path /webhooks/mews-pos

```

## Securing Mews POS webhooks

`X-Signature` is a base64 HMAC-SHA256 of the raw body, keyed with your secret. Mews's [webhook security page](https://docs.mews.com/pos-api/events/wh-security) gives a Ruby example and recommends a constant-time comparison over the plain `==` in its first snippet.

### Node.js verification

```javascript
const crypto = require('node:crypto');

function decodeBase64(value) {
  if (typeof value !== 'string' || !/^[A-Za-z0-9+/]+={0,2}$/.test(value)) return null;
  const bytes = Buffer.from(value, 'base64');
  return bytes.length > 0 && bytes.toString('base64').replace(/=+$/, '') === value.replace(/=+$/, '')
    ? bytes : null;
}

function verifySignature(rawBody, headers, secret) {
  if (!Buffer.isBuffer(rawBody) || typeof secret !== 'string' || !secret) return false;
  const signature = headers['x-signature'];
  if (typeof signature !== 'string') return false;
  const key = Buffer.from(secret, 'utf8');
  const message = rawBody;
  const candidates = [signature];
  const expected = crypto.createHmac('sha256', key).update(message).digest();
  return candidates.some((candidate) => {
    const received = decodeBase64(candidate);
    if (!received) return false;
    return received.length === expected.length && crypto.timingSafeEqual(received, expected);
  });
}

```

The handler routes on `meta.eventType`, and accepts an array of events in case Mews groups several into one message:

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

const app = express();

app.post('/webhooks/mews-pos', express.raw({ type: '*/*' }), async (req, res) => {
  const secret = process.env.MEWS_POS_WEBHOOK_SECRET;
  if (!secret) return res.sendStatus(500);
  if (!verifySignature(req.body, req.headers, secret)) return res.sendStatus(401);

  let body;
  try {
    body = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }
  if (!body || typeof body !== 'object' || Array.isArray(body)) return res.sendStatus(400);
  try {
    const events = Array.isArray(body) ? body : [body];
    for (const event of events) {
      await recordPosChange({
        eventType: event.meta?.eventType,
        entityType: event.data?.type,
        entityId: event.data?.id,
        attributes: event.data?.attributes ?? {},
      });
    }
  } catch {
    return res.sendStatus(503);
  }

  res.sendStatus(200);
});

```

`recordPosChange` should key on event type, entity ID and `attributes.updatedAt`, since there's no event ID, and ignore anything older than the state it already holds.

Mews signs no timestamp. Hookdeck's Mews POS source checks the same base64 HMAC, replies `200`, and records a mismatch as failed verification.

> **Make Mews POS webhooks production-ready.** [Hookdeck Event Gateway](/event-gateway) verifies `X-Signature`, records every booking and order change, and retries your kitchen or accounting service under a policy you can see, which Mews doesn't document for the POS API.

## Mews POS webhook limitations and pain points

### Delivery behaviour is undocumented

**The Problem:** You can't tell whether a missed `order.payments.added` will be resent.

**Why It Happens:** The POS API's webhook pages don't describe retries, a timeout or which status codes count as success. The Connector API's FAQ does describe its own behaviour, but it covers a different product.

**Workarounds:**

* Respond quickly with `200` and process afterwards.
* Reconcile open orders and bookings through the POS API after an outage.

**How Hookdeck Can Help:** Hookdeck accepts each message immediately and applies a retry policy you define to your own service.

### Undocumented secret source

**The Problem:** It isn't clear where the signing key comes from.

**Why It Happens:** The security page refers to "your secret key" without describing how it's issued.

**Workarounds:**

* Check the Create webhook endpoint response and your integration settings.
* Ask your Mews contact, and record which key belongs to which endpoint.

**How Hookdeck Can Help:** Once the key is set on a Hookdeck source, failed verifications appear in the request log, which quickly shows whether you have the right key.

### Messages may contain several events

**The Problem:** Code that reads one `meta.eventType` per request can miss events.

**Why It Happens:** Mews says a message can contain all simultaneous events for subscribed entities, but every example shows a single event.

**Workarounds:**

* Handle both a single object and an array, as the example does.
* Log a few production messages at a busy time to confirm the real shape.

**How Hookdeck Can Help:** Hookdeck keeps each message in full, so you can see exactly how Mews batched events during service.

### Events carry state, not detail

**The Problem:** An `order.status.updated` tells you the new status but not the order's items or customer.

**Why It Happens:** Each event includes the entity ID and changed attributes only.

**Workarounds:**

* Fetch the order or booking from the POS API in a worker.
* Cache product and order data to limit API calls.

**How Hookdeck Can Help:** Hookdeck transformations can add routing information, and filters can send events for each entity type to the service that needs them.

## Best practices

### Route on `meta.eventType`

Several events share `data.type`. Use the event type to choose a handler.

### Ignore stale updates

Compare `attributes.updatedAt` with the stored value before applying a new status, which also absorbs duplicates. The [webhook idempotency guide](/webhooks/guides/implement-webhook-idempotency) covers the storage side.

### Keep the response fast

A busy service produces bursts of order events. Store them and reply, then process them in the background, as described in [our guide to asynchronous webhook processing](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

### Don't confuse the two Mews APIs

Connector API webhooks aren't signed with `X-Signature`, so a POS verifier won't work for them, and the reverse.

## Conclusion

Mews POS webhooks report booking, order, payment and product availability changes, signed with a base64 HMAC-SHA256 in `X-Signature`. Verify the raw body, route on `meta.eventType`, be ready for several events in one message, and fetch details from the API when the event's attributes aren't enough. Because the POS API doesn't document its retries, plan your own recovery path.

[Hookdeck Event Gateway](https://hookdeck.com) gives Mews POS events a durable record and a retry policy you control.

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