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

http
POST /v1/projects/:projectId/webhooks

A webhook watches one or more event types for the project, up to twenty triggers.

json
{
  "name": "Billing alerts",
  "type": "http",
  "url": "https://app.example.com/webhooks/vanta",
  "events": ["payment_failed", "refunded"]
}

Request fields:

FieldTypeDescription
namestringHuman readable name. Required.
typestringhttp or discord.
urlstringDelivery URL. Publicly reachable; private ranges and metadata endpoints are rejected.
eventsstring[]Event types and incident triggers to watch (1–20). Required unless eventType is used.
eventTypestringShorthand for a webhook watching a single event type.
discordConfigobjectMessage 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

MethodPathDescription
GET/v1/projects/:projectId/webhooksList webhooks.
GET/v1/projects/:projectId/webhooks/:webhookIdGet a webhook.
PATCH/v1/projects/:projectId/webhooks/:webhookIdUpdate a webhook.
DELETE/v1/projects/:projectId/webhooks/:webhookIdDelete a webhook.
POST/v1/projects/:projectId/webhooks/:webhookId/testSend a test delivery.
GET/v1/projects/:projectId/webhooks/:webhookId/deliveriesList delivery history.
POST/v1/projects/:projectId/webhooks/:webhookId/deliveries/:deliveryId/retryRetry 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:

http
X-Vanta-Signature: t=1756026000,v1=a3f0...
X-Vanta-Event-Id: e1f4c6d2...
X-Vanta-Delivery-Id: b8c2e9a1...
User-Agent: Vanta-Webhooks/1.0

The 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.

typescript
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:

jinja
{{event.type}}              payment_failed
{{event.userId}}            user_842
{{event.data.amount}}       49.99
{{event.timestamp}}         2026-08-24T10:00:00.000Z

Objects 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.