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
- Open AffiliateBase.
- Go to Settings -> Webhooks.
- In Create webhook endpoint, enter an Endpoint name, such as
Production CRM. - Enter an Endpoint URL. Production endpoints must use HTTPS.
- Under Events, select the event types you want to receive.
affiliate.createdandcommission.createdare selected by default. - Click Create endpoint.
- 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:
| Event | When it fires |
|---|---|
affiliate.created | An affiliate record is created |
affiliate.approved | An affiliate moves into the active state |
affiliate.banned | An affiliate is banned (once for each affiliate record of the person) |
affiliate.unbanned | A ban is lifted and the affiliate moves to disabled |
referral.created | A referral is created from tracking activity |
referral.converted | A referral reaches conversion state |
commission.created | A commission is created from a sale |
commission.due | A commission moves into due state |
commission.voided | A commission is voided |
payout.paid | A payout is marked paid. Sent for each payment, so a payout that Stripe returned and that is paid again sends it again |
payout.failed | Stripe 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:
- Go to Settings -> Webhooks and click the endpoint name in the Webhook endpoints table.
- Click Send test.
- Check your receiver logs.
- Confirm your endpoint returns a
2xxstatus 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.
{
"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:
- Trigger the workflow from an AffiliateBase
commission.createdwebhook. - Read the commission ID from
data.id. - Add an HTTP request action in your automation tool or integration code.
- 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.emailsale.referral.customer.namesale.referral.customer.first_namesale.referral.customer.last_namesale.referral.stripe_customer_idsale.affiliate.idsale.affiliate.emailsale.affiliate.first_namesale.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_bodyThe signature uses HMAC SHA-256, keyed with your endpoint signing secret (the full whsec_... string as shown, not decoded).
Example verification in Node.js
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:
- Fix the receiver.
- Click Retry on one failed delivery. This works while the endpoint is disabled, and a successful delivery clears the failure count.
- Click Enable at the top of the endpoint page. The button appears only while the endpoint is disabled.
- 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
POSTrequests withContent-Type: application/json - verify the signature before doing work
- return a
2xxstatus 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.