Webhooks
Webhooks let you receive real-time HTTP notifications whenever important events happen in your deals — files uploaded, request statuses changed, team members added, and more. Use them to integrate Vetting Vault with your existing tools and automate deal workflows.
Paid feature
Overview
Instead of polling the platform for changes, webhooks push event data to your server the moment something happens. This is useful for a wide range of integrations:
- CRM updates — Automatically update your CRM when a deal request status changes or a new file is uploaded.
- Slack or Teams notifications — Post a message to a channel whenever a team member is added or a question is answered.
- Compliance logging — Stream all deal activity to an external audit system for regulatory recordkeeping.
- Custom dashboards — Feed live deal progress data into your own reporting tools.
- Workflow automation — Trigger downstream processes (approvals, checklists, notifications) in tools like Zapier, Make, or your own backend.
Vetting Vault follows the Standard Webhooks specification for signing and delivery, so if your team has worked with webhooks from Stripe, GitHub, or similar platforms, the patterns will be familiar.
Creating a Webhook Endpoint
To start receiving webhook events, create an endpoint in your account settings under the Webhooks section. You will need to provide:
- URL — The HTTPS URL where Vetting Vault will POST event payloads.
- Description (optional) — A human-readable label to help you identify the endpoint (e.g., “Production CRM sync” or “Slack deal alerts”).
- Event types — Choose which event types this endpoint should receive. You can select specific types or leave it open to receive all events.
- Deal subscriptions — Select which deals should send events to this endpoint. You must have owner or admin access to a deal to subscribe it.
Once created, the endpoint is active immediately and will begin receiving events for the deals and event types you selected.
Endpoint URL Requirements
Your endpoint URL must meet the following requirements:
- Must use HTTPS (HTTP is not accepted)
- Must be publicly reachable from the internet
- Must not resolve to a private or internal IP address (SSRF protection)
- Must respond within 30 seconds
- Must return a 2xx status code to acknowledge receipt
Signing Secret
When you create a webhook endpoint, Vetting Vault generates a unique signing secret. This secret is displayed once at creation time — copy it immediately and store it securely. The secret is used to verify that incoming webhook payloads genuinely originated from Vetting Vault and have not been tampered with.
Signing secrets use the whsec_ prefix followed by a base64-encoded key, for example: whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw.
Save your secret immediately
Event Types
Vetting Vault emits events across every major area of deal activity. Each event type follows a resource.action naming convention. You can filter which types each endpoint receives, or subscribe to all of them.
Deal Events
| Event Type | Description |
|---|---|
deal.updated | Deal settings changed (name, description, etc.) |
deal.locked | Deal locked or unlocked |
deal.archived | Deal archived |
Team Events
| Event Type | Description |
|---|---|
team_member.added | Member added to the deal |
team_member.removed | Member removed from the deal |
team_member.role_changed | Member’s role changed |
Project & Section Events
| Event Type | Description |
|---|---|
project.created | New project created |
project.updated | Project updated |
project.deleted | Project deleted |
section.created | New section created within a project |
section.updated | Section updated |
Request Events
| Event Type | Description |
|---|---|
request.created | New file request or question created |
request.status_changed | Request status updated (e.g., submitted, approved, to discuss) |
request.assigned | Request assigned to a team member |
request.updated | Request details changed |
File Events
| Event Type | Description |
|---|---|
file.uploaded | File uploaded to a request |
file.deleted | File deleted from a request |
Comment & Q&A Events
| Event Type | Description |
|---|---|
comment.created | Comment added to a request |
question.created | Question posted in Q&A |
question.answered | Answer submitted to a question |
question.updated | Question or answer edited |
Chat Events
| Event Type | Description |
|---|---|
chat.message_sent | Chat message sent in deal chat |
Guest Link Events
| Event Type | Description |
|---|---|
guest_link.created | Guest view link created |
guest_submission.received | Guest uploaded a file through a guest link |
Other Events
| Event Type | Description |
|---|---|
written_response.saved | Written response saved on a request |
sync.completed | File synced to external cloud storage |
sync.failed | Cloud storage sync failed |
Start with the events you need
request.status_changed and file.uploaded for a CRM sync, or team_member.added for an onboarding automation. You can always add more event types later.Deal Subscriptions
Each webhook endpoint is subscribed to specific deals. When an event occurs in a subscribed deal, and the event type matches the endpoint’s filter, Vetting Vault delivers the event payload to your URL.
To subscribe an endpoint to a deal, you must have owner or admin access to that deal. This ensures that only authorized users can configure event delivery for a deal’s data.
You can update deal subscriptions at any time. Adding a new deal starts delivering events for that deal immediately. Removing a deal stops delivery going forward — previously delivered events are not affected.
A single endpoint can be subscribed to up to 50 deals. If you need to monitor more deals from one integration, create additional endpoints.
Delivery & Retry Logic
Vetting Vault is designed for reliable delivery. Every event is persisted in the database before any delivery attempt, so events are never lost even if your server is temporarily unavailable.
Payload Format
Every webhook delivery is an HTTP POST with a JSON body. The payload follows a consistent envelope structure:
{
"id": "evt_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "request.status_changed",
"version": "1",
"occurred_at": "2026-04-06T14:30:00.000Z",
"deal_id": 42,
"deal_name": "Acme Corp Acquisition",
"data": {
"request_id": 185,
"request_title": "3 Years of Tax Returns",
"old_status": "submitted",
"new_status": "approved",
"changed_by": "jane@example.com"
}
}The data field contains event-specific information. Its shape varies by event type, but always includes enough context to identify the affected resource.
Delivery Headers
Each delivery includes Standard Webhooks headers for verification:
| Header | Description |
|---|---|
webhook-id | Unique event identifier (matches the id in the payload) |
webhook-timestamp | Unix timestamp (seconds) when the delivery was signed |
webhook-signature | HMAC-SHA256 signature in v1,<base64> format |
Content-Type | application/json |
User-Agent | VettingVault-Webhooks/1.0 |
Retry Schedule
If your server returns a non-2xx response or the connection fails, Vetting Vault retries the delivery with exponential backoff. Each delivery is attempted up to 15 times over approximately 3 days:
| Attempt | Delay After Failure |
|---|---|
| 1 | 1 minute |
| 2 | 2 minutes |
| 3 | 5 minutes |
| 4 | 15 minutes |
| 5 | 30 minutes |
| 6 | 1 hour |
| 7 | 2 hours |
| 8 | 4 hours |
| 9 | 6 hours |
| 10 | 8 hours |
| 11–12 | 12 hours |
| 13–15 | 24 hours |
A small amount of random jitter (up to 20%) is added to each retry delay to prevent thundering-herd scenarios when your server recovers.
Permanent failures: If your server responds with a 4xx status code (except 429 Too Many Requests), the delivery is immediately moved to the dead-letter queue without further retries. A 429 response is treated as a temporary failure and retried normally.
After all 15 attempts are exhausted, the delivery is moved to the dead-letter queue (DLQ). You can manually retry DLQ deliveries from the delivery history in your settings.
Auto-Disable
If an endpoint accumulates 50 consecutive failures across all its deliveries, Vetting Vault automatically disables it to prevent wasting resources. You will see the disabled reason in your endpoint settings.
To re-enable a disabled endpoint, fix the underlying issue with your server, then click “Enable” in the webhook settings. The failure counter resets when you re-enable, and you can send a test ping to verify the endpoint is working before live events resume.
Managing Webhooks
Delivery History
Every webhook endpoint has a delivery history that shows each event delivery and its outcome. For each delivery you can see:
- The event type and deal that triggered it
- Current delivery status —
pending,delivered,failed, ordlq - Number of delivery attempts and the timestamp of each
- HTTP status code returned by your server
- Error messages for failed attempts
- Response latency for each attempt
You can filter the delivery history by status to quickly find failed deliveries that need attention. Failed or DLQ deliveries can be manually retried with one click.
Testing with Ping
Before subscribing an endpoint to live deals, use the Ping feature to send a test event. A ping sends a small test payload to your endpoint URL and reports whether the delivery succeeded:
{
"id": "ping_1712412345678",
"type": "ping",
"version": "1",
"occurred_at": "2026-04-06T14:30:00.000Z",
"deal_id": null,
"deal_name": null,
"data": {
"message": "Webhook endpoint verification"
}
}If the ping succeeds (your server returns a 2xx response), the endpoint is confirmed active. If it fails, you will see the HTTP status code or error message to help you debug.
Rotating Secrets
If you need to change your endpoint’s signing secret — for example, if the secret was accidentally exposed or as part of a regular rotation policy — you can rotate it from the endpoint settings.
When you rotate a secret:
- A new
whsec_secret is generated and shown to you once. - The old secret is immediately invalidated.
- You should update your consumer to use the new secret within 24 hours.
Update your consumer promptly
Limits
The following limits apply to webhook configuration:
| Resource | Limit |
|---|---|
| Endpoints per user | 10 |
| Deal subscriptions per endpoint | 50 |
| Event types per endpoint | 30 |
| Delivery attempts per event | 15 |
| API rate limit | 60 requests per minute |
| Response timeout | 30 seconds |
| Consecutive failures before auto-disable | 50 |
Events API
In addition to push-based webhooks, Vetting Vault provides a cursor-paginated Events API for each deal. This is useful for:
- Catch-up — If your consumer was down for a period, query the Events API to retrieve events you may have missed.
- Backfill — When setting up a new integration, pull historical events to populate your system.
- Verification — Cross-reference webhook deliveries against the Events API to confirm you received everything.
Any accepted deal member can query the Events API for that deal. You can filter by event type and paginate through results using a cursor. Results are returned in chronological order, up to 100 events per page.
Security
Verifying Signatures
Every webhook delivery is signed using HMAC-SHA256 with your endpoint’s signing secret. To verify a delivery is authentic:
- Extract the headers — Read
webhook-id,webhook-timestamp, andwebhook-signaturefrom the request. - Build the signature input — Concatenate the message ID, timestamp, and raw request body with periods:
{webhook-id}.{webhook-timestamp}.{body} - Compute the HMAC — Decode the base64 portion of your
whsec_secret and compute the HMAC-SHA256 of the signature input. - Compare signatures — The computed signature should match the value after the
v1,prefix in thewebhook-signatureheader. Always use a timing-safe comparison to prevent timing attacks.
Here is an example in Node.js:
const crypto = require('crypto');
function verifyWebhook(payload, headers, secret) {
const msgId = headers['webhook-id'];
const timestamp = headers['webhook-timestamp'];
const signature = headers['webhook-signature'];
// Strip the whsec_ prefix and decode
const secretBytes = Buffer.from(
secret.replace('whsec_', ''),
'base64'
);
// Build the signed content
const signedContent = `${msgId}.${timestamp}.${payload}`;
// Compute expected signature
const expected = crypto
.createHmac('sha256', secretBytes)
.update(signedContent)
.digest('base64');
// Compare using timing-safe equality
const expectedSig = `v1,${expected}`;
const receivedBuf = Buffer.from(signature.split(' ')[0]);
const expectedBuf = Buffer.from(expectedSig);
if (receivedBuf.length !== expectedBuf.length) return false;
return crypto.timingSafeEqual(receivedBuf, expectedBuf);
}Check the timestamp too
webhook-timestamp is within an acceptable window (e.g., 5 minutes) of your server’s current time. Reject deliveries with timestamps that are too old or too far in the future.Best Practices
- Always verify signatures — Never process a webhook payload without verifying its signature first. This prevents spoofed events from being acted upon.
- Respond quickly, process later — Return a 200 status code as soon as you receive and verify the payload. Do any heavy processing asynchronously. If your server takes too long to respond, Vetting Vault may treat it as a timeout and retry.
- Handle duplicates — In rare cases (network issues, retries), you may receive the same event more than once. Use the
webhook-idheader or theidfield in the payload to deduplicate. - Store your secret securely — Keep the signing secret in an environment variable or a secrets manager. Never commit it to source control or expose it in client-side code.
- Monitor your delivery history — Periodically check the delivery history in your settings to catch failures early. Set up your own alerting if deliveries start failing.
- Use the Events API for catch-up — If your consumer goes down, don’t rely solely on retries. Query the Events API to backfill any events that may have been missed or moved to the dead-letter queue.
- Rotate secrets periodically — Even if your secret has not been compromised, rotating it on a regular cadence (e.g., quarterly) is a good security practice.
