Webhooks
Receive real-time notifications for usage and billing events
Webhooks
Webhooks let you receive real-time HTTP notifications when specific events occur in your SoxAI account.
Configuring a Webhook
Console: Settings → Webhooks → Add Webhook
| Field | Description |
|---|---|
| URL | HTTPS endpoint to receive events |
| Events | Select which event types to subscribe to |
| Secret | Used to verify webhook signatures |
Event Types
| Event | Description |
|---|---|
quota.threshold | A quota window has reached a configured percentage (e.g. 80%) |
quota.exceeded | A quota window has been fully consumed |
balance.low | Account balance has dropped below a threshold |
balance.depleted | Account balance has reached zero |
request.completed | A single API request completed (high-volume, use carefully) |
Webhook Payload
All events follow this structure:
{
"id": "evt_01jq4abc",
"type": "quota.threshold",
"created_at": "2026-03-31T12:00:00Z",
"tenant_id": 1,
"data": {
"window": "1h",
"dimension": "input_tokens",
"used": 80000,
"limit": 100000,
"percentage": 80,
"resets_at": "2026-03-31T12:47:00Z"
}
}Verifying Signatures
SoxAI signs every webhook with an HMAC-SHA256 signature using your webhook secret. Always verify this in production:
import hashlib
import hmac
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = "whsec_your-secret-here"
@app.route("/webhook", methods=["POST"])
def handle_webhook():
signature = request.headers.get("X-SoxAI-Signature")
timestamp = request.headers.get("X-SoxAI-Timestamp")
if not signature or not timestamp:
abort(400)
# Prevent replay attacks — reject events older than 5 minutes
import time
if abs(time.time() - int(timestamp)) > 300:
abort(400)
# Verify signature
payload = f"{timestamp}.{request.get_data(as_text=True)}"
expected = hmac.new(
WEBHOOK_SECRET.encode(),
payload.encode(),
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(f"sha256={expected}", signature):
abort(403)
event = request.get_json()
handle_event(event)
return "", 200
def handle_event(event):
if event["type"] == "quota.exceeded":
send_alert_to_slack(event)
elif event["type"] == "balance.low":
trigger_auto_topup(event)Node.js Verification
import crypto from "crypto";
import express from "express";
const app = express();
const WEBHOOK_SECRET = process.env.SOXAI_WEBHOOK_SECRET!;
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-soxai-signature"] as string;
const timestamp = req.headers["x-soxai-timestamp"] as string;
const payload = `${timestamp}.${req.body.toString()}`;
const expected = crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(payload)
.digest("hex");
if (`sha256=${expected}` !== signature) {
return res.status(403).send("Invalid signature");
}
const event = JSON.parse(req.body.toString());
console.log("Event:", event.type);
res.sendStatus(200);
});Retry Policy
If your endpoint returns a non-2xx response or times out (timeout: 30 seconds), SoxAI retries the delivery with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 5 minutes |
| 2nd retry | 30 minutes |
| 3rd retry | 2 hours |
| 4th retry | 12 hours |
After 4 failed retries, the event is marked as permanently failed. You can view failed deliveries and manually replay them in the Console under Settings → Webhooks → [Webhook] → Deliveries.
Testing Webhooks
Use the Send Test Event button in the Console to send a sample payload to your endpoint. This helps verify your signature verification code before going live.