Webhooks API
Push events out of Vanta to your endpoints or Discord channels.
Access control
Webhooks are managed with session authentication, not the SDK. Creating or editing a webhook requires an owner or admin role in the project. Deliveries are signed and sent from Vanta to your endpoint; no API key is needed on the receiving side.
Webhook types
- HTTP: posts a signed JSON payload to an endpoint you control.
- Discord: posts message content and rich embeds to a Discord channel via a standard Discord webhook URL.
Create an HTTP webhook
POST /v1/projects/:projectId/webhooksA webhook watches one or more event types for the project, up to twenty triggers.
{
"name": "Billing alerts",
"type": "http",
"url": "https://app.example.com/webhooks/vanta",
"events": ["payment_failed", "refunded"]
}Request fields:
| Field | Type | Description |
|---|---|---|
name | string | Human readable name. Required. |
type | string | http or discord. |
url | string | Delivery URL. Publicly reachable; private ranges and metadata endpoints are rejected. |
events | string[] | Event types and incident triggers to watch (1–20). Required unless eventType is used. |
eventType | string | Shorthand for a webhook watching a single event type. |
discordConfig | object | Message and embed configuration. Required for Discord webhooks. |
Incident triggers are incident.started and incident.recovered. Incident notifications are delivered at most once per incident phase, so reopening a resolved incident does not re-fire a delivery.
After creation, an HTTP webhook returns a signing secret. The secret is shown once, so store it immediately. It is used to verify every delivery signature.
Manage webhooks
| Method | Path | Description |
|---|---|---|
GET | /v1/projects/:projectId/webhooks | List webhooks. |
GET | /v1/projects/:projectId/webhooks/:webhookId | Get a webhook. |
PATCH | /v1/projects/:projectId/webhooks/:webhookId | Update a webhook. |
DELETE | /v1/projects/:projectId/webhooks/:webhookId | Delete a webhook. |
POST | /v1/projects/:projectId/webhooks/:webhookId/test | Send a test delivery. |
GET | /v1/projects/:projectId/webhooks/:webhookId/deliveries | List delivery history. |
POST | /v1/projects/:projectId/webhooks/:webhookId/deliveries/:deliveryId/retry | Retry a failed delivery. |
PATCH accepts the same body fields and replaces the webhook's trigger set wholesale: pass events to set the full list of triggers, or use eventType to reduce it to a single one.
Delivery headers
Every HTTP delivery includes these headers:
X-Vanta-Signature: t=1756026000,v1=a3f0...
X-Vanta-Event-Id: e1f4c6d2...
X-Vanta-Delivery-Id: b8c2e9a1...
User-Agent: Vanta-Webhooks/1.0The payload includes the full event under event and the project reference under project.
Verifying signatures
The signature is HMAC-SHA256 computed over {t}.{rawBody} using your signing secret. Verify it by recomputing the digest and comparing against v1. Reject deliveries older than five minutes to prevent replay attacks.
import { createHmac, timingSafeEqual } from 'crypto'
function verifyVantaSignature(secret: string, rawBody: string, header: string) {
const parts = Object.fromEntries(
header.split(',').map((part) => part.split('=') as [string, string]),
)
const age = Math.abs(Math.floor(Date.now() / 1000) - Number(parts.t))
if (age > 300) return false
const expected = createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex')
return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1, 'hex'))
}Template variables
Discord webhooks support template variables that pull values from the delivered event. Paths resolve safely and missing properties become empty strings:
{{event.type}} payment_failed
{{event.userId}} user_842
{{event.data.amount}} 49.99
{{event.timestamp}} 2026-08-24T10:00:00.000ZObjects and arrays serialize as JSON. Only property access is supported, so templates can never execute anything unexpected.
Incident notifications add the monitor and incident to the scope, so templates can reference {monitor.name}, {monitor.slug}, {incident.status}, {incident.startedAt}, {incident.recoveredAt}, and {incident.durationMs}. The date variables {incident.startedAt} and {incident.recoveredAt} render as MM/DD/YYYY HH:MM UTC.
Retries and timeouts
- Successful deliveries (any 2xx) are never retried.
- Server errors and network failures retry after roughly 30s, 2m, 10m, and 30m.
- Client errors (other 4xx) are permanent and not retried.
- A delivery gives up after 5 attempts; each request times out after 10 seconds.
Discord webhooks
Discord webhooks use the URL Discord generates for a channel (https://discord.com/api/webhooks/...). The discordConfig supports plain message content and up to ten embeds, each with an author, title, description, accent color, footer, thumbnail, image, and up to 25 fields, all of which support template variables.