Agent skill
Mews POS Webhooks Skill
Receive and verify Mews POS webhooks. Use when setting up Mews POS API webhook handlers, debugging X-Signature verification failures, or handling hospitality point-of-sale events like booking.status.updated, order.status.updated, order.payments.added, or product.availability.updated.
Install this skill
npx skills add hookdeck/webhook-skills --skill mews-pos-webhooks
When to Use This Skill
- How do I receive Mews POS webhooks?
- How do I verify the Mews POS
X-Signatureheader? - Why is my Mews POS webhook signature verification failing?
- How do I handle
booking.status.updatedororder.payments.addedevents?
This skill covers the Mews POS API (restaurant and bar point of sale). Only the POS API signs webhooks; the Mews Connector (PMS) API is a different product.
Verification (core)
Every request carries X-Signature: the standard Base64 (padded, 44 characters, no prefix) of HMAC-SHA256 over the raw request body bytes, keyed with your secret key as UTF-8 bytes. Nothing else is signed (no timestamp or id), so there is no replay window to check. Validate the header's format before comparing, and compare the whole string in constant time.
Node:
const crypto = require('crypto');
function verify(rawBody /* Buffer */, signature, secret) {
if (!secret || typeof signature !== 'string' || !/^[A-Za-z0-9+/]{43}=$/.test(signature)) return false;
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('base64');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected)); // both 44 bytes
}
Python:
import base64, hashlib, hmac, re
def verify(raw_body: bytes, signature: str | None, secret: str) -> bool:
if not secret or not signature or not re.fullmatch(r"[A-Za-z0-9+/]{43}=", signature):
return False
expected = base64.b64encode(hmac.new(secret.encode(), raw_body, hashlib.sha256).digest())
return hmac.compare_digest(signature.encode("ascii"), expected)
Comparing the Base64 string (not just the decoded bytes) matters: a value whose last character carries stray low bits decodes to the same bytes but is not what Mews sends, and Hookdeck's Mews POS source rejects it. Respond 401 for a missing, malformed or mismatching signature.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Common Event Types
Dispatch on meta.eventType. data.type is the entity (bookings, orders, products) and is shared by several events, so don't key on it.
| Event | Description |
|---|---|
booking.status.updated | A booking's status changed (for example to seated) |
order.state.updated | An order's state changed |
order.status.updated | An order's status changed |
order.total.updated | An order's total changed |
order.payments.added | A payment was added to an order |
product.availability.updated | A product's availability changed |
Full reference: Mews POS webhooks. Retries, timeout and success codes are not documented, and the docs say a message "encapsulates all simultaneous events" without showing a multi-event body, so handlers should return
2xxquickly and tolerate unknown events.
Environment Variables
MEWS_POS_WEBHOOK_SECRET=your_secret_key # The secret key used to sign webhooks, used as-is (UTF-8 bytes)
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 mews-pos --path /webhooks/mews-pos
Reference Materials
- references/overview.md - Mews POS webhook concepts and events
- references/setup.md - Configuration guide
- references/verification.md - Signature verification details