Webhooks
Receive real-time HTTP notifications when your APIs approach their rate limits. Point QuotaWatch at your endpoint and we'll POST a signed JSON payload whenever a threshold is crossed.
Setup
- Go to Settings → Email Alerts
- Create an alert and set Delivery to Webhook only or Email + Webhook
- Enter your endpoint URL
- Use the signing secret to verify incoming requests (see below)
Payload
Every webhook is a POST request with Content-Type: application/json.
POST /your-endpointjson
{
"event": "quota.threshold_reached",
"timestamp": "2026-05-08T12:00:00.000Z",
"data": {
"projectId": "550e8400-e29b-41d4-a716-446655440000",
"apiName": "OpenAI",
"environment": "production",
"limitType": "daily",
"thresholdPct": 80,
"currentCount": 8000,
"limitValue": 10000,
"pctUsed": 80.0,
"periodStart": "2026-05-08T00:00:00.000Z"
}
}Headers
| Header | Value |
|---|---|
| Content-Type | application/json |
| X-QuotaWatch-Signature | sha256=<hmac-signature> |
| X-QuotaWatch-Event | quota.threshold_reached |
| User-Agent | QuotaWatch-Webhooks/1.0 |
Verifying signatures
Every request is signed with HMAC-SHA256 using your WEBHOOK_SIGNING_SECRET. Always verify signatures in production.
Node.js
webhook-handler.tstypescript
import crypto from 'crypto';
function verifySignature(
body: string,
signature: string,
secret: string
): boolean {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
// Express example
app.post('/webhooks/quotawatch', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-quotawatch-signature'] as string;
const body = req.body.toString();
if (!verifySignature(body, signature, process.env.QUOTAWATCH_WEBHOOK_SECRET!)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(body);
console.log('Received:', event.event, event.data.apiName, event.data.pctUsed + '%');
res.json({ received: true });
});Python
webhook_handler.pypython
import hmac
import hashlib
from flask import Flask, request, jsonify
app = Flask(__name__)
def verify_signature(body: bytes, signature: str, secret: str) -> bool:
expected = 'sha256=' + hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
@app.post('/webhooks/quotawatch')
def handle_webhook():
signature = request.headers.get('X-QuotaWatch-Signature', '')
if not verify_signature(request.data, signature, QUOTAWATCH_WEBHOOK_SECRET):
return jsonify(error='Invalid signature'), 401
event = request.json
print(f"Received: {event['event']} — {event['data']['apiName']} at {event['data']['pctUsed']}%")
return jsonify(received=True)Retry behaviour
If your endpoint returns a non-2xx status or times out (10s), QuotaWatch retries up to 3 times with exponential backoff (2s, 4s, 8s). After 3 failures the alert is marked as failed — check your BullMQ dashboard or logs.
ℹ
Your endpoint should respond within 10 seconds and return any 2xx status. Process the event asynchronously if needed.
Events reference
| Event | Description |
|---|---|
| quota.threshold_reached | Fired once per period when usage crosses a configured threshold (80% or 95%). Will not fire again for the same API/threshold/period. |