Webhooks Overview

Receive real-time notifications when events occur in your affiliate program.

What are webhooks?

Webhooks let your application receive real-time HTTP POST notifications when events happen in your Anderro affiliate program - such as when an affiliate signs up through a referral link or when a commission is generated.

Setup

  1. Go to your product's settings in the Anderro dashboard
  2. Navigate to the Webhooks tab
  3. Enter your endpoint URL (must be HTTPS)
  4. Select the events you want to receive
  5. Save - Anderro will generate a webhook secret for signature verification

Webhook payload

All webhook deliveries are HTTP POST requests with a JSON body:

json
{
  "id": "evt_cm1abc2def3",
  "event": "affiliate.signup",
  "created_at": "2026-04-08T12:00:00.000Z",
  "data": {
    "customer_email": "[email protected]",
    "affiliate": {
      "email": "[email protected]",
      "name": "Jane Smith",
      "referral_code": "jane-smith"
    },
    "product": {
      "id": "cuid_abc123",
      "name": "My SaaS"
    }
  }
}

Security headers

Every webhook request includes two headers for signature verification:

HeaderDescription
x-webhook-signatureHMAC-SHA256 signature of the payload
x-webhook-timestampUnix timestamp when the webhook was sent

Verifying signatures

To verify a webhook is genuinely from Anderro, compute HMAC-SHA256 over the timestamp, a dot, and the raw request bytes, then compare it with the header value. Do not re-serialize a parsed object: whitespace, escaping, or key order changes will invalidate the signature. In Express, register this route before any express.json() middleware:

javascript
import express from "express";
import { createHmac, timingSafeEqual } from "crypto";

const app = express();
const WEBHOOK_SECRET = process.env.ANDERRO_WEBHOOK_SECRET;

function verifyWebhook(rawBody, signature, timestamp, secret) {
  if (typeof signature !== "string" || !/^[a-f0-9]{64}$/i.test(signature)) {
    return false;
  }
  if (typeof timestamp !== "string" || !/^\d+$/.test(timestamp)) {
    return false;
  }
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
    return false;
  }
  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.`)
    .update(rawBody)
    .digest();

  return timingSafeEqual(expected, Buffer.from(signature, "hex"));
}

app.post("/webhooks/anderro", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-webhook-signature"];
  const timestamp = req.headers["x-webhook-timestamp"];

  if (!verifyWebhook(req.body, signature, timestamp, WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }

  // Parse only after verifying the original bytes.
  const event = JSON.parse(req.body.toString("utf8"));
  console.log(`Received ${event.event}:`, event.data);

  res.status(200).send("OK");
});

// Register JSON parsing for other routes after the webhook route.
app.use(express.json());

Timestamp validation

For additional security, verify that the timestamp is within an acceptable window (e.g., 5 minutes) to prevent replay attacks.

Delivery behavior

  • Timeout: Anderro waits up to 5 seconds for your endpoint to respond
  • Expected response: Return any 2xx status code to acknowledge receipt
  • Retries: When automatic retries are enabled, timeouts, connection errors, 5xx, 408, and 429 responses are retried after 1 minute, 5 minutes, 30 minutes, 2 hours, and 12 hours, for 6 attempts total. These delays are measured from the preceding attempt; delivery may take slightly longer. Other 4xx responses and invalid or blocked URLs are not retried
  • Deduplication: The x-webhook-id header equals the payload id and stays constant across attempts. Retries send the same payload with a fresh timestamp and signature; use the ID to avoid processing an event twice
  • Delivery log: The dashboard's Webhooks tab shows each delivery and its attempts, including the attempt count and next scheduled attempt

Test webhooks

You can send test webhooks from the dashboard to verify your endpoint is working correctly. Test events include a top-level "sandbox": true field in the payload so you can distinguish them from real events.

Available events

Tracking events

EventDescription
affiliate.signupA customer clicked an affiliate's referral link and signed up
affiliate.paymentA referred customer made a payment; the commission may be null

Affiliate lifecycle events

EventDescription
affiliate.approvedA pending affiliate was approved to join your program
affiliate.rejectedAn affiliate application was rejected
affiliate.removedAn affiliate was removed from your program

Commission & payout events

EventDescription
commission.createdA new commission was generated from a referred payment
commission.refundedA commission was voided or reduced because the underlying payment was refunded
commission.restoredA dispute clawback was reversed after the merchant won the dispute
payout.createdA new payout was generated for an affiliate
payout.completedA payout was marked as paid

Opt-in events

New event types are not automatically enabled. To receive new events, go to your product's Webhooks tab in the dashboard and select the events you want to subscribe to.