# 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