Appearance
Webhooks
Optional
Complete the Quick start first. Webhooks are for server-side message events.
Sendsar can send signed POST requests to your webhook handler on message events.
What you need
- A webhook endpoint on your server (publicly reachable)
- Your webhook secret (
whsec_…) stored server-side
Webhook secret is not an API key
Use the webhook secret only to verify webhook signatures. Do not send it in API headers.
Verify signatures
Sendsar sends a signature in the X-Webhook-Signature header:
- Format:
sha256=<hex> - Body: HMAC-SHA256 of the raw request body using your webhook secret
js
import { createHmac } from "node:crypto";
const expected =
"sha256=" +
createHmac("sha256", process.env.SENDSAR_WEBHOOK_SECRET)
.update(rawBody)
.digest("hex");
if (expected !== request.headers.get("x-webhook-signature")) {
throw new Error("Invalid webhook signature");
}php
$expected = 'sha256=' . hash_hmac(
'sha256',
$request->getContent(),
config('sendsar.webhook_secret')
);
if (!hash_equals($expected, $request->header('X-Webhook-Signature', ''))) {
abort(401, 'Invalid webhook signature');
}py
import hmac
import hashlib
expected = "sha256=" + hmac.new(
WEBHOOK_SECRET.encode(),
raw_body,
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(expected, request.headers.get("X-Webhook-Signature", "")):
raise HTTPException(status_code=401, detail="Invalid signature")go
mac := hmac.New(sha256.New, []byte(webhookSecret))
mac.Write(rawBody)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(expected), []byte(r.Header.Get("X-Webhook-Signature"))) {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
}ruby
expected = "sha256=" + OpenSSL::HMAC.hexdigest(
"SHA256",
ENV.fetch("SENDSAR_WEBHOOK_SECRET"),
request.raw_post
)
head :unauthorized unless ActiveSupport::SecurityUtils.secure_compare(
expected,
request.headers["X-Webhook-Signature"].to_s
)