Webhooks
Receive real-time notifications for authentication events.
Overview
Webhooks notify your application when authentication events occur. Use webhooks to sync user data, trigger workflows, and audit authentication activity.
Event Types
User Events
| Event | Description |
|---|---|
user.created | New user account created |
user.updated | User profile or settings changed |
user.deleted | User account deleted |
user.blocked | User account blocked |
user.unblocked | User account unblocked |
Authentication Events
| Event | Description |
|---|---|
login.success | Successful login |
login.failed | Failed login attempt |
logout | User logged out |
session.created | New session started |
session.revoked | Session ended |
Password Events
| Event | Description |
|---|---|
password.reset | Password was reset |
password.changed | User changed password |
password.reset_requested | Password reset email sent |
MFA Events
| Event | Description |
|---|---|
mfa.enrolled | MFA factor enrolled |
mfa.removed | MFA factor removed |
mfa.challenge.success | MFA challenge passed |
mfa.challenge.failed | MFA challenge failed |
Organization Events
| Event | Description |
|---|---|
organization.created | Organization created |
organization.updated | Organization updated |
organization.deleted | Organization deleted |
organization.member.added | Member added |
organization.member.removed | Member removed |
organization.invitation.sent | Invitation sent |
organization.invitation.accepted | Invitation accepted |
Token Events
| Event | Description |
|---|---|
token.created | New token issued |
token.revoked | Token revoked |
token.refreshed | Token refreshed |
Creating Webhooks
Via Dashboard
- Go to Auth > Webhooks
- Click Create Webhook
- Enter the webhook URL
- Select events to subscribe to
- Copy the signing secret
Via API
TypeScript:
const webhook = await client.auth.webhooks.create({
url: 'https://yourapp.com/webhooks/auth',
events: [
'user.created',
'login.success',
'login.failed',
'mfa.enrolled',
],
headers: {
'X-Custom-Header': 'custom-value',
},
});
console.log('Webhook ID:', webhook.id);
console.log('Secret:', webhook.secret);Python:
webhook = client.auth.webhooks.create(
url="https://yourapp.com/webhooks/auth",
events=[
"user.created",
"login.success",
"login.failed",
],
)
print(f"Webhook ID: {webhook.id}")
print(f"Secret: {webhook.secret}")Webhook Payload
Format
{
"id": "evt_xxx",
"type": "user.created",
"timestamp": "2024-01-01T12:00:00.000Z",
"data": {
"user": {
"id": "user_xxx",
"email": "user@example.com",
"createdAt": "2024-01-01T12:00:00.000Z"
}
},
"metadata": {
"ip": "203.0.113.1",
"userAgent": "Mozilla/5.0..."
}
}Event-Specific Data
user.created:
{
"type": "user.created",
"data": {
"user": {
"id": "user_xxx",
"email": "user@example.com",
"emailVerified": false,
"profile": {
"firstName": "John",
"lastName": "Doe"
}
}
}
}login.success:
{
"type": "login.success",
"data": {
"user": {
"id": "user_xxx",
"email": "user@example.com"
},
"session": {
"id": "session_xxx"
},
"connection": "email", // or "google", "saml", etc.
},
"metadata": {
"ip": "203.0.113.1",
"userAgent": "Mozilla/5.0...",
"location": {
"country": "US",
"city": "San Francisco"
}
}
}login.failed:
{
"type": "login.failed",
"data": {
"email": "user@example.com",
"reason": "INVALID_PASSWORD",
"attemptCount": 3
},
"metadata": {
"ip": "203.0.113.1"
}
}mfa.enrolled:
{
"type": "mfa.enrolled",
"data": {
"user": {
"id": "user_xxx",
"email": "user@example.com"
},
"factor": {
"id": "factor_xxx",
"type": "TOTP",
"name": "Authenticator App"
}
}
}Verifying Webhooks
All webhooks are signed with HMAC-SHA256. Verify signatures to ensure authenticity.
Signature Header
X-Transactional-Signature: sha256=xxxxxxxxxxxx
X-Transactional-Timestamp: 1704067200
Verification Code
TypeScript:
import crypto from 'crypto';
function verifyWebhook(
payload: string,
signature: string,
timestamp: string,
secret: string
): boolean {
// Check timestamp (within 5 minutes)
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - parseInt(timestamp)) > 300) {
return false;
}
// Compute expected signature
const signedPayload = `${timestamp}.${payload}`;
const expected = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// Compare signatures (timing-safe)
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expected}`)
);
}
// Express middleware
app.post('/webhooks/auth', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-transactional-signature'];
const timestamp = req.headers['x-transactional-timestamp'];
if (!verifyWebhook(req.body.toString(), signature, timestamp, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
handleWebhookEvent(event);
res.status(200).send('OK');
});Python:
import hmac
import hashlib
import time
def verify_webhook(payload: str, signature: str, timestamp: str, secret: str) -> bool:
# Check timestamp
now = int(time.time())
if abs(now - int(timestamp)) > 300:
return False
# Compute expected signature
signed_payload = f"{timestamp}.{payload}"
expected = hmac.new(
secret.encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()
# Compare signatures
return hmac.compare_digest(signature, f"sha256={expected}")Handling Webhooks
Best Practices
- Respond quickly - Return 200 within 30 seconds
- Process asynchronously - Queue events for later processing
- Handle duplicates - Events may be delivered multiple times
- Verify signatures - Always validate webhook authenticity
Example Handler
app.post('/webhooks/auth', async (req, res) => {
// Verify signature first
if (!verifySignature(req)) {
return res.status(401).send('Invalid signature');
}
const event = req.body;
// Acknowledge receipt immediately
res.status(200).send('OK');
// Process asynchronously
await processEventAsync(event);
});
async function processEventAsync(event) {
switch (event.type) {
case 'user.created':
await syncUserToDatabase(event.data.user);
break;
case 'login.success':
await recordLoginActivity(event.data);
break;
case 'login.failed':
await checkForBruteForce(event.data);
break;
case 'mfa.enrolled':
await updateSecurityScore(event.data.user);
break;
}
}Retry Logic
Failed webhook deliveries are retried automatically.
Retry Schedule
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
After 5 failed attempts, the webhook delivery is marked as failed.
View Delivery Logs
const deliveries = await client.auth.webhooks.listDeliveries('webhook_xxx', {
status: 'FAILED',
count: 50,
});
for (const delivery of deliveries) {
console.log(`Event: ${delivery.eventType}`);
console.log(`Status: ${delivery.status}`);
console.log(`Response: ${delivery.responseStatus}`);
console.log(`Error: ${delivery.error}`);
}Retry Manually
await client.auth.webhooks.retryDelivery('delivery_xxx');Managing Webhooks
List Webhooks
const webhooks = await client.auth.webhooks.list();
for (const webhook of webhooks) {
console.log(`${webhook.url}: ${webhook.isActive ? 'Active' : 'Inactive'}`);
}Update Webhook
await client.auth.webhooks.update('webhook_xxx', {
events: ['user.created', 'user.updated'],
isActive: true,
});Delete Webhook
await client.auth.webhooks.delete('webhook_xxx');Rotate Secret
const { secret } = await client.auth.webhooks.rotateSecret('webhook_xxx');
console.log('New secret:', secret);Testing Webhooks
Send Test Event
await client.auth.webhooks.test('webhook_xxx', {
eventType: 'user.created',
});Local Development
For local testing, use a tunnel service:
- Install ngrok:
npm install -g ngrok - Start tunnel:
ngrok http 3000 - Use ngrok URL as webhook endpoint
Next Steps
- Security - Configure security policies
- API Reference - Complete API documentation
On This Page
- Overview
- Event Types
- User Events
- Authentication Events
- Password Events
- MFA Events
- Organization Events
- Token Events
- Creating Webhooks
- Via Dashboard
- Via API
- Webhook Payload
- Format
- Event-Specific Data
- Verifying Webhooks
- Signature Header
- Verification Code
- Handling Webhooks
- Best Practices
- Example Handler
- Retry Logic
- Retry Schedule
- View Delivery Logs
- Retry Manually
- Managing Webhooks
- List Webhooks
- Update Webhook
- Delete Webhook
- Rotate Secret
- Testing Webhooks
- Send Test Event
- Local Development
- Next Steps