Skip to content
CaviusConnect
Documentation menu

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 signingSecret is returned only when the subscription is created. Store it securely; you need it to verify incoming deliveries.

Event types

EventFires when
message.deliveredA message was delivered to the recipient
message.failedA 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 2xx quickly 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