Webhooks
Vanta uses webhooks to notify your application in real time when events occur in your account. Instead of polling the API for changes, you can register an endpoint URL and Vanta will send an HTTP POST request to it whenever a subscribed event fires.
Webhooks are powered by Svix, an enterprise webhook delivery platform. This means you get automatic retries, delivery guarantees, and signature verification out of the box.
Setting up webhooks
To start receiving webhooks, you need to register at least one endpoint. An endpoint is a URL on your server that will receive webhook POST requests from Vanta.
Adding an endpoint
- Navigate to Settings > Webhooks in the Vanta dashboard.
- Click Add Endpoint.
- Enter the URL of your endpoint (must be HTTPS).
- Select the event types you want to subscribe to, or leave it blank to receive all events.
- Click Create.
Browsing available events
The full list of available event types, along with their descriptions and payload schemas, is available directly in the Vanta dashboard under Settings > Webhooks. When adding or editing an endpoint, you can browse all events and select the ones relevant to your integration.
Tips for setting up your endpoint
- Your endpoint must be publicly accessible over HTTPS.
- It should return a
2xxstatus code within 15 seconds to acknowledge receipt. If it doesn't, the delivery will be marked as failed and retried. - Disable CSRF protection for your webhook endpoint, since webhook requests won't include CSRF tokens.
- Process webhook payloads asynchronously. Return a
2xximmediately, then handle the event in a background job or queue. This prevents timeouts on long-running operations. - Implement idempotent handling. Webhook delivery is "at least once," so your endpoint may receive the same event more than once. Use the
svix-idheader to deduplicate events. - Signature verification requires the raw request body as a string, not a parsed object. Make sure your framework preserves the raw body on the webhook route. For example, in Express use
express.raw({ type: 'application/json' })instead ofexpress.json().
Testing your endpoint
Before going to production, you should verify that your endpoint can receive and process webhooks correctly.
- Go to Settings > Webhooks in the Vanta dashboard.
- Select the endpoint you want to test.
- Navigate to the Testing tab.
- Choose an event type and click Send Example.
This will send a test message with an example payload to your endpoint, letting you confirm that your server handles it correctly.
Verifying webhook signatures
Webhook signatures let you verify that webhook messages are actually sent by Vanta and not by a malicious third party. While not strictly required, we strongly recommend verifying signatures in production.
Each webhook message includes three headers used for verification:
| Header | Description |
|---|---|
svix-id | The unique message identifier. |
svix-timestamp | The timestamp of the message attempt (seconds since epoch). |
svix-signature | The Base64-encoded signature(s), space-delimited. |
Using the Svix libraries (recommended)
The simplest way to verify signatures is to use the official Svix libraries. Install the library for your language and use the Webhook.verify method.
You can find your endpoint's signing secret in the Vanta webhook dashboard by clicking the endpoint and looking in the Signing Secret section.
Node.js
import { Webhook } from "svix";
const secret = "whsec_..."; // Your signing secret
const wh = new Webhook(secret);
app.post("/webhook", (req, res) => {
try {
const payload = wh.verify(req.body, req.headers);
// payload is the verified JSON body
console.log("Verified webhook:", payload);
res.status(200).send("OK");
} catch (err) {
console.error("Verification failed:", err.message);
res.status(400).send("Invalid signature");
}
});Python
from svix.webhooks import Webhook
secret = "whsec_..." # Your signing secret
wh = Webhook(secret)
def handle_webhook(request):
try:
payload = wh.verify(request.body, request.headers)
# payload is the verified dict
print("Verified webhook:", payload)
return HttpResponse(status=200)
except Exception as e:
print("Verification failed:", e)
return HttpResponse(status=400)Go
import svix "github.com/svix/svix-webhooks/go"
secret := "whsec_..." // Your signing secret
wh, _ := svix.NewWebhook(secret)
func handleWebhook(w http.ResponseWriter, r *http.Request) {
payload, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
err = wh.Verify(payload, r.Header)
if err != nil {
http.Error(w, "Invalid signature", http.StatusBadRequest)
return
}
// payload is verified
w.WriteHeader(http.StatusOK)
}Ruby
require "svix"
secret = "whsec_..." # Your signing secret
wh = Svix::Webhook.new(secret)
post "/webhook" do
begin
payload = wh.verify(request.body.read, request.env)
# payload is the verified hash
puts "Verified webhook: #{payload}"
status 200
rescue Svix::WebhookVerificationError => e
puts "Verification failed: #{e.message}"
status 400
end
endManual verification
If you prefer not to use a library, you can verify signatures manually:
- Extract the
svix-id,svix-timestamp, andsvix-signatureheaders. - Construct the signed content by concatenating:
{svix-id}.{svix-timestamp}.{body}(the raw request body as a string). - Base64-decode the signing secret (remove the
whsec_prefix first). - Compute an HMAC-SHA256 of the signed content using the decoded secret.
- Base64-encode the result and compare it against the signature(s) in the
svix-signatureheader (split by space, each prefixed withv1,).
You should also verify that the svix-timestamp is recent (within 5 minutes) to prevent replay attacks.
Retry schedule
If your endpoint fails to respond with a 2xx status code, Vanta will automatically retry the delivery using an exponential backoff schedule:
| Attempt | Delay after previous attempt |
|---|---|
| 1 | Immediately |
| 2 | 5 seconds |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 8 hours |
| 7 | 1 day |
| 8 | 2 days |
After all retry attempts are exhausted (approximately 5 days total), the message is marked as failed.
You can also manually retry failed messages from the webhook dashboard by navigating to the endpoint and clicking Retry on a specific message.
Note: A response is considered failed if the server doesn't respond with a
2xxstatus code within 15 seconds, including network timeouts.
Troubleshooting
Webhook requests are failing with 4xx errors
- Verify that your endpoint URL is correct and publicly accessible over HTTPS.
- Ensure that CSRF protection is disabled for the webhook endpoint.
- Check that your server is returning a
2xxstatus code.
Signature verification is failing
- Make sure you are using the raw request body (not a parsed JSON object) when verifying the signature.
- Confirm that the signing secret matches the one displayed in the webhook dashboard.
- Check that you haven't accidentally modified or re-serialized the request body before verification.
Webhook requests are timing out
Your endpoint must respond within 15 seconds. If your processing takes longer, acknowledge the webhook immediately with a 200 response and handle the event asynchronously in a background job or queue.
Recovering missed events
If your endpoint was down for an extended period, you can recover missed events through the webhook dashboard:
- Go to Settings > Webhooks.
- Select the affected endpoint.
- Browse the message history to find failed deliveries.
- Click Retry on individual messages, or use Bulk Retry to replay all failed messages within a time range.
Updated about 2 hours ago
