Skip to Content
IntegrationsOutgoing webhooks

Send webhooks to your app

Webhooks tell your server when a record changes. For example, a new customer can start a task in your app. Only program owners can manage webhooks.

Add an endpoint

  1. Open Webhooks in your dashboard.
  2. Choose Add endpoint.
  3. Enter a name and your server’s HTTPS address.
  4. Pick the events you need. Leave Enable endpoint checked.
  5. Choose Save endpoint.
  6. Copy the signing key. Store it on your server, then close the window.

Use a public HTTPS address on port 443. Local and private network addresses are blocked. A program can have 20 endpoints. Keep the signing key out of browser code and source control.

An endpoint and its delivery history

Event types

Each row has a created event and an updated event.

RecordCreatedUpdated
Affiliatepartner.createdpartner.updated
Customercustomer.createdcustomer.updated
Referral linklink.createdlink.updated
Paymenttransaction.createdtransaction.updated
Commissioncommission.createdcommission.updated

Updates are sent when a field in the webhook payload changes. Private settings do not produce an event. Deleting a record does not produce an event.

Read a payload

Each POST has data, type, and timestamp. The data includes the record ID and program ID. Other fields depend on the record. Fields with no saved value may be absent. Amounts use the currency’s smallest unit, such as cents for USD.

{ "data": { "id": "customer-id", "program_id": "program-id", "name": "Taylor Smith", "email": "taylor@example.test", "status": "active" }, "type": "customer.created", "timestamp": "2026-09-11T12:00:00.000Z" }
RecordOther fields
Affiliatename, email, status, group_id, created_at, updated_at
Customername, email, identifier, status, partner_id, link_id, created_at, updated_at
Referral linkpartner_id, parameter, value, destination, disabled, created_at, updated_at
Paymentcustomer_id, partner_id, amount, currency, refunded_amount, product_id, quantity, interval, billing_type, occurred_at, created_at
Commissioncustomer_id, partner_id, transaction_id, flow_id, amount, currency, reversed_amount, approval, settlement, created_at

Verify the signature

Check the signature before you trust a payload. Use the raw request body. Parsing and rebuilding JSON can change the bytes and break the signature.

The headers are webhook-id, webhook-timestamp, and webhook-signature. The ID stays the same on retries. The header timestamp is in seconds and changes on each attempt.

This Node.js function checks the signature and rejects timestamps more than five minutes old or ahead. Pass your stored signing key, the request headers, and the raw body to it.

import { createHmac, timingSafeEqual } from 'node:crypto'; export function verifyWebhook(secret, headers, rawBody) { const id = headers.get('webhook-id'); const timestamp = headers.get('webhook-timestamp'); const signature = headers.get('webhook-signature') ?? ''; if (!id || !timestamp || !/^\d+$/.test(timestamp)) return false; if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false; if (!secret.startsWith('whsec_')) return false; const expected = createHmac('sha256', Buffer.from(secret.slice(6), 'base64')) .update(`${id}.${timestamp}.${rawBody}`) .digest(); return signature.split(' ').some(value => { const [version, encoded] = value.split(','); if (version !== 'v1' || !encoded) return false; const actual = Buffer.from(encoded, 'base64'); return actual.length === expected.length && timingSafeEqual(actual, expected); }); }

Save the event ID in your database with your work. Use a unique key or a transaction so two copies cannot do the same work twice. Return a 2xx response after the event is safely saved. Delivery can arrive more than once or out of order.

Check a delivery

Open Webhooks, then read Deliveries. Choose View payload and attempts to see the event and each response. Use Load older deliveries or Load older attempts to reach older history.

Your server must respond within 15 seconds. Any 2xx response means success. Redirects do not count as success. After a failure, the next waits are 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours, and 10 hours. There are at most eight attempts in one cycle. The worker may run later during a backlog.

A receiver can return the header webhook-delivery: abort-message with a failed response to stop automatic retries for that message.

Stop repeated failures

By default, an endpoint is disabled after five days of ongoing failures. The clock starts only after failures span at least 12 hours within one day. A successful delivery clears the clock.

You can turn this off in Edit endpoint. If an endpoint is disabled, fix your receiver and choose Enable. Replay the messages you still need. Changing the endpoint or replacing its key also resets the clock.

Failure protection setting on a phone

Replay messages

For one message, choose Replay message. A message that is still queued or sending cannot be replayed yet.

For a date range:

  1. Choose Replay by date on an enabled endpoint.
  2. Enter the start and end time in UTC. Choose up to 31 days in the past.
  3. Leave the checkbox off to retry failed or canceled deliveries. Turn it on to send events with no delivery record for this endpoint.
  4. Choose Start replay.
  5. If another page is available, choose Replay next page until the form says it is complete.

Each page checks up to 100 events. Only the endpoint’s selected event types are queued. Missing-event replay can only use events saved after your program first added a webhook endpoint. It cannot recover earlier history. Replay keeps the original event ID and payload.

Change or remove an endpoint

Edit endpoint changes its address or events. Disable stops new deliveries. Delete endpoint removes it from the list but keeps delivery history. Changes stop queued messages from using old settings. A request already in progress may still finish.

Replace signing key creates a new key. Update your receiver, then replay any canceled messages you still need. You cannot view an old key after closing its window.