What webhooks do
Webhooks push signed HTTP POST requests to a URL you control whenever a KavachOS auth event occurs. Use them to sync user records, trigger onboarding flows, alert on suspicious logins, or feed events into your analytics pipeline.
Setup
import { createKavach, createWebhookModule } from 'kavachos';
const kavach = await createKavach({
database: { provider: 'sqlite', url: 'kavach.db' },
plugins: [
createWebhookModule({
secret: process.env.KAVACH_WEBHOOK_SECRET,
endpoints: [
{
url: 'https://myapp.com/webhooks/kavach',
events: ['user.created', 'auth.login', 'agent.created'],
},
],
}),
],
});
Store the webhook secret in an environment variable, not in source code. KavachOS uses it to sign every request with HMAC-SHA256.
Subscribing to events
Each endpoint subscribes to one or more event types. Use '*' to receive all events.
createWebhookModule({
secret: process.env.KAVACH_WEBHOOK_SECRET,
endpoints: [
{
url: 'https://myapp.com/webhooks/all',
events: ['*'],
},
{
url: 'https://myapp.com/webhooks/agents',
events: ['agent.created', 'agent.revoked'],
},
],
});
Event reference
| Event | Fired when |
|---|
user.created | A new human user registers |
user.deleted | A user account is deleted |
auth.login | A successful login occurs |
auth.logout | A session is terminated |
auth.failed | A login attempt fails |
agent.created | A new agent identity is created |
agent.revoked | An agent is revoked |
delegation.granted | An agent receives a delegation grant |
delegation.revoked | A delegation grant is removed |
approval.requested | A human approval request is opened |
approval.resolved | An approval request is approved or denied |
Every webhook delivery includes these headers:
| Header | Value |
|---|
X-Kavach-Signature | sha256=<hmac>. HMAC-SHA256 of the raw body |
X-Kavach-Event | Event type, e.g. user.created |
X-Kavach-Delivery | Unique UUID for this delivery attempt |
X-Kavach-Timestamp | Unix timestamp (seconds) of delivery |
Verifying signatures
Always verify the signature before trusting the payload.
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyWebhook(rawBody: Buffer, signature: string, secret: string): boolean {
const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
// Express handler
app.post('/webhooks/kavach', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-kavach-signature'] as string;
if (!verifyWebhook(req.body, sig, process.env.KAVACH_WEBHOOK_SECRET!)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body.toString());
// handle event...
res.sendStatus(200);
});
async function verifyWebhook(rawBody: string, signature: string, secret: string): Promise<boolean> {
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign'],
);
const mac = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(rawBody));
const expected = 'sha256=' + Array.from(new Uint8Array(mac)).map(b => b.toString(16).padStart(2, '0')).join('');
return expected === signature;
}
Retry behavior
If your endpoint returns a non-2xx status or times out, KavachOS retries the delivery three times with exponential backoff:
| Attempt | Delay |
|---|
| 1 | 30 seconds |
| 2 | 5 minutes |
| 3 | 30 minutes |
After three failures the delivery is marked failed and no further retries occur.
Testing a webhook URL
Use the kavach.webhooks.test() method to send a synthetic ping event to any registered endpoint:
await kavach.webhooks.test('https://myapp.com/webhooks/kavach');
The test delivery sends { event: 'ping', timestamp: '...' } and respects the same signing and retry logic as real events.
Next steps
Lifecycle hooks
Run async callbacks on auth events inside the SDK process.
Audit log
Query the full record of every authorization decision.