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

  1. Go to Settings → Email Alerts
  2. Create an alert and set Delivery to Webhook only or Email + Webhook
  3. Enter your endpoint URL
  4. 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

HeaderValue
Content-Typeapplication/json
X-QuotaWatch-Signaturesha256=<hmac-signature>
X-QuotaWatch-Eventquota.threshold_reached
User-AgentQuotaWatch-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

EventDescription
quota.threshold_reachedFired once per period when usage crosses a configured threshold (80% or 95%). Will not fire again for the same API/threshold/period.