Skip to main content
Instead of receiving Qwairy emails, a white-label team can have notifications POSTed to its own endpoint as signed JSON events — so you deliver them to your users under your own brand, from your own systems. When a webhook is enabled, the matching Qwairy email is not sent.
Outbound webhooks are part of Agencies, an Enterprise capability. The controls are OWNER-only.

Events

report.weekly

A workspace’s weekly report.

export.ready

A data export has finished and is ready to download.

credits.low

The team is projected to run out of credits before renewal.
A webhook.test event is also sent by the Send test event button so you can validate your endpoint before turning delivery on.

Set It Up

1

Open Notifications

Go to Team Management > Notifications and find Outbound webhooks.
2

Add your endpoint URL

Enter a public https URL (private addresses and http are rejected) and save. A signing secret is generated and shown once — copy and store it now.
3

Send a test event

Click Send test event to confirm Qwairy can reach your endpoint and your signature check passes.
4

Enable delivery

Toggle Deliver via webhook on. From now on, the events above go to your endpoint instead of being emailed.
You can rotate the secret at any time with Regenerate secret (the new value is shown once), and remove the webhook to fall back to email.

Payload

Every request is a POST with a JSON envelope:
{
  "event": "credits.low",
  "id": "8b40e94f-79f4-40cc-8d38-bde11ef0d8d9",
  "createdAt": "2026-06-18T09:00:00.000Z",
  "team": { "id": "team_abc123", "name": "Acme" },
  "data": {
    "creditsRemaining": 120,
    "renewalDate": "2026-07-01T00:00:00.000Z"
  }
}
The data object is event-specific. Treat it as forward-compatible: new fields may be added, so ignore the ones you don’t use rather than rejecting the payload.

Headers

HeaderValue
X-Qwairy-Eventthe event name, e.g. credits.low
X-Qwairy-TimestampUnix time (seconds) when the request was signed
X-Qwairy-Signaturesha256=<hex> HMAC of the request

Verifying the Signature

The signature is an HMAC-SHA256, keyed with your signing secret, over the timestamp and the raw request body joined with a dot — <timestamp>.<body> — using the raw bytes, before any JSON parsing.
import crypto from 'crypto';

function isValidQwairyWebhook(req, secret) {
  const signature = req.headers['x-qwairy-signature']; // "sha256=<hex>"
  const timestamp = req.headers['x-qwairy-timestamp'];

  // Reject stale requests to prevent replay (e.g. older than 5 minutes).
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;

  const expected =
    'sha256=' +
    crypto
      .createHmac('sha256', secret)
      .update(`${timestamp}.${req.rawBody}`)
      .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
Verify the signature on every request and reject anything that fails. Use a constant-time comparison, and reject timestamps outside a short window to block replays.

Delivery Behavior

  • Qwairy expects a 2xx response. Non-2xx, timeouts, and redirects count as failures.
  • For the weekly report and low-credit events, a failed delivery is retried on the next scheduled run; the matching email is not sent in its place.
  • The export-ready event is one-shot: if delivery fails, the export still appears in your workspace’s Exports hub.