Webhooks
Webhooks push delivery events to your application as they happen, so you don't have to poll for message status.
Subscribing
Create a subscription with the URL to deliver to and the event types you care about:
Node.js
const sub = await client.webhooks.subscriptions.create({
url: "https://your.app/cavius-webhook",
eventTypes: ["message.delivered", "message.failed"],
});
console.log("Save this secret somewhere safe:", sub.signingSecret);
Python
sub = client.webhooks.subscriptions.create(
url="https://your.app/cavius-webhook",
event_types=["message.delivered", "message.failed"],
)
print("Save this secret somewhere safe:", sub["signingSecret"])
The
signingSecretis returned only when the subscription is created. Store it securely; you need it to verify incoming deliveries.
Event types
| Event | Fires when |
|---|---|
message.delivered | A message was delivered to the recipient |
message.failed | A message could not be delivered |
Verifying signatures
Every delivery is signed with HMAC-SHA256 over the raw request body,
using your subscription's signing secret. The signature arrives in the
X-Cavius-Signature header as sha256= followed by the lowercase hex
digest:
X-Cavius-Signature: sha256=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Always verify before trusting a payload, and compare in constant time:
Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyCaviusSignature(
rawBody: string,
header: string,
secret: string,
): boolean {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(header);
return a.length === b.length && timingSafeEqual(a, b);
}
// Express example: capture the raw body, not the parsed JSON
app.post("/cavius-webhook", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.header("X-Cavius-Signature") ?? "";
if (!verifyCaviusSignature(req.body.toString("utf8"), signature, process.env.CAVIUS_WEBHOOK_SECRET!)) {
return res.status(401).end();
}
const event = JSON.parse(req.body.toString("utf8"));
// handle event ...
res.status(200).end();
});
Python
import hashlib
import hmac
def verify_cavius_signature(raw_body: bytes, header: str, secret: str) -> bool:
digest = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(f"sha256={digest}", header)
Common pitfalls:
- Verify the raw bytes of the request body, exactly as received. Re-serializing parsed JSON changes whitespace and key order, and the signature will not match.
- Respond
2xxquickly and process the event asynchronously; slow responses count as delivery failures and trigger retries.
Inspecting deliveries
Past deliveries (payload, response code, attempts) are available via the
webhooks.deliveries resource in both SDKs and in the dashboard under
Developer → Webhooks.
See also
- Authentication: creating keys with
webhooks:write - API reference: subscription and delivery endpoints
