# Rewardful Webhooks

Rewardful (affiliate and referral tracking for Stripe SaaS) sends a JSON `POST` to each
endpoint you add on the dashboard's Webhooks page when affiliates, links, coupons, referrals,
sales, commissions or payouts change. Each request carries an `X-Rewardful-Signature` header:
the lowercase hex HMAC-SHA256 of the **raw request body**, keyed with that endpoint's Signing
Secret.

## When to Use This Skill

* How do I receive Rewardful webhooks?
* How do I verify the `X-Rewardful-Signature` header?
* How do I handle `affiliate.confirmed`, `referral.converted`, `commission.created` or `payout.paid` events?
* Why does my Rewardful webhook signature verification fail (401)?
* Why does Rewardful keep retrying my webhook?

## Verification (core)

Header: `X-Rewardful-Signature: <64 hex chars>`, no prefix. Signed content: the raw body bytes
only. No timestamp, so there is no replay window to check. Key: the endpoint's Signing Secret as
UTF-8 bytes, used as-is (not base64- or hex-decoded).

```javascript
const crypto = require('crypto');

function verifyRewardfulSignature(rawBody, signature, secret = process.env.REWARDFUL_WEBHOOK_SECRET) {
  if (!secret || !Buffer.isBuffer(rawBody)) return false;
  // Validate before decoding: Buffer.from(x, 'hex') silently drops bad characters.
  if (typeof signature !== 'string' || !/^[0-9a-fA-F]{64}$/.test(signature)) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest();
  return crypto.timingSafeEqual(expected, Buffer.from(signature, 'hex')); // equal length: 32 bytes
}

```

* Sign the **raw bytes** (`express.raw({ type: '*/*' })`, `await request.arrayBuffer()`,
  `await request.body()`); parsing and re-serializing the JSON breaks the signature.
* Missing, malformed or mismatched signature: `401`. Bad JSON after a valid signature: `400`.
* Return `200` to acknowledge. Rewardful counts only `200` as success; any other status is
  retried with exponential backoff for 3 days, then dropped.
* In Python use `hmac.compare_digest` on **bytes** (`bytes.fromhex` after a `[0-9a-fA-F]{64}` check).

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/rewardful-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/rewardful-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/rewardful-webhooks/examples/fastapi/).

## Event Types

Dispatch on the body's `event.type` (`<object>.<action>`); no header names the event. The body is
`{ object, event: { id, type, created_at, api_version }, request: { id } }`. Dedupe on `event.id`.

| Event | Sent When |
| --- | --- |
| `affiliate.created` | An affiliate signed up or was created in the dashboard or API |
| `affiliate.confirmed` | An affiliate confirmed their email address |
| `referral.lead` | A referral became a lead |
| `referral.converted` | A referral became a paying customer |
| `sale.created` | A sale was created |
| `sale.refunded` | A sale was refunded |
| `commission.created` | A commission was created |
| `commission.paid` | A commission was paid |
| `payout.due` | A payout is due |
| `payout.paid` | A payout was paid |
| `payout.failed` | A payout failed |

Rewardful documents 31 types in total (`affiliate_link.*`, `affiliate_coupon.*`, `sale.*`,
`commission.*`, `payout.*` and more); see [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/rewardful-webhooks/references/overview.md).

## Environment Variables

```bash
REWARDFUL_WEBHOOK_SECRET=your_endpoint_signing_secret   # Webhooks page in the Rewardful dashboard

```

## Local Development

Rewardful can't reach `localhost`. Tunnel with the Hookdeck CLI (no account required):

```bash
npx hookdeck-cli listen 3000 rewardful --path /webhooks/rewardful

```

Use the printed URL as the endpoint URL in Rewardful.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/rewardful-webhooks/references/overview.md) - What the webhooks are, all event types, payload and delivery
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/rewardful-webhooks/references/setup.md) - Add an endpoint and find the Signing Secret
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/rewardful-webhooks/references/verification.md) - Signature details, gotchas, debugging 401s
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/rewardful-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests