Subscribe to 18 event types for contacts, companies, campaigns, research, meetings, and engagement. Connect your HTTPS endpoint to Kolvera with the REST API.
Create an API subscription, then verify signed deliveries. Settings does not contain a separate webhook builder.
POST /api/v2/webhooks
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
{"url":"https://example.com/kolvera-events","events":["contact.created"],"secret":"YOUR_RECEIVER_SIGNING_SECRET"}
Use GET /api/v2/webhooks for your subscriptions and supported events. Use PATCH or DELETE /api/v2/webhooks/<id> to manage a subscription. OAuth credentials require webhooks:manage. Supply a signing secret and verify X-Kolvera-Signature against the raw request body; deduplicate deliveries using their event ID.
Create a named kv_ API token under Settings → Developers → API tokens, then create a subscription with POST /api/v2/webhooks. Your endpoint must accept HTTPS POST requests and return a 2xx status. See API endpoints.
Include the required event types in your subscription API request. You can subscribe to all events or only the ones your integration needs. Events not subscribed to are never sent.
Supply your own secret when creating a subscription to enable X-Kolvera-Signature. Store it securely: API responses omit the secret. Without a secret, requests are unsigned.
All 18 event types and when they fire.
| Event | Category | Fires when |
|---|---|---|
contact.created |
Contact | A new contact is added to your workspace |
contact.updated |
Contact | Contact details are changed |
contact.enriched |
Contact | An email address is found through the API email lookup |
company.created |
Company | A new company record is added |
company.updated |
Company | Company details are changed |
campaign.activated |
Campaign | A campaign is started or resumed |
campaign.paused |
Campaign | A campaign is paused |
campaign.completed |
Campaign | A contact finishes the full sequence |
campaign.replied |
Campaign | A reply is detected on a campaign email |
campaign.bounced |
Campaign | A campaign email hard-bounces |
meeting.created |
Meeting | A meeting is scheduled |
meeting.status_changed |
Meeting | A meeting's status is updated (booked / attended / no-show / cancelled) |
research.completed |
Research | A Deep Research job finishes processing |
credit.low |
Credit | Your credit balance drops to 10 or below |
email.opened |
Engagement | A recipient opens a campaign email |
link.clicked |
Engagement | A recipient clicks a tracked link in a campaign email |
enrolment.complete |
Campaign | Contacts are successfully enrolled in a campaign |
scrape.complete |
Pipeline | A job-board scrape finishes |
Delivery coverage varies by workflow. contact.created, contact.updated, and company.updated are emitted primarily by core API writes; company.created is emitted when API contact creation creates a company. UI changes and imports can bypass these events. contact.enriched covers API email lookup, not phone lookup. For a complete local copy, reconcile periodically through the REST API; treat events as change hints.
Every POST body has this envelope. id stays the same across retries, so use it to deduplicate. data varies by event:
{"id":"evt_...","event":"enrolment.complete","timestamp":"2026-10-05T00:00:00+00:00","data":{"campaign_id":"...","enrolled":3}}
| Event | data fields |
|---|---|
contact.created | Created contact object |
contact.updated | contact_id, changed fields names |
contact.enriched | contact_id, type (email), email |
company.created | company_id, name |
company.updated | company_id, changed fields names |
campaign.activated, campaign.paused | campaign_id, name |
campaign.completed, campaign.replied | campaign_id, contact_id, enrollment_id |
campaign.bounced | campaign_id, contact_id, email, error |
meeting.created | Created meeting object |
meeting.status_changed | meeting_id, status, contact_id |
research.completed | report_id, brief, companies_found |
credit.low | balance, action (source dependent) |
email.opened | contact_id, enrollment_id |
link.clicked | contact_id, enrollment_id, token |
enrolment.complete | campaign_id, enrolled count |
scrape.complete | source, search_config_ids |
enrolment.complete means successful campaign enrolment; campaign.completed means one contact finished the sequence. No event fires for each email sent.
Return 2xx to acknowledge. Kolvera retries HTTP 408, 425, 429, 500, 502, 503, and 504 after 30 seconds, 2 minutes, 10 minutes, 30 minutes, and 60 minutes, up to five retries. Other 4xx responses are not retried. Ten failed events disable a subscription; retry attempts for one event count as one failure. If the task broker is unavailable, the thread fallback makes one attempt without retries.
Verify that requests come from Kolvera, not a third party.
X-Kolvera-Signature header formatted sha256=<64 lowercase hex characters>
hmac.compare_digest in Python, crypto.timingSafeEqual in Node.js
id in the payload — use it to de-duplicate
# Python — verify an incoming webhook request
import hmac, hashlib
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
if not isinstance(signature, str) or not signature.startswith("sha256="):
return False
digest = signature[7:]
if len(digest) != 64 or any(c not in "0123456789abcdef" for c in digest):
return False
expected = hmac.new(secret.encode(), payload, hashlib.sha256).digest()
return hmac.compare_digest(expected, bytes.fromhex(digest))
// Node.js — verify an incoming webhook request
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
if (typeof signature !== 'string' || !/^sha256=[0-9a-f]{64}$/.test(signature)) return false;
const expected = crypto.createHmac('sha256', secret).update(payload).digest();
const received = Buffer.from(signature.slice(7), 'hex');
return crypto.timingSafeEqual(expected, received);
}
Webhooks work natively with the most popular automation platforms.
Create a Zap with a "Webhooks by Zapier" trigger set to "Catch Hook". Register its HTTPS URL through POST /api/v2/webhooks.
Add a "Webhooks" module with the "Custom Webhook" trigger. Copy the webhook URL and register it using the Kolvera webhook subscription API. Make parses the JSON payload automatically.
Use the "Webhook" trigger node in your n8n workflow. Set the HTTP method to POST and register the production URL through POST /api/v2/webhooks.
Common patterns to get you started.
| Trigger event | Automation idea |
|---|---|
contact.created |
Add a new row to a Google Sheet prospect tracker |
campaign.replied |
Post a Slack notification to your team channel |
meeting.created |
Create or update a deal in your CRM |
contact.enriched |
Push the updated contact to HubSpot or Pipedrive |
campaign.bounced |
Flag the contact in your CRM for manual review |
research.completed |
Trigger a downstream pipeline job in Make or n8n |
Webhooks are available on all plans — free trial, 50 credits, no card required.
Start free trial