Get Webhook Secret
The secret your webhooks are signed with, created on first request. It is separate from your API key, so a webhook receiver never holds a key that can spend.
Each delivery is a POST of {type, created_at, data} with data the job as GET /v1/videos/{id} or GET /v1/music/{id} returns it. Events: video., video., video., video., and the same four as music.generation.* for music jobs. Headers:
X-Vultr-Signature: t=<unix>,v1=<hex>, the HMAC-SHA256 of<t>.<raw body>with this secret. Reject old timestamps. During a rotation a secondv1=is signed with the previous secret.X-Vultr-Idempotency-Key: <job id>-<status>; retried deliveries repeat it.
A 2xx within 10 seconds is delivered; anything else is retried after 30 s, 2 min, 10 min, 1 h and 6 h. Deliveries can arrive more than once or out of order: use the idempotency key, and the job's status, rather than arrival order.
Verifying a delivery. Compute the HMAC over the raw request body, before any JSON parsing, with the whole secret including its whsec_ prefix. Accept the delivery when any v1= value matches.
import hashlib, hmac, time
def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
pairs = [part.split("=", 1) for part in header.split(",")]
t = next((v for k, v in pairs if k == "t"), None)
if t is None or abs(time.time() - int(t)) > tolerance:
return False
expected = hmac.new(secret.encode(), t.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return any(k == "v1" and hmac.compare_digest(expected, v) for k, v in pairs)const crypto = require("crypto");
function verify(rawBody, header, secret, tolerance = 300) {
const pairs = header.split(",").map((part) => part.split("="));
const t = pairs.find(([k]) => k === "t")?.[1];
if (!t || Math.abs(Date.now() / 1000 - Number(t)) > tolerance) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.`).update(rawBody).digest();
return pairs.some(([k, v]) => k === "v1" && v.length === 64 &&
crypto.timingSafeEqual(expected, Buffer.from(v, "hex")));
}Responses
OK
application/json · webhook-secret-payload
The API key is missing, unknown or not active. Send it as Authorization: Bearer <key>.
application/json · error-payload
curl https://api.vultrinference.com/v1/webhooks/secret \
-H "Authorization: Bearer $VULTR_INFERENCE_API_KEY"import os
import requests
response = requests.get(
"https://api.vultrinference.com/v1/webhooks/secret",
headers={"Authorization": f"Bearer {os.environ['VULTR_INFERENCE_API_KEY']}"},
)
print(response.json())const response = await fetch("https://api.vultrinference.com/v1/webhooks/secret", {
headers: {
Authorization: `Bearer ${process.env.VULTR_INFERENCE_API_KEY}`,
},
});
console.log(await response.json());{
"object": "webhook_secret",
"secret": "whsec_9a1c...",
"created_at": 1790000000,
"rotated_at": 1790000000,
"previous_secret_valid_until": 0
}