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-Signature header?
  • Why is my Mews POS webhook signature verification failing?
  • How do I handle booking.status.updated or order.payments.added events?

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.

EventDescription
booking.status.updatedA booking's status changed (for example to seated)
order.state.updatedAn order's state changed
order.status.updatedAn order's status changed
order.total.updatedAn order's total changed
order.payments.addedA payment was added to an order
product.availability.updatedA 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 2xx quickly 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


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 12, 2026

View on GitHub →