Skip to main content

Set Up Affiliate Event Webhooks

Configure AffiliateBase event webhooks and check delivery status. Find developer guidance for signatures, payloads, and manual retry.

Table of Contents

Outbound webhooks notify your own systems when something happens in your affiliate program. This guide is for merchants and developers who want to create a webhook endpoint in AffiliateBase, verify its signature, and check delivery status.

Use them when you want to:

  • create or update affiliate records in your CRM
  • notify your team when an affiliate is approved
  • sync referrals and conversions into a warehouse
  • trigger internal commission review or payout workflows
  • keep another system up to date without polling the REST API

Create a webhook endpoint

  1. Open AffiliateBase.
  2. Go to Settings -> Webhooks.
  3. In Create webhook endpoint, enter an Endpoint name, such as Production CRM.
  4. Enter an Endpoint URL. Production endpoints must use HTTPS.
  5. Under Events, select the event types you want to receive. affiliate.created and commission.created are selected by default.
  6. Click Create endpoint.
  7. Copy the signing secret from the Signing secret panel at the top of the page. AffiliateBase shows it only once.

To remove an endpoint, click Delete in the Webhook endpoints table. Delivery history stays available.

Start with a test or staging endpoint. Create the production endpoint after your receiver validates signatures and returns successful responses.

Available events

AffiliateBase sends events for these affiliate program changes:

EventWhen it fires
affiliate.createdAn affiliate record is created
affiliate.approvedAn affiliate moves into the active state
affiliate.bannedAn affiliate is banned (once for each affiliate record of the person)
affiliate.unbannedA ban is lifted and the affiliate moves to disabled
referral.createdA referral is created from tracking activity
referral.convertedA referral reaches conversion state
commission.createdA commission is created from a sale
commission.dueA commission moves into due state
commission.voidedA commission is voided
payout.paidA payout is marked paid. Sent for each payment, so a payout that Stripe returned and that is paid again sends it again
payout.failedStripe could not pay a payout (it failed, was canceled, was returned, or its Stripe transfer was fully reversed). The payout is due again so you can retry it

Your endpoint receives only the events it subscribes to.

Test delivery

After you create an endpoint:

  1. Go to Settings -> Webhooks and click the endpoint name in the Webhook endpoints table.
  2. Click Send test.
  3. Check your receiver logs.
  4. Confirm your endpoint returns a 2xx status code.

A test event uses the first event type listed for the endpoint, and its payload is test: true plus the endpoint and account IDs rather than a real affiliate or commission record. Send test delivers only to the endpoint you opened.

Payload shape

Webhook requests are JSON.

JSON
{
  "id": "webhook_event_id",
  "type": "commission.created",
  "occurred_at": "2026-05-29T14:21:12Z",
  "data": {
    "id": "commission_id",
    "state": "pending",
    "amount_cents": 1000,
    "currency": "USD",
    "affiliate_id": "affiliate_id",
    "campaign_id": "campaign_id",
    "sale_id": "sale_id",
    "sale_customer_email": "customer@example.com",
    "sale_customer_name": "Customer Person",
    "sale_customer_first_name": "Customer",
    "sale_customer_last_name": "Person",
    "sale": {
      "id": "sale_id",
      "customer": {
        "email": "customer@example.com",
        "name": "Customer Person",
        "first_name": "Customer",
        "last_name": "Person"
      }
    },
    "created_at": "2026-05-29T14:21:12Z"
  }
}

Money amounts are in cents. Timestamps use ISO 8601.

Enrich webhook events with the REST API

Webhook payloads are compact. For example, commission.created includes the commission ID as data.id, plus fields such as affiliate_id, campaign_id, sale_id, amount, currency, state, and sale customer name/email fields when AffiliateBase has received them.

When your automation needs more customer, referral, or affiliate detail, use the webhook as the trigger, then call the REST API with the ID from the payload.

For a CRM or automation workflow:

  1. Trigger the workflow from an AffiliateBase commission.created webhook.
  2. Read the commission ID from data.id.
  3. Add an HTTP request action in your automation tool or integration code.
  4. Request:
GET https://app.affiliatebase.io/api/v1/commissions/{commission_id}?account_id={account_id}
Authorization: Bearer {api_key}

GET /api/v1/commissions/:id returns the commission object directly, with the campaign and sale expanded. The sale includes customer, referral, and affiliate details. Useful fields include:

  • sale.referral.customer.email
  • sale.referral.customer.name
  • sale.referral.customer.first_name
  • sale.referral.customer.last_name
  • sale.referral.stripe_customer_id
  • sale.affiliate.id
  • sale.affiliate.email
  • sale.affiliate.first_name
  • sale.affiliate.last_name

Customer email is available when AffiliateBase receives it from Stripe or your conversion tracking flow. Customer name is included when available. Phone is not a standard AffiliateBase customer field, so get phone numbers from Stripe or your CRM.

Signed requests

AffiliateBase signs each webhook request with Svix-style headers:

  • svix-id: the delivery ID. It stays the same when you retry that delivery.
  • svix-timestamp: the Unix time of the attempt, in seconds.
  • svix-signature: v1, followed by the base64 HMAC signature.

Verify the signature before processing the event.

The signed content is:

svix-id.svix-timestamp.raw_request_body

The signature uses HMAC SHA-256, keyed with your endpoint signing secret (the full whsec_... string as shown, not decoded).

Example verification in Node.js

JavaScript
import crypto from "node:crypto";

function verifyAffiliateBaseWebhook({ rawBody, headers, signingSecret }) {
  const messageId = headers["svix-id"];
  const timestamp = headers["svix-timestamp"];
  const signatureHeader = headers["svix-signature"] || "";
  const expected = crypto
    .createHmac("sha256", signingSecret)
    .update(`${messageId}.${timestamp}.${rawBody}`)
    .digest("base64");

  const received = signatureHeader.replace(/^v1,/, "");

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(received)
  );
}

Use the raw request body exactly as received. If you parse and re-stringify the JSON before verification, the signature input changes.

Delivery logs and retry

AffiliateBase records every delivery attempt. Open an endpoint from Settings -> Webhooks to see its Recent deliveries table (the latest 50), with these columns:

  • Event: the event type and when it occurred
  • State: Pending, In Progress, Delivered, Failed, or Canceled
  • Attempts: the number of delivery attempts
  • Last result: the last HTTP status, an error code such as timeout, or Queued

AffiliateBase does not retry failed deliveries automatically. Retry appears on deliveries in the Failed state. Fix the receiver, then click Retry.

After repeated failed deliveries, AffiliateBase disables the endpoint, and it stops receiving events. Send test doesn’t reach a disabled endpoint, and Enable alone doesn’t clear its failure count. To recover:

  1. Fix the receiver.
  2. Click Retry on one failed delivery. This works while the endpoint is disabled, and a successful delivery clears the failure count.
  3. Click Enable at the top of the endpoint page. The button appears only while the endpoint is disabled.
  4. Click Retry on any other failed deliveries you still need.

Events that happen while the endpoint is disabled aren’t saved for it and can’t be retried. Use the REST API to catch up on anything that changed in that time.

Receiver requirements

Your endpoint should:

  • accept POST requests with Content-Type: application/json
  • verify the signature before doing work
  • return a 2xx status code after the event is accepted
  • process events idempotently by the payload id, which identifies the event and stays the same on retries
  • finish quickly and hand off slow work to a background job

AffiliateBase treats non-2xx responses as failed deliveries.

Security notes

  • Use HTTPS for production endpoints.
  • Store the signing secret in your server-side secrets manager.
  • Do not expose the signing secret in client-side code.
  • Reject requests with missing or invalid signatures.
  • Keep a replay window for svix-timestamp, such as five minutes.
  • Make your receiver idempotent so retries do not duplicate work.