Setting up your webhook URL
Configure your webhook endpoint in the Svara dashboard under Settings > Webhooks. Svara sends POST requests to this URL whenever a voice note status changes.
Your endpoint must:
- Accept
POSTrequests with a JSON body - Return a
200status code within 10 seconds - Be publicly accessible via HTTPS
If your endpoint returns a non-2xx status code or times out, Svara retries the delivery up to 5 times with exponential backoff (1s, 5s, 30s, 2min, 10min).
Event types
| Event | Description |
|---|---|
| voice_note.sent | The voice note was successfully delivered to the platform |
| voice_note.delivered | The recipient received/opened the voice note (where supported) |
| voice_note.failed | Delivery failed permanently after all retries |
Payload format
Every webhook request includes a JSON body with the event type and full message details:
{
"event": "voice_note.sent",
"timestamp": "2026-03-14T12:00:02Z",
"data": {
"id": "msg_a1b2c3d4e5f6",
"platform": "telegram",
"recipient": "123456789",
"status": "sent",
"created_at": "2026-03-14T12:00:00Z",
"delivered_at": "2026-03-14T12:00:02Z",
"error": null
}
}
For failed deliveries, the error field contains details:
{
"event": "voice_note.failed",
"timestamp": "2026-03-14T12:01:30Z",
"data": {
"id": "msg_x9y8z7w6v5u4",
"platform": "linkedin",
"recipient": "john-doe-12345",
"status": "failed",
"created_at": "2026-03-14T12:00:00Z",
"delivered_at": null,
"error": {
"code": "session_expired",
"message": "The LinkedIn session cookie has expired."
}
}
}
Signature verification
Every webhook request includes an X-Svara-Signature header containing an HMAC-SHA256 signature of the request body. Verify this signature to ensure the webhook was sent by Svara and has not been tampered with.
Your webhook secret is available in the dashboard under Settings > Webhooks.
Verification steps
- Read the raw request body as a string (do not parse JSON first).
- Compute an HMAC-SHA256 digest of the body using your webhook secret.
- Compare the computed signature to the
X-Svara-Signatureheader value. - Reject the request if the signatures do not match.
Example: Node.js webhook handler
import crypto from "crypto";
import express from "express";
const app = express();
const WEBHOOK_SECRET = process.env.SVARA_WEBHOOK_SECRET;
app.post("/webhooks/svara", express.raw({ type: "application/json" }), (req, res) => {
// Verify the signature
const signature = req.headers["x-svara-signature"];
const expectedSignature = crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(req.body)
.digest("hex");
if (signature !== expectedSignature) {
console.error("Invalid webhook signature");
return res.status(401).send("Invalid signature");
}
// Parse the event
const event = JSON.parse(req.body);
switch (event.event) {
case "voice_note.sent":
console.log(`Voice note ${event.data.id} delivered to ${event.data.platform}`);
break;
case "voice_note.delivered":
console.log(`Voice note ${event.data.id} opened by recipient`);
break;
case "voice_note.failed":
console.error(`Voice note ${event.data.id} failed: ${event.data.error.message}`);
// Retry, alert, or log the failure
break;
}
res.status(200).send("OK");
});
app.listen(3000);
Example: Python webhook handler
import hmac
import hashlib
import json
from flask import Flask, request
app = Flask(__name__)
WEBHOOK_SECRET = "your_webhook_secret"
@app.route("/webhooks/svara", methods=["POST"])
def handle_webhook():
# Verify the signature
signature = request.headers.get("X-Svara-Signature")
expected = hmac.new(
WEBHOOK_SECRET.encode(),
request.data,
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(signature, expected):
return "Invalid signature", 401
event = request.get_json()
if event["event"] == "voice_note.sent":
print(f"Delivered: {event['data']['id']}")
elif event["event"] == "voice_note.failed":
print(f"Failed: {event['data']['id']} - {event['data']['error']['message']}")
return "OK", 200
Testing webhooks locally
During development, use a tool like ngrok to expose your local server:
ngrok http 3000
Copy the HTTPS URL from ngrok and set it as your webhook URL in the Svara dashboard. You can also use sk_test_ keys to trigger simulated webhook events without sending real voice notes.