Agent skill

Felloh Webhooks Skill

Receive and verify Felloh webhooks. Use when setting up a Felloh webhook endpoint, debugging X-Signature HMAC-SHA256 verification, or handling transaction status, refund status, booking and user events for UK travel payments.

Install this skill

npx skills add hookdeck/webhook-skills --skill felloh-webhooks


Felloh, payments and trust-account infrastructure for UK travel businesses, sends a JSON webhook to your endpoint when a transaction changes status, a refund changes status, a booking is created or a user is created. Each request is signed: the X-Signature header holds the hex HMAC-SHA256 of the raw body, keyed with the signing key shown when you create the webhook.

When to Use This Skill

  • How do I receive Felloh webhooks?
  • How do I verify the Felloh X-Signature header?
  • How do I tell transaction, refund, booking and user deliveries apart? (There is no event-name field)
  • Why does my Felloh webhook signature verification fail?
  • How do I handle Felloh test deliveries (X-Felloh-Test)?

Verification (core)

const crypto = require('crypto');

const HEX_SHA256 = /^[0-9a-fA-F]{64}$/;

// rawBody: the Buffer exactly as received. secret: the signing key, used as UTF-8 bytes.
function verifyFellohSignature(rawBody, signature, secret) {
  if (!secret || typeof signature !== 'string' || !HEX_SHA256.test(signature)) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest();
  return crypto.timingSafeEqual(Buffer.from(signature, 'hex'), expected); // validated above
}
import hashlib, hmac, re

def verify_felloh_signature(raw_body: bytes, signature: str | None, secret: str) -> bool:
    if not secret or not signature or not re.fullmatch(r"[0-9a-fA-F]{64}", signature):
        return False
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature.lower().encode(), expected.encode())
  • Header: X-Signature. The whole value is the bare hex digest: no sha256= prefix, no list, no timestamp.
  • Signed content: the raw request body bytes only. Don't parse or re-serialise the JSON first (express.raw({ type: '*/*' }), Next.js await request.arrayBuffer(), FastAPI await request.body()).
  • Key: the signing key as UTF-8 bytes, used as-is (not hex- or base64-decoded). It is shown once when you create the webhook.
  • Check the format (exactly 64 hex characters) before decoding, then compare in constant time. Hex is case-insensitive.
  • Missing, empty, malformed or mismatched signature: 401.
  • Nothing time-based is signed, so there is no replay window to check. Make handlers idempotent instead.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Event Types

Felloh sends no event-name field or header. The events are chosen when you register the webhook, and you tell deliveries apart by payload shape. The names below are labels used by this skill (and Hookdeck's sample payloads), not values on the wire.

Event (label)Payload shapeRetried?
transaction-statustransaction.id plus a string status (for example COMPLETE), amount, currency, booking, providerYes: up to 5 times, about a minute apart
refund-statusauthorisation_code, status object (status.id, for example PENDING_AUTHORISATION), transaction objectNo
bookingbooking object (id, booking_reference), customer_name, departure_date, return_dateNo
user-createuser object (id), email, first_name, last_name, organisationNo

Dashboard test deliveries carry X-Felloh-Test: true, are signed with the real key and are never retried.

Important Headers

HeaderDescription
X-SignatureHex HMAC-SHA256 of the raw body
X-Felloh-Testtrue on dashboard test deliveries

Delivery

Any non-2xx response is a failure. Timeout is 30 seconds. Only transaction status events are retried; refund, booking and user events are sent once, so return 2xx quickly and process asynchronously. Felloh keeps delivery logs for 30 days.

Environment Variables

FELLOH_WEBHOOK_SECRET=your_signing_key   # shown once when you create the webhook

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 felloh --path /webhooks/felloh

Use the printed URL as the webhook URL in Felloh.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 12, 2026

View on GitHub →