Agent skill

Fourthwall Webhooks Skill

Receive and verify Fourthwall webhooks. Use when setting up Fourthwall shop webhook handlers, debugging X-Fourthwall-Hmac-SHA256 signature verification, or handling creator commerce events like ORDER_PLACED, DONATION, GIFT_PURCHASE, or SUBSCRIPTION_PURCHASED.

Install this skill

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


When to Use This Skill

  • How do I receive Fourthwall webhooks?
  • How do I verify the X-Fourthwall-Hmac-SHA256 signature?
  • Why is my Fourthwall webhook signature verification failing?
  • How do I handle ORDER_PLACED, DONATION, GIFT_PURCHASE or SUBSCRIPTION_PURCHASED events?
  • How do I find my Fourthwall webhook secret key?

Verification (core)

Fourthwall signs the entire raw request body with HMAC-SHA256, keyed with your shop's webhook secret key (a UUID-like string, used as its UTF-8 text), and sends the digest base64-encoded in the X-Fourthwall-Hmac-SHA256 header. There is no timestamp and no signed prefix: the signed content is just the body. Use the raw bytes, never re-serialized JSON, and compare timing-safe.

Node:

const crypto = require('crypto');

function verify(rawBody, signatureHeader, secret) {
  if (!signatureHeader) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('base64');
  try {
    return crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
  } catch {
    return false; // different lengths
  }
}

Python:

import base64, hashlib, hmac

def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
    if not signature_header:
        return False
    digest = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).digest()
    expected = base64.b64encode(digest).decode("utf-8")
    return hmac.compare_digest(expected.encode(), signature_header.encode())

Shop webhooks only. Platform App webhooks use the same algorithm with the X-Fourthwall-Hmac-Apps-SHA256 header and the HMAC key from the app's settings. This skill covers shop webhooks (X-Fourthwall-Hmac-SHA256).

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

Fourthwall has no official webhook verification SDK, so verification is manual in every framework. In Express, read the body with express.raw({ type: '*/*' }) so the raw bytes are available whatever content type is sent.

Payload Shape

Every event shares one envelope; the event-specific object is in data:

{
  "testMode": false,
  "id": "weve_geAva6c1RAuyb9HQxbSlmA",
  "webhookId": "wcon_P-VkRfmJTBaC6_Tst22cew",
  "shopId": "sh_7ad0c438-beda-4779-a885-0dc325a755c1",
  "type": "ORDER_PLACED",
  "apiVersion": "V1_BETA",
  "createdAt": "2023-07-12T15:05:11.078089+00:00",
  "data": {}
}

Dispatch on the body's type field (there is no event-type header). Treat a repeated id as a duplicate: delivery is at-least-once and ordering is not guaranteed. Events sent by the dashboard's Send test notification button have testMode: true.

Common Event Types

Event (type)Description
ORDER_PLACEDNew order placed and paid
ORDER_UPDATEDOrder status, shipping address or email changed
GIFT_PURCHASEGift card purchased
DONATIONNew donation received
SUBSCRIPTION_PURCHASEDNew membership subscription
SUBSCRIPTION_CHANGEDMembership changed (e.g. tier change)
SUBSCRIPTION_EXPIREDMembership expired
PRODUCT_CREATEDProduct created
PRODUCT_UPDATEDProduct updated

See references/overview.md for the full list of 21 event types.

Response Requirements

Respond 200 OK within 2 seconds; any other status or a slow response counts as a failed delivery and is retried. Do slow work asynchronously.

Environment Variables

FOURTHWALL_WEBHOOK_SECRET=your_shop_webhook_secret_key   # Dashboard > Settings > For developers > Webhooks

Local Development

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

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 4, 2026

View on GitHub →