Webhooks
Signed HTTP callbacks when things happen in your workspace. Subscribe to 40+ events.
Subscribing
Create webhook subscriptions in Settings → Webhooks or via the REST API endpoint POST /v1/webhooks. Each subscription has a URL, a set of event names, and a signing secret generated at creation.
Event catalog
sync.completed— a sync run finished, regardless of success.sync.failed— a sync run failed after retries.anomaly.detected— AI layer flagged a KPI outside baseline.integration.created,integration.updated,integration.brokenstats.daily_ready— daily rollup completed, numbers are stable.invoice.generated,invoice.paid,invoice.overduesub_affiliate.joined,sub_affiliate.paidautomation.fired— any rule fired.
Delivery guarantees
We retry failed deliveries with exponential backoff: 30s, 2m, 10m, 1h, 6h, 24h — 6 attempts total. If all fail, the delivery is marked dead and appears in Settings → Webhooks → Dead lettersfor manual replay. Successful delivery means your endpoint returned a 2xx within 10 seconds.
Signature verification
Every payload is signed with HMAC-SHA256 using your webhook's signing secret. The signature is sent in the X-xuly.io-Signature header as t=<timestamp>,v1=<hex>.
import crypto from 'node:crypto';
function verify(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(
signatureHeader.split(',').map((p) => p.split('=')),
);
const expected = crypto
.createHmac('sha256', secret)
.update(parts.t + '.' + rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(parts.v1),
);
}Example payload
{
"id": "evt_01HZXY...",
"type": "sync.completed",
"created": "2026-04-22T14:03:11Z",
"data": {
"integration_id": "int_abc123",
"brand_slug": "binance-affiliates",
"status": "success",
"rows_ingested": 48,
"duration_ms": 2140
}
}Replaying events
Go to Settings → Webhooks → [Your endpoint] → History, filter to "Delivery failed", and click Retry on any row. Replays use the same event ID, so handlers should be idempotent.