# 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:

```javascript
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:

```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/](https://github.com/hookdeck/webhook-skills/tree/main/skills/fourthwall-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/fourthwall-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/fourthwall-webhooks/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`:

```json
{
  "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_PLACED` | New order placed and paid |
| `ORDER_UPDATED` | Order status, shipping address or email changed |
| `GIFT_PURCHASE` | Gift card purchased |
| `DONATION` | New donation received |
| `SUBSCRIPTION_PURCHASED` | New membership subscription |
| `SUBSCRIPTION_CHANGED` | Membership changed (e.g. tier change) |
| `SUBSCRIPTION_EXPIRED` | Membership expired |
| `PRODUCT_CREATED` | Product created |
| `PRODUCT_UPDATED` | Product updated |

See [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/fourthwall-webhooks/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

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

```

## Local Development

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

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/fourthwall-webhooks/references/overview.md) - Fourthwall webhook concepts, event types, payload
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/fourthwall-webhooks/references/setup.md) - Dashboard and API configuration, secret key
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/fourthwall-webhooks/references/verification.md) - Signature verification details and gotchas