Signed webhooks
Signed webhooks
Last verified: 2026-06-16.
The signed webhook channel delivers a JSON event to a URL you control whenever a covered event occurs — for streaming sign-in events into a SIEM or automation. This is a paid feature.
Configure an endpoint
Under Awthy Security → Notifications (administrators only):
- Add a destination URL (HTTPS).
- Provide a signing secret. The secret is encrypted at rest and is write-only — it is never shown again after you save it.
- Use Send test to deliver a signed test event and confirm your endpoint verifies it.
Request format
Each delivery is an HTTP POST with a JSON body and these headers:
Awthy-Webhook-Id— a stable idempotency id. The same id is reused across retries of the same event, so your receiver can deduplicate.Awthy-Signature—t=<unix-timestamp>,v1=<hex-hmac>.
The signature is HMAC-SHA256 over the exact string timestamp.id.raw_body, using your signing secret.
Verify against the raw request body bytes — do not re-serialize the JSON first, or the signature will not match.
Verifying a request (Node.js)
import crypto from 'node:crypto';
function verify(req, rawBody, secret) {
const sigHeader = req.headers['awthy-signature'] ?? '';
const id = req.headers['awthy-webhook-id'] ?? '';
const parts = Object.fromEntries(sigHeader.split(',').map((p) => p.split('=')));
const timestamp = parts.t;
const presented = parts.v1;
// Reject stale deliveries (replay protection): ~5 minute tolerance.
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${id}.${rawBody}`)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(presented));
}
Use a constant-time comparison (timingSafeEqual) so verification does not leak the secret through timing.
Retry behavior
If your endpoint does not return a 2xx status, Awthy retries with exponential backoff, up to three attempts. Each
attempt is signed with a fresh timestamp, but the request body and Awthy-Webhook-Id stay the same — so deduplicate on
the webhook id, not the timestamp.