Reference
Webhooks to your systems
Signed events Referralmeter sends you, on Pro and Growth.
Referralmeter can tell your own systems when something happens. Add an endpoint under Webhooks in the dashboard, on Pro or Growth. You can have 5. Your plan lapsing pauses delivery; it doesn't delete anything.
Events
| Event | When |
|---|---|
affiliate.applied | Someone applied through your portal |
affiliate.approved | An affiliate was approved |
referral.created | An affiliate's visitor signed up or bought for the first time |
customer.converted | A referred customer made their first payment |
commission.created | A payment earned a commission |
commission.approved | A commission became payable |
commission.reversed | A refund or dispute clawed a commission back |
payout.created | A payout batch was created |
payout.paid | A payout batch was marked paid |
verification.passed | An install verification passed |
Verification test traffic only ever sends verification.passed. Events carry livemode where it applies, so you can tell provider test mode apart.
The request
{
"id": "9f1c2b9e-…",
"type": "commission.created",
"created_at": "2026-11-06T12:00:00.000Z",
"data": {
"commission_id": "…",
"affiliate_id": "…",
"affiliate_code": "maya",
"referral_id": "…",
"amount_cents": 1470,
"currency": "usd",
"status": "pending",
"rate_label": "30% recurring",
"livemode": true
}
}Affiliate events include the affiliate's name and email, and customer events the customer's email and your user id. Events never include payout details, tax forms or IP data. id is the same on every retry, so use it to ignore duplicates.
Check the signature
Every request has a Referralmeter-Signature header: t=<unix seconds>,v1=<hex>. v1 is an HMAC-SHA256 of <t>.<raw body> with your endpoint's signing secret, which is shown once, when you add it. Check it against the raw body, and refuse old timestamps so a captured request can't be replayed.
import { createHmac, timingSafeEqual } from "node:crypto";
function verified(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const want = createHmac("sha256", secret).update(t + "." + rawBody).digest();
const got = Buffer.from(v1 ?? "", "hex");
return got.length === want.length && timingSafeEqual(got, want);
}Delivery
Answer with any 2xx within 5 seconds. A failed delivery is tried six times over about 40 hours, then marked failed; retries run hourly, so a retry can come later than its schedule. The dashboard lists recent deliveries with what your endpoint answered, for 30 days, and Send test posts a webhook.test event on demand.
Endpoints must be public https on the standard port. Requests are never sent to private addresses, checked again on each send, and redirects are not followed. The event body is deleted from our side once a delivery ends.