Agent skill
Tremendous Webhooks Skill
Receive and verify Tremendous webhooks. Use when setting up Tremendous webhook handlers, debugging Tremendous-Webhook-Signature verification, or handling rewards and payouts events like REWARDS.DELIVERY.SUCCEEDED, REWARDS.DELIVERY.FAILED, ORDERS.APPROVED, and CAMPAIGNS.CREATED.
Install this skill
npx skills add hookdeck/webhook-skills --skill tremendous-webhooks
When to Use This Skill
- How do I receive Tremendous webhooks?
- How do I verify the
Tremendous-Webhook-Signatureheader? - Why is my Tremendous webhook signature verification failing?
- How do I handle
REWARDS.DELIVERY.SUCCEEDEDandREWARDS.DELIVERY.FAILEDevents? - How do I get the webhook
private_keyfor a Tremendous webhook? - How do I handle
ORDERS.APPROVED,ORDERS.FAILED, orFUNDING_SOURCES.FUNDEDevents?
Verification (core)
Tremendous signs the raw request body with HMAC-SHA256, keyed with the webhook's private_key, and sends the hex digest in Tremendous-Webhook-Signature as sha256=<hex>. The prefix is always present: require it and strip it before comparing. There is no timestamp or message ID in the signed content. Read the raw body (any content type) and compare in constant time.
Node:
const crypto = require('crypto');
function verify(rawBody, signatureHeader, privateKey) {
if (!signatureHeader || !signatureHeader.startsWith('sha256=')) return false;
const sig = signatureHeader.slice('sha256='.length);
// Check hex shape first: Buffer.from(x, 'hex') silently truncates invalid input
if (!/^[0-9a-f]{64}$/i.test(sig)) return false;
const expected = crypto.createHmac('sha256', privateKey).update(rawBody).digest();
return crypto.timingSafeEqual(Buffer.from(sig, 'hex'), expected);
}
Python:
import hmac, hashlib
def verify(raw_body: bytes, signature_header: str, private_key: str) -> bool:
if not signature_header or not signature_header.startswith("sha256="):
return False
sig = signature_header[len("sha256="):].lower()
expected = hmac.new(private_key.encode(), raw_body, hashlib.sha256).hexdigest()
# Compare bytes so a non-ASCII header returns False instead of raising
return hmac.compare_digest(sig.encode(), expected.encode())
In Express use express.raw({ type: '*/*' }) on the route so verification works whatever Content-Type arrives. Tremendous has no SDK helper for webhook verification, so verification is manual in every framework.
For complete handlers with route wiring, event dispatch, and tests, see:
Common Event Types
Events are uppercase and dotted. The payload's event field carries the name:
| Event | Description |
|---|---|
REWARDS.DELIVERY.SUCCEEDED | Tremendous received confirmation the reward email was delivered (not sent for LINK rewards) |
REWARDS.DELIVERY.FAILED | Reward delivery failed, usually an email bounce (payload.meta has the failure details) |
REWARDS.CANCELED | A reward was canceled |
REWARDS.FLAGGED | A reward was flagged for fraud review |
ORDERS.CREATED | An order was created (payload.meta holds the Order) |
ORDERS.APPROVED | An admin approved an order (only fires when order approvals are on) |
ORDERS.CANCELED | An order held for review was rejected |
ORDERS.FAILED | An order failed, typically a declined card |
CAMPAIGNS.CREATED | A campaign was created in the UI |
FUNDING_SOURCES.FUNDED | Funds were added and made available on your balance |
INVOICES.PAID | An invoice was paid |
Tremendous sends every event to the one receiver registered for your organization, so filter in your handler. The full list (36 events across campaigns, connected organizations, funding sources, invoices, members, orders, products, rewards, fraud reviews, reports and topups) is in references/overview.md.
Important Headers and Payload
| Item | Description |
|---|---|
Tremendous-Webhook-Signature | sha256=<hex> HMAC-SHA256 of the raw body |
event | Event name, e.g. ORDERS.APPROVED |
uuid | Unique ID of this webhook; use it to dedupe retries |
payload.resource | { id, type } of the resource the event refers to |
Environment Variables
TREMENDOUS_WEBHOOK_PRIVATE_KEY=your_webhook_private_key # returned when you create or retrieve the webhook via the API
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 tremendous --path /webhooks/tremendous
Reference Materials
- references/overview.md - Tremendous webhook concepts, events, payloads, retries
- references/setup.md - Create the webhook via the API and get the private key
- references/verification.md - Signature verification details and gotchas