Agent skill

Violet Webhooks Skill

Receive and verify Violet webhooks. Use when setting up Violet (violet.io) webhook handlers, debugging X-Violet-Hmac signature verification, or handling events like ORDER_COMPLETED, ORDER_SHIPPED, MERCHANT_CONNECTED, or TRANSFER_SENT.

Install this skill

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


When to Use This Skill

  • How do I receive Violet webhooks?
  • How do I verify the X-Violet-Hmac signature on a Violet webhook?
  • Why is my Violet webhook signature verification failing?
  • How do I handle ORDER_COMPLETED, ORDER_SHIPPED, MERCHANT_CONNECTED or TRANSFER_SENT events?
  • Why did Violet pause or disable my webhook?

Verification (core)

Violet sends X-Violet-Hmac: <base64>: the standard, padded base64 of the HMAC-SHA256 of the raw request body bytes, keyed with your app secret (the same secret you use for API calls) used as UTF-8 — not hex- or base64-decoded. Nothing else is signed (no timestamp or id), and the value has no prefix. Check the header's format, then compare the whole value in constant time.

Node:

const crypto = require('crypto');

function verifyVioletSignature(rawBody, header, secret) {
  // 44 chars of padded base64: validate before comparing (never Buffer.from(header, 'base64'))
  if (typeof header !== 'string' || !/^[A-Za-z0-9+/]{43}=$/.test(header)) return false;
  const expected = Buffer.from(crypto.createHmac('sha256', secret).update(rawBody).digest('base64'));
  const received = Buffer.from(header);
  return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}

Python:

import base64, hashlib, hmac, re

def verify_violet_signature(raw_body: bytes, header: str | None, secret: str) -> bool:
    if not header or not re.fullmatch(r"[A-Za-z0-9+/]{43}=", header, re.ASCII):
        return False
    digest = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).digest()
    return hmac.compare_digest(header.encode("ascii"), base64.b64encode(digest))

Respond 401 when the header is missing, malformed, or doesn't match. Compare the base64 string exactly: a value whose last character carries stray low bits decodes to the same bytes but is not what Violet sends, and Hookdeck's Violet source rejects it too.

For complete handlers with route wiring, event dispatch, and tests, see:

Dispatch on X-Violet-Topic

The body is the entity itself (a full Order, an Offer, a PayoutAccount, a transfer...) with no event field, and all ORDER_* events send an Order. Only the X-Violet-Topic header names the event, so a request without it can't be classified. Parse the body only after verification, and treat every field as untrusted (check types before reading into it).

Common Event Types (Topics)

TopicDescription
ORDER_ACCEPTEDOrder accepted
ORDER_UPDATEDOrder changed
ORDER_COMPLETEDOrder completed
ORDER_SHIPPEDOrder shipped
ORDER_DELIVEREDOrder delivered
ORDER_REFUNDEDOrder refunded
ORDER_CANCELLEDOrder cancelled
ORDER_FAILEDOrder failed
MERCHANT_CONNECTEDMerchant connected to your app
MERCHANT_DISCONNECTEDMerchant disconnected
MERCHANT_ENABLED / MERCHANT_DISABLEDMerchant enabled or disabled
MERCHANT_NEEDS_ATTENTIONMerchant health check needs attention
OFFER_ADDED / OFFER_UPDATED / OFFER_REMOVED / OFFER_DELETEDOffer availability and changes
PRODUCT_SYNC_COMPLETED / PRODUCT_SYNC_FAILEDCatalog sync results
TRANSFER_SENT / TRANSFER_FAILED / TRANSFER_REVERSEDPayouts to merchants

For the full list of 52 events, see references/overview.md and the Violet webhook events docs.

Delivery and Retries

  • Respond 2XX within 10 seconds; queue the work and process it afterwards
  • A failed event is retried 10 times with back-off over 24 hours, then marked FAILED
  • More than 50 failures in 30 minutes pauses the webhook for 1 hour, then 3 hours, then up to 24 hours; if it still fails after three pauses the webhook is DISABLED until you re-activate it
  • X-Violet-Event-Id identifies the event: use it for idempotency. X-Violet-Event-Id-Source marks a resend

Environment Variables

VIOLET_WEBHOOK_SECRET=your_violet_app_secret   # The app secret, used as UTF-8 bytes

Local Development

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

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 13, 2026

View on GitHub →