Skip to content

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:

  1. Obtain an API key with webhooks:write scope
  2. Send POST request to /api/v1/webhooks/subscriptions
  3. Provide target URL and desired event types
  4. 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:

  1. Verify the HMAC-SHA256 signature using your signing secret
  2. 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:

  1. Extract X-Webhook-Timestamp and X-Webhook-Signature headers
  2. Compute HMAC-SHA256 of: "{timestamp}.{raw_request_body}" using signing secret
  3. Convert hash to lowercase hex and prepend sha256=
  4. Compare with X-Webhook-Signature using 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:

  1. Subscription is enabled
  2. Event types are configured correctly
  3. Events are actually occurring in the CRM
  4. 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:

  1. Identify root cause from delivery logs
  2. Fix endpoint issue
  3. Test with /test endpoint
  4. 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