# Webhooks Subscribe to real-time notifications when contacts change jobs or companies experience key business events. Webhooks deliver HTTP POST requests to your endpoints when signals occur - from promotions and job changes to company growth. > For a full list of available signals, refer to [**Signal Options**](https://docs.lusha.com/apis/openapi/signals/getsignaloptions). --- **Key Features:** - Real-time contact & company signal notifications - Bulk subscription management (up to 25 items per request) - Secure delivery with HMAC-SHA256 signatures - Automatic URL verification during setup - Delivery monitoring with audit logs **Available Endpoints:** | Method | Endpoint | Purpose | |--------|----------|---------| | POST | `/api/subscriptions` | Create subscriptions (bulk supported) | | GET | `/api/subscriptions` | List all subscriptions | | GET | `/api/subscriptions/{id}` | Get subscription by ID | | PATCH | `/api/subscriptions/{id}` | Update subscription | | POST | `/api/subscriptions/delete` | Delete subscriptions (bulk supported) | | POST | `/api/subscriptions/{id}/test` | Test subscription delivery | | GET | `/api/audit-logs` | Get webhook delivery logs | | GET | `/api/audit-logs/stats` | Get delivery statistics | | GET | `/api/account/secret` | Get account webhook secret | | POST | `/api/account/secret/regenerate` | Regenerate account secret | > **Webhook Verification:** When creating subscriptions, Lusha sends a GET request with a `challenge` parameter. Your endpoint must return `{"challenge": "value"}` with HTTP 200. ---
Rate Limits | Operation | Limit | |-----------|-------| | API Requests | 100 requests/minute per account | | Create Subscriptions | 25 items per request | | Delete Subscriptions | 25 items per request |
---
Security & Verification **HTTPS Requirement:** - Production webhook URLs **must** use HTTPS - HTTP URLs are not accepted **Webhook Verification:** When creating or updating a subscription, Lusha verifies your webhook URL by sending a GET request with a `challenge` query parameter. **Verification Request:** ``` GET https://your-webhook-url.com?challenge=abc123xyz ``` **Expected Response (200 OK):** ```json { "challenge": "abc123xyz" } ``` **Requirements:** - Return HTTP 200 status - Return Content-Type: application/json - Echo back the exact challenge value **Signature Verification:** All webhook deliveries include an `X-Lusha-Signature` header containing an HMAC-SHA256 signature. Verify this signature to ensure the request is from Lusha: 1. Extract the `X-Lusha-Signature` and `X-Lusha-Timestamp` headers 2. Concatenate: `timestamp + "." + JSON.stringify(payload)` 3. Compute HMAC-SHA256 using your webhook secret 4. Compare the computed signature with the received signature **Example (Node.js):** ```javascript const crypto = require('crypto'); function verifySignature(payload, signature, timestamp, secret) { const signedPayload = `${timestamp}.${JSON.stringify(payload)}`; const expectedSignature = crypto .createHmac('sha256', secret) .update(signedPayload) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expectedSignature) ); } ``` > **Security Best Practice:** Always verify webhook signatures to prevent spoofed requests.
---
Credits & Billing **Credit Charges:** - Credits are charged when signals are detected and delivered to your webhook - The `creditsCharged` field in the webhook payload indicates how many credits were used - Credits are deducted from your account balance per signal type **No Duplicate Charges:** - Each signal is delivered once and charged once - Webhook delivery retries do not incur additional charges
---
Error Response Format All error responses follow this format: ```json { "statusCode": 400, "message": "Validation failed", "errors": ["entityType must be one of: contact, company"] } ``` | Field | Type | Description | |-------|------|-------------| | `statusCode` | number | HTTP status code | | `message` | string | Error message | | `errors` | string[] | Detailed error messages (optional) |
--- ## Create Subscription - [POST /api/subscriptions](https://docs.lusha.com/apis/openapi/webhooks/createsubscription.md): Creates one or more webhook subscriptions for real-time signal notifications. URL Validation: - The URL is validated once before creating any subscriptions - Includes format checking and challenge request verification - If URL validation fails, no subscriptions are created Delivery & Reliability: - Webhooks are delivered with automatic retry on failures - Maximum 3 retry attempts with exponential backoff - Subscriptions auto-disable after max retries exceeded - All deliveries are logged in audit logs > Limit: Maximum 25 subscriptions per request Endpoint: (POST) https://api.lusha.com/api/subscriptions --- ### Webhook Payload You'll Receive When a signal is triggered, this payload is sent to your webhook URL: json { "id": "f3b87e05-0402-4f3e-8e26-6a38fd0ad62c", "type": "promotion", "entityType": "contact", "entityId": "4158887495", "subscriptionId": "507f1f77bcf86cd799439011", "data": { "personId": 4158887495, "currentCompanyId": 40823133, "currentCompanyName": "OMG Hospitality Group LLC", "currentDomain": "omghospitalitygroup.com", "currentTitle": "Bartender", "currentDepartments": [ { "id": 7, "value": "Other" } ], "previousCompanyName": "First Watch Restaurants", "previousDomain": "firstwatch.com", "signalDate": "2025-07-01" }, "timestamp": "2026-01-14T16:16:35.841Z", "billing": { "creditsCharged": 1 } } Headers Included: | Header | Description | |--------|-------------| | X-Lusha-Signature | HMAC-SHA256 signature for verification | | X-Lusha-Timestamp | Unix timestamp of the request | | Content-Type | application/json | | User-Agent | Lusha-Webhooks/1.0 | --- ⚠️ Important: Ensure your account has a webhook secret before creating subscriptions. Create one via the Regenerate Account Secret endpoint. --- ## List Subscriptions - [GET /api/subscriptions](https://docs.lusha.com/apis/openapi/webhooks/listsubscriptions.md): Returns all webhook subscriptions for your account with pagination support. Endpoint: (GET) https://api.lusha.com/api/subscriptions Pagination: - Results are sorted by createdAt in descending order (newest first) - Default limit: 10, max limit: 100 - Use offset for pagination through large result sets > Note: The webhook secret is never returned in list responses for security. ## Get Subscription by ID - [GET /api/subscriptions/{id}](https://docs.lusha.com/apis/openapi/webhooks/getsubscriptionbyid.md): Returns a single webhook subscription by ID. Endpoint: (GET) https://api.lusha.com/api/subscriptions/{id} ## Update Subscription - [PATCH /api/subscriptions/{id}](https://docs.lusha.com/apis/openapi/webhooks/updatesubscription.md): Updates an existing webhook subscription. All fields are optional. Endpoint: (PATCH) https://api.lusha.com/api/subscriptions/{id} --- Reactivating Disabled Subscriptions: When setting isActive: true on a previously disabled subscription, the system automatically: - Clears the blockReason field - Clears the blockedAt timestamp - Resets the retry counter Regenerating Secrets: Set regenerateSecret: true to generate a new webhook secret. The new secret: - Affects all subscriptions for your account - Is only shown once in the response - Immediately invalidates the old secret --- ## Test Subscription - [POST /api/subscriptions/{id}/test](https://docs.lusha.com/apis/openapi/webhooks/testsubscription.md): Test a webhook subscription by sending a test signal. Supports three test modes. Endpoint: (POST) https://api.lusha.com/api/subscriptions/{id}/test --- Test Modes: - direct - Quick HTTP check only (validates URL responds correctly) - kafka - Fanout handler only (tests Kafka message processing) - full - Complete Kafka flow (default - end-to-end test) What Gets Tested: - URL accessibility and response time - Challenge verification (for direct mode) - Complete delivery pipeline (for full mode) - Signature generation and headers Important Notes: - Test deliveries do NOT consume credits - Test payloads use mock data - Useful for verifying webhook configuration before going live ## Delete Subscriptions - [POST /api/subscriptions/delete](https://docs.lusha.com/apis/openapi/webhooks/deletesubscriptions.md): Delete one or more webhook subscriptions. Returns detailed results for each deletion with partial success support. Endpoint: (POST) https://api.lusha.com/api/subscriptions/delete --- Behavior: - Each subscription is processed independently - Returns detailed results for each item including deleted subscription info - Invalid ID formats are gracefully handled and reported as NOT_FOUND - Duplicate IDs are automatically deduplicated - Deletion is permanent and cannot be undone ## Get Audit Logs - [GET /api/audit-logs](https://docs.lusha.com/apis/openapi/webhooks/getauditlogs.md): Retrieve webhook delivery logs for your account. Endpoint: (GET) https://api.lusha.com/api/audit-logs What's Logged: - All webhook delivery attempts (success and failures) - HTTP status codes and response times - Error messages for failed deliveries - Delivery timestamps and duration metrics Filtering: - Filter by subscription ID to see logs for specific subscriptions - Filter by status to see only successes, failures, or permanent failures Rate Limit: 100 requests/minute per account > Note: Logs are retained for 90 days (successful) and 180 days (failed/DLQ) ## Get Audit Log Statistics - [GET /api/audit-logs/stats](https://docs.lusha.com/apis/openapi/webhooks/getauditlogstats.md): Get delivery statistics for your account. Endpoint: (GET) https://api.lusha.com/api/audit-logs/stats ## Get Account Secret - [GET /api/account/secret](https://docs.lusha.com/apis/openapi/webhooks/getaccountsecret.md): Retrieve the current account webhook secret. Endpoint: (GET) https://api.lusha.com/api/account/secret ## Regenerate Account Secret - [POST /api/account/secret/regenerate](https://docs.lusha.com/apis/openapi/webhooks/regenerateaccountsecret.md): Regenerate the account webhook secret. Affects all subscriptions for the account. Endpoint: (POST) https://api.lusha.com/api/account/secret/regenerate Behavior: - If a secret already exists: Replaces with new secret (old secret is invalidated) - If no secret exists: Creates new secret automatically Important Notes: - The secret is only shown once in the response. Store it securely. - This endpoint always succeeds (upsert operation) - Regenerating invalidates the old secret for all subscriptions (if one existed) - An account secret must exist before webhooks can be delivered