Webhook Subscriptions and Security¶
This document covers webhook subscription management and security for CRM administrators.
What Are Webhook Subscriptions¶
Webhook subscriptions enable real-time notifications when events occur in your CRM. When an event matches a subscription: - The platform sends an HTTP POST to your registered URL - Delivery includes the event data and cryptographic signature - Automatic retries occur if delivery fails
Creating Subscriptions¶
Via Admin Portal¶
Webhook subscriptions are typically created via the Integration API. Contact your administrator if UI-based subscription management is required.
Via API¶
Subscriptions are typically created via the Integration API:
- Obtain an API key with
webhooks:writescope - Send POST request to
/api/v1/webhooks/subscriptions - Provide target URL and desired event types
- Store the returned signing secret securely
See 04-api-reference/webhooks-api.md for API details.
Managed by External Tools¶
Zapier, n8n, and Make automatically create and manage their own subscriptions. These appear in the system but cannot be edited manually.
Configuring Event Types¶
When creating a subscription, specify which event types to receive:
Available event types (v1):
- client.created
- client.status_changed
- lead.created
- invoice.created
- invoice.status_changed
- payment.succeeded
- payment.failed
- document.uploaded
- agreement.signed
- affiliate.created
Best practices: - Subscribe only to events you need - Use separate subscriptions for different purposes - Test with a subset of events before subscribing to all
Signing Secrets¶
Purpose¶
Every webhook subscription has a signing secret used to generate HMAC-SHA256 signatures. This proves: - The webhook came from WhiteLabelCRO - The payload was not modified in transit
Generation¶
Signing secrets are: - Auto-generated when you create a subscription (32 random bytes, Base64 encoded) - Returned only once at creation - Cannot be retrieved later
You may optionally provide your own signing secret during subscription creation.
Storage¶
Critical: Store the signing secret securely: - Use environment variables or secret managers - Do not commit to source control - Treat like a password
If you lose the signing secret, you must create a new subscription.
Signature Verification¶
Purpose¶
Webhook signatures ensure that deliveries are authentic and from WhiteLabelCRO. Each webhook includes cryptographic proof that validates both the sender and content integrity.
How It Works¶
Every webhook delivery includes two security headers:
- X-Webhook-Timestamp - When the webhook was sent (Unix epoch seconds)
- X-Webhook-Signature - HMAC-SHA256 signature in format sha256=<hex>
Implementation¶
Your webhook endpoint should:
- Verify the HMAC-SHA256 signature using your signing secret
- Recommended: Reject requests with timestamps too old (e.g., > 5 minutes)
The signing secret is provided when you create the subscription. Store it securely - it cannot be retrieved later.
Diagram¶
sequenceDiagram
participant WLCRO as WhiteLabelCRO
participant Endpoint as Your Webhook Endpoint
Note over WLCRO: Event occurs
WLCRO->>WLCRO: Compute HMAC-SHA256<br/>payload = timestamp + body<br/>signature = sha256=hex
WLCRO->>Endpoint: POST with headers:<br/>X-Webhook-Timestamp<br/>X-Webhook-Signature<br/>+ JSON body
Endpoint->>Endpoint: Compute HMAC-SHA256<br/>using signing secret
Endpoint->>Endpoint: Compare signatures<br/>(constant-time)
alt Signature Valid
Endpoint->>Endpoint: (Optional) Check timestamp age
alt Timestamp Fresh
Endpoint->>Endpoint: Process event
Endpoint->>WLCRO: 200 OK
else Timestamp Too Old
Endpoint->>WLCRO: Reject (4xx)
end
else Signature Invalid
Endpoint->>WLCRO: 401 Unauthorized
end
Technical Details & Code Examples
Note: Examples are illustrative; use the raw request body exactly as received.
Verification Process:
- Extract
X-Webhook-TimestampandX-Webhook-Signatureheaders - Compute HMAC-SHA256 of:
"{timestamp}.{raw_request_body}"using signing secret - Convert hash to lowercase hex and prepend
sha256= - Compare with
X-Webhook-Signatureusing constant-time comparison
C# Example:
var timestamp = Request.Headers["X-Webhook-Timestamp"].ToString();
var receivedSig = Request.Headers["X-Webhook-Signature"].ToString();
var payload = $"{timestamp}.{requestBody}";
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(signingSecret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
var computedSig = "sha256=" + BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
if (!CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(computedSig),
Encoding.UTF8.GetBytes(receivedSig)))
{
return Unauthorized();
}
Node.js Example:
const crypto = require('crypto');
const timestamp = req.headers['x-webhook-timestamp'];
const receivedSig = req.headers['x-webhook-signature'];
const payload = `${timestamp}.${rawBody}`;
const computedSig = 'sha256=' + crypto
.createHmac('sha256', signingSecret)
.update(payload)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(computedSig), Buffer.from(receivedSig))) {
return res.status(401).send('Invalid signature');
}
URL Security¶
HTTPS Requirements¶
Production webhook URLs must use HTTPS. HTTP is not permitted for live integrations.
Endpoint Requirements¶
Your webhook endpoint must: - Respond with 2xx status code (200-299) for successful receipt - Respond within 10 seconds (timeout) - Handle duplicate deliveries (idempotency via event ID)
SSRF Protection¶
The platform blocks webhook URLs pointing to: - Private IP ranges (10.x, 172.16.x, 192.168.x) - Loopback addresses (127.x, localhost) - Invalid or non-resolvable domains
This prevents webhooks from being used to probe internal networks.
Managing Subscriptions¶
Enabling and Disabling¶
Subscriptions can be enabled or disabled: - Enabled: Events matching subscription types are delivered - Disabled: No deliveries occur; events are not queued
Use this to temporarily pause deliveries without deleting the subscription.
Updating Event Types¶
You can modify which event types a subscription receives via PATCH request.
Exception: Tool-managed subscriptions (Zapier, n8n) cannot be modified. The managing tool controls configuration.
Deletion¶
Deleting a subscription: - Stops all future deliveries immediately - Does not affect past delivery logs - Cannot be undone
If you delete by mistake, create a new subscription.
Delivery Behavior¶
Success and Retry¶
- Success: Your endpoint returns 2xx status
- Failure: Timeout, connection error, or non-2xx status
- Retry policy: Up to 10 attempts with exponential backoff (2^attempt minutes, capped at 360 minutes)
Circuit Breaker¶
After 20 consecutive failures, the subscription is automatically disabled. This prevents endless retry attempts.
Re-enable the subscription once the endpoint issue is resolved.
Duplicate Deliveries¶
Due to retries, the same event may be delivered multiple times. Use the event id field to deduplicate.
Monitoring¶
Delivery Logs¶
Every delivery attempt is logged with: - Subscription ID - Event ID - HTTP status code - Response body (truncated) - Elapsed time
Administrators can access these logs for troubleshooting. Contact your administrator for log access.
Failure Patterns¶
Common patterns in logs: - Timeouts: Endpoint too slow (> 10 seconds) - Connection refused: Endpoint offline or URL incorrect - 401/403: Endpoint rejecting deliveries (check signature verification) - 500: Endpoint application error
Success Rate¶
Monitor delivery logs to track subscription health. Contact your administrator for detailed success rate metrics.
Troubleshooting¶
Webhooks Not Arriving¶
Symptom: Expected events not being received
Check:
- Subscription is enabled
- Event types are configured correctly
- Events are actually occurring in the CRM
- Endpoint URL is correct and reachable
Test:
- Use the /api/v1/webhooks/subscriptions/{id}/test endpoint to send a test delivery
Signature Verification Failing¶
Symptom: Your endpoint receives webhooks but signature validation fails
Check:
- Using the correct signing secret (from subscription creation)
- Computing HMAC over "{timestamp}.{raw_body}" (exact format)
- Comparing signatures with constant-time comparison
- Not parsing/modifying the request body before verification
All Deliveries Failing¶
Symptom: 100% failure rate in logs
Causes: - Endpoint always returns non-2xx status - Endpoint always times out - URL is incorrect
Resolution: - Check endpoint logs for errors - Verify endpoint returns 200-299 for valid requests - Test endpoint independently (curl, Postman)
Circuit Breaker Triggered¶
Symptom: Subscription auto-disabled after repeated failures
Resolution:
- Identify root cause from delivery logs
- Fix endpoint issue
- Test with
/testendpoint - Re-enable subscription
Lost Signing Secret¶
Symptom: Cannot verify signatures, lost secret at creation
Resolution: - No way to retrieve the original secret - Create a new subscription - Update your endpoint configuration - Delete the old subscription